《Create Your own PHP Framework》筆記

来源:http://www.cnblogs.com/pheye/archive/2017/09/02/7468386.html
-Advertisement-
Play Games

前言 大力推薦該教程: "《Create Your own PHP Framework》" Symfony的學習蠻累的,官方文檔雖然很豐富,但是組織方式像參考書而不是指南,一些不錯的指導性文檔常常是是看組件文檔時提到了才偶然發現的,這方面感覺就跟看Laravel和Webpack的官方文檔有差距。同時 ...


前言

大力推薦該教程:《Create Your own PHP Framework》

Symfony的學習蠻累的,官方文檔雖然很豐富,但是組織方式像參考書而不是指南,一些不錯的指導性文檔常常是是看組件文檔時提到了才偶然發現的,這方面感覺就跟看Laravel和Webpack的官方文檔有差距。同時Google中找Symfony的問題也不像Laravel容易找到答案,經常是自己看完整個官方文檔結合源碼才解決,進度趕的時候真是折磨人。總的來講,雖然非常非常強大,但是在掌握上,確實不像Laravel那麼方便學習。如果從Linux的設計哲學上來講,我認為Laravel是策略,Symfony是機制。策略的目標是在易用的前提下,提供足夠的靈活性;而機制相反,在保證靈活性的情況下,足夠易用,比較難學是自然的。策略需要依賴於機制之上,所以Laravel依賴Symfony。

之前在學Laravel時,看了《如何Composer一步一步構建自己的PHP框架》這個系列,對於Laravel的學習大有裨益。於是在學Symfony時,也是希望有個類似的教程,結果在Symfony官方文檔中偶然找到了《Create Your own PHP Framework》,學完後再看Symfony確實清晰了很多。

這裡簡單做下每一節的筆記,主要記了一些設計思想的點,比較零散,看完原文再來看估計會有所共鳴。

筆記

introduction

When creating a framework, following the MVC pattern is not the right goal. The main goal should be the Separation of Concerns.”

看到這句話時,我想起之前跟別人談如何一步步學習Laravel時說:“路由是框架的基石,而在這之上,通過構建MVC的每一層就完成了基本框架;然後再搭配一些現代必備特性比如命令行、測試;以及一些常用服務:隊列、安全認證等。就能理解Laravel。”

一下子就被打臉了,Symfony提出了構建框架的主要目標是關註點分離。從理念層面上是對的,MVC不是唯一的解決,不過太過抽象,MVC只是一種具體的關註點分離的方法,對於普通開發者會比較容易掌握。實際上,如果我只想著關註點分離,也還不知道如何下手。

為什麼要自己寫一個框架?

  • 研究Symfony, 這是我的主要目的;
  • 根據自己特殊需求做一個自己的框架;
  • 純粹出於探索的樂趣;
  • 重構舊代碼以便符合現代的最佳實踐;
  • 證明你自己。。。

原文:introduction

The HttpFoundation Componen

即便是最簡單的事情,使用框架也好於不使用。再簡單的代碼都面臨以下問題:

  • 對參數的判斷
  • 安全問題,比如XSS攻擊;
  • 方便單元測試;

再簡單的問題如果要滿足上面的條件,寫出的代碼都比使用框架還累。

如果你認為安全性可測試性不足以說服你停止寫舊代碼,趕緊採用新框架的話 ,那麼你可以停止讀本書並繼續你以前的工作方式了。(深深地感受到作者的高冷)

框架存在的目的是讓你更快地寫出更好的代碼,而不是讓你有所犧牲,如果有什麼犧牲的話,我想應該是學習成本的增加吧。

以後就算不使用框架,也應該使用HttpFoundation組件的RequestResponse處理請求與響應。

原文:The HttpFoundation Component

The Front Controller

用於分配路由的控制器稱為前端控制器(Front Controller),它根據$request->getPathInfo()調用不同目標代碼。這個框架到此最大的問題在於路由於於簡單,所以下一節應該是解決路由問題。

原文:The Front Controller

The Routing Component

上面簡單的路由並不太能滿足我們的要求,比如我們想實現路由的通配符匹配就比較麻煩。
因此,使用第三方的路由庫是必要的。symfony/routing就很方便。這個路由很好,對象卻有點多,剛看時還真是不太好理解。

Routing組件的基本對象:

  • RouteCollection 路由集合
  • Route 單個路由
  • RequestContext 請求上下文,通過fromRequest方法與Request綁定。(這種分離有利於測試)
  • UrlMatcherRouteCollectionRequestContext綁定

然後通過

$attributes = $matcher->match($request->getPathInfo());

獲取當前的路由信息,下麵這些實例表明每個路由都會有_route這個屬性,同時如果定義了通配屬性,也會變成對應的變數。

print_r($matcher->match('/bye'));
/* Gives:
array (
  '_route' => 'bye',
);
*/

