【问题标题】:How does Symfony2 passes the parameter of a URI to the controller Action method?Symfony2 如何将一个 URI 的参数传递给控制器​​的 Action 方法?
【发布时间】:2014-11-27 06:50:45
【问题描述】:

我已经开始学习 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 内)调用调用 user_func_array() PHP 内置函数:

$arguments = $this->resolver->getArguments($request, $controller);
$response = call_user_func_array($controller, $arguments);

并且对 getArguments() 方法的调用返回一个参数数组以传递给 action 方法。但如果控制器是:

// 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 的参数。

我希望这个问题可以理解,如果不是,请告诉我,我会进行编辑。

感谢关注!

【问题讨论】:

    标签: php symfony controller action


    【解决方案1】:

    这一切都在控制器解析器HttpKernel/Controller/ControllerResolver.php 中通过getArguments() 和doArguments() 完成。

    为了更好的理解,你会在Getting The Controller Arguments找到你需要的东西


    编辑:回答评论。

    Symfony 是否在内部使用 ReflectionParameter 类来跟踪方法签名的参数并将它们与路由参数匹配?

    是的,ControllerResolver 使用:

    方法如下:

     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());
        }
    

    【讨论】:

    • Symfone 是否在内部使用ReflectionParameter 类来跟踪方法签名的参数并将它们与路由参数匹配?
    • 感谢您的说明!
    • 对不起,我有最后一个问题。所以 Symfony 会在每个请求时进行控制(无论您是在开发环境还是生产环境中),对吗?
    【解决方案2】:
    1. 请求由 Symfony 前端控制器处理(例如 app.php);
    2. Symfony 核心(即内核)要求路由器检查请求;
    3. 路由器将传入的 URL 与特定路由匹配,并返回有关该路由的信息,包括应执行的控制器;
    4. Symfony 内核执行控制器,最终返回一个 Response 对象。

    Resource

    【讨论】:

    • 是的,我知道Symfony处理请求,我不明白的是Symfony如何保证路由的参数与动作控制器方法的参数完全对应,即如果我有这个路由path: /hello/{firstName}/{lastName} 与 HelloController::indexAction($firstName, $lastName) 匹配,symfony 如何控制 indexAction 控制器方法所需的每个参数必须对应于路由的参数?为什么像 HelloController::indexAction($first_name, $last_name) 这样的方法会抛出 RuntimeException? Symf 有吗?使用反射类?
    • 是的 @user3019105,看我的回答
    【解决方案3】:

    在您的示例中,您的操作只有一个参数,因此我们很明显需要从路由中填充它。

    为了扩展您的示例,如果您在路由中添加了另一个参数,例如:

    # 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 来解决这一切。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-19
      • 1970-01-01
      • 2017-04-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多