Вход Регистрация
Файл: vendor/symfony/http-kernel/Controller/ControllerResolver.php
Строк: 382
<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace SymfonyComponentHttpKernelController;

use 
PsrLogLoggerInterface;
use 
SymfonyComponentHttpFoundationExceptionBadRequestException;
use 
SymfonyComponentHttpFoundationRequest;
use 
SymfonyComponentHttpKernelAttributeAsController;

/**
 * This implementation uses the '_controller' request attribute to determine
 * the controller to execute.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Tobias Schultze <http://tobion.de>
 */
class ControllerResolver implements ControllerResolverInterface
{
    private ?
LoggerInterface $logger;
    private array 
$allowedControllerTypes = [];
    private array 
$allowedControllerAttributes = [AsController::class => AsController::class];

    public function 
__construct(?LoggerInterface $logger null)
    {
        
$this->logger $logger;
    }

    
/**
     * @param array<class-string> $types
     * @param array<class-string> $attributes
     */
    
public function allowControllers(array $types = [], array $attributes = []): void
    
{
        foreach (
$types as $type) {
            
$this->allowedControllerTypes[$type] = $type;
        }

        foreach (
$attributes as $attribute) {
            
$this->allowedControllerAttributes[$attribute] = $attribute;
        }
    }

    
/**
     * @throws BadRequestException when the request has attribute "_check_controller_is_allowed" set to true and the controller is not allowed
     */
    
public function getController(Request $request): callable|false
    
{
        if (!
$controller $request->attributes->get('_controller')) {
            
$this->logger?->warning('Unable to look for the controller as the "_controller" parameter is missing.');

            return 
false;
        }

        if (
is_array($controller)) {
            if (isset(
$controller[0]) && is_string($controller[0]) && isset($controller[1])) {
                try {
                    
$controller[0] = $this->instantiateController($controller[0]);
                } catch (
Error|LogicException $e) {
                    if (
is_callable($controller)) {
                        return 
$this->checkController($request$controller);
                    }

                    throw 
$e;
                }
            }

            if (!
is_callable($controller)) {
                throw new 
InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '$request->getPathInfo()).$this->getControllerError($controller));
            }

            return 
$this->checkController($request$controller);
        }

        if (
is_object($controller)) {
            if (!
is_callable($controller)) {
                throw new 
InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '$request->getPathInfo()).$this->getControllerError($controller));
            }

            return 
$this->checkController($request$controller);
        }

        if (
function_exists($controller)) {
            return 
$this->checkController($request$controller);
        }

        try {
            
$callable $this->createController($controller);
        } catch (
InvalidArgumentException $e) {
            throw new 
InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '$request->getPathInfo()).$e->getMessage(), 0$e);
        }

        if (!
is_callable($callable)) {
            throw new 
InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '$request->getPathInfo()).$this->getControllerError($callable));
        }

        return 
$this->checkController($request$callable);
    }

    
/**
     * Returns a callable for the given controller.
     *
     * @throws InvalidArgumentException When the controller cannot be created
     */
    
protected function createController(string $controller): callable
    {
        if (!
str_contains($controller'::')) {
            
$controller $this->instantiateController($controller);

            if (!
is_callable($controller)) {
                throw new 
InvalidArgumentException($this->getControllerError($controller));
            }

            return 
$controller;
        }

        [
$class$method] = explode('::'$controller2);

        try {
            
$controller = [$this->instantiateController($class), $method];
        } catch (
Error|LogicException $e) {
            try {
                if ((new 
ReflectionMethod($class$method))->isStatic()) {
                    return 
$class.'::'.$method;
                }
            } catch (
ReflectionException) {
                throw 
$e;
            }

            throw 
$e;
        }

        if (!
is_callable($controller)) {
            throw new 
InvalidArgumentException($this->getControllerError($controller));
        }

        return 
$controller;
    }

    
/**
     * Returns an instantiated controller.
     */
    
protected function instantiateController(string $class): object
    
{
        return new 
$class();
    }

