AliasWhitelist.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <?php
  2. namespace Drupal\Core\Path;
  3. use Drupal\Core\Cache\CacheBackendInterface;
  4. use Drupal\Core\Cache\CacheCollector;
  5. use Drupal\Core\State\StateInterface;
  6. use Drupal\Core\Lock\LockBackendInterface;
  7. /**
  8. * Extends CacheCollector to build the path alias whitelist over time.
  9. */
  10. class AliasWhitelist extends CacheCollector implements AliasWhitelistInterface {
  11. /**
  12. * The Key/Value Store to use for state.
  13. *
  14. * @var \Drupal\Core\State\StateInterface
  15. */
  16. protected $state;
  17. /**
  18. * The Path CRUD service.
  19. *
  20. * @var \Drupal\Core\Path\AliasStorageInterface
  21. */
  22. protected $aliasStorage;
  23. /**
  24. * Constructs an AliasWhitelist object.
  25. *
  26. * @param string $cid
  27. * The cache id to use.
  28. * @param \Drupal\Core\Cache\CacheBackendInterface $cache
  29. * The cache backend.
  30. * @param \Drupal\Core\Lock\LockBackendInterface $lock
  31. * The lock backend.
  32. * @param \Drupal\Core\State\StateInterface $state
  33. * The state keyvalue store.
  34. * @param \Drupal\Core\Path\AliasStorageInterface $alias_storage
  35. * The alias storage service.
  36. */
  37. public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, StateInterface $state, AliasStorageInterface $alias_storage) {
  38. parent::__construct($cid, $cache, $lock);
  39. $this->state = $state;
  40. $this->aliasStorage = $alias_storage;
  41. }
  42. /**
  43. * {@inheritdoc}
  44. */
  45. protected function lazyLoadCache() {
  46. parent::lazyLoadCache();
  47. // On a cold start $this->storage will be empty and the whitelist will
  48. // need to be rebuilt from scratch. The whitelist is initialized from the
  49. // list of all valid path roots stored in the 'router.path_roots' state,
  50. // with values initialized to NULL. During the request, each path requested
  51. // that matches one of these keys will be looked up and the array value set
  52. // to either TRUE or FALSE. This ensures that paths which do not exist in
  53. // the router are not looked up, and that paths that do exist in the router
  54. // are only looked up once.
  55. if (empty($this->storage)) {
  56. $this->loadMenuPathRoots();
  57. }
  58. }
  59. /**
  60. * Loads menu path roots to prepopulate cache.
  61. */
  62. protected function loadMenuPathRoots() {
  63. if ($roots = $this->state->get('router.path_roots')) {
  64. foreach ($roots as $root) {
  65. $this->storage[$root] = NULL;
  66. $this->persist($root);
  67. }
  68. }
  69. }
  70. /**
  71. * {@inheritdoc}
  72. */
  73. public function get($offset) {
  74. $this->lazyLoadCache();
  75. // this may be called with paths that are not represented by menu router
  76. // items such as paths that will be rewritten by hook_url_outbound_alter().
  77. // Therefore internally TRUE is used to indicate whitelisted paths. FALSE is
  78. // used to indicate paths that have already been checked but are not
  79. // whitelisted, and NULL indicates paths that have not been checked yet.
  80. if (isset($this->storage[$offset])) {
  81. if ($this->storage[$offset]) {
  82. return TRUE;
  83. }
  84. }
  85. elseif (array_key_exists($offset, $this->storage)) {
  86. return $this->resolveCacheMiss($offset);
  87. }
  88. }
  89. /**
  90. * {@inheritdoc}
  91. */
  92. public function resolveCacheMiss($root) {
  93. $exists = $this->aliasStorage->pathHasMatchingAlias('/' . $root);
  94. $this->storage[$root] = $exists;
  95. $this->persist($root);
  96. if ($exists) {
  97. return TRUE;
  98. }
  99. }
  100. /**
  101. * {@inheritdoc}
  102. */
  103. public function clear() {
  104. parent::clear();
  105. $this->loadMenuPathRoots();
  106. }
  107. }