摘要:畢竟,我們還將在接下來的開發(fā)之旅中使用其他框架開發(fā)者編寫的輔助包。缺乏行業(yè)標(biāo)準(zhǔn)必然意味著,框架中的這些組件高度耦合。如果你嘗試對(duì)這個(gè)類進(jìn)行單元測試,會(huì)發(fā)現(xiàn)根本不可行。在做單元測試的時(shí)候,我們可以很好地模擬數(shù)據(jù)庫連接,并將其傳入使用。
我為你們準(zhǔn)備了一個(gè)富有挑戰(zhàn)性的事情。接下來你們將以 無 框架的方式開啟一個(gè)項(xiàng)目之旅。
首先聲明, 這篇并非又臭又長的反框架裹腳布文章。也不是推銷 非原創(chuàng) 思想?。畢竟, 我們還將在接下來的開發(fā)之旅中使用其他框架開發(fā)者編寫的輔助包。我對(duì)這個(gè)領(lǐng)域的創(chuàng)新也是持無可非議的態(tài)度。
這無關(guān)他人,而是關(guān)乎己身。作為一名開發(fā)者,它將有機(jī)會(huì)讓你成長。
也許無框架開發(fā)令你受益匪淺的地方就是,可以從底層運(yùn)作的層面中汲取豐富的知識(shí)。拋卻依賴神奇的,幫你處理無法調(diào)試和無法真正理解的東西的框架,你將清楚的看到這一切是如何發(fā)生的。
很有可能下一份工作中,你并不能隨心所以地選擇框架開拓新項(xiàng)目。現(xiàn)實(shí)就是,在很多高價(jià)值,關(guān)鍵業(yè)務(wù)的 PHP 工作中均使用現(xiàn)有應(yīng)用。 并且該應(yīng)用程序是否構(gòu)建在當(dāng)前令人舒爽的 Laravel 或 Symfony 等流行框架中,亦或是陳舊過時(shí)的 CodeIgniter 或者 FuelPHP 中,更有甚者它可能廣泛出現(xiàn)在令人沮喪的? “面向包含體系結(jié)構(gòu)” 的傳統(tǒng)的 PHP 應(yīng)用 之中,所以無框架開發(fā)會(huì)在將來你所面臨的任何 PHP 項(xiàng)目中助你一臂之力。
上古時(shí)代, 因?yàn)?某些系統(tǒng) 不得不解釋分發(fā) HTTP 請(qǐng)求,發(fā)送 HTTP 響應(yīng),管理依賴關(guān)系,無框架開發(fā)就是痛苦的鏖戰(zhàn)。缺乏行業(yè)標(biāo)準(zhǔn)必然意味著,框架中的這些組件高度耦合 。如果你從無框架開始,你終將難逃自建框架的命運(yùn)。
時(shí)至今日,幸虧有?PHP-FIG 完成所有的自動(dòng)加載和交互工作,無框架開發(fā)并非讓你白手起家。各色供應(yīng)商都有這么多優(yōu)秀的可交互的軟件包。把他們組合起來容易得超乎你的想象!
PHP 是如何工作的?在做其他事之前,搞清楚 PHP 如何與外界溝通是非常重要的。
PHP 以請(qǐng)求 / 響應(yīng)為周期運(yùn)行服務(wù)端應(yīng)用程序。與你的應(yīng)用程序的每一次交互——無論是來自瀏覽器,命令行還是 REST API ——都是作為請(qǐng)求進(jìn)入應(yīng)用程序的。 當(dāng)接收到請(qǐng)求以后:
程序開始啟動(dòng);
開始處理請(qǐng)求;
產(chǎn)生響應(yīng);
接著,響應(yīng)返回給產(chǎn)生請(qǐng)求的相應(yīng)客戶端;
最后程序關(guān)閉。
每一個(gè) 請(qǐng)求都在重復(fù)以上的交互。
前端控制器用這些知識(shí)把自己武裝起來以后,就可以先從我們的前端控制器開始編寫程序了。前端控制器是一個(gè) PHP 文件,它處理程序的每一個(gè)請(qǐng)求。控制器是請(qǐng)求進(jìn)入程序后遇到的第一個(gè) PHP 文件,并且(本質(zhì)上)也是響應(yīng)走出你應(yīng)用程序所經(jīng)過的最后一個(gè)文件。
我們使用經(jīng)典的 Hello, world! 作為例子來確保所有東西都正確連接上,這個(gè)例子由?PHP 的內(nèi)置服務(wù)器??驅(qū)動(dòng)。在你開始這樣做之前,請(qǐng)確保你已經(jīng)安裝了 PHP7.1 或者更高版本。
創(chuàng)建一個(gè)含有?public?目錄的項(xiàng)目,然后在該目錄里面創(chuàng)建一個(gè)index.php 文件,文件里面寫入如下代碼:
注意,這里我們聲明了使用嚴(yán)格模式 —— 作為最佳實(shí)踐,你應(yīng)該在應(yīng)用程序的?每個(gè) PHP 文件的開頭?都這樣做。因?yàn)閷?duì)從你后面來的開發(fā)者來說類型提示對(duì) 調(diào)試和清晰的交流意圖很重要?。
使用命令行(比如 macOS 的終端)切換到你的項(xiàng)目目錄并啟動(dòng) PHP 的內(nèi)置服務(wù)器。
php -S localhost:8080 -t public/現(xiàn)在,在瀏覽器中打開?http://localhost:8080/?。是不是成功地看到了 "Hello, world!" 輸出?
很好。接下來我們可以開始進(jìn)入正題了!
自動(dòng)加載與第三方包當(dāng)你第一次使用 PHP 時(shí),你可能會(huì)在你的程序中使用?includes?或?requires?語句來從其他 PHP 文件導(dǎo)入功能和配置。 通常,我們會(huì)避免這么干,因?yàn)檫@會(huì)使得其他人更難以遵循你的代碼路徑和理解依賴在哪里。這讓調(diào)試成為了一個(gè)?真正的 噩夢。
解決辦法是使用自動(dòng)加載(autoloading)。 自動(dòng)加載的意思是:當(dāng)你的程序需要使用一個(gè)類, PHP 在調(diào)用該類的時(shí)候知道去哪里找到并加載它。雖然從 PHP 5 開始就可以使用這個(gè)特性了, 但是得益于?PSR-0?(?自動(dòng)加載標(biāo)準(zhǔn),后來被 PSR-4 取代),其使用率才開始有真正的提升。
我們可以編寫自己的自動(dòng)加載器來完成任務(wù),但是由于我們將要使用的管理第三方依賴的 ?Composer?已經(jīng)包含了一個(gè)完美的可用的自動(dòng)加載器,那我們用它就行了。
確保你已經(jīng)在你的系統(tǒng)上?安裝?了 Composer。然后為此項(xiàng)目初始化 Composer:
composer init這條命令通過交互式引導(dǎo)你創(chuàng)建?composer.json 配置文件。 一旦文件創(chuàng)建好了,我們就可以在編輯器中打開它然后向里面寫入?autoload?字段,使他看起來像這個(gè)樣子(這確保了自動(dòng)加載器知道從哪里找到我們項(xiàng)目中的類):
{ "name": "kevinsmith/no-framework", "description": "An example of a modern PHP application bootstrapped without a framework.", "type": "project", "require": {}, "autoload": { "psr-4": { "ExampleApp": "src/" } } }現(xiàn)在為此項(xiàng)目安裝 composer,它引入了依賴(如果有的話),并為我們創(chuàng)建好了自動(dòng)加載器:
composer install更新?public/index.php?文件來引入自動(dòng)加載器。在理想情況下,這將是你在程序當(dāng)中使用的少數(shù)『包含』語句之一。
此時(shí)如果你刷新瀏覽器,你將不會(huì)看到任何變化。因?yàn)樽詣?dòng)加載器沒有修改或者輸出任何數(shù)據(jù),所以我們看到的是同樣的內(nèi)容。讓我們把?Hello, world!?這個(gè)例子移動(dòng)到一個(gè)已經(jīng)自動(dòng)加載的類里面看看它是如何運(yùn)作的。
在項(xiàng)目根目錄創(chuàng)建一個(gè)名為 src 的目錄,然后在里面添加一個(gè)叫?HelloWorld.php 的文件,寫入如下代碼:
現(xiàn)在到?public/index.php 里面用 ?HelloWorld?類的?announce 方法替換掉 echo 語句。
// ... require_once dirname(__DIR__) . "/vendor/autoload.php"; $helloWorld = new ExampleAppHelloWorld(); $helloWorld->announce();刷新瀏覽器查看新的信息!
什么是依賴注入?依賴注入是一種編程技術(shù),每個(gè)依賴項(xiàng)都供給它需要的對(duì)象,而不是在對(duì)象外獲得所需的信息或功能。
舉個(gè)例子,假設(shè)應(yīng)用中的類方法需要從數(shù)據(jù)庫中讀取。為此,你需要一個(gè)數(shù)據(jù)庫連接。常用的技術(shù)就是創(chuàng)建一個(gè)全局可見的新連接。
class AwesomeClass { public function doSomethingAwesome() { $dbConnection = return new PDO( "{$_ENV["type"]}:host={$_ENV["host"]};dbname={$_ENV["name"]}", $_ENV["user"], $_ENV["pass"] ); // Make magic happen with $dbConnection } }但是這樣做顯得很亂,它把一個(gè)并非屬于這里的職責(zé)置于此地---創(chuàng)建一個(gè) 數(shù)據(jù)庫連接對(duì)象 ,?檢查憑證 ,?還有?處理一些連接失敗的問題---它會(huì)導(dǎo)致應(yīng)用中出現(xiàn) 大量? 重復(fù)代碼。如果你嘗試對(duì)這個(gè)類進(jìn)行單元測試,會(huì)發(fā)現(xiàn)根本不可行。這個(gè)類和應(yīng)用環(huán)境以及數(shù)據(jù)庫高度耦合。
相反,為何不一開始就搞清楚你的類需要什么?我們只需要首先將 “PDO” 對(duì)象注入該類即可。
class AwesomeClass { private $dbConnection; public function __construct(PDO $dbConnection) { $this->dbConnection = $dbConnection; } public function doSomethingAwesome() { // Make magic happen with $this->dbConnection } }這樣更簡潔清晰易懂,且更不易產(chǎn)生 Bug。通過類型提示和依賴注入,該方法可以清楚準(zhǔn)確地聲明它要做的事情,而無需依賴外部調(diào)用去獲取。在做單元測試的時(shí)候,我們可以很好地模擬數(shù)據(jù)庫連接,并將其傳入使用。
依賴注入容器 是一個(gè)工具,你可以圍繞整個(gè)應(yīng)用程序來處理創(chuàng)建和注入這些依賴關(guān)系。容器并不需要能夠使用依賴注入技術(shù),但隨著應(yīng)用程序的增長并變得更加復(fù)雜,它將大有裨益。
我們將使用 PHP 中最受歡迎的 DI 容器之一:名副其實(shí)的?PHP-DI。(值得推薦的是它文檔中的 依賴注入另解?可能會(huì)對(duì)讀者有所幫助)
依賴注入容器現(xiàn)在我們已經(jīng)安裝了 Composer ,那么安裝 PHP-DI 就輕而易舉了,我們繼續(xù)回到命令行來搞定它。
composer require php-di/php-di修改?public/index.php?用來配置和構(gòu)建容器。
// ... require_once dirname(__DIR__) . "/vendor/autoload.php"; $containerBuilder = new DIContainerBuilder(); $containerBuilder->useAutowiring(false); $containerBuilder->useAnnotations(false); $containerBuilder->addDefinitions([ ExampleAppHelloWorld::class => DIcreate(ExampleAppHelloWorld::class) ]); $container = $containerBuilder->build(); $helloWorld = $container->get(ExampleAppHelloWorld::class); $helloWorld->announce();沒啥大不了的。它仍是一個(gè)單文件的簡單示例,你很容易能看清它是怎么運(yùn)行的。
迄今為止, 我們只是在 配置容器 ,所以我們必須?顯式地聲明依賴關(guān)系?(而不是使用?自動(dòng)裝配?或?注解),并且從容器中檢索 HelloWorld 對(duì)象。
小貼士:自動(dòng)裝配在你開始構(gòu)建應(yīng)用程序的時(shí)候是一個(gè)很不錯(cuò)的特性,但是它隱藏了依賴關(guān)系,難以維護(hù)。 很有可能在接下里的歲月里, 另一個(gè)開發(fā)者在不知情的狀況下引入了一個(gè)新庫,然后就造就了多個(gè)庫實(shí)現(xiàn)一個(gè)單接口的局面,這將會(huì)破壞自動(dòng)裝配,導(dǎo)致一系列讓接手者很容易忽視的的不可見的問題。
盡量?引入命名空間,可以增加代碼的可讀性。
useAutowiring(false); $containerBuilder->useAnnotations(false); $containerBuilder->addDefinitions([ HelloWorld::class => create(HelloWorld::class) ]); $container = $containerBuilder->build(); $helloWorld = $container->get(HelloWorld::class); $helloWorld->announce();現(xiàn)在看來,我們好像是把以前已經(jīng)做過的事情再拿出來小題大做。
毋需煩心,當(dāng)我們添加其他工具來幫助我們引導(dǎo)請(qǐng)求時(shí),容器就有用武之地了。它會(huì)在適當(dāng)?shù)臅r(shí)機(jī)下按需加載正確的類。
中間件如果把你的應(yīng)用想象成一個(gè)洋蔥,請(qǐng)求從外部進(jìn)入,到達(dá)洋蔥中心,最后變成響應(yīng)返回出去。那么中間件就是洋蔥的每一層。它接收請(qǐng)求并且可以處理請(qǐng)求。要么把請(qǐng)求傳遞到更里層,要么向更外層返回一個(gè)響應(yīng)(如果中間件正在檢查請(qǐng)求不滿足的特定條件,比如請(qǐng)求一個(gè)不存在的路由,則可能發(fā)生這種情況)。
如果請(qǐng)求通過了所有的層,那么程序就會(huì)開始處理它并把它轉(zhuǎn)換為響應(yīng),中間件接收到響應(yīng)的順序與接收到請(qǐng)求的順序相反,并且也能對(duì)響應(yīng)做修改,然后再把它傳遞給下一個(gè)中間件。
下面是一些中間件用例的閃光點(diǎn):
在開發(fā)環(huán)境中調(diào)試問題
在生產(chǎn)環(huán)境中優(yōu)雅的處理異常
對(duì)傳入的請(qǐng)求進(jìn)行頻率限制
對(duì)請(qǐng)求傳入的不支持資源類型做出響應(yīng)
處理跨域資源共享(CORS)
將請(qǐng)求路由到正確的處理類
那么中間件是實(shí)現(xiàn)這些功能的唯一方式嗎?當(dāng)然不是。但是中間件的實(shí)現(xiàn)使得你對(duì)請(qǐng)求 / 響應(yīng)這個(gè)生命周期的理解更清晰。這也意味著你調(diào)試起來更簡單,開發(fā)起來更快速。
我們將從上面列出的最后一條用例,也就是路由,當(dāng)中獲益。
路由路由依靠傳入的請(qǐng)求信息來確定應(yīng)當(dāng)由哪個(gè)類來處理它。(例如 URI ?/products/purple-dress/medium?應(yīng)該被 ?ProductDetails::class類接收處理,同時(shí) purple-dress?和?medium?作為參數(shù)傳入)
在范例應(yīng)用中,我們將使用流行的?FastRoute?路由,基于?PSR-15兼容的中間件實(shí)現(xiàn)。
中間件調(diào)度器為了讓我們的應(yīng)用可以和 FastRoute 中間件---以及我們安裝的其他中間件協(xié)同工作---我們需要一個(gè)中間件調(diào)度器。
PSR-15是為中間件和調(diào)度器定義接口的中間件標(biāo)準(zhǔn)(在規(guī)范中又稱“請(qǐng)求處理器”),它允許各式各樣的中間件和調(diào)度器互相交互。我們只需選擇兼容 PSR-15 的調(diào)度器,這樣就可以確保它能和任何兼容 PSR-15 的中間件協(xié)同工作。
我們先安裝一個(gè)?Relay?作為調(diào)度器。
composer require relay/relay:2.x@dev而且根據(jù) PSR-15 的中間件標(biāo)準(zhǔn)要求實(shí)現(xiàn)可傳遞?兼容 PSR-7 的 HTTP 消息, 我們使用?Zend Diactoros?作為 PSR-7 的實(shí)現(xiàn)。
composer require zendframework/zend-diactoros我們用 Relay 去接收中間件。
// ... use DIContainerBuilder; use ExampleAppHelloWorld; use RelayRelay; use ZendDiactorosServerRequestFactory; use function DIcreate; // ... $container = $containerBuilder->build(); $middlewareQueue = []; $requestHandler = new Relay($middlewareQueue); $requestHandler->handle(ServerRequestFactory::fromGlobals());我們?cè)诘?16 行使用?ServerRequestFactory::fromGlobals()? 把?創(chuàng)建新請(qǐng)求的必要信息合并起來?然后把它傳給 Relay。 這正是 Request?進(jìn)入我們中間件堆棧的起點(diǎn)。
現(xiàn)在我們繼續(xù)添加 FastRoute 和請(qǐng)求處理器中間件。 ( FastRoute 確定請(qǐng)求是否合法,究竟能否被應(yīng)用程序處理,然后請(qǐng)求處理器發(fā)送?Request 到路由配置表中已注冊(cè)過的相應(yīng)處理程序中)
composer require middlewares/fast-route middlewares/request-handler然后我們給?Hello, world!?處理類定義一個(gè)路由。我們?cè)诖耸褂?/hello?路由來展示基本 URI 之外的路由。
// ... use DIContainerBuilder; use ExampleAppHelloWorld; use FastRouteRouteCollector; use MiddlewaresFastRoute; use MiddlewaresRequestHandler; use RelayRelay; use ZendDiactorosServerRequestFactory; use function DIcreate; use function FastRoutesimpleDispatcher; // ... $container = $containerBuilder->build(); $routes = simpleDispatcher(function (RouteCollector $r) { $r->get("/hello", HelloWorld::class); }); $middlewareQueue[] = new FastRoute($routes); $middlewareQueue[] = new RequestHandler(); $requestHandler = new Relay($middlewareQueue); $requestHandler->handle(ServerRequestFactory::fromGlobals());為了能運(yùn)行,你還需要修改?HelloWorld 使其成為一個(gè)可調(diào)用的類, 也就是說?這里類可以像函數(shù)一樣被隨意調(diào)用.
// ... class HelloWorld { public function __invoke(): void { echo "Hello, autoloaded world!"; exit; } }(注意在魔術(shù)方法?__invoke()?中加入exit;。 我們只需1秒鐘就能搞定--只是不想讓你遺漏這個(gè)事)
現(xiàn)在打開?http://localhost:8080/hello?,開香檳吧!
萬能膠水睿智的讀者可能很快看出,雖然我們?nèi)耘f囿于配置和構(gòu)建 DI 容器的藩籬之中,容器現(xiàn)在實(shí)際上對(duì)我們毫無用處。調(diào)度器和中間件在沒有它的情況下也一樣運(yùn)作。
那它何時(shí)才能發(fā)揮威力?
嗯,如果---在實(shí)際應(yīng)用程序中總是如此---HelloWorld類具有依賴關(guān)系呢?
我們來講解一個(gè)簡單的依賴關(guān)系,看看究竟發(fā)生了什么。
// ... class HelloWorld { private $foo; public function __construct(string $foo) { $this->foo = $foo; } public function __invoke(): void { echo "Hello, {$this->foo} world!"; exit; } }刷新瀏覽器..
WOW!
看下這個(gè)?ArgumentCountError.
發(fā)生這種情況是因?yàn)?HelloWorld?類在構(gòu)造的時(shí)候需要注入一個(gè)字符串才能運(yùn)行,在此之前它只能等著。?這?正是容器要幫你解決的痛點(diǎn)。
我們?cè)谌萜髦卸x該依賴關(guān)系,然后將容器傳給?RequestHandler?去?解決這個(gè)問題.// ... use ZendDiactorosServerRequestFactory; use function DIcreate; use function DIget; use function FastRoutesimpleDispatcher; // ... $containerBuilder->addDefinitions([ HelloWorld::class => create(HelloWorld::class) ->constructor(get("Foo")), "Foo" => "bar" ]); $container = $containerBuilder->build(); // ... $middlewareQueue[] = new FastRoute($routes); $middlewareQueue[] = new RequestHandler($container); $requestHandler = new Relay($middlewareQueue); $requestHandler->handle(ServerRequestFactory::fromGlobals());嗟夫!當(dāng)刷新瀏覽器的時(shí)候, "Hello, bar world!"將映入你的眼簾!
正確地發(fā)送響應(yīng)是否還記得我之前提到過的位于?HelloWorld 類中的 exit?語句?
當(dāng)我們構(gòu)建代碼時(shí),它可以讓我們簡單粗暴的獲得響應(yīng),但是它絕非輸出到瀏覽器的最佳選擇。這種粗暴的做法給?HelloWorld?附加了額外的響應(yīng)工作---其實(shí)應(yīng)該由其他類負(fù)責(zé)的---它會(huì)過于復(fù)雜的發(fā)送正確的頭部信息和 狀態(tài)碼,然后立刻退出了應(yīng)用,使得?HelloWorld?之后 的中間件也無機(jī)會(huì)運(yùn)行了。
記住,每個(gè)中間件都有機(jī)會(huì)在?Request?進(jìn)入我們應(yīng)用時(shí)修改它,然后 (以相反的順序) 在響應(yīng)輸出時(shí)修改響應(yīng)。 除了 Request 的通用接口, PSR-7 同樣也定義了另外一種 HTTP 消息結(jié)構(gòu),以輔助我們?cè)趹?yīng)用運(yùn)行周期的后半部分之用:Response。(如果你想真正了解這些細(xì)節(jié),請(qǐng)閱讀 HTTP 消息以及什么讓 PSR-7 請(qǐng)求和響應(yīng)標(biāo)準(zhǔn)如此之好。)
修改?HelloWorld?返回一個(gè) Response。
// ... namespace ExampleApp; use PsrHttpMessageResponseInterface; class HelloWorld { private $foo; private $response; public function __construct( string $foo, ResponseInterface $response ) { $this->foo = $foo; $this->response = $response; } public function __invoke(): ResponseInterface { $response = $this->response->withHeader("Content-Type", "text/html"); $response->getBody() ->write("Hello, {$this->foo} world!"); return $response; } }然后修改容器給?HelloWorld 提供一個(gè)新的 Response?對(duì)象。
// ... use MiddlewaresRequestHandler; use RelayRelay; use ZendDiactorosResponse; use ZendDiactorosServerRequestFactory; use function DIcreate; // ... $containerBuilder->addDefinitions([ HelloWorld::class => create(HelloWorld::class) ->constructor(get("Foo"), get("Response")), "Foo" => "bar", "Response" => function() { return new Response(); }, ]); $container = $containerBuilder->build(); // ...如果你現(xiàn)在刷新頁面,會(huì)發(fā)現(xiàn)一片空白。我們的應(yīng)用正在從中間件調(diào)度器返回正確的?Response?對(duì)象,但是... 腫么回事?
它啥都沒干,就這樣。
我們還需要一件東西來包裝下:發(fā)射器。發(fā)射器位于應(yīng)用程序和 Web 服務(wù)器(Apache,nginx等)之間,將響應(yīng)發(fā)送給發(fā)起請(qǐng)求的客戶端。它實(shí)際上拿到了?Response 對(duì)象并將其轉(zhuǎn)化為 服務(wù)端 API?可理解的信息。
好消息! 我們已經(jīng)用來封裝請(qǐng)求的 Zend Diactoros 包同樣也內(nèi)置了發(fā)送 PSR-7 響應(yīng)的發(fā)射器。
值得注意的是,為了舉例,我們只是對(duì)發(fā)射器的使用小試牛刀。雖然它們可能會(huì)更復(fù)雜點(diǎn),真正的應(yīng)用應(yīng)該配置成自動(dòng)化的流式發(fā)射器用來應(yīng)對(duì)大量下載的情況, Zend 博客展示了如何實(shí)現(xiàn)它。
修改?public/index.php?,用來從調(diào)度器那里接收?Response?,然后傳給發(fā)射器。
// ... use RelayRelay; use ZendDiactorosResponse; use ZendDiactorosResponseSapiEmitter; use ZendDiactorosServerRequestFactory; use function DIcreate; // ... $requestHandler = new Relay($middlewareQueue); $response = $requestHandler->handle(ServerRequestFactory::fromGlobals()); $emitter = new SapiEmitter(); return $emitter->emit($response);刷新瀏覽器,業(yè)務(wù)恢復(fù)了!這次我們用了一種更健壯的方式來處理響應(yīng)。
以上代碼的第 15 行是我們應(yīng)用中請(qǐng)求/響應(yīng)周期結(jié)束的地方,同時(shí)也是 web 服務(wù)器接管的地方。
總結(jié)現(xiàn)在你已經(jīng)獲得了現(xiàn)代化的 PHP 代碼。 僅僅 44 行代碼,在幾個(gè)被廣泛使用,經(jīng)過全面測試和擁有可靠互操作性的組件的幫助下,我們就完成了一個(gè)現(xiàn)代化 PHP 程序的引導(dǎo)。它兼容 PSR-4,?PSR-7,PSR-11 以及?PSR-15,這意味著你可以使用自己選擇的其他任一供應(yīng)商對(duì)這些標(biāo)準(zhǔn)的實(shí)現(xiàn),來構(gòu)建自己的 HTTP 消息, DI 容器,中間件,還有中間件調(diào)度器。
我們深入理解了我們決策背后使用的技術(shù)和原理,但我更希望你能明白,在沒有框架的情況下,引導(dǎo)一個(gè)新的程序是多么簡單的一件事。或許更重要的是,我希望在有必要的時(shí)候你能更好的把這些技術(shù)運(yùn)用到已有的項(xiàng)目中去。
你可以在?這個(gè)例子的 GitHub 倉庫 上免費(fèi) fork 和下載它。
如果你正在尋找更高質(zhì)量的解耦軟件包資源,我衷心推薦你看看?Aura,?了不起的軟件包聯(lián)盟,?Symfony 組件,?Zend Framework?組件,Paragon 計(jì)劃的聚焦安全的庫,?還有這個(gè)?關(guān)于 PSR-15 中間件的清單.
如果你想把這個(gè)例子的代碼用到生產(chǎn)環(huán)境中, 你可能需要把路由和?容器定義?分離到它們各自的文件里面,以便將來項(xiàng)目復(fù)雜度提升的時(shí)候更好維護(hù)。我也建議?實(shí)現(xiàn) EmitterStack?來更好的處理文件下載以及其他的大量響應(yīng)。
如果有任何問題,疑惑或者建議,請(qǐng) 給我留言。
更多現(xiàn)代化 PHP 知識(shí),請(qǐng)前往 Laravel / PHP 知識(shí)社區(qū)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/28528.html
摘要:大刀闊斧的改造在學(xué)習(xí)了兩遍之后,基于教程開發(fā)的校園二手書交易平臺(tái)熊能本周閱讀清單紙牌屋弗蘭克知道的太晚了實(shí)現(xiàn)微信紅包拆分算法聊聊最近求職發(fā)生的故事無銘更多現(xiàn)代化知識(shí),請(qǐng)前往知識(shí)社區(qū) showImg(https://segmentfault.com/img/bV8ctF?w=1650&h=1100); 最新資訊 Laravel 5.6 中文文檔翻譯完成,譯者 60 人,耗時(shí) 10 天...
摘要:大家有好的文章可以在評(píng)論下面分享出來共同進(jìn)步本文鏈接數(shù)組使用之道程序員進(jìn)階學(xué)習(xí)書籍參考指南教你在不使用框架的情況下也能寫出現(xiàn)代化代碼巧用數(shù)組函數(shù)框架中間件實(shí)現(xiàn)沒錯(cuò),這就是面向?qū)ο缶幊淘O(shè)計(jì)模式需要遵循的個(gè)基本原則令人困惑的在中使用協(xié)程實(shí)現(xiàn)多任 大家有好的文章,可以在評(píng)論下面分享出來, 共同進(jìn)步! 本文github鏈接 php PHP 數(shù)組使用之道 PHP程序員進(jìn)階學(xué)習(xí)書籍參考指南 教你...
摘要:編寫不可維護(hù)的代碼是一個(gè)特殊的技能,但奇怪的是,似乎對(duì)某些開發(fā)者來說是很自然的。維護(hù)人員沒有時(shí)間去理解你的代碼。你的代碼不能看起來不可維護(hù)因?yàn)閯e人會(huì)懷疑的它必須是不可維護(hù)。我希望你相信你自己也能做到,你也可以編寫不可維護(hù)的代碼。 showImg(https://segmentfault.com/img/remote/1460000015221325); 譯者注:這是一篇很棒文章,使用有...
摘要:本文將從零開始搭建一個(gè)現(xiàn)代化的框架,該框架會(huì)擁有現(xiàn)代框架的一切特征,如單入口,路由,依賴注入,類自動(dòng)加載機(jī)制等等,如同時(shí)下最流行的框架一樣。執(zhí)行控制器文件中的邏輯代碼,最終將數(shù)據(jù)通過對(duì)應(yīng)的視圖層顯示出來。 本文將從零開始搭建一個(gè)現(xiàn)代化的PHP框架,該框架會(huì)擁有現(xiàn)代框架的一切特征,如單入口,路由,依賴注入,composer類自動(dòng)加載機(jī)制等等,如同時(shí)下最流行的Laravel框架一樣。 一、...
摘要:這大概是我沒有及早使用,或多數(shù)開發(fā)者流連現(xiàn)狀造成的。它就是,一個(gè)的框架。行為驅(qū)動(dòng)開發(fā)是來自測試驅(qū)動(dòng)開發(fā)的開發(fā)過程。簡單的說,它就是經(jīng)常可能一天幾次將小塊代碼整合進(jìn)基礎(chǔ)代碼當(dāng)中的行為。 showImg(https://segmentfault.com/img/remote/1460000013769815); 這是一篇社區(qū)協(xié)同翻譯的文章,已完成翻譯,更多信息請(qǐng)點(diǎn)擊?協(xié)同翻譯介紹?。 文章...
閱讀 1013·2021-11-22 13:52
閱讀 1448·2021-11-19 09:40
閱讀 3173·2021-11-16 11:44
閱讀 1275·2021-11-15 11:39
閱讀 3908·2021-10-08 10:04
閱讀 5366·2021-09-22 14:57
閱讀 3106·2021-09-10 10:50
閱讀 3187·2021-08-17 10:13