d6ba1cf6dd3c77f05c11bc3197042c9ae9fd5125
[yaffs-website] / web / core / lib / Drupal / Core / Access / CheckProvider.php
1 <?php
2
3 namespace Drupal\Core\Access;
4
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;
10
11 /**
12  * Loads access checkers from the container.
13  */
14 class CheckProvider implements CheckProviderInterface, ContainerAwareInterface {
15
16   use ContainerAwareTrait;
17
18   /**
19    * Array of registered access check service ids.
20    *
21    * @var array
22    */
23   protected $checkIds = [];
24
25   /**
26    * Array of access check objects keyed by service id.
27    *
28    * @var \Drupal\Core\Routing\Access\AccessInterface[]
29    */
30   protected $checks;
31
32   /**
33    * Array of access check method names keyed by service ID.
34    *
35    * @var array
36    */
37   protected $checkMethods = [];
38
39   /**
40    * Array of access checks which only will be run on the incoming request.
41    */
42   protected $checksNeedsRequest = [];
43
44   /**
45    * An array to map static requirement keys to service IDs.
46    *
47    * @var array
48    */
49   protected $staticRequirementMap;
50
51   /**
52    * An array to map dynamic requirement keys to service IDs.
53    *
54    * @var array
55    */
56   protected $dynamicRequirementMap;
57
58   /**
59    * {@inheritdoc}
60    */
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;
66     }
67     foreach ($applies_checks as $applies_check) {
68       $this->staticRequirementMap[$applies_check][] = $service_id;
69     }
70   }
71
72   /**
73    * {@inheritdoc}
74    */
75   public function getChecksNeedRequest() {
76     return $this->checksNeedsRequest;
77   }
78
79   /**
80    * {@inheritdoc}
81    */
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);
87       }
88     }
89   }
90
91   /**
92    * {@inheritdoc}
93    */
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));
98       }
99
100       $check = $this->container->get($service_id);
101
102       if (!($check instanceof AccessInterface)) {
103         throw new AccessException('All access checks must implement AccessInterface.');
104       }
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));
107       }
108
109       $this->checks[$service_id] = $check;
110     }
111     return [$this->checks[$service_id], $this->checkMethods[$service_id]];
112   }
113
114   /**
115    * Determine which registered access checks apply to a route.
116    *
117    * @param \Symfony\Component\Routing\Route $route
118    *   The route to get list of access checks for.
119    *
120    * @return array
121    *   An array of service ids for the access checks that apply to passed
122    *   route.
123    */
124   protected function applies(Route $route) {
125     $checks = [];
126
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;
133         }
134       }
135     }
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;
140       }
141     }
142
143     return $checks;
144   }
145   /**
146    * Compiles a mapping of requirement keys to access checker service IDs.
147    */
148   protected function loadDynamicRequirementMap() {
149     if (isset($this->dynamicRequirementMap)) {
150       return;
151     }
152
153     // Set them here, so we can use the isset() check above.
154     $this->dynamicRequirementMap = [];
155
156     foreach ($this->checkIds as $service_id) {
157       if (empty($this->checks[$service_id])) {
158         $this->loadCheck($service_id);
159       }
160
161       // Add the service ID to an array that will be iterated over.
162       if ($this->checks[$service_id] instanceof AccessCheckInterface) {
163         $this->dynamicRequirementMap[] = $service_id;
164       }
165     }
166   }
167
168 }