3 namespace Drupal\Core\Access;
5 use Drupal\Core\Routing\Access\AccessInterface;
6 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
7 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
8 use Symfony\Component\Routing\Route;
9 use Symfony\Component\Routing\RouteCollection;
12 * Loads access checkers from the container.
14 class CheckProvider implements CheckProviderInterface, ContainerAwareInterface {
16 use ContainerAwareTrait;
19 * Array of registered access check service ids.
23 protected $checkIds = [];
26 * Array of access check objects keyed by service id.
28 * @var \Drupal\Core\Routing\Access\AccessInterface[]
33 * Array of access check method names keyed by service ID.
37 protected $checkMethods = [];
40 * Array of access checks which only will be run on the incoming request.
42 protected $checksNeedsRequest = [];
45 * An array to map static requirement keys to service IDs.
49 protected $staticRequirementMap;
52 * An array to map dynamic requirement keys to service IDs.
56 protected $dynamicRequirementMap;
61 public function addCheckService($service_id, $service_method, array $applies_checks = [], $needs_incoming_request = FALSE) {
62 $this->checkIds[] = $service_id;
63 $this->checkMethods[$service_id] = $service_method;
64 if ($needs_incoming_request) {
65 $this->checksNeedsRequest[$service_id] = $service_id;
67 foreach ($applies_checks as $applies_check) {
68 $this->staticRequirementMap[$applies_check][] = $service_id;
75 public function getChecksNeedRequest() {
76 return $this->checksNeedsRequest;
82 public function setChecks(RouteCollection $routes) {
83 $this->loadDynamicRequirementMap();
84 foreach ($routes as $route) {
85 if ($checks = $this->applies($route)) {
86 $route->setOption('_access_checks', $checks);
94 public function loadCheck($service_id) {
95 if (empty($this->checks[$service_id])) {
96 if (!in_array($service_id, $this->checkIds)) {
97 throw new \InvalidArgumentException(sprintf('No check has been registered for %s', $service_id));
100 $check = $this->container->get($service_id);
102 if (!($check instanceof AccessInterface)) {
103 throw new AccessException('All access checks must implement AccessInterface.');
105 if (!is_callable([$check, $this->checkMethods[$service_id]])) {
106 throw new AccessException(sprintf('Access check method %s in service %s must be callable.', $this->checkMethods[$service_id], $service_id));
109 $this->checks[$service_id] = $check;
111 return [$this->checks[$service_id], $this->checkMethods[$service_id]];
115 * Determine which registered access checks apply to a route.
117 * @param \Symfony\Component\Routing\Route $route
118 * The route to get list of access checks for.
121 * An array of service ids for the access checks that apply to passed
124 protected function applies(Route $route) {
127 // Iterate through map requirements from appliesTo() on access checkers.
128 // Only iterate through all checkIds if this is not used.
129 foreach ($route->getRequirements() as $key => $value) {
130 if (isset($this->staticRequirementMap[$key])) {
131 foreach ($this->staticRequirementMap[$key] as $service_id) {
132 $checks[] = $service_id;
136 // Finally, see if any dynamic access checkers apply.
137 foreach ($this->dynamicRequirementMap as $service_id) {
138 if ($this->checks[$service_id]->applies($route)) {
139 $checks[] = $service_id;
147 * Compiles a mapping of requirement keys to access checker service IDs.
149 protected function loadDynamicRequirementMap() {
150 if (isset($this->dynamicRequirementMap)) {
154 // Set them here, so we can use the isset() check above.
155 $this->dynamicRequirementMap = [];
157 foreach ($this->checkIds as $service_id) {
158 if (empty($this->checks[$service_id])) {
159 $this->loadCheck($service_id);
162 // Add the service ID to an array that will be iterated over.
163 if ($this->checks[$service_id] instanceof AccessCheckInterface) {
164 $this->dynamicRequirementMap[] = $service_id;