Я начал изучать Symfony2. Я столкнулся с сомнением: если у меня есть этот маршрут:
# app/config/routing.yml
hello:
path: /hello/{name}
defaults: { _controller: AcmeHelloBundle:Hello:index }
И этот контроллер:
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction($name)
{
return new Response('<html><body>Ciao '.$name.'!</body></html>');
}
}
Внутренне Symfony2 (внутри app / bootstrap.php.cache) вызывает встроенную функцию PHP user_func_array ():
$arguments = $this->resolver->getArguments($request, $controller);
$response = call_user_func_array($controller, $arguments);
А вызов метода getArguments () возвращает массив аргументов для передачи в метод действия. Но если бы контроллер был:
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction($n)
{
return new Response('<html><body>Ciao '.$n.'!</body></html>');
}
}
Symfony пожаловался бы на исключение RuntimeException, поскольку параметр $ n не установлен.
Мой вопрос: как Symfony контролирует это поведение, я имею в виду, если у маршрута есть параметр {name}, почему у контроллера должен быть метод действия с параметром $ name и параметром должен называться $ name?
Причина в простом PHP, это будет работать:
$name = 'Alex';
function indexAction($n) {
echo $n;
}
$f = 'indexAction';
$arguments = array($name);
call_user_func_array($f, $arguments);
Даже если сигнатура функции принимает параметр с именем $ n, а не как $ name.
Я надеюсь, что этот вопрос понятен, если нет, пожалуйста, скажите мне, и я сделаю правку.
Спасибо за внимание!
Все это делается в контроллере Resolver HttpKernel / Controller / ControllerResolver.php с помощью getArguments()
& doArguments()
,
Для лучшего понимания вы найдете то, что вам нужно в Получение Аргументов Контроллера
Редактировать: Ответить на комментарий.
Использует ли Symfony класс ReflectionParameter для внутреннего отслеживания параметров сигнатуры метода и сопоставления их с параметрами маршрута?
Да, ControllerResolver использует:
ReflectionMethod отслеживать параметры подписи метода, если $controller
это метод
ReflectionObject если $controller
это объект
ReflectionFunction если $controller
это функция
Вот как:
public function getArguments(Request $request, $controller)
{
if (is_array($controller)) {
$r = new \ReflectionMethod($controller[0], $controller[1]);
} elseif (is_object($controller) && !$controller instanceof \Closure) {
$r = new \ReflectionObject($controller);
$r = $r->getMethod('__invoke');
} else {
$r = new \ReflectionFunction($controller);
}
return $this->doGetArguments($request, $controller, $r->getParameters());
}
В вашем примере у вас есть только один параметр в вашем действии, поэтому для нас очевидно, что его необходимо заполнить с маршрута.
Чтобы расширить ваш пример, если вы добавили еще один параметр в маршрут, например:
# app/config/routing.yml
hello:
path: /hello/{name}/{surname}
defaults: { _controller: AcmeHelloBundle:Hello:index }
И изменил свой контроллер на:
// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
public function indexAction($s, $n)
{
return new Response('<html><body>Ciao '.$n.' '.$s.'!</body></html>');
}
}
Symfony не знает, какая переменная $s
или же $n
для заполнения с каким параметром маршрута.
Если вы измените свое действие:
public function indexAction($surname, $name)
{
return new Response('<html><body>Ciao '.$name.' '.$surname.'!</body></html>');
}
Теперь Symfony может просматривать имена ваших аргументов и сопоставлять их с параметрами маршрута. Это также означает, что вы можете иметь indexAction
аргументы в любом порядке, если их имена соответствуют параметрам маршрута. Я полагаю, что Symfony внутренне использует API Reflection, чтобы понять все это.