CheckProvider.php

Same filename in other branches
  1. 9 core/lib/Drupal/Core/Access/CheckProvider.php
  2. 10 core/lib/Drupal/Core/Access/CheckProvider.php
  3. 11.x core/lib/Drupal/Core/Access/CheckProvider.php

Namespace

Drupal\Core\Access

File

core/lib/Drupal/Core/Access/CheckProvider.php

View source
<?php

namespace Drupal\Core\Access;

use Drupal\Core\Routing\Access\AccessInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

/**
 * Loads access checkers from the container.
 */
class CheckProvider implements CheckProviderInterface, ContainerAwareInterface {
    use ContainerAwareTrait;
    
    /**
     * Array of registered access check service ids.
     *
     * @var array
     */
    protected $checkIds = [];
    
    /**
     * Array of access check objects keyed by service id.
     *
     * @var \Drupal\Core\Routing\Access\AccessInterface[]
     */
    protected $checks;
    
    /**
     * Array of access check method names keyed by service ID.
     *
     * @var array
     */
    protected $checkMethods = [];
    
    /**
     * Array of access checks which only will be run on the incoming request.
     */
    protected $checksNeedsRequest = [];
    
    /**
     * An array to map static requirement keys to service IDs.
     *
     * @var array
     */
    protected $staticRequirementMap;
    
    /**
     * An array to map dynamic requirement keys to service IDs.
     *
     * @var array
     */
    protected $dynamicRequirementMap;
    
    /**
     * {@inheritdoc}
     */
    public function addCheckService($service_id, $service_method, array $applies_checks = [], $needs_incoming_request = FALSE) {
        $this->checkIds[] = $service_id;
        $this->checkMethods[$service_id] = $service_method;
        if ($needs_incoming_request) {
            $this->checksNeedsRequest[$service_id] = $service_id;
        }
        foreach ($applies_checks as $applies_check) {
            $this->staticRequirementMap[$applies_check][] = $service_id;
        }
    }
    
    /**
     * {@inheritdoc}
     */
    public function getChecksNeedRequest() {
        return $this->checksNeedsRequest;
    }
    
    /**
     * {@inheritdoc}
     */
    public function setChecks(RouteCollection $routes) {
        $this->loadDynamicRequirementMap();
        foreach ($routes as $route) {
            if ($checks = $this->applies($route)) {
                $route->setOption('_access_checks', $checks);
            }
        }
    }
    
    /**
     * {@inheritdoc}
     */
    public function loadCheck($service_id) {
        if (empty($this->checks[$service_id])) {
            if (!in_array($service_id, $this->checkIds)) {
                throw new \InvalidArgumentException(sprintf('No check has been registered for %s', $service_id));
            }
            $check = $this->container
                ->get($service_id);
            if (!$check instanceof AccessInterface) {
                throw new AccessException('All access checks must implement AccessInterface.');
            }
            if (!is_callable([
                $check,
                $this->checkMethods[$service_id],
            ])) {
                throw new AccessException(sprintf('Access check method %s in service %s must be callable.', $this->checkMethods[$service_id], $service_id));
            }
            $this->checks[$service_id] = $check;
        }
        return [
            $this->checks[$service_id],
            $this->checkMethods[$service_id],
        ];
    }
    
    /**
     * Determine which registered access checks apply to a route.
     *
     * @param \Symfony\Component\Routing\Route $route
     *   The route to get list of access checks for.
     *
     * @return array
     *   An array of service ids for the access checks that apply to passed
     *   route.
     */
    protected function applies(Route $route) {
        $checks = [];
        // Iterate through map requirements from appliesTo() on access checkers.
        // Only iterate through all checkIds if this is not used.
        foreach ($route->getRequirements() as $key => $value) {
            if (isset($this->staticRequirementMap[$key])) {
                foreach ($this->staticRequirementMap[$key] as $service_id) {
                    $checks[] = $service_id;
                }
            }
        }
        // Finally, see if any dynamic access checkers apply.
        foreach ($this->dynamicRequirementMap as $service_id) {
            if ($this->checks[$service_id]
                ->applies($route)) {
                $checks[] = $service_id;
            }
        }
        return $checks;
    }
    
    /**
     * Compiles a mapping of requirement keys to access checker service IDs.
     */
    protected function loadDynamicRequirementMap() {
        if (isset($this->dynamicRequirementMap)) {
            return;
        }
        // Set them here, so we can use the isset() check above.
        $this->dynamicRequirementMap = [];
        foreach ($this->checkIds as $service_id) {
            if (empty($this->checks[$service_id])) {
                $this->loadCheck($service_id);
            }
            // Add the service ID to an array that will be iterated over.
            if ($this->checks[$service_id] instanceof AccessCheckInterface) {
                $this->dynamicRequirementMap[] = $service_id;
            }
        }
    }

}

Classes

Title Deprecated Summary
CheckProvider Loads access checkers from the container.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.