10000 feature #15738 Implement service-based Resource (cache) validation (m… · symfony/config@b0df4ee · GitHub
[go: up one dir, main page]

Skip to content

Commit b0df4ee

Browse files
committed
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
2 parents 2278d43 + 7c62403 commit b0df4ee

16 files changed

+574
-159
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ Before: `InvalidArgumentException` (variable must contain at least two
2020
distinct elements).
2121
After: the code will work as expected and it will restrict the values of the
2222
`variable` option to just `value`.
23+
24+
* deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they
25+
can be validated that way, make them implement the new `SelfCheckingResourceInterface`.
2326

2427
2.7.0
2528
-----

ConfigCache.php

Lines changed: 21 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,38 @@
1111

1212
namespace Symfony\Component\Config;
1313

14-
use Symfony\Component\Config\Resource\ResourceInterface;
15-
use Symfony\Component\Filesystem\Exception\IOException;
16-
use Symfony\Component\Filesystem\Filesystem;
14+
use Symfony\Component\Config\Resource\BCResourceInterfaceChecker;
15+
use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;
1716

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

3136
/**
3237
* @param string $file The absolute cache path
3338
* @param bool $debug Whether debugging is enabled or not
3439
*/
3540
public function __construct($file, $debug)
3641
{
37-
$this->file = $file;
42+
parent::__construct($file, array(
43+
new SelfCheckingResourceChecker(),
44+
new BCResourceInterfaceChecker(),
45+
));
3846
$this->debug = (bool) $debug;
3947
}
4048

@@ -49,90 +57,23 @@ public function __toString()
4957
{
5058
@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);
5159

52-
return $this->file;
53-
}
54-
55-
/**
56-
* Gets the cache file path.
57-
*
58-
* @return string The cache file path
59-
*/
60-
public function getPath()
61-
{
62-
return $this->file;
60+
return $this->getPath();
6361
}
6462

6563
/**
6664
* Checks if the cache is still fresh.
6765
*
68-
* This method always returns true when debug is off and the
66+
* This implementation always returns true when debug is off and the
6967
* cache file exists.
7068
*
7169
* @return bool true if the cache is fresh, false otherwise
7270
*/
7371
public function isFresh()
7472
{
75-
if (!is_file($this->file)) {
76-
return false;
77-
}
78-
79-
if (!$this->debug) {
73+
if (!$this->debug && is_file($this->getPath())) {
8074
return true;
8175
}
8276

83-
$metadata = $this->getMetaFile();
84-
if (!is_file($metadata)) {
85-
return false;
86-
}
87-
88-
$time = filemtime($this->file);
89-
$meta = unserialize(file_get_contents($metadata));
90-
foreach ($meta as $resource) {
91-
if (!$resource->isFresh($time)) {
92-
return false;
93-
}
94-
}
95-
96-
return true;
97-
}
98-
99-
/**
100-
* Writes cache.
101-
*
102-
* @param string $content The content to write in the cache
103-
* @param ResourceInterface[] $metadata An array of ResourceInterface instances
104-
*
105-
* @throws \RuntimeException When cache file can't be written
106-
*/
107-
public function write($content, array $metadata = null)
108-
{
109-
$mode = 0666;
110-
$umask = umask();
111-
$filesystem = new Filesystem();
112-
$filesystem->dumpFile($this->file, $content, null);
113-
try {
114-
$filesystem->chmod($this->file, $mode, $umask);
115-
} catch (IOException $e) {
116-
// discard chmod failure (some filesystem may not support it)
117-
}
118-
119-
if (null !== $metadata && true === $this->debug) {
120-
$filesystem->dumpFile($this->getMetaFile(), serialize($metadata), null);
121-
try {
122-
$filesystem->chmod($this->getMetaFile(), $mode, $umask);
123-
} catch (IOException $e) {
124-
// discard chmod failure (some filesystem may not support it)
125-
}
126-
}
127-
}
128-
129-
/**
130-
* Gets the meta file path.
131-
*
132-
* @return string The meta file path
133-
*/
134-
private function getMetaFile()
135-
{
136-
return $this->file.'.meta';
77+
return parent::isFresh();
13778
}
13879
}

ConfigCacheFactory.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
namespace Symfony\Component\Config;
1313

