HttpKernel组件:控制器解析器
编辑本页警告:您正在浏览的文档欧宝体育电话欧宝娱乐app下载地址Symfony 5.2,现已不再维护。
读本页的更新版本用于Sy欧宝娱乐app下载地址mfony 6.2(当前稳定版本)。
HttpKernel组件:控制器解析器
您可能认为我们的框架已经相当可靠,您可能是对的。但让我们看看如何改进它。
现在,我们所有的示例都使用过程性代码,但请记住,控制器可以是任何有效的PHP回调。让我们把控制器转换成一个合适的类:
1 2 3 4 5 6 7 8 9 10 11
类LeapYearController{公共函数指数($请求){如果(is_leap_year ($请求->属性->get (“年”))) {返回新响应(“是的,今年是闰年!”);}返回新响应(“不,今年不是闰年。”);}}
更新相应的路由定义:
1 2 3 4
$路线->add (“leap_year”,新路由\路线(“/ is_leap_year /{一}”, (“年”= >零,“_controller”= > [新LeapYearController (),“指数”)));
这一举措非常简单,一旦你创建更多的页面,就会有很多意义,但你可能已经注意到一个不理想的副作用……的LeapYearController
类是总是实例化,即使所请求的URL不匹配leap_year
路线。这很糟糕,主要原因是:在性能方面,所有路由的所有控制器现在都必须为每个请求实例化。如果控制器是惰性加载的,那么只有与匹配路由相关联的控制器才会被实例化。
为了解决这个问题,让我们安装并使用HttpKernel组件:
1
$编译器需要symfony/http欧宝娱乐app下载地址-kernel
HttpKernel组件有许多有趣的特性,但是我们现在需要的是控制器解析器而且参数解析器.控制器解析器知道如何确定要执行的控制器,参数解析器根据Request对象确定要传递给它的参数。所有控制器解析器实现以下接口:
1 2 3 4 5 6 7
名称空间欧宝娱乐app下载地址\组件\HttpKernel\控制器;/ /……接口ControllerResolverInterface{公共函数getController(请求$请求);}
的getController ()
方法依赖于与前面定义的约定相同的约定:_controller
request属性必须包含与request相关的控制器。除了内置的PHP回调,getController ()
也支持由类名后跟两个冒号和方法名组成的字符串作为有效的回调,如'class::method':
1 2 3 4
$路线->add (“leap_year”,新路由\路线(“/ is_leap_year /{一}”, (“年”= >零,“_controller”= >“LeapYearController:指数”)));
要使这段代码工作,修改框架代码以使用HttpKernel中的控制器解析器:
1 2 3 4 5 6 7 8 9
使用欧宝娱乐app下载地址\组件\HttpKernel;$controllerResolver=新HttpKernel \控制器\ ControllerResolver ();$argumentResolver=新HttpKernel \控制器\ ArgumentResolver ();$控制器=$controllerResolver->getController ($请求);$参数=$argumentResolver->getArguments ($请求,$控制器);$响应=中的call_user_func_array ($控制器,$参数);
请注意
作为额外的奖励,控制器解析器正确地为您处理错误管理:当您忘记定义_controller
属性为路由实例。
现在,让我们看看如何猜测控制器参数。getArguments ()
自省控制器签名,以确定通过使用本机PHP将哪些参数传递给它反射.该方法定义在如下接口中:
1 2 3 4 5 6 7
名称空间欧宝娱乐app下载地址\组件\HttpKernel\控制器;/ /……接口ArgumentResolverInterface{公共函数getArguments(请求$请求,$控制器);}
的index ()
方法需要Request对象作为参数。getArguments ()
知道什么时候正确地注入它,如果它是正确的类型提示:
1 2 3 4
公共函数指数(请求$请求)//赢得了't工作公共函数指数($请求)
更有趣的是,getArguments ()
也可以注入任何Request属性;如果参数与对应的属性有相同的名称:
1
公共函数指数($一年)
你也可以同时注入Request和一些属性(因为匹配是在参数名或类型提示上完成的,参数的顺序不重要):
1 2 3
公共函数指数(请求$请求,$一年)公共函数指数($一年,请求$请求)
最后,你还可以为任何匹配Request的可选属性的参数定义默认值:
1
公共函数指数($一年=2012)
让我们注入美元一年
请求属性为我们的控制器:
1 2 3 4 5 6 7 8 9 10 11
类LeapYearController{公共函数指数($一年){如果(is_leap_year ($一年)) {返回新响应(“是的,今年是闰年!”);}返回新响应(“不,今年不是闰年。”);}}
解析器还负责验证控制器可调用对象及其参数。如果出现问题,它会抛出一个异常,并附上一条解释问题的漂亮消息(控制器类不存在,方法没有定义,参数没有匹配属性,等等)。
请注意
有了默认控制器解析器和参数解析器的巨大灵活性,您可能想知道为什么有人想要创建另一个(如果没有接口,为什么还要有接口?)两个例子:在Symfony中,欧宝娱乐app下载地址getController ()
增强以支持控制器即服务;而且getArguments ()
提供一个扩展点来改变或增强参数的解析。
让我们用框架的新版本来总结:
12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/ / example.com/web/front.phprequire_once__DIR__.“/ . . /供应商/ autoload.php”;使用欧宝娱乐app下载地址\组件\HttpFoundation\请求;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;使用欧宝娱乐app下载地址\组件\HttpKernel;使用欧宝娱乐app下载地址\组件\路由;函数render_template(请求$请求){提取($请求->属性->所有(),EXTR_SKIP);ob_start ();包括sprintf (__DIR__.“/ . . / src /页面/ % s.php ',$_route);返回新响应(ob_get_clean ());}$请求=请求::createFromGlobals ();$路线=包括__DIR__.“/ . . / src / app.php”;$上下文=新路由\ RequestContext ();$上下文->fromRequest ($请求);$匹配器=新路由\匹配器\ UrlMatcher ($路线,$上下文);$controllerResolver=新HttpKernel \控制器\ ControllerResolver ();$argumentResolver=新HttpKernel \控制器\ ArgumentResolver ();试一试{$请求->属性->add ($匹配器->匹配($请求->getPathInfo ()));$控制器=$controllerResolver->getController ($请求);$参数=$argumentResolver->getArguments ($请求,$控制器);$响应=中的call_user_func_array ($控制器,$参数);}抓(路由\异常\ ResourceNotFoundException$异常){$响应=新响应(“没有找到”,404);}抓(异常$异常){$响应=新响应(“发生错误”,500);}$响应->send ();
再想想:我们的框架比以往任何时候都更健壮、更灵活,而它的代码还不到50行。