背景
一次無意的訪問,點擊到了一個專門做PHP性能測試的網站,看這里PHP Benchmarks。
在里面發現了框架性能測試的結果,發現Laravel的框架性能盡然是最低的。瞬間受到了一萬點的暴擊,誰讓最近一直用Laravel開發項目的呢。
說到底還是Laravel好用呀,方便不說,各方面支持的也不錯,業務方面做的也是內部系統,哪怕性能慢點,也可以用前后端分離、負載均衡等手段解決掉,大體上也是夠用。
不過,作為一個開發人員,理想還是要有的,這時就在想能不能采取Laravel框架的優點,用到什么就裝什么,去掉一些請求到響應之間用不到的組件,精簡框架。
之前也熟讀過Laravel的源碼,知道它的底層用的是Symfony的組件,畢竟沒必要重復的造輪子。那么我們的框架之旅也將基於Symfony組件。。。
目錄
一、Composer運行機制
二、框架前期准備
三、HttpFoundation組件封裝Request、Response
四、路由處理
五、控制器處理相應功能(C)
六、分離模板(V)
七、分離模型(M)
八、剝離核心代碼
九、優化框架
十、依賴注入(Dependency Injection)
正文
一、Composer運行機制
Composer的使用最關鍵的得益於PHP標准規范的出現,特別是其中的psr4,自動加載規范,規范了如何指定文件路徑從而自動加載類定義,以及自動加載文件的位置。
既然講到php文件的加載,我們就要聊一聊PHP的加載機制了。
在早前時,加載文件用的都是include、require,但這種加載有很大的局限性,相信同學們都知道,無論用到用不到都要加載大量的文件,相當繁瑣。
於是就出現了autoload加載機制,它可以實現懶加載。
function __autoload($class)
{
require_once ($class.".php");
}
當程序引用了未加載的類,就會自動調用__autoload方法,只要維護了__autoload方法,就可以懶加載文件。
但這里有一個很大的問題,就是程序中只能定義一次__autoload,這就需要花大盡力在__autoload中維護文件和空間的對應關系,特別是在大型項目,多人合作中更是繁瑣。
而解決這個問題就是SPL Autoload。
SPL Autoload:__autoload調用堆棧。
怎么理解這個堆棧呢,舉個例子。
現有的框架比如ThinkPHP、Laravel等都有一個vendor目錄,用於存放第三方庫,現在vendor下有兩個庫。
monolog 處理系統日志
guzzlehttp 處理HTTP
當程序引用這兩個庫的命名空間,並調用monolog、guzzlehttp下面的類時,發現調用的類文件都能被找到。
這主要原理是monolog、guzzlehttp都自定義了類似autoload的方法,然后用spl_autoload_register將方法注冊到了SPL堆棧中。
這樣的話,當程序調用類的時候,就會統一到SPL堆棧中尋找注冊到堆棧中的autoload方法,並加載相應的文件。
以上就是php加載文件的方式,下面就用實戰談一談composer的運行機制。
創建composer項目
# mkdir phoenix
# cd phoenix
composer init
phoenix是接下來搭建的框架名。
創建成功后,發現當前文件夾下會生成一個composer.json文件,里面是剛寫入的內容。
composer dump
tree后,就會發現多了一個vendor的目錄,里面的autoload.php以及composer文件夾下文件就是整個框架的加載核心。
接下來看一遍這些文件。
在整個框架中,第一行必然要引用 vendor/autoload.php 文件,畢竟這是加載核心,那么就從autoload.php看起。
# autoload.php
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit599fa618dd1395bdde5fc3a08ff3e4e6::getLoader();
只調用了autoload_real.php里面的getLoader()方法。
#autoload_real.php 精簡后的代碼
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
#創建ClassLoader類
spl_autoload_register(array('ComposerAutoloaderInit599fa618dd1395bdde5fc3a08ff3e4e6', 'loadClassLoader'), true, true);
#初始化ClassLoader對象(主要就是將命名空間和文件的映射寫入ClassLoader的屬性中)
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit599fa618dd1395bdde5fc3a08ff3e4e6', 'loadClassLoader'));
#loadClass方法(類似autoload方法)注冊到 SPL Autoload
$loader->register(true);
}
autoload_real.php 的作用就是引入ClassLoader類、初始化ClassLoader類,並注冊到SPL堆棧中。
ClassLoader類中有很多屬性,這些屬性的作用也很簡單:主要就是方便后面程序快速的通過命名空間找到它所映射的類文件。
具體用到這些屬性的方法就在ClassLoader類中。
# ClassLoader.php
# 一個快速找到文件的算法,很有意思,感興趣的可以研究下
# 主要通過首字符找到命名空間以及長度,再根據命名空間以及長度找到文件
private function findFileWithExtension($class, $ext)
{
......
}
那么ClassLoader類屬性里面的值是什么時候寫入的呢?
答案很簡單:當為項目安裝組件時,即composer require xxx時,會更新ClassLoader類的屬性值,也就是將命名空間和文件地址做一個關聯。
接下來看看它的register方法。
# ClassLoader.php
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
看,其實很簡單,就是將loadClass注冊到SPL堆棧中。
那么現在就很清楚了,當程序使用了一個還未加載的類時,會調用什么方法?
當然是loadClass方法,再來看看loadClass方法。
# ClassLoader.php
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
根據方法的名稱就能看出它的功能:1、找到文件 2、加載文件。
總結一下Composer的運行機制:
**1、在composer require安裝時,更新ClassLoader類的屬性 **。
2、運行對象時(new \Test()),如果未加載就會執行loadClass(),通過首字符找到命名空間以及長度,再根據命名空間以及長度找到文件,最后include文件。
以上就是Composer的運行機制,接下來,就進入真正的框架搭建了。
二、框架前期准備
在正式進入搭建框架之前,先看下整體的架構圖以及一些前期准備。
整個架構跟Laravel、ThinkPHP等框架是差不多的,一次請求,一次返回,一個入口,中間根據路由規則交給相應的控制器去執行,在控制器中處理數據以及視圖。
接下來做一些前期准備,進入phoenix項目。
# vi index.php 一個入口
ini_set('display_errors', 1); # 顯示錯誤
error_reporting(-1);
require_once __DIR__.'/vendor/autoload.php'; # 引入核心加載類
$name = $_GET['name'];
dump($name);
# dump()
composer require symfony/var-dumper # 類似var_dump,輸出的變量體驗更好些。
配置Nginx,訪問域名為:http://dev.phoenix.goods/?name=SexyPhoenix, 可以正常顯示SexyPhoenix。
三、HttpFoundation組件封裝Request、Response
現有的程序只是一個面向過程的代碼,一個簡單的請求,響應。
對於搭建web框架,這種痛苦寫法當然是要被舍棄的,OOP編程才是正路。
既然要面向對象編程,首先要做的就是對流程中的Request、Response進行封裝。而Symfony中專門的組件。
composer require symfony/http-foundation
改造代碼
# index.php
ini_set('display_errors', 1);
error_reporting(-1);
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals(); # 創建request對象
$name = $request->get('name', 'World'); # 獲取參數,可移入控制器或從模型得到數據
$response = new Response();
$response->setContent('<b>Hello '.$name.'</b>'); # 設置內容,可用view處理
$response->send(); # 返回
下面來做一個簡單的分析。
$request = Request::createFromGlobals();
這一行代碼,是相當重要的,它從對象層面上處理了php的全局變量,例如 GET,POST,SESSION......。
這樣處理就可以輕易的從request對象中獲取所需要的信息以及對請求頭等信息的修改。
后期路由這些附加的信息也是存在request的attributes屬性中,及其好用。
$response = new Response();
通過response對象,可以輕易的控制返回的信息。比如頭信息的各種緩存策略......
四、路由處理
從架構圖上看,接着就要處理路由了。
phoneix框架用了普遍的做法,統一index.php入口。
那么下面要做的就是如何將路由的附加參數和要處理的控制器進行映射。
對於路由一般框架都是通過配置來的,這里也一樣做成可配置,方便。
Yaml格式配置路由
在phoenix項目下,創建routes文件夾,在routes下繼續創建web.yaml文件。
dashboard:
path: /dashboard
defaults: {
_controller: 'App\Http\Controllers\DashboardController::index'
}
下載symfony的Config組件、Yaml組件、Routing組件。
composer require symfony/config
composer require symfony/yaml
composer require symfony/routing
更新代碼
# index.php
ini_set('display_errors', 1);
error_reporting(-1);
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Loader\YamlFileLoader; # add
use Symfony\Component\Config\FileLocator; # add
$request = Request::createFromGlobals();
$fileLoader = new YamlFileLoader(new FileLocator(array(__DIR__))); # add
$collection = $fileLoader->load('routes/web.yaml'); # add
$name = $request->get('name', 'World');
$response = new Response();
$response->setContent('<b>Hello '.$name.'</b>');
$response->send();
dump($collection),可以看到返回了路由的Collection對象,里面有定義的路由。
這個時候,框架只是得到了定義的路由,但還沒有和URL做映射,下面改造繼續。
URL和配置路由映射
ini_set('display_errors', 1);
error_reporting(-1);
require_once __DIR__.'/vendor/autoload.php';
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; # add
use Symfony\Component\Routing\Matcher\UrlMatcher; # add
$request = Request::createFromGlobals();
$fileLoader = new YamlFileLoader(new FileLocator(array(__DIR__)));
$collection = $fileLoader->load('routes/web.yaml');
#解析url
$context = new RequestContext(); # add
$context->fromRequest($request); # add
#初始化UrlMatcher
$matcher = new UrlMatcher($collection, $context); # add
#url和路由配置映射
$route = $matcher->match($request->getPathInfo()) # add
$name = $request->get('name', 'World');
$response = new Response();
$response->setContent('<b>Hello '.$name.'</b>');
$response->send();
繼續分析。
$context = new RequestContext();
$context->fromRequest($request);
context對象主要就是對url進行解析。現在的域名:http://dev.phoenix.goods/dashboard
既然解析出url的參數,就要用解析出的參數和配置中的路由做精准關聯了,初始化matcher,傳入路由配置和url對象。
得到url和配置中的路由的映射。
$route = $matcher->match($request->getPathInfo());
五、控制器處理相應功能(C)
在路由處理中,框架已經得到了路由和控制器的關聯關系。下面就要執行相應的控制器(上面的_controller值)。
首先,在phoenix項目下,創建app/Http/Controllers/DashboardController.php(仿造Laravel的目錄結構)。
# DashboardController.php
namespace App\Http\Controllers; # 注意這里App命名空間,自己定義,並沒有注冊到autoload
class DashboardController{
public function index()
{
echo 'Hello SexyPhoenix';
}
}
App命名空間是框架定義的,需要注冊后,才能用,打開項目的composer.json文件。
# composer.json
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
composer dump-autoload # 更新命名空間
到這里,控制器的准備工作就做完了,接下來的問題就是如果利用得到的路由和控制器的映射關系去執行控制器,也就是下面的代碼。
App\Http\Controllers\DashboardController::index
其實也很簡單,就是用"::"分隔,得到兩個值,一個是類名,一個是方法名,再用php的call_user_func去執行。
但自己去寫可能過去粗暴,可用性低,在執行前,要先判斷DashboardController類是否存在,index方法是否存在,index方法的權限,是否是公共方法,以及各種參數等等,
自己去寫的話,會很麻煩,為了方便,繼續用symfony的組件。
composer require symfony/http-kernel
http-kernel組件,是框架的內核,很重要的組件,它提供了各種鈎子,及其方便框架擴展,也提供了控制器及其參數的“解析器”(這里需要了解下php的反射機制)。
更新index.php代碼。
# index.php
......
use Symfony\Component\HttpKernel\Controller\ControllerResolver; # add
use Symfony\Component\HttpKernel\Controller\ArgumentResolver; # add
......
$route = $matcher->match($request->getPathInfo());
$request->attributes->add($route); # add 將路由映射關系寫入request對象的附加屬性中。
$controller = (new ControllerResolver())->getController($request); # add 處理控制器
$arguments = (new ArgumentResolver())->getArguments($request, $controller); # add 處理方法的參數
$response = call_user_func_array($controller, $arguments);
$response->send();
更新DashboardController.php代碼。
namespace App\Http\Controllers;
use Symfony\Component\HttpFoundation\Request; # add
use Symfony\Component\HttpFoundation\Response;# add
class DashboardController{
public function index(Request $request)
{
$name = $request->get('name', 'world'); # add
return new Response('Hello '.$name); # add
}
}
用http-kernel好處就是可以處理各種問題,比如Request作為參數注入。
訪問 http://dev.phoenix.goods/dashboard?name=SexyPhoenix, 得到 Hello SexyPhoenix。
六、分離模板(V)
現在的框架只是簡單的輸出字符串,在正式環境中當然不可能這么簡單,要能夠返回正常的HTML頁面。
而復雜的HTML也不能放在控制器中處理,需要分離出來,單獨處理。Symfony為框架同樣提供了相關的組件。
composer require symfony/templating
處理框架的目錄結構。
在phoenix項目下,創建resources/views文件夾,繼續在views下創建dashboard.php文件。
# dashboard.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Phoenix</title>
<style>
html, body {
color: #000;
font-family: 'Raleway', sans-serif;
font-weight: 100;
height: 100vh;
margin: 0;
}
</style>
</head>
<body>
<div>
<h2>Hello, <b><?php echo $name?></b></h2>
<h3>your mailbox:<?php echo $email?></h3>
<h3>your github:<?php echo $github?></h3>
</div>
</body>
</html>
在app/Http/Controllers下創建Controller.php文件。
# Controller.php
namespace App\Http\Controllers;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Templating\PhpEngine;
use Symfony\Component\Templating\TemplateNameParser;
use Symfony\Component\Templating\Loader\FilesystemLoader;
class Controller {
/**
* $templete 模板文件
* $data 數據
*/
public function render($templete, array $data)
{
return new Response(
(new PhpEngine(
new TemplateNameParser(),
new FilesystemLoader(getcwd().'/resources/views/%name%')
))
->render($templete, $data)
);
}
}
改造DashboardController.php 代碼。
namespace App\Http\Controllers;
use Symfony\Component\HttpFoundation\Request;
class DashboardController extends Controller{ # 繼承Controller
public function index(Request $request)
{
$name = $request->get('name', 'world');
$data = [
'name' => $name,
'email' => 'sexyphoenix@163.com',
'github' => 'https://github.com/SexyPhoenix'
];
return $this->render('dashboard.php', $data);
}
}
訪問 http://dev.phoenix.goods/dashboard?name=SexyPhoenix, 頁面正常顯示。
七、分離模型(M)
分離完模板后,架構的數據還是在控制器中處理,同樣要做分離。不過這一步,同學們可以根據自己的意願來,比如你可以添加倉庫層、服務層等。
這里就做簡單點,在app目錄下,創建Models文件夾,繼續創建User.php文件。
# User.php
namespace App\Models;
class User {
protected $emails = [];
protected $githubs = [];
public function getEmailByName(string $name)
{
$this->setEmails();
return array_key_exists($name, $this->emails) ? $this->emails[$name] : '';
}
public function getGithubByName($name)
{
$this->setGithubs();
return array_key_exists($name, $this->githubs) ? $this->githubs[$name] : '';
}
public function setEmails()
{
$this->emails = [
'SexyPhoenix' => 'sexyphoenix@163.com'
];
}
public function setGithubs()
{
$this->githubs = [
'SexyPhoenix' => 'https://github.com/SexyPhoenix'
];
}
}
更新DashboardController.php。
# DashboardController.php
......
use App\Models\User #add
......
public function index(Request $request)
{
$name = $request->get('name', 'world');
$user = new User(); # add
$data = [
'name' => $name,
'email' => $user->getEmailByName($name), # update
'github' => $user->getGithubByName($name),# update
];
return $this->render('dashboard.php', $data);
}
訪問頁面,正常顯示。
八、剝離核心代碼
框架的基本架構已經搭建完成,但此時的核心代碼都寫在了index.php里面,另寫項目的話,無法復用此架構,接下來剝離出核心代碼。
在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模式設計的,這種模式帶來的好處,就是使框架的擴展性得到極大的提高。
在請求到響應之前設計了八種鈎子,方便后期擴展,詳情看下面的鏈接。
同時,也可以用另一種監聽事件的方式,通過一個event subscriber(事件訂閱器),向派遣器精確通報它要訂閱哪些事件。下面對路由優化時,會用到這。
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');
}
}
index.php的代碼不用變,HttpKernel類里面也有handle方法。建議同學們看看HttpKernel類的源碼。
十、依賴注入(Dependency Injection)
Phoenix類繼承了HttpKernel,是整個架構的核心,在框架里面定義了“路由監聽”,但如果框架不僅僅要對路由進行監聽,還要對response階段進行監聽呢?是不是繼續修改Phoenix類呢?
這樣的設計對於框架來說,是絕對不友好的。那有沒有方法解決呢?
當然有,可以通過在外面注入對象,框架通過type檢測,自動引入相關對象。
首先下載Symfony的DependencyInjection組件。
composer require symfony/dependency-injection
在core文件夾下創建container.php文件
# container.php
namespace Core;
use Symfony\Component\DependencyInjection;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
$app = new ContainerBuilder();
$app->register('context', 'Symfony\Component\Routing\RequestContext');
$app->register('matcher', 'Symfony\Component\Routing\Matcher\UrlMatcher')
->setArguments(array(getCollection(), new Reference('context')));
$app->register('request_stack', 'Symfony\Component\HttpFoundation\RequestStack');
$app->register('controller_resolver', 'Symfony\Component\HttpKernel\Controller\ControllerResolver');
$app->register('argument_resolver', 'Symfony\Component\HttpKernel\Controller\ArgumentResolver');
$app->register('listener.router', 'Symfony\Component\HttpKernel\EventListener\RouterListener') # 路由監聽
->setArguments(array(new Reference('matcher'), new Reference('request_stack')));
$app->register('dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher')
->addMethodCall('addSubscriber', array(new Reference('listener.router')));
$app->register('phoenix', 'Core\Phoenix')
->setArguments(array(
new Reference('dispatcher'),
new Reference('controller_resolver'),
new Reference('request_stack'),
new Reference('argument_resolver'),
));
return $app;
function getCollection()
{
return (
new YamlFileLoader(
new FileLocator(array(getcwd()))
)
)->load('routes/web.yaml');
}
別名和對象一一對應,后面可以通過別名獲取對象。
去掉core/phoenix.php里面的代碼。
namespace Core;
use Symfony\Component\HttpKernel\HttpKernel;
class Phoenix extends HttpKernel{
// public function __construct()
// {
// $matcher = new UrlMatcher($this->getCollection(), new RequestContext());
// $requestStack = new RequestStack();
// $dispatcher = new EventDispatcher();
// $dispatcher->addSubscriber(new RouterListener($matcher, $requestStack));
// parent::__construct(
// $dispatcher,
// new ControllerResolver(),
// $requestStack,
// new ArgumentResolver()
// );
// }
// 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';
$app = require_once __DIR__.'/core/container.php'; # add
$response = $app->get('phoenix') # 通過別名獲取
->handle(
Symfony\Component\HttpFoundation\Request::createFromGlobals()
);
$response->send();
訪問 http://dev.phoenix.goods/dashboard?name=SexyPhoenix, 顯示正常。
到這里,框架的整個基本設計就結束了,之后需要什么功能,就可以自己用composer安裝組件了,composer還是很好用的。
同學們如果有什么疑問的,歡迎在評論區一起交流,ヾ(●´∀`●) 。
最后,附一份最終代碼 phoenix web 架構。