    private function 
getControllerError(mixed $callable): string
    
{
        if (
is_string($callable)) {
            if (
str_contains($callable'::')) {
                
$callable explode('::'$callable2);
            } else {
                return 
sprintf('Function "%s" does not exist.'$callable);
            }
        }

        if (
is_object($callable)) {
            
$availableMethods $this->getClassMethodsWithoutMagicMethods($callable);
            
$alternativeMsg $availableMethods sprintf(' or use one of the available methods: "%s"'implode('", "'$availableMethods)) : '';

            return 
sprintf('Controller class "%s" cannot be called without a method name. You need to implement "__invoke"%s.'get_debug_type($callable), $alternativeMsg);
        }

        if (!
is_array($callable)) {
            return 
sprintf('Invalid type for controller given, expected string, array or object, got "%s".'get_debug_type($callable));
        }

        if (!isset(
$callable[0]) || !isset($callable[1]) || !== count($callable)) {
            return 
'Invalid array callable, expected [controller, method].';
        }

        [
$controller$method] = $callable;

        if (
is_string($controller) && !class_exists($controller)) {
            return 
sprintf('Class "%s" does not exist.'$controller);
        }

        
$className is_object($controller) ? get_debug_type($controller) : $controller;

        if (
method_exists($controller$method)) {
            return 
sprintf('Method "%s" on class "%s" should be public and non-abstract.'$method$className);
        }

        
$collection $this->getClassMethodsWithoutMagicMethods($controller);

        
$alternatives = [];

        foreach (
$collection as $item) {
            
$lev levenshtein($method$item);

            if (
$lev <= strlen($method) / || str_contains($item$method)) {
                
$alternatives[] = $item;
            }
        }

        
asort($alternatives);

        
$message sprintf('Expected method "%s" on class "%s"'$method$className);

        if (
count($alternatives) > 0) {
            
$message .= sprintf(', did you mean "%s"?'implode('", "'$alternatives));
        } else {
            
$message .= sprintf('. Available methods: "%s".'implode('", "'$collection));
        }

        return 
$message;
    }

    private function 
getClassMethodsWithoutMagicMethods($classOrObject): array
    {
        
$methods get_class_methods($classOrObject);

        return 
array_filter($methodsfn (string $method) => !== strncmp($method'__'2));
    }

    private function 
checkController(Request $request, callable $controller): callable
    {
        if (!
$request->attributes->get('_check_controller_is_allowed'false)) {
            return 
$controller;
        }

        
$r null;

        if (
is_array($controller)) {
            [
$class$name] = $controller;
            
$name = (is_string($class) ? $class $class::class).'::'.$name;
        } elseif (
is_object($controller) && !$controller instanceof Closure) {
            
$class $controller;
            
$name $class::class.'::__invoke';
        } else {
            
$r = new ReflectionFunction($controller);
            
$name $r->name;

            if (
str_contains($name'{closure')) {
                
$name $class Closure::class;
            } elseif (
$class PHP_VERSION_ID >= 80111 $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
                
$class $class->name;
                
$name $class.'::'.$name;
            }
        }

        if (
$class) {
            foreach (
$this->allowedControllerTypes as $type) {
                if (
is_a($class$typetrue)) {
                    return 
$controller;
                }
            }
        }

        
$r ??= new ReflectionClass($class);

        foreach (
$r->getAttributes() as $attribute) {
            if (isset(
$this->allowedControllerAttributes[$attribute->getName()])) {
                return 
$controller;
            }
        }

        if (
str_contains($name'@anonymous')) {
            
$name preg_replace_callback('/[a-zA-Z_x7f-xff][\\a-zA-Z0-9_x7f-xff]*+@anonymousx00.*?.php(?:0x?|:[0-9]++$)?[0-9a-fA-F]++/'fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' $m[0], $name);
        }

        if (-
=== $request->attributes->get('_check_controller_is_allowed')) {
            
trigger_deprecation('symfony/http-kernel''6.4''Callable "%s()" is not allowed as a controller. Did you miss tagging it with "#[AsController]" or registering its type with "%s::allowControllers()"?'$nameself::class);

            return 
$controller;
        }

        throw new 
BadRequestException(sprintf('Callable "%s()" is not allowed as a controller. Did you miss tagging it with "#[AsController]" or registering its type with "%s::allowControllers()"?'$nameself::class));
    }
}
Онлайн: 1
Реклама