Skip to content

[ConfigCache] Use non-local files resources #10761

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 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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,37 @@
<?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\Resource\Refresher;

use Symfony\Component\Config\Resource\Refresher\RefresherInterface;
use Symfony\Component\Config\Resource\MutableResourceInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* @author Luc Vieillescazes <luc@vieillescazes.net>
*/
class ContainerAwareRefresher implements RefresherInterface
{
private $container;

public function __construct(ContainerInterface $container)
{
$this->container = $container;
}

public function refresh(MutableResourceInterface $resource)
{
if ($resource instanceof ContainerAwareInterface) {
$resource->setContainer($this->container);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?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\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;

class ConfigResourceRefresherPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('config.resource.chain_refresher')) {
return;
}

$definition = $container->getDefinition('config.resource.chain_refresher');

foreach ($container->findTaggedServiceIds('config.resource_refresher') as $id => $attributes) {
$definition->addMethodCall('addRefresher', array(new Reference($id)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public function load(array $configs, ContainerBuilder $container)
$loader->load('web.xml');
$loader->load('services.xml');
$loader->load('fragment_renderer.xml');
$loader->load('config.xml');

// A translator must always be registered (as support is included by
// default in the Form component). If disabled, an identity translator
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\ConfigResourceRefresherPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\Scope;
Expand Down Expand Up @@ -81,6 +82,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 ConfigResourceRefresherPass());

if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_AFTER_REMOVING);
Expand Down
20 changes: 20 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?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.resource.chain_refresher.class">Symfony\Component\Config\Resource\Refresher\ChainRefresher</parameter>
<parameter key="config.resource.container_aware_refresher.class">Symfony\Bundle\FrameworkBundle\Config\Resource\Refresher\ContainerAwareRefresher</parameter>
</parameters>

<services>
<service id="config.resource.chain_refresher" class="%config.resource.chain_refresher.class%">
</service>
<service id="config.resource.container_aware_refresher" class="%config.resource.container_aware_refresher.class%">
<argument type="service" id="service_container" />
<tag name="config.resource_refresher" />
</service>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<argument key="cache_dir">%kernel.cache_dir%/translations</argument>
<argument key="debug">%kernel.debug%</argument>
</argument>
<argument type="service" id="config.resource.chain_refresher" />
</service>

<service id="translator" class="%translator.identity.class%">
Expand Down
7 changes: 5 additions & 2 deletions src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\Translation\MessageSelector;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Resource\Refresher\RefresherInterface;

/**
* Translator.
Expand All @@ -29,6 +30,7 @@ class Translator extends BaseTranslator
'debug' => false,
);
protected $loaderIds;
protected $refresher;

/**
* Constructor.
Expand All @@ -45,10 +47,11 @@ class Translator extends BaseTranslator
*
* @throws \InvalidArgumentException
*/
public function __construct(ContainerInterface $container, MessageSelector $selector, $loaderIds = array(), array $options = array())
public function __construct(ContainerInterface $container, MessageSelector $selector, $loaderIds = array(), array $options = array(), RefresherInterface $refresher = null)
{
$this->container = $container;
$this->loaderIds = $loaderIds;
$this->refresher = $refresher;

// check option names
if ($diff = array_diff(array_keys($options), array_keys($this->options))) {
Expand Down Expand Up @@ -88,7 +91,7 @@ protected function loadCatalogue($locale)
}

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

parent::loadCatalogue($locale);
Expand Down
40 changes: 29 additions & 11 deletions src/Symfony/Component/Config/ConfigCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace Symfony\Component\Config;

use Symfony\Component\Config\Resource\ResourceInterface;
use Symfony\Component\Config\Resource\MutableResourceInterface;
use Symfony\Component\Config\Resource\Refresher\RefresherInterface;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;

Expand Down Expand Up @@ -58,25 +60,25 @@ public function __toString()
*
* @return bool true if the cache is fresh, false otherwise
*/
public function isFresh()
public function isFresh(RefresherInterface $refresher = null)
{
if (!is_file($this->file)) {
return false;
}

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

$metadata = $this->getMetaFile();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be kept IMO. In non-debug mode (i.e. in prod), we don't want to consider the cache resources as mutable, even if they are mutable in debug mode (i.e. in dev). This is the (only) goal of this flag and it should be preserved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, it's one of the goal of this PR. We want to refresh cache (translations/routes) easily in production.
I know that the cost is one more is_file() call....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iamluc IMO, if you define your routes in the DB, you should have a look at the extended routing system implemented by the CMF project rather than trying to hack the cache reloading in a non-BC way

if (!is_file($metadata)) {
return false;
return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is BC break isn't it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it is

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really,
With only the old ResourceInterface (the only one shipped at the moment), you still have a metadata file in dev, but not in prod.

So in prod, before the method ended with

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

now it ends with

if (!is_file($metadata)) {
    return true;
}

In dev, we still check all resources like before

}

$time = filemtime($this->file);
$meta = unserialize(file_get_contents($metadata));
$timestamp = filemtime($this->file);
foreach ($meta as $resource) {
if (!$resource->isFresh($time)) {
if (null !== $refresher && $resource instanceof MutableResourceInterface) {
$refresher->refresh($resource);
}

if (!$resource->isFresh($timestamp)) {
return false;
}
}
Expand Down Expand Up @@ -104,13 +106,30 @@ public function write($content, array $metadata = null)
// discard chmod failure (some filesystem may not support it)
}

if (null !== $metadata && true === $this->debug) {
$filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null);
$mutables = array();
if (is_array($metadata)) {
foreach ($metadata as $resource) {
if ($resource instanceof MutableResourceInterface) {
if (true === $resource->isMutable()) {
$mutables[] = $resource;
}
} else {
if (true === $this->debug) {
$mutables[] = $resource;
}
}
}
}

if (count($mutables) > 0) {
$filesystem->dumpFile($this->getMetaFile(), serialize($mutables), null);
try {
$filesystem->chmod($this->getMetaFile(), $mode, $umask);
} catch (IOException $e) {
// discard chmod failure (some filesystem may not support it)
}
} else {
$filesystem->remove($this->getMetaFile());
}
}

Expand All @@ -123,5 +142,4 @@ private function getMetaFile()
{
return $this->file.'.meta';
}

}
22 changes: 22 additions & 0 deletions src/Symfony/Component/Config/Resource/MutableResourceInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?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;

/**
* MutableResourceInterface is the interface that must be implemented by all Resource classes.
*
* @author Luc Vieillescazes <luc@vieillescazes.net>
*/
interface MutableResourceInterface extends ResourceInterface
{
public function isMutable();
}
34 changes: 34 additions & 0 deletions src/Symfony/Component/Config/Resource/Refresher/ChainRefresher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?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\Refresher;

use Symfony\Component\Config\Resource\MutableResourceInterface;

/**
* @author Luc Vieillescazes <luc@vieillescazes.net>
*/
class ChainRefresher implements RefresherInterface
{
private $refreshers = array();

public function addRefresher(RefresherInterface $refresher)
{
$this->refreshers[] = $refresher;
}

public function refresh(MutableResourceInterface $resource)
{
foreach ($this->refreshers as $refresher) {
$refresher->refresh($resource);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?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\Refresher;

use Symfony\Component\Config\Resource\MutableResourceInterface;

/**
* @author Luc Vieillescazes <luc@vieillescazes.net>
*/
interface RefresherInterface
{
public function refresh(MutableResourceInterface $resource);
}
10 changes: 1 addition & 9 deletions src/Symfony/Component/Config/Tests/ConfigCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,14 @@ public function testCacheIsNotFreshIfFileDoesNotExist()

public function testCacheIsAlwaysFreshIfFileExistsWithDebugDisabled()
{
unlink($this->metaFile);
$this->makeCacheStale();

$cache = new ConfigCache($this->cacheFile, false);

$this->assertTrue($cache->isFresh());
}

public function testCacheIsNotFreshWithoutMetaFile()
{
unlink($this->metaFile);

$cache = new ConfigCache($this->cacheFile, true);

$this->assertFalse($cache->isFresh());
}

public function testCacheIsFreshIfResourceIsFresh()
{
$cache = new ConfigCache($this->cacheFile, true);
Expand Down