1414
/**
15-
* Basic implementation for ConfigCacheFactoryInterface
16-
* that will simply create an instance of ConfigCache.
15+
* Basic implementation of ConfigCacheFactoryInterface that
16+
* creates an instance of the default ConfigCache.
17+
*
18+
* This factory and/or cache <em>do not</em> support cache validation
19+
* by means of ResourceChecker instances (that is, service-based).
1720
*
1821
* @author Matthias Pigulla <mp@webfactory.de>
1922
*/
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Config\Resource;
13+
14+
/**
15+
* Resource checker for the ResourceInterface. Exists for BC.
16+
*
17+
* @author Matthias Pigulla <mp@webfactory.de>
18+
*
19+
* @deprecated since 2.8, to be removed in 3.0.
20+
*/
21+
class BCResourceInterfaceChecker extends SelfCheckingResourceChecker
22+
{
23+
public function supports(ResourceInterface $metadata)
24+
{
25+
/* As all resources must be instanceof ResourceInterface,
26+
we support them all. */
27+
return true;
28+
}
29+
30+
public function isFresh(ResourceInterface $resource, $timestamp)
31+
{
32+
@trigger_error('Resource checking through ResourceInterface::isFresh() is deprecated since 2.8 and will be removed in 3.0', E_USER_DEPRECATED);
33+
34+
return parent::isFresh($resource, $timestamp); // For now, $metadata features the isFresh() method, so off we go (quack quack)
35+
}
36+
}

Resource/DirectoryResource.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
* @author Fabien Potencier <fabien@symfony.com>
1818
*/
19-
class DirectoryResource implements ResourceInterface, \Serializable
19+
class DirectoryResource implements SelfCheckingResourceInterface, \Serializable
2020
{
2121
private $resource;
2222
private $pattern;

Resource/FileExistenceResource.php

< F438 button data-component="IconButton" type="button" class="prc-Button-ButtonBase-c50BI ml-1 flex-shrink-0 prc-Button-IconButton-szpyj" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="invisible" aria-describedby=":Rt6rdlab:-loading-announcement" aria-labelledby=":R16rdlab:">
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
*
2020
* @author Charles-Henri Bruyand <charleshenri.bruyand@gmail.com>
2121
*/
22-
class FileExistenceResource implements ResourceInterface, \Serializable
22+
class FileExistenceResource implements SelfCheckingResourceInterface, \Serializable
2323
{
2424
private $resource;
2525

Resource/FileResource.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*
1919
* @author Fabien Potencier <fabien@symfony.com>
2020
*/
21-
class FileResource implements ResourceInterface, \Serializable
21+
class FileResource implements SelfCheckingResourceInterface, \Serializable
2222
{
2323
/**
2424
* @var string|false

Resource/ResourceInterface.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@ interface ResourceInterface
2121
/**
2222
* Returns a string representation of the Resource.
2323
*
24-
* @return string A string representation of the Resource
24+
* This method is necessary to allow for resource de-duplication, for example by means
25+
* of array_unique(). The string returned need not have a particular meaning, but has
26+
* to be identical for different ResourceInterface instances referring to the same
27+
* resource; and it should be unlikely to collide with that of other, unrelated
28+
* resource instances.
29+
*
30+
* @return string A string representation unique to the underlying Resource
2531
*/
2632
public function __toString();
2733

@@ -31,6 +37,9 @@ public function __toString();
3137
* @param int $timestamp The last time the resource was loaded
3238
*
3339
* @return bool True if the resource has not been updated, false otherwise
40+
*
41+
* @deprecated since 2.8, to be removed in 3.0. If your resource can check itself for
42+
* freshness implement the SelfCheckingResourceInterface instead.
3443
*/
3544
public function isFresh($timestamp);
3645

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Config\Resource;
13+
14+
use Symfony\Component\Config\ResourceCheckerInterface;
15+
16+
/**
17+
* Resource checker for instances of SelfCheckingResourceInterface.
18+
*
19+
* As these resources perform the actual check themselves, we can provide
20+
* this class as a standard way of validating them.
21+
*
22+
* @author Matthias Pigulla <mp@webfactory.de>
23+
*/
24+
class SelfCheckingResourceChecker implements ResourceCheckerInterface
25+
{
26+
public function supports(ResourceInterface $metadata)
27+
{
28+
return $metadata instanceof SelfCheckingResourceInterface;
29+
}
30+
31+
public function isFresh(ResourceInterface $resource, $timestamp)
32+
{
33+
/* @var SelfCheckingResourceInterface $resource */
34+
return $resource->isFresh($timestamp);
35+
}
36+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Config\Resource;
13+
14+
/**
15+
* Interface for Resources that can check for freshness autonomously,
16+
* without special support from external services.
17+
*
18+
* @author Matthias Pigulla <mp@webfactory.de>
19+
*/
20+
interface SelfCheckingResourceInterface extends ResourceInterface
21+
{
22+
/**
23+
* Returns true if the resource has not been updated since the given timestamp.
24+
*
25+
* @param int $timestamp The last time the resource was loaded
26+
*
27+
* @return bool True if the resource has not been updated, false otherwise
28+
*/
29+
public function isFresh($timestamp);
30+
}

0 commit comments

Comments
 (0)
0