在phoenix项目下创建Core文件夹,继续创建Phoenix.php文件,移入核心代码并优化。
# Phoenix.php namespace Core; #注意此命名空间需要注册 use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\HttpKernel\Controller\ControllerResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver; class Phoenix { public $request; public $routeMap; public function handle(Request $request) { $this->request = $request; try { //url map $this->getRouteMap(); $this->setRequestRoute(); $controller = (new ControllerResolver())->getController($request); $arguments = (new ArgumentResolver())->getArguments($request, $controller); return call_user_func_array($controller, $arguments); } catch(\Exception $e) { return new Response('File Not Found', 404); } } public function setRequestRoute() { $this->request->attributes->add($this->routeMap->match($this->request->getPathInfo())); } public function getRouteMap() { $this->routeMap = new UrlMatcher( $this->getCollection(), (new RequestContext())->fromRequest($this->request) ); } public function getCollection() { return ( new YamlFileLoader( new FileLocator(array(getcwd())) ) )->load('routes/web.yaml'); } }更新index.php代码。
ini_set('display_errors', 1); error_reporting(-1); require_once __DIR__.'/vendor/autoload.php'; $kernel = new Core\Phoenix(); $response = $kernel->handle( Symfony\Component\HttpFoundation\Request::createFromGlobals() ); $response->send();注册Core命名空间,打开composer.json文件。
# composer.json "autoload": { "psr-4": { "App\\": "app/", "Core\\": "core/" } } composer dump-autoload # 更新命名空间刷新页面,显示正常。
九、优化框架在前面用到HttpKernel组件时,为什么介绍它是框架的内核呢?
因为HttpKernel里面有个很重要的概念,派遣事件,给注册过的不同监听器监听。
是用Mediator模式设计的,这种模式带来的好处,就是使框架的扩展性得到极大的提高。
在请求到响应之前设计了八种钩子,方便后期扩展,详情看下面的链接。
KernelEvents钩子介绍
同时,也可以用另一种监听事件的方式,通过一个event subscriber(事件订阅器),向派遣器精确通报它要订阅哪些事件。下面对路由优化时,会用到这。
EventDispatcher组件使用说明
HttpKernel组件的功能仅止于此吗? 当然不,它里面有一个很重要的类“HttpKernel类”,将框架的核心Core/Phoenix.php的程序都实现了。
只要phoenix框架核心类Phoenix继承HttpKernel,并调用它的构造方法就行了。
下面来改造Core/Phoenix.php代码。
# Phoenix.php namespace Core; use Symfony\Component\HttpFoundation\RequestStack; # add use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\EventDispatcher\EventDispatcher; # add use Symfony\Component\HttpKernel\HttpKernel; # add use Symfony\Component\HttpKernel\Controller\ControllerResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver; use Symfony\Component\HttpKernel\EventListener\RouterListener; class Phoenix extends HttpKernel{ # 继承HttpKernel public function __construct() { $matcher = new UrlMatcher($this->getCollection(), new RequestContext()); $requestStack = new RequestStack(); $dispatcher = new EventDispatcher(); $dispatcher->addSubscriber(new RouterListener($matcher, $requestStack)); # 订阅路由 # HttpKernel的构造函数,可以点下面的链接进去看看 parent::__construct( $dispatcher, new ControllerResolver(), $requestStack, new ArgumentResolver() ); } public function getCollection() { return ( new YamlFileLoader( new FileLocator(array(getcwd())) ) )->load('routes/web.yaml'); } }HttpKernel类
index.php的代码不用变,HttpKernel类里面也有handle方法。建议同学们看看HttpKernel类的源码。
十、依赖注入(Dependency Injection)Phoenix类继承了HttpKernel,是整个架构的核心,在框架里面定义了“路由监听”,但如果框架不仅仅要对路由进行监听,还要对response阶段进行监听呢?是不是继续修改Phoenix类呢?
这样的设计对于框架来说,是绝对不友好的。那有没有方法解决呢?
当然有,可以通过在外面注入对象,框架通过type检测,自动引入相关对象。
首先下载Symfony的DependencyInjection组件。
composer require symfony/dependency-injection