diff --git a/core/lib/Drupal/Core/Access/CustomAccessCheck.php b/core/lib/Drupal/Core/Access/CustomAccessCheck.php index e7a3b0891504b4b509dfc24c169e0a01a27869c4..353d09cd8060829bdd094ebbfec22bfc30a4a580 100644 --- a/core/lib/Drupal/Core/Access/CustomAccessCheck.php +++ b/core/lib/Drupal/Core/Access/CustomAccessCheck.php @@ -61,7 +61,14 @@ public function __construct(ControllerResolverInterface $controller_resolver, Ac * The access result. */ public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account) { - $callable = $this->controllerResolver->getControllerFromDefinition($route->getRequirement('_custom_access')); + try { + $callable = $this->controllerResolver->getControllerFromDefinition($route->getRequirement('_custom_access')); + } + catch (\InvalidArgumentException $e) { + // The custom access controller method was not found. + throw new \BadMethodCallException(sprintf('The "%s" method is not callable as a _custom_access callback in route "%s"', $route->getRequirement('_custom_access'), $route->getPath())); + } + $arguments_resolver = $this->argumentsResolverFactory->getArgumentsResolver($route_match, $account); $arguments = $arguments_resolver->getArguments($callable); diff --git a/core/tests/Drupal/Tests/Core/Access/CustomAccessCheckTest.php b/core/tests/Drupal/Tests/Core/Access/CustomAccessCheckTest.php index 9f0c2c9db40353650a29208d1290cb85dd93340d..aeb7f791bbe04828694bbed9c330bba983df1365 100644 --- a/core/tests/Drupal/Tests/Core/Access/CustomAccessCheckTest.php +++ b/core/tests/Drupal/Tests/Core/Access/CustomAccessCheckTest.php @@ -9,8 +9,13 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\CustomAccessCheck; +use Drupal\Core\Controller\ControllerResolver; +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\Session\AccountInterface; use Drupal\Tests\UnitTestCase; use Symfony\Component\Routing\Route; +use Drupal\Core\DependencyInjection\ClassResolverInterface; +use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; /** * @coversDefaultClass \Drupal\Core\Access\CustomAccessCheck @@ -106,6 +111,34 @@ public function testAccess() { $this->assertEquals(AccessResult::allowed(), $this->accessChecker->access($route, $route_match, $account)); } + /** + * Tests the access method exception for invalid access callbacks. + */ + public function testAccessException() { + // Create two mocks for the ControllerResolver constructor. + $httpMessageFactory = $this->getMockBuilder(HttpMessageFactoryInterface::class)->getMock(); + $controllerResolver = $this->getMockBuilder(ClassResolverInterface::class)->getMock(); + + // Re-create the controllerResolver mock with proxy to original methods. + $this->controllerResolver = $this->getMockBuilder(ControllerResolver::class) + ->setConstructorArgs([$httpMessageFactory, $controllerResolver]) + ->enableProxyingToOriginalMethods() + ->getMock(); + + // Overwrite the access checker using the newly mocked controller resolve. + $this->accessChecker = new CustomAccessCheck($this->controllerResolver, $this->argumentsResolverFactory); + + // Add a route with a _custom_access route that doesn't exist. + $route = new Route('/test-route', [], ['_custom_access' => '\Drupal\Tests\Core\Access\NonExistentController::nonExistentMethod']); + $route_match = $this->getMock(RouteMatchInterface::class); + $account = $this->getMock(AccountInterface::class); + + $this->setExpectedException(\BadMethodCallException::class, 'The "\Drupal\Tests\Core\Access\NonExistentController::nonExistentMethod" method is not callable as a _custom_access callback in route "/test-route"'); + + // Run the access check. + $this->accessChecker->access($route, $route_match, $account); + } + } class TestController {