print_r($matcher->match('/hello/Fabien'));
/* Gives:
array (
  'name' => 'Fabien',
  '_route' => 'hello',
);
*/

print_r($matcher->match('/hello'));
/* Gives:
array (
  'name' => 'World',
  '_route' => 'hello',
);
*/

另外,當match不到時,會拋出如下異常:Routing\Exception\ResourceNotFoundException
使用Routing有個額外的好處,就是可以從根據路由生成路徑:

echo $generator->generate(
    'hello',
    array('name' => 'Fabien'),
    UrlGeneratorInterface::ABSOLUTE_URL
);
// outputs something like http://example.com/somewhere/hello/Fabien

路由的問題解決了,但是到現在還沒控制器,這個後面應該要解決了。

原文:The Routing Component

Templating

直接渲染模板是有問題的,當業務邏輯稍微複雜一點就無法在模板中完成。因此需要將邏輯與渲染模板分開。

這一節為什麼不是直接談控制器呢,我想跟第一節作者提到的關註點分離的概念有關,目前為止,框架的問題在於邏輯在模板中做很困難,所以當前事情是要把模板與邏輯抽離出來,本節模板邏輯分離是目的,控制器只是慣例做法。

按照Symfony的慣例。通過給Route屬性,增加_controller這個鍵值,它指明路由對應的方法,框架將直接調用_controller完成各種不同的工作。

這裡有個註意點,路由的屬性都被保存到$request->attributes中,該屬性用保存跟HTTP沒有直接相關的信息。

增加了_controller屬性之後,再將路由信息剝離到單獨一個文件src/app.php,現在模板與業務邏輯區分開了。

原文:Templating

The HtppKernel Component:The Controller Resolver]

上一節為止,所有的操作都是過程化的。我們希望將_controller指向一個類的方法,比如LeapYearControllerindexAction。改造起來也很簡單。將路由的_controller改為[new LeapYearController(), ‘indexAction’]即可。

然而這也帶來了另外一個缺點,不論路由有沒有用到,在它們添加的時候,控制器都被初始化,這對性能是個很大的影響。因此我們希望只有用到的路由才初始化。這個問題可以使用http-kernel模塊解決。

http-kernel提供了非常豐富的功能,不過我們現在只關心HttpKernel\Controller\ControllerResolverHttpKernel\Controller\ArgumentResolver

前者可以用來路由中確定出要調用的方法;後者用來確定要傳遞給方法的參數;參數解析器使用了反射機制,以便實現依賴註入和將路由的attributes的同名參數傳遞進去。調用路由方法與傳參,自己做還是要費一定功夫的,所以使用這兩個解析器都是必須的。

原文:The HtppKernel Component:The Controller Resolver

The Separation Of Concerns

我們的目標是構建一個框架,前面的代碼雖然可滿足要求,但是缺少封裝,沒有放到命名空間,這個在規模擴大時並不方便。同時每建一個新站都需要複製整個front.php。對它們做封裝可提高可用性和可測試性。

本節引入了命名空間,創建Simplex\Framework的類和控制器以及增加psr-4的自動載入。

本節的分離關註的意義其實是從工程層面體現的:通過對前面實現的功能做一次代碼整理,揭示現代WEB PHP框架的基本目錄組織方法。

原文:The Separation Of Concerns

Unit Testing

這一節,對於Framework這個的類測試了404, 500正常響應,該類的測試覆蓋率為100%。這一節對於後續學習單元測試是很有啟發性的:

  • 如何配置單元測試文件phpunit.xml.dist
  • 如何創建Mock Object,以避免要依賴真實環境;
  • 如何儘可能的覆蓋測試,通過404500正常響應的示例說明;
  • 如何生成覆蓋率報告:
$phpunit --coverage-text # 命令行輸出
$phpunit --coverage-html=cov/ # 輸出HTML文檔

這一節的啟發在於:在寫代碼時,傳參應該儘量設計成介面才方便Mock;而錯誤以throw的方式拋出;這樣子會方便測試。另外如果你能從單元測試的角度去考慮框架,就會發現很多框架中覺得可能多餘的設計並不是多餘的。比如LaravelFacade
原文:Unit Testing

event dispatcher

整個框架雖然是完備的,但稱不上是一個好框架。所有的好框架都有很強的可擴展性。那麼什麼是可擴展性呢,作者給了一個蠻不錯的定義:

Being extensible means that the developer should be able to easily hook into the framework life cycle to modify the way the request is handled.

實際上,event dispatcher這個名字不好理解,我是直接把它當成Laravelmiddleware來看待。

原文:event dispatcher

The HttpKernel Component: HttpKernelInterface

