MenuRouterRebuildSubscriber.php 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. <?php
  2. namespace Drupal\Core\EventSubscriber;
  3. use Drupal\Core\Cache\Cache;
  4. use Drupal\Core\Database\ReplicaKillSwitch;
  5. use Drupal\Core\Lock\LockBackendInterface;
  6. use Drupal\Core\Menu\MenuLinkManagerInterface;
  7. use Drupal\Core\Routing\RoutingEvents;
  8. use Symfony\Component\EventDispatcher\Event;
  9. use Drupal\Core\Database\Connection;
  10. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  11. /**
  12. * Rebuilds the default menu links and runs menu-specific code if necessary.
  13. */
  14. class MenuRouterRebuildSubscriber implements EventSubscriberInterface {
  15. /**
  16. * @var \Drupal\Core\Lock\LockBackendInterface
  17. */
  18. protected $lock;
  19. /**
  20. * The menu link plugin manager.
  21. *
  22. * @var \Drupal\Core\Menu\MenuLinkManagerInterface
  23. */
  24. protected $menuLinkManager;
  25. /**
  26. * The replica kill switch.
  27. *
  28. * @var \Drupal\Core\Database\ReplicaKillSwitch
  29. */
  30. protected $replicaKillSwitch;
  31. /**
  32. * The database connection.
  33. *
  34. * @var \Drupal\Core\Database\Connection
  35. */
  36. protected $connection;
  37. /**
  38. * Constructs the MenuRouterRebuildSubscriber object.
  39. *
  40. * @param \Drupal\Core\Lock\LockBackendInterface $lock
  41. * The lock backend.
  42. * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
  43. * The menu link plugin manager.
  44. * @param \Drupal\Core\Database\Connection $connection
  45. * The database connection.
  46. * @param \Drupal\Core\Database\ReplicaKillSwitch $replica_kill_switch
  47. * The replica kill switch.
  48. */
  49. public function __construct(LockBackendInterface $lock, MenuLinkManagerInterface $menu_link_manager, Connection $connection, ReplicaKillSwitch $replica_kill_switch) {
  50. $this->lock = $lock;
  51. $this->menuLinkManager = $menu_link_manager;
  52. $this->connection = $connection;
  53. $this->replicaKillSwitch = $replica_kill_switch;
  54. }
  55. /**
  56. * Rebuilds the menu links and deletes the local_task cache tag.
  57. *
  58. * @param \Symfony\Component\EventDispatcher\Event $event
  59. * The event object.
  60. */
  61. public function onRouterRebuild(Event $event) {
  62. $this->menuLinksRebuild();
  63. Cache::invalidateTags(['local_task']);
  64. }
  65. /**
  66. * Perform menu-specific rebuilding.
  67. */
  68. protected function menuLinksRebuild() {
  69. if ($this->lock->acquire(__FUNCTION__)) {
  70. $transaction = $this->connection->startTransaction();
  71. try {
  72. // Ensure the menu links are up to date.
  73. $this->menuLinkManager->rebuild();
  74. // Ignore any database replicas temporarily.
  75. $this->replicaKillSwitch->trigger();
  76. }
  77. catch (\Exception $e) {
  78. $transaction->rollBack();
  79. watchdog_exception('menu', $e);
  80. }
  81. $this->lock->release(__FUNCTION__);
  82. }
  83. else {
  84. // Wait for another request that is already doing this work.
  85. // We choose to block here since otherwise the router item may not
  86. // be available during routing resulting in a 404.
  87. $this->lock->wait(__FUNCTION__);
  88. }
  89. }
  90. /**
  91. * {@inheritdoc}
  92. */
  93. public static function getSubscribedEvents() {
  94. // Run after CachedRouteRebuildSubscriber.
  95. $events[RoutingEvents::FINISHED][] = ['onRouterRebuild', 100];
  96. return $events;
  97. }
  98. }