feature #15738 Implement service-based Resource (cache) validation (mpdude)
This PR was squashed before being merged into the 2.8 branch (closes #15738).
Discussion
----------
Implement service-based Resource (cache) validation
| Q | A
| ------------- | ---
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | yes
| Tests pass? | yes
| Fixed tickets | #7230, #15692, #7782
| License | MIT
| Doc PR | symfony/symfony-docs#5136
### Overview
Currently, any metadata passed to `ConfigCache` (namely implementations of `ResourceInterface`) is serialized to disk. When the `ConfigCache` is validated, the metadata is unserialized and queried through `ResourceInterface::isFresh()` to determine whether the cache is fresh. That way, `ResourceInterface` implementations cannot interact with services, for example a database connection.
This PR introduces the new concept of `ResourceCheckers`. Services implementing `ResourceCheckerInterface` can be tagged as `config_cache.resource_checker` with an optional priority.
Clients that wish to use `ConfigCache` can then obtain an instance from the `config_cache_factory` service (which implements `ConfigCacheFactoryInterface`). The factory will take care of injecting resource checkers into the `ConfigCache` instance so that they can be used for cache validation.
Checking cache metadata is easy for `ResourceCheckers`:
* First, the `ResourceCheckerInterface::supports()` implementation is passed the metadata object in question. If the checker cannot handle the type of resource passed, `supports()` should return `false`.
* Otherwise, the `ResourceCheckerInterface::isFresh()` method will be called and given the resource as well as the timestamp at which the cache was initialized. If that method returns `false`, the cache is considered stale. If it returns `true`, the resource is considered unchanged and will *not* be passed to any additional checkers.
### BC and migration path
This PR does not (intend to) break BC but it comes with deprecations. The main reason is that `ResourceInterface` contains an `isFresh()` method that does not make sense in the general case of resources.
Thus, `ResourceInterface::isFresh()` is marked as deprecated and should be removed in Symfony 3.0. Resource implementations that can (or wish to) be validated in that simple manner can implement the `SelfCheckingResourceInterface` sub-interface that still contains (and will keep) the `isFresh()` method. The change should be as simple as changing the `extends` list.
Apart from that, `ResourceInterface` will be kept as the base interface for resource implementations. It is used in several `@api` interfaces and thus cannot easily be substituted.
For the Symfony 2.x series, a `BCResourceInterfaceChecker` will be kept that performs validation through `ResourceInterface::isFresh()` but will trigger a deprecation warning. The remedy is to either implement a custom ResourceChecker with a priority higher than -1000; or to switch to the aforementioned `SelfCheckingResourceInterface` which is used at a priority of -990 (without deprecation warning).
The `ConfigCache` and `ConfigCacheFactory` classes can be used as previously but do not feature checker-based cache validation.
### Outlook and closing remarks:
This PR supersedes #7230, #15692 and works at least in parts towards the goal of #7176.
The `ResourceCheckerInterface`, `...ConfigCache` and `...ConfigCacheFactory` no longer need to be aware of the `debug` flag. The different validation rules applied previously are now just a matter of `ResourceChecker` configuration (i. e. "no checkers" in `prod`).
It might be possible to remove the `debug` flag from Symfony's `Router` and/or `Translator` classes in the future as well because it was only passed on to the `ConfigCache` there.
Commits
-------
20d3722
Implement service-based Resource (cache) validation
This commit is contained in:
commit
d60428c9ca
@ -442,3 +442,34 @@ Security
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Config
|
||||||
|
------
|
||||||
|
|
||||||
|
* The `\Symfony\Component\Config\Resource\ResourceInterface::isFresh()` method has been
|
||||||
|
deprecated and will be removed in Symfony 3.0 because it assumes that resource
|
||||||
|
implementations are able to check themselves for freshness.
|
||||||
|
|
||||||
|
If you have custom resources that implement this method, change them to implement the
|
||||||
|
`\Symfony\Component\Config\Resource\SelfCheckingResourceInterface` sub-interface instead
|
||||||
|
of `\Symfony\Component\Config\Resource\ResourceInterface`.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Symfony\Component\Config\Resource\ResourceInterface;
|
||||||
|
|
||||||
|
class MyCustomResource implements ResourceInterface { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```php
|
||||||
|
use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
|
||||||
|
|
||||||
|
class MyCustomResource implements SelfCheckingResourceInterface { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, if you have implemented cache validation strategies *using* `isFresh()`
|
||||||
|
yourself, you should have a look at the new cache validation system based on
|
||||||
|
`ResourceChecker`s.
|
||||||
|
@ -1201,3 +1201,10 @@ UPGRADE FROM 2.x to 3.0
|
|||||||
* `Process::setStdin()` and `Process::getStdin()` have been removed. Use
|
* `Process::setStdin()` and `Process::getStdin()` have been removed. Use
|
||||||
`Process::setInput()` and `Process::getInput()` that works the same way.
|
`Process::setInput()` and `Process::getInput()` that works the same way.
|
||||||
* `Process::setInput()` and `ProcessBuilder::setInput()` do not accept non-scalar types.
|
* `Process::setInput()` and `ProcessBuilder::setInput()` do not accept non-scalar types.
|
||||||
|
|
||||||
|
### Config
|
||||||
|
|
||||||
|
* `\Symfony\Component\Config\Resource\ResourceInterface::isFresh()` has been removed. Also,
|
||||||
|
cache validation through this method (which was still supported in 2.8 for BC) does no longer
|
||||||
|
work because the `\Symfony\Component\Config\Resource\BCResourceInterfaceChecker` helper class
|
||||||
|
has been removed as well.
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
<?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 => $tags) {
|
||||||
|
$priority = isset($tags[0]['priority']) ? $tags[0]['priority'] : 0;
|
||||||
|
$resourceCheckers[$priority][] = new Reference($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($resourceCheckers)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort by priority and flatten
|
||||||
|
krsort($resourceCheckers);
|
||||||
|
$resourceCheckers = call_user_func_array('array_merge', $resourceCheckers);
|
||||||
|
|
||||||
|
$container->getDefinition('config_cache_factory')->replaceArgument(0, $resourceCheckers);
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CompilerDebugDum
|
|||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationExtractorPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationExtractorPass;
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationDumperPass;
|
||||||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass;
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass;
|
||||||
use Symfony\Component\Debug\ErrorHandler;
|
use Symfony\Component\Debug\ErrorHandler;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
|
||||||
@ -92,6 +93,7 @@ class FrameworkBundle extends Bundle
|
|||||||
if ($container->getParameter('kernel.debug')) {
|
if ($container->getParameter('kernel.debug')) {
|
||||||
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
|
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||||
$container->addCompilerPass(new CompilerDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
|
$container->addCompilerPass(new CompilerDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
|
||||||
|
$container->addCompilerPass(new ConfigCachePass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,9 @@
|
|||||||
</argument>
|
</argument>
|
||||||
<argument type="service" id="router.request_context" on-invalid="ignore" />
|
<argument type="service" id="router.request_context" on-invalid="ignore" />
|
||||||
<argument type="service" id="logger" on-invalid="ignore" />
|
<argument type="service" id="logger" on-invalid="ignore" />
|
||||||
|
<call method="setConfigCacheFactory">
|
||||||
|
<argument type="service" id="config_cache_factory" />
|
||||||
|
</call>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="router" alias="router.default" />
|
<service id="router" alias="router.default" />
|
||||||
|
@ -63,5 +63,21 @@
|
|||||||
<service id="uri_signer" class="%uri_signer.class%">
|
<service id="uri_signer" class="%uri_signer.class%">
|
||||||
<argument>%kernel.secret%</argument>
|
<argument>%kernel.secret%</argument>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="config_cache_factory" class="Symfony\Component\Config\ResourceCheckerConfigCacheFactory">
|
||||||
|
<argument type="collection"></argument>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service class="Symfony\Component\Config\Resource\SelfCheckingResourceChecker" public="false">
|
||||||
|
<tag name="config_cache.resource_checker" priority="-990" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This service is deprecated and will be removed in 3.0.
|
||||||
|
-->
|
||||||
|
<service class="Symfony\Component\Config\Resource\BCResourceInterfaceChecker" public="false">
|
||||||
|
<tag name="config_cache.resource_checker" priority="-1000" />
|
||||||
|
</service>
|
||||||
|
|
||||||
</services>
|
</services>
|
||||||
</container>
|
</container>
|
||||||
|
@ -45,6 +45,9 @@
|
|||||||
<argument key="debug">%kernel.debug%</argument>
|
<argument key="debug">%kernel.debug%</argument>
|
||||||
</argument>
|
</argument>
|
||||||
<argument type="collection" /> <!-- translation resources -->
|
<argument type="collection" /> <!-- translation resources -->
|
||||||
|
<call method="setConfigCacheFactory">
|
||||||
|
<argument type="service" id="config_cache_factory" />
|
||||||
|
</call>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="translator.logging" class="Symfony\Component\Translation\LoggingTranslator" public="false">
|
<service id="translator.logging" class="Symfony\Component\Translation\LoggingTranslator" public="false">
|
||||||
|
@ -5,7 +5,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand;
|
|||||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
use Symfony\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand\Fixture\TestAppKernel;
|
use Symfony\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand\Fixture\TestAppKernel;
|
||||||
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
|
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\Config\Resource\ResourceInterface;
|
||||||
use Symfony\Component\Console\Input\ArrayInput;
|
use Symfony\Component\Console\Input\ArrayInput;
|
||||||
use Symfony\Component\Console\Output\NullOutput;
|
use Symfony\Component\Console\Output\NullOutput;
|
||||||
@ -47,15 +47,13 @@ class CacheClearCommandTest extends TestCase
|
|||||||
$metaFiles = $finder->files()->in($this->kernel->getCacheDir())->name('*.php.meta');
|
$metaFiles = $finder->files()->in($this->kernel->getCacheDir())->name('*.php.meta');
|
||||||
// simply check that cache is warmed up
|
// simply check that cache is warmed up
|
||||||
$this->assertGreaterThanOrEqual(1, count($metaFiles));
|
$this->assertGreaterThanOrEqual(1, count($metaFiles));
|
||||||
|
$configCacheFactory = new ConfigCacheFactory(true);
|
||||||
|
$that = $this;
|
||||||
|
|
||||||
foreach ($metaFiles as $file) {
|
foreach ($metaFiles as $file) {
|
||||||
$configCache = new ConfigCache(substr($file, 0, -5), true);
|
$configCacheFactory->cache(substr($file, 0, -5), function () use ($that, $file) {
|
||||||
$this->assertTrue(
|
$that->fail(sprintf('Meta file "%s" is not fresh', (string) $file));
|
||||||
$configCache->isFresh(),
|
});
|
||||||
sprintf(
|
|
||||||
'Meta file "%s" is not fresh',
|
|
||||||
(string) $file
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that app kernel file present in meta file of container's cache
|
// check that app kernel file present in meta file of container's cache
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
<?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('replaceArgument')
|
||||||
|
->with(0, array(
|
||||||
|
new Reference('checker_1'),
|
||||||
|
new Reference('checker_2'),
|
||||||
|
new Reference('checker_3'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$pass = new ConfigCachePass();
|
||||||
|
$pass->process($container);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testThatCheckersCanBeMissing()
|
||||||
|
{
|
||||||
|
$definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
|
||||||
|
$container = $this->getMock(
|
||||||
|
'Symfony\Component\DependencyInjection\ContainerBuilder',
|
||||||
|
array('findTaggedServiceIds')
|
||||||
|
);
|
||||||
|
|
||||||
|
$container->expects($this->atLeastOnce())
|
||||||
|
->method('findTaggedServiceIds')
|
||||||
|
->will($this->returnValue(array()));
|
||||||
|
|
||||||
|
$pass = new ConfigCachePass();
|
||||||
|
$pass->process($container);
|
||||||
|
}
|
||||||
|
}
|
@ -250,7 +250,7 @@ abstract class FrameworkExtensionTest extends TestCase
|
|||||||
);
|
);
|
||||||
|
|
||||||
$calls = $container->getDefinition('translator.default')->getMethodCalls();
|
$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()
|
public function testTranslatorMultipleFallbacks()
|
||||||
@ -258,7 +258,7 @@ abstract class FrameworkExtensionTest extends TestCase
|
|||||||
$container = $this->createContainerFromFile('translator_fallbacks');
|
$container = $this->createContainerFromFile('translator_fallbacks');
|
||||||
|
|
||||||
$calls = $container->getDefinition('translator.default')->getMethodCalls();
|
$calls = $container->getDefinition('translator.default')->getMethodCalls();
|
||||||
$this->assertEquals(array('en', 'fr'), $calls[0][1][0]);
|
$this->assertEquals(array('en', 'fr'), $calls[1][1][0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,34 +104,6 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$translator->trans('foo');
|
$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()
|
public function testLoadResourcesWithoutCaching()
|
||||||
{
|
{
|
||||||
$loader = new \Symfony\Component\Translation\Loader\YamlFileLoader();
|
$loader = new \Symfony\Component\Translation\Loader\YamlFileLoader();
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
"php": ">=5.3.9",
|
"php": ">=5.3.9",
|
||||||
"symfony/asset": "~2.7|~3.0.0",
|
"symfony/asset": "~2.7|~3.0.0",
|
||||||
"symfony/dependency-injection": "~2.8",
|
"symfony/dependency-injection": "~2.8",
|
||||||
"symfony/config": "~2.4",
|
"symfony/config": "~2.8",
|
||||||
"symfony/event-dispatcher": "~2.8|~3.0.0",
|
"symfony/event-dispatcher": "~2.8|~3.0.0",
|
||||||
"symfony/http-foundation": "~2.4.9|~2.5,>=2.5.4|~3.0.0",
|
"symfony/http-foundation": "~2.4.9|~2.5,>=2.5.4|~3.0.0",
|
||||||
"symfony/http-kernel": "~2.8",
|
"symfony/http-kernel": "~2.8",
|
||||||
|
@ -21,6 +21,9 @@ distinct elements).
|
|||||||
After: the code will work as expected and it will restrict the values of the
|
After: the code will work as expected and it will restrict the values of the
|
||||||
`variable` option to just `value`.
|
`variable` option to just `value`.
|
||||||
|
|
||||||
|
* deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they
|
||||||
|
can be validated that way, make them implement the new `SelfCheckingResourceInterface`.
|
||||||
|
|
||||||
2.7.0
|
2.7.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
@ -11,22 +11,27 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Config;
|
namespace Symfony\Component\Config;
|
||||||
|
|
||||||
use Symfony\Component\Config\Resource\ResourceInterface;
|
use Symfony\Component\Config\Resource\BCResourceInterfaceChecker;
|
||||||
use Symfony\Component\Filesystem\Exception\IOException;
|
use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;
|
||||||
use Symfony\Component\Filesystem\Filesystem;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ConfigCache manages PHP cache files.
|
* ConfigCache caches arbitrary content in files on disk.
|
||||||
*
|
*
|
||||||
* When debug is enabled, it knows when to flush the cache
|
* When in debug mode, those metadata resources that implement
|
||||||
* thanks to an array of ResourceInterface instances.
|
* \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 Fabien Potencier <fabien@symfony.com>
|
||||||
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
*/
|
*/
|
||||||
class ConfigCache implements ConfigCacheInterface
|
class ConfigCache extends ResourceCheckerConfigCache
|
||||||
{
|
{
|
||||||
private $debug;
|
private $debug;
|
||||||
private $file;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $file The absolute cache path
|
* @param string $file The absolute cache path
|
||||||
@ -34,7 +39,10 @@ class ConfigCache implements ConfigCacheInterface
|
|||||||
*/
|
*/
|
||||||
public function __construct($file, $debug)
|
public function __construct($file, $debug)
|
||||||
{
|
{
|
||||||
$this->file = $file;
|
parent::__construct($file, array(
|
||||||
|
new SelfCheckingResourceChecker(),
|
||||||
|
new BCResourceInterfaceChecker(),
|
||||||
|
));
|
||||||
$this->debug = (bool) $debug;
|
$this->debug = (bool) $debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,90 +57,23 @@ class ConfigCache implements ConfigCacheInterface
|
|||||||
{
|
{
|
||||||
@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);
|
@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;
|
return $this->getPath();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the cache file path.
|
|
||||||
*
|
|
||||||
* @return string The cache file path
|
|
||||||
*/
|
|
||||||
public function getPath()
|
|
||||||
{
|
|
||||||
return $this->file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the cache is still fresh.
|
* 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.
|
* cache file exists.
|
||||||
*
|
*
|
||||||
* @return bool true if the cache is fresh, false otherwise
|
* @return bool true if the cache is fresh, false otherwise
|
||||||
*/
|
*/
|
||||||
public function isFresh()
|
public function isFresh()
|
||||||
{
|
{
|
||||||
if (!is_file($this->file)) {
|
if (!$this->debug && is_file($this->getPath())) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->debug) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$metadata = $this->getMetaFile();
|
return parent::isFresh();
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,11 @@
|
|||||||
namespace Symfony\Component\Config;
|
namespace Symfony\Component\Config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic implementation for ConfigCacheFactoryInterface
|
* Basic implementation of ConfigCacheFactoryInterface that
|
||||||
* that will simply create an instance of ConfigCache.
|
* creates an instance of the default ConfigCache.
|
||||||
|
*
|
||||||
|
* This factory and/or cache <em>do not</em> support cache validation
|
||||||
|
* by means of ResourceChecker instances (that is, service-based).
|
||||||
*
|
*
|
||||||
* @author Matthias Pigulla <mp@webfactory.de>
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
*/
|
*/
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
<?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\Config\Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource checker for the ResourceInterface. Exists for BC.
|
||||||
|
*
|
||||||
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
|
*
|
||||||
|
* @deprecated since 2.8, to be removed in 3.0.
|
||||||
|
*/
|
||||||
|
class BCResourceInterfaceChecker extends SelfCheckingResourceChecker
|
||||||
|
{
|
||||||
|
public function supports(ResourceInterface $metadata)
|
||||||
|
{
|
||||||
|
/* As all resources must be instanceof ResourceInterface,
|
||||||
|
we support them all. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isFresh(ResourceInterface $resource, $timestamp)
|
||||||
|
{
|
||||||
|
@trigger_error('Resource checking through ResourceInterface::isFresh() is deprecated since 2.8 and will be removed in 3.0', E_USER_DEPRECATED);
|
||||||
|
|
||||||
|
return parent::isFresh($resource, $timestamp); // For now, $metadata features the isFresh() method, so off we go (quack quack)
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,7 @@ namespace Symfony\Component\Config\Resource;
|
|||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
*/
|
*/
|
||||||
class DirectoryResource implements ResourceInterface, \Serializable
|
class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
|
||||||
{
|
{
|
||||||
private $resource;
|
private $resource;
|
||||||
private $pattern;
|
private $pattern;
|
||||||
|
@ -19,7 +19,7 @@ namespace Symfony\Component\Config\Resource;
|
|||||||
*
|
*
|
||||||
* @author Charles-Henri Bruyand <charleshenri.bruyand@gmail.com>
|
* @author Charles-Henri Bruyand <charleshenri.bruyand@gmail.com>
|
||||||
*/
|
*/
|
||||||
class FileExistenceResource implements ResourceInterface, \Serializable
|
class FileExistenceResource implements SelfCheckingResourceInterface, \Serializable
|
||||||
{
|
{
|
||||||
private $resource;
|
private $resource;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ namespace Symfony\Component\Config\Resource;
|
|||||||
*
|
*
|
||||||
* @author Fabien Potencier <fabien@symfony.com>
|
* @author Fabien Potencier <fabien@symfony.com>
|
||||||
*/
|
*/
|
||||||
class FileResource implements ResourceInterface, \Serializable
|
class FileResource implements SelfCheckingResourceInterface, \Serializable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string|false
|
* @var string|false
|
||||||
|
@ -21,7 +21,13 @@ interface ResourceInterface
|
|||||||
/**
|
/**
|
||||||
* Returns a string representation of the Resource.
|
* Returns a string representation of the Resource.
|
||||||
*
|
*
|
||||||
* @return string A string representation of the Resource
|
* This method is necessary to allow for resource de-duplication, for example by means
|
||||||
|
* of array_unique(). The string returned need not have a particular meaning, but has
|
||||||
|
* to be identical for different ResourceInterface instances referring to the same
|
||||||
|
* resource; and it should be unlikely to collide with that of other, unrelated
|
||||||
|
* resource instances.
|
||||||
|
*
|
||||||
|
* @return string A string representation unique to the underlying Resource
|
||||||
*/
|
*/
|
||||||
public function __toString();
|
public function __toString();
|
||||||
|
|
||||||
@ -31,6 +37,9 @@ interface ResourceInterface
|
|||||||
* @param int $timestamp The last time the resource was loaded
|
* @param int $timestamp The last time the resource was loaded
|
||||||
*
|
*
|
||||||
* @return bool True if the resource has not been updated, false otherwise
|
* @return bool True if the resource has not been updated, false otherwise
|
||||||
|
*
|
||||||
|
* @deprecated since 2.8, to be removed in 3.0. If your resource can check itself for
|
||||||
|
* freshness implement the SelfCheckingResourceInterface instead.
|
||||||
*/
|
*/
|
||||||
public function isFresh($timestamp);
|
public function isFresh($timestamp);
|
||||||
|
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
<?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\Config\Resource;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\ResourceCheckerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resource checker for instances of SelfCheckingResourceInterface.
|
||||||
|
*
|
||||||
|
* As these resources perform the actual check themselves, we can provide
|
||||||
|
* this class as a standard way of validating them.
|
||||||
|
*
|
||||||
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
|
*/
|
||||||
|
class SelfCheckingResourceChecker implements ResourceCheckerInterface
|
||||||
|
{
|
||||||
|
public function supports(ResourceInterface $metadata)
|
||||||
|
{
|
||||||
|
return $metadata instanceof SelfCheckingResourceInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isFresh(ResourceInterface $resource, $timestamp)
|
||||||
|
{
|
||||||
|
/* @var SelfCheckingResourceInterface $resource */
|
||||||
|
return $resource->isFresh($timestamp);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
<?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\Config\Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for Resources that can check for freshness autonomously,
|
||||||
|
* without special support from external services.
|
||||||
|
*
|
||||||
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
|
*/
|
||||||
|
interface SelfCheckingResourceInterface extends ResourceInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns true if the resource has not been updated since the given timestamp.
|
||||||
|
*
|
||||||
|
* @param int $timestamp The last time the resource was loaded
|
||||||
|
*
|
||||||
|
* @return bool True if the resource has not been updated, false otherwise
|
||||||
|
*/
|
||||||
|
public function isFresh($timestamp);
|
||||||
|
}
|
140
src/Symfony/Component/Config/ResourceCheckerConfigCache.php
Normal file
140
src/Symfony/Component/Config/ResourceCheckerConfigCache.php
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?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\Config;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Resource\ResourceInterface;
|
||||||
|
use Symfony\Component\Filesystem\Exception\IOException;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ResourceCheckerConfigCache uses instances of ResourceCheckerInterface
|
||||||
|
* to check whether cached data is still fresh.
|
||||||
|
*
|
||||||
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
|
*/
|
||||||
|
class ResourceCheckerConfigCache implements ConfigCacheInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ResourceCheckerInterface[]
|
||||||
|
*/
|
||||||
|
private $resourceCheckers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $file The absolute cache path
|
||||||
|
* @param ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check
|
||||||
|
*/
|
||||||
|
public function __construct($file, array $resourceCheckers = array())
|
||||||
|
{
|
||||||
|
$this->file = $file;
|
||||||
|
$this->resourceCheckers = $resourceCheckers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getPath()
|
||||||
|
{
|
||||||
|
return $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the cache is still fresh.
|
||||||
|
*
|
||||||
|
* This implementation will make a decision solely based on the ResourceCheckers
|
||||||
|
* passed in the constructor.
|
||||||
|
*
|
||||||
|
* The first ResourceChecker that supports a given resource is considered authoritative.
|
||||||
|
* Resources with no matching ResourceChecker will silently be ignored and considered fresh.
|
||||||
|
*
|
||||||
|
* @return bool true if the cache is fresh, false otherwise
|
||||||
|
*/
|
||||||
|
public function isFresh()
|
||||||
|
{
|
||||||
|
if (!is_file($this->file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->resourceCheckers) {
|
||||||
|
return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadata = $this->getMetaFile();
|
||||||
|
if (!is_file($metadata)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$time = filemtime($this->file);
|
||||||
|
$meta = unserialize(file_get_contents($metadata));
|
||||||
|
|
||||||
|
foreach ($meta as $resource) {
|
||||||
|
/* @var ResourceInterface $resource */
|
||||||
|
foreach ($this->resourceCheckers as $checker) {
|
||||||
|
if (!$checker->supports($resource)) {
|
||||||
|
continue; // next checker
|
||||||
|
}
|
||||||
|
if ($checker->isFresh($resource, $time)) {
|
||||||
|
break; // no need to further check this resource
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // cache is stale
|
||||||
|
}
|
||||||
|
// no suitable checker found, ignore this resource
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes cache.
|
||||||
|
*
|
||||||
|
* @param string $content The content to write in the cache
|
||||||
|
* @param ResourceInterface[] $metadata An array of metadata
|
||||||
|
*
|
||||||
|
* @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) {
|
||||||
|
$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';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
<?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\Config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A ConfigCacheFactory implementation that validates the
|
||||||
|
* cache with an arbitrary set of ResourceCheckers.
|
||||||
|
*
|
||||||
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
|
*/
|
||||||
|
class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ResourceCheckerInterface[]
|
||||||
|
*/
|
||||||
|
private $resourceCheckers = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ResourceCheckerInterface[] $resourceCheckers
|
||||||
|
*/
|
||||||
|
public function __construct(array $resourceCheckers = array())
|
||||||
|
{
|
||||||
|
$this->resourceCheckers = $resourceCheckers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function cache($file, $callback)
|
||||||
|
{
|
||||||
|
if (!is_callable($callback)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', gettype($callback)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers);
|
||||||
|
if (!$cache->isFresh()) {
|
||||||
|
call_user_func($callback, $cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cache;
|
||||||
|
}
|
||||||
|
}
|
49
src/Symfony/Component/Config/ResourceCheckerInterface.php
Normal file
49
src/Symfony/Component/Config/ResourceCheckerInterface.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?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\Config;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Resource\ResourceInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for ResourceCheckers.
|
||||||
|
*
|
||||||
|
* When a ResourceCheckerConfigCache instance is checked for freshness, all its associated
|
||||||
|
* metadata resources are passed to ResourceCheckers. The ResourceCheckers
|
||||||
|
* can then inspect the resources and decide whether the cache can be considered
|
||||||
|
* fresh or not.
|
||||||
|
*
|
||||||
|
* @author Matthias Pigulla <mp@webfactory.de>
|
||||||
|
* @author Benjamin Klotz <bk@webfactory.de>
|
||||||
|
*/
|
||||||
|
interface ResourceCheckerInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Queries the ResourceChecker whether it can validate a given
|
||||||
|
* resource or not.
|
||||||
|
*
|
||||||
|
* @param ResourceInterface $metadata The resource to be checked for freshness
|
||||||
|
*
|
||||||
|
* @return bool True if the ResourceChecker can handle this resource type, false if not
|
||||||
|
*/
|
||||||
|
public function supports(ResourceInterface $metadata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the resource.
|
||||||
|
*
|
||||||
|
* @param ResourceInterface $resource The resource to be validated.
|
||||||
|
* @param int $timestamp The timestamp at which the cache associated with this resource was created.
|
||||||
|
*
|
||||||
|
* @return bool True if the resource has not changed since the given timestamp, false otherwise.
|
||||||
|
*/
|
||||||
|
public function isFresh(ResourceInterface $resource, $timestamp);
|
||||||
|
|
||||||
|
}
|
@ -12,29 +12,20 @@
|
|||||||
namespace Symfony\Component\Config\Tests;
|
namespace Symfony\Component\Config\Tests;
|
||||||
|
|
||||||
use Symfony\Component\Config\ConfigCache;
|
use Symfony\Component\Config\ConfigCache;
|
||||||
use Symfony\Component\Config\Resource\FileResource;
|
use Symfony\Component\Config\Tests\Resource\ResourceStub;
|
||||||
|
|
||||||
class ConfigCacheTest extends \PHPUnit_Framework_TestCase
|
class ConfigCacheTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
private $resourceFile = null;
|
|
||||||
|
|
||||||
private $cacheFile = null;
|
private $cacheFile = null;
|
||||||
|
|
||||||
private $metaFile = null;
|
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
$this->resourceFile = tempnam(sys_get_temp_dir(), '_resource');
|
|
||||||
$this->cacheFile = tempnam(sys_get_temp_dir(), 'config_');
|
$this->cacheFile = tempnam(sys_get_temp_dir(), 'config_');
|
||||||
$this->metaFile = $this->cacheFile.'.meta';
|
|
||||||
|
|
||||||
$this->makeCacheFresh();
|
|
||||||
$this->generateMetaFile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
{
|
{
|
||||||
$files = array($this->cacheFile, $this->metaFile, $this->resourceFile);
|
$files = array($this->cacheFile, $this->cacheFile.'.meta');
|
||||||
|
|
||||||
foreach ($files as $file) {
|
foreach ($files as $file) {
|
||||||
if (file_exists($file)) {
|
if (file_exists($file)) {
|
||||||
@ -43,96 +34,65 @@ class ConfigCacheTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetPath()
|
/**
|
||||||
|
* @dataProvider debugModes
|
||||||
|
*/
|
||||||
|
public function testCacheIsNotValidIfNothingHasBeenCached($debug)
|
||||||
{
|
{
|
||||||
$cache = new ConfigCache($this->cacheFile, true);
|
unlink($this->cacheFile); // remove tempnam() side effect
|
||||||
|
$cache = new ConfigCache($this->cacheFile, $debug);
|
||||||
$this->assertSame($this->cacheFile, $cache->getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCacheIsNotFreshIfFileDoesNotExist()
|
|
||||||
{
|
|
||||||
unlink($this->cacheFile);
|
|
||||||
|
|
||||||
$cache = new ConfigCache($this->cacheFile, false);
|
|
||||||
|
|
||||||
$this->assertFalse($cache->isFresh());
|
$this->assertFalse($cache->isFresh());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCacheIsAlwaysFreshIfFileExistsWithDebugDisabled()
|
public function testIsAlwaysFreshInProduction()
|
||||||
{
|
{
|
||||||
$this->makeCacheStale();
|
$staleResource = new ResourceStub();
|
||||||
|
$staleResource->setFresh(false);
|
||||||
|
|
||||||
$cache = new ConfigCache($this->cacheFile, false);
|
$cache = new ConfigCache($this->cacheFile, false);
|
||||||
|
$cache->write('', array($staleResource));
|
||||||
|
|
||||||
$this->assertTrue($cache->isFresh());
|
$this->assertTrue($cache->isFresh());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCacheIsNotFreshWithoutMetaFile()
|
/**
|
||||||
|
* @dataProvider debugModes
|
||||||
|
*/
|
||||||
|
public function testIsFreshWhenNoResourceProvided($debug)
|
||||||
{
|
{
|
||||||
unlink($this->metaFile);
|
$cache = new ConfigCache($this->cacheFile, $debug);
|
||||||
|
$cache->write('', array());
|
||||||
$cache = new ConfigCache($this->cacheFile, true);
|
$this->assertTrue($cache->isFresh());
|
||||||
|
|
||||||
$this->assertFalse($cache->isFresh());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCacheIsFreshIfResourceIsFresh()
|
public function testFreshResourceInDebug()
|
||||||
{
|
{
|
||||||
|
$freshResource = new ResourceStub();
|
||||||
|
$freshResource->setFresh(true);
|
||||||
|
|
||||||
$cache = new ConfigCache($this->cacheFile, true);
|
$cache = new ConfigCache($this->cacheFile, true);
|
||||||
|
$cache->write('', array($freshResource));
|
||||||
|
|
||||||
$this->assertTrue($cache->isFresh());
|
$this->assertTrue($cache->isFresh());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCacheIsNotFreshIfOneOfTheResourcesIsNotFresh()
|
public function testStaleResourceInDebug()
|
||||||
{
|
{
|
||||||
$this->makeCacheStale();
|
$staleResource = new ResourceStub();
|
||||||
|
$staleResource->setFresh(false);
|
||||||
|
|
||||||
$cache = new ConfigCache($this->cacheFile, true);
|
$cache = new ConfigCache($this->cacheFile, true);
|
||||||
|
$cache->write('', array($staleResource));
|
||||||
|
|
||||||
$this->assertFalse($cache->isFresh());
|
$this->assertFalse($cache->isFresh());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWriteDumpsFile()
|
public function debugModes()
|
||||||
{
|
{
|
||||||
unlink($this->cacheFile);
|
return array(
|
||||||
unlink($this->metaFile);
|
array(true),
|
||||||
|
array(false),
|
||||||
$cache = new ConfigCache($this->cacheFile, false);
|
);
|
||||||
$cache->write('FOOBAR');
|
|
||||||
|
|
||||||
$this->assertFileExists($this->cacheFile, 'Cache file is created');
|
|
||||||
$this->assertSame('FOOBAR', file_get_contents($this->cacheFile));
|
|
||||||
$this->assertFileNotExists($this->metaFile, 'Meta file is not created');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testWriteDumpsMetaFileWithDebugEnabled()
|
|
||||||
{
|
|
||||||
unlink($this->cacheFile);
|
|
||||||
unlink($this->metaFile);
|
|
||||||
|
|
||||||
$metadata = array(new FileResource($this->resourceFile));
|
|
||||||
|
|
||||||
$cache = new ConfigCache($this->cacheFile, true);
|
|
||||||
$cache->write('FOOBAR', $metadata);
|
|
||||||
|
|
||||||
$this->assertFileExists($this->cacheFile, 'Cache file is created');
|
|
||||||
$this->assertFileExists($this->metaFile, 'Meta file is created');
|
|
||||||
$this->assertSame(serialize($metadata), file_get_contents($this->metaFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function makeCacheFresh()
|
|
||||||
{
|
|
||||||
touch($this->resourceFile, filemtime($this->cacheFile) - 3600);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function makeCacheStale()
|
|
||||||
{
|
|
||||||
touch($this->cacheFile, filemtime($this->resourceFile) - 3600);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generateMetaFile()
|
|
||||||
{
|
|
||||||
file_put_contents($this->metaFile, serialize(array(new FileResource($this->resourceFile))));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
src/Symfony/Component/Config/Tests/Resource/ResourceStub.php
Normal file
39
src/Symfony/Component/Config/Tests/Resource/ResourceStub.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?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\Config\Tests\Resource;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
|
||||||
|
|
||||||
|
class ResourceStub implements SelfCheckingResourceInterface
|
||||||
|
{
|
||||||
|
private $fresh = true;
|
||||||
|
|
||||||
|
public function setFresh($isFresh)
|
||||||
|
{
|
||||||
|
$this->fresh = $isFresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return 'stub';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isFresh($timestamp)
|
||||||
|
{
|
||||||
|
return $this->fresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResource()
|
||||||
|
{
|
||||||
|
return 'stub';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
<?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\Config\Tests;
|
||||||
|
|
||||||
|
use Symfony\Component\Config\Tests\Resource\ResourceStub;
|
||||||
|
use Symfony\Component\Config\ResourceCheckerConfigCache;
|
||||||
|
|
||||||
|
class ResourceCheckerConfigCacheTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
private $cacheFile = null;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->cacheFile = tempnam(sys_get_temp_dir(), 'config_');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown()
|
||||||
|
{
|
||||||
|
$files = array($this->cacheFile, "{$this->cacheFile}.meta");
|
||||||
|
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if (file_exists($file)) {
|
||||||
|
unlink($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetPath()
|
||||||
|
{
|
||||||
|
$cache = new ResourceCheckerConfigCache($this->cacheFile);
|
||||||
|
|
||||||
|
$this->assertSame($this->cacheFile, $cache->getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCacheIsNotFreshIfEmpty()
|
||||||
|
{
|
||||||
|
$checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface')
|
||||||
|
->expects($this->never())->method('supports');
|
||||||
|
|
||||||
|
/* If there is nothing in the cache, it needs to be filled (and thus it's not fresh).
|
||||||
|
It does not matter if you provide checkers or not. */
|
||||||
|
|
||||||
|
unlink($this->cacheFile); // remove tempnam() side effect
|
||||||
|
$cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker));
|
||||||
|
|
||||||
|
$this->assertFalse($cache->isFresh());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCacheIsFreshIfNocheckerProvided()
|
||||||
|
{
|
||||||
|
/* For example in prod mode, you may choose not to run any checkers
|
||||||
|
at all. In that case, the cache should always be considered fresh. */
|
||||||
|
$cache = new ResourceCheckerConfigCache($this->cacheFile);
|
||||||
|
$this->assertTrue($cache->isFresh());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testResourcesWithoutcheckersAreIgnoredAndConsideredFresh()
|
||||||
|
{
|
||||||
|
/* As in the previous test, but this time we have a resource. */
|
||||||
|
$cache = new ResourceCheckerConfigCache($this->cacheFile);
|
||||||
|
$cache->write('', array(new ResourceStub()));
|
||||||
|
|
||||||
|
$this->assertTrue($cache->isFresh()); // no (matching) ResourceChecker passed
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsFreshWithchecker()
|
||||||
|
{
|
||||||
|
$checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface');
|
||||||
|
|
||||||
|
$checker->expects($this->once())
|
||||||
|
->method('supports')
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$checker->expects($this->once())
|
||||||
|
->method('isFresh')
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker));
|
||||||
|
$cache->write('', array(new ResourceStub()));
|
||||||
|
|
||||||
|
$this->assertTrue($cache->isFresh());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsNotFreshWithchecker()
|
||||||
|
{
|
||||||
|
$checker = $this->getMock('\Symfony\Component\Config\ResourceCheckerInterface');
|
||||||
|
|
||||||
|
$checker->expects($this->once())
|
||||||
|
->method('supports')
|
||||||
|
->willReturn(true);
|
||||||
|
|
||||||
|
$checker->expects($this->once())
|
||||||
|
->method('isFresh')
|
||||||
|
->willReturn(false);
|
||||||
|
|
||||||
|
$cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker));
|
||||||
|
$cache->write('', array(new ResourceStub()));
|
||||||
|
|
||||||
|
$this->assertFalse($cache->isFresh());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCacheKeepsContent()
|
||||||
|
{
|
||||||
|
$cache = new ResourceCheckerConfigCache($this->cacheFile);
|
||||||
|
$cache->write('FOOBAR');
|
||||||
|
|
||||||
|
$this->assertSame('FOOBAR', file_get_contents($cache->getPath()));
|
||||||
|
}
|
||||||
|
}
|
@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\HttpKernel\Config;
|
namespace Symfony\Component\HttpKernel\Config;
|
||||||
|
|
||||||
use Symfony\Component\Config\Resource\ResourceInterface;
|
use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EnvParametersResource represents resources stored in prefixed environment variables.
|
* EnvParametersResource represents resources stored in prefixed environment variables.
|
||||||
*
|
*
|
||||||
* @author Chris Wilkinson <chriswilkinson84@gmail.com>
|
* @author Chris Wilkinson <chriswilkinson84@gmail.com>
|
||||||
*/
|
*/
|
||||||
class EnvParametersResource implements ResourceInterface, \Serializable
|
class EnvParametersResource implements SelfCheckingResourceInterface, \Serializable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
"symfony/phpunit-bridge": "~2.7|~3.0.0",
|
"symfony/phpunit-bridge": "~2.7|~3.0.0",
|
||||||
"symfony/browser-kit": "~2.3|~3.0.0",
|
"symfony/browser-kit": "~2.3|~3.0.0",
|
||||||
"symfony/class-loader": "~2.1|~3.0.0",
|
"symfony/class-loader": "~2.1|~3.0.0",
|
||||||
"symfony/config": "~2.7",
|
"symfony/config": "~2.8",
|
||||||
"symfony/console": "~2.3|~3.0.0",
|
"symfony/console": "~2.3|~3.0.0",
|
||||||
"symfony/css-selector": "~2.0,>=2.0.5|~3.0.0",
|
"symfony/css-selector": "~2.0,>=2.0.5|~3.0.0",
|
||||||
"symfony/dependency-injection": "~2.8|~3.0.0",
|
"symfony/dependency-injection": "~2.8|~3.0.0",
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Translation\Tests;
|
namespace Symfony\Component\Translation\Tests;
|
||||||
|
|
||||||
use Symfony\Component\Config\Resource\ResourceInterface;
|
use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
|
||||||
use Symfony\Component\Translation\Loader\ArrayLoader;
|
use Symfony\Component\Translation\Loader\ArrayLoader;
|
||||||
use Symfony\Component\Translation\Loader\LoaderInterface;
|
use Symfony\Component\Translation\Loader\LoaderInterface;
|
||||||
use Symfony\Component\Translation\Translator;
|
use Symfony\Component\Translation\Translator;
|
||||||
@ -228,7 +228,7 @@ class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
public function testRefreshCacheWhenResourcesAreNoLongerFresh()
|
public function testRefreshCacheWhenResourcesAreNoLongerFresh()
|
||||||
{
|
{
|
||||||
$resource = $this->getMock('Symfony\Component\Config\Resource\ResourceInterface');
|
$resource = $this->getMock('Symfony\Component\Config\Resource\SelfCheckingResourceInterface');
|
||||||
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
|
$loader = $this->getMock('Symfony\Component\Translation\Loader\LoaderInterface');
|
||||||
$resource->method('isFresh')->will($this->returnValue(false));
|
$resource->method('isFresh')->will($this->returnValue(false));
|
||||||
$loader
|
$loader
|
||||||
@ -281,7 +281,7 @@ class TranslatorCacheTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StaleResource implements ResourceInterface
|
class StaleResource implements SelfCheckingResourceInterface
|
||||||
{
|
{
|
||||||
public function isFresh($timestamp)
|
public function isFresh($timestamp)
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/phpunit-bridge": "~2.7|~3.0.0",
|
"symfony/phpunit-bridge": "~2.7|~3.0.0",
|
||||||
"symfony/config": "~2.7",
|
"symfony/config": "~2.8",
|
||||||
"symfony/intl": "~2.4|~3.0.0",
|
"symfony/intl": "~2.4|~3.0.0",
|
||||||
"symfony/yaml": "~2.2|~3.0.0",
|
"symfony/yaml": "~2.2|~3.0.0",
|
||||||
"psr/log": "~1.0"
|
"psr/log": "~1.0"
|
||||||
|
Reference in New Issue
Block a user