HttpKernelInterfaceHttpKernel組件最重要的一個方法。許多組件都依賴於該介面,比如HttpCache。所以自己設計框架的時候,應該實現該介面,以便更好地利用現有組件。(這一節跟下麵一節總結起來呢就是一句話:自己實現的框架核心會有很多問題,還是使用HttpKernel這個組件好)

原文:The HttpKernel Component: HttpKernelInterface

The HttpKernel Component: The HttpKernel Class

HttpKernelHttpKernelInterface的預設實現。相比於自己實現,它提供了更完備的處理機制,比如我們自己的框架只處理了404500的錯誤,但還有其他的錯誤沒處理;另外,它提供了event dispatcher的各種預設機制,允許靈活地控制異常時、控制器進入前後、渲染視圖時的顯示;最後,在安全方面和規模增長後的表現也在各個實際的網站中表現得十分優異。
原文:The HttpKernel Component: The HttpKernel Class

The DependencyInjection Comonent

front.php的代碼基本上在每個應用中都是重覆的,可以考慮將其移到Framework構造函數中,但是你會發現:沒法添加新的listener, 沒辦法模擬介面做單元測試等等。在實際場景中,我們需要區分開發環境與生產環境;或者想要添加越來越多的dispatcher;改變response的輸出字元集等,由於相關的類都只在front.php中出現,所以這些改動都要在front.php中增加代碼完成,最終顯然會導致front.php越來越大。而當我們搞一個新的應用時又需要將front.php拷貝過去,萬一要改時就顯得更不方便。有沒有一個好的方法,能夠保持依然當前框架的靈活性,但是又要可定製,可以單元測試,同時又沒有重覆代碼嗎?依賴註入(DI)就是解決這個問題的好方法。symfony/dependency-injection就是一個棒的DI組件,另外一個輕量級Pimple也是廣受好評。

通過依賴註入,不同的服務都變成了可配置的。框架本身也通過容器初始化,初始化時的參數也都是容器,可根據需要傳遞不同的實現。而disptacher也是個容器,配置的時候可以根據實際情況在初始化階段添加儘可能多的listener。最終,front.php的代碼就變成獲取framework的容器即可,其他的事情則在container.php配置。當程式變複雜時,將listener單獨獨立出來,將配置單獨獨立出來,都是很簡單的事情。基本上可以說,依賴註入是現代框架的標配了。

原文:The DependencyInjection Comonent

END


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 郵件的內容其實是就HTML,傳統的做法都是通過在程式中拼接字元串來生成郵件的內容,生成困難,維護也困難。Razor是MVC裡面使用的視圖引擎,用來生成HTML非常方便,ZKEACMS中就是使用了Razor視圖引擎,用cshtml作為郵件模板來生成郵件內容。這樣很方便維護和修改。 ...
  • using System;using System.Drawing;using System.Windows.Forms; namespace 案例演示{ public partial class frmlogo : Form { public frmlogo() { InitializeCompo ...
  • 總結和開始記錄實驗的現象,調試代碼的經驗,同時開始用博客記錄一些技術上的進步 2017-09-01 09:01:02 1、 picturebox 上 進行覆蓋,移動,大小改變,都會觸發控制項重繪事件,重繪其本身和其所有的子控制項, 所以以繪圖區域為父容器的pictureBox在程式運行時進行的任意操作只 ...
  • python的文件操縱方法: file.readline() 讀取下一行文件,返回含有內容的字元串 file.readlines() 讀取整個文件,返回一個字元串列表 file.read() 讀取整個文件,返回一個字元串 f = open("filename","mode") 打開一個文件,mode ...
  • 要自己寫一個存儲系統,可以依照以下步驟: 1.寫一個繼承自django.core.files.storage.Storage的子類。 2.Django必須可以在無任何參數的情況下實例化MyStorage,所以任何環境設置必須來自django.conf.settings。 3.根據Storage的op ...
  • 什麼是抽象類?這名字聽著就挺抽象的,第一次聽到這個名字還真有可能被唬住。但是,就像老人家所說的,一切反動派都是紙老虎,一切有著裝x名字的概念也是紙老虎。好吧,我們已經從戰略上做到了藐視它,現在就要戰術上重視它,如同要解決紙老虎,就要一個牙齒一個牙齒地敲,一個爪子一個爪子地拔;解決這種抽象概念也一樣, ...
  • 在類SpringbootdemoApplication上右鍵Run as選擇Spring Boot App後Console輸出報錯日誌如下: com.mongodb.MongoSocketOpenException: Exception opening socketat com.mongodb.co ...
  • HDU 2000.ASCII碼排序 Description: 輸入三個字元後,按各字元的ASCII碼從小到大的順序輸出這三個字元。 Input: 輸入數據有多組,每組占一行,有三個字元組成,之間無空格。 Output: 對於每組輸入數據,輸出一行,字元中間用一個空格分開。 SampleInput: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...