Skip to content

Commit

Permalink
Implement service-based Resource (cache) validation
Browse files Browse the repository at this point in the history
  • Loading branch information
mpdude authored and fabpot committed Sep 25, 2015
1 parent 7d343e6 commit 20d3722
Show file tree
Hide file tree
Showing 32 changed files with 767 additions and 206 deletions.
32 changes: 32 additions & 0 deletions UPGRADE-2.8.md
Expand Up @@ -402,3 +402,35 @@ FrameworkBundle
session:
cookie_httponly: false
```

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.

7 changes: 7 additions & 0 deletions UPGRADE-3.0.md
Expand Up @@ -1201,3 +1201,10 @@ UPGRADE FROM 2.x to 3.0
* `Process::setStdin()` and `Process::getStdin()` have been removed. Use
`Process::setInput()` and `Process::getInput()` that works the same way.
* `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);
}
}
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
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());
}
}
}
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
16 changes: 16 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml
Expand Up @@ -63,5 +63,21 @@
<service id="uri_signer" class="%uri_signer.class%">
<argument>%kernel.secret%</argument>
</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>
</container>
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
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
@@ -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);
}
}
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
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
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.8",
Expand Down
3 changes: 3 additions & 0 deletions src/Symfony/Component/Config/CHANGELOG.md
Expand Up @@ -20,6 +20,9 @@ Before: `InvalidArgumentException` (variable must contain at least two
distinct elements).
After: the code will work as expected and it will restrict the values of the
`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
-----
Expand Down

0 comments on commit 20d3722

Please sign in to comment.