-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Description
Symfony version(s) affected
>= 3.4
Description
If apc.enable_cli
is not set to true, the ChainAdapter
will skip the ApcuAdapter
even if ApcuAdapter::isSupported()
returns true
.
This change was introduced in #36555
This means that CLI can't be used to warm APCu cache even when that is actually supported.
How to reproduce
<?php
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
require_once __DIR__ . '/vendor/autoload.php';
$cacheNamespace = 'MyCacheExample';
$cacheLifetime = 300;
$isCli = 'cli' === \PHP_SAPI && !ini_get('apc.enable_cli');
$adapters = [
new FilesystemAdapter($cacheNamespace, $cacheLifetime, __DIR__ . '/' . $cacheNamespace)
];
if (ApcuAdapter::isSupported()) {
$apcuAdapter = new ApcuAdapter($cacheNamespace, $cacheLifetime);
// Note the order doesn't REALLY matter for this demonstration, but we should still ideally have the cache we *know* we can read from first.
if ($isCli) {
// Add it second so we're pulling from the filesystem, since APCu isn't available for reading
$adapters[] = $apcuAdapter;
} else {
// Add it first so we're pulling from memory where possible
array_unshift($adapters, $apcuAdapter);
}
}
$cache = new ChainAdapter($adapters, $cacheLifetime);
if ($isCli) {
echo "CLI" . PHP_EOL;
$cache->clear();
} else {
echo "Not CLI" . PHP_EOL;
}
$cache->get('my-cache-item', function () use ($isCli) {
$value = $isCli ? 'CLI value' : 'not-CLI value';
return $value . time();
});
$cache->commit();
echo $cache->getItem('my-cache-item')->get();
I think the order you need to run this is:
- from a non-CLI context (or with
apc.enable_cli
set to true) - from a CLI context with
apc.enable_cli
set to false (or maybe not set at all?) - from a non-CLI context (or with
apc.enable_cli
set to true) again.
The first run will set the cache in APCu.
The second run will clear the cache - but since it's skipping APCu it only clears the (already empty) filesystem cache. It then sets a value into the filesystem cache.
The third run will get a cache hit from APCu - it completely ignores the fact that the CLI run was meant to clear it and set a new value.
This should get you into a state where the CLI output is always different from the non-CLI output (at least until the APCu cache times out, at which point you might get lucky and they'll sync up)
Possible Solution
Ideally, that continue
in ChainAdapter
's constructor would just be outright removed.
If we want to be really careful, then if apc.enable_cli
is not set to true
the chain adapter could skip it anywhere that reads the adapter's cache, but still use it to set cache so it can warm that cache up and clear it.
I don't fully understand how the internals of this component work so I can't speak to the feasibility of that.
Additional Context
Note that in #25080 the suggestion to check apc.enable_cli
to determine whether APCu was rejected specifically because it would prevent this use case - so obviously this at least used to be an intended use case.