- Issue created by @dewalt
- 🇧🇾Belarus dewalt
For a now we've added "route.name" extra cache key to avoid this behavior.
- 🇧🇾Belarus dewalt
Created Drush issue - https://github.com/drush-ops/drush/issues/6352
Running some code with CLI (e.g., with Drush), "$request->getPathInfo()" returns "/", which is the same as the front page path, but "$pathMatcher->isFrontPage();" returns `0`.
Cacheable plugins or other structures that use "$pathMatcher->isFrontPage();" and "url.path.is_front" have different cache records for CLI and HTTP. This itself isn't an issue. However, when "url.path" is added to the cache context by another plugin/alter function, cache tags are optimized, and the higher-level "url.path" absorbs "url.path.is_front". From this point, CLI and HTTP cache intersect, leading to unexpected behavior. Cache built in CLI follows non-front page logic, and then the front page via HTTP uses this cache and displays incorrect data.
Some code experiments:
Test code:
$s = \Drupal::service("cache_contexts_manager");
var_dump($s->convertTokensToKeys(["url.path"])->getKeys());
var_dump($s->convertTokensToKeys(["url.path.is_front"])->getKeys());
var_dump($s->convertTokensToKeys(["url.path", "url.path.is_front"])->getKeys());
HTTP:
array(1) {
[0] => string(12) "[url.path]=/"
}
array(1) {
[0]=> string(30) "[url.path.is_front]=is_front.1" // Look here.
}
array(1) {
[0]=> string(12) "[url.path]=/" // Keys optimized, value lost.
}
CLI:
array(1) {
[0] => string(12) "[url.path]=/"
}
array(1) {
[0]=> string(30) "[url.path.is_front]=is_front.0" // Look here.
}
array(1) {
[0]=> string(12) "[url.path]=/" // Keys optimized, value lost.
}
Partially, this is a Drush issue because it creates the request as "$request = Request::createFromGlobals();", and it has "/" in "::getPathInfo()".
However, there are things that I think should be highlighted to Drupal core:
We found this issue while creating an access policy that allows anonymous users to see the front page only. With the "url.path.is_front" cache context, it worked perfectly. Then, we added simple pages to the allowed area (like About Us, Privacy Policy) with the "url.path" cache context, and sometimes the front page became forbidden for a user, while other times it worked. Code to check:
namespace Drupal\example\Session;
use Drupal\Core\Path\PathMatcherInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccessPolicyBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\CalculatedPermissionsItem;
use Drupal\Core\Session\RefinableCalculatedPermissionsInterface;
/**
* Grants view permissions on some pages for anonymous user.
*/
class AnonymousAccessPolicy extends AccessPolicyBase {
/**
* The path matcher.
*
* @var \Drupal\Core\Path\PathMatcherInterface
*/
protected $pathMatcher;
/**
* The current route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* {@inheritdoc}
*/
public function __construct(PathMatcherInterface $path_matcher, RouteMatchInterface $route_match) {
$this->pathMatcher = $path_matcher;
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public function calculatePermissions(AccountInterface $account, string $scope): RefinableCalculatedPermissionsInterface {
$calculated_permissions = parent::calculatePermissions($account, $scope)
->addCacheContexts($this->getPersistentCacheContexts());
if ($account->isAnonymous()) {
$calculated_permissions->addCacheContexts(['url.path', 'url.path.is_front']);
if ($this->routeMatch->getRouteName() === 'entity.node.canonical') {
$node = $this->routeMatch->getParameter('node');
}
if ($this->pathMatcher->isFrontPage() || (isset($node) && $node->bundle() === 'page')) {
$calculated_permissions->addItem(new CalculatedPermissionsItem(['access content', 'view media']));
}
}
return $calculated_permissions;
}
/**
* {@inheritdoc}
*/
public function getPersistentCacheContexts(): array {
return ['user.roles:anonymous'];
}
}
Generally it works well, but if cache is build in CLI (e.g. during drush cron) it doesn't work. Use to check:
drush cr && drush ev '\Drupal::currentUser()->hasPermission("access content");'
Cache with ID "access_policies:drupal:[languages:language_interface]=en:[url.path]=/:[user.is_super_user]=0:[user.roles]=anonymous" is created without "access content" permissions because "$pathMatcher->isFrontPage()" returns 0 in CLI.
For a now we've added "route.name" extra cache key to avoid this behavior.
Created Drush issue - https://github.com/drush-ops/drush/issues/6352