目錄結構
- system
- constant.php
- function.php
- url.php
system資料夾用來放置構建框架環境的所有檔案。
1.0 版本中只提供了路由解析功能,由 url.php 提供;constant.php 用來定義常量,function.php 則是框架級的函式庫,與之對應,APP 中可以定義應用級的函式庫,如果存在應用級的函式庫,兩者之間的關係是上下級。
constant.php 和 function.php 與 url.php 分屬於不同的功能劃分。
從功能劃分角度來看,constant.php 和 function.php 都是必須存在且唯一的,屬於框架配置;url.php 屬於框架功能,與它處於同一級別的還可以有很多其他的功能元件。
功能元件這個概念,為構建超大型的專案提供了一種相當效率的解決思路。
不同的功能被抽象成一個個零部件,通過組合使用這些功能元件,完成業務需求。並且隨著功能元件的不斷增加,後續的需求研發會越發簡單起來,主要的精力放在演算法即可,用輪子就好,無需再花費相當的精力去造輪子。
這裡要特別提及 composer,PHP 專案利用 composer 包管理器可以非常便利的利用別人造的輪子,基本上可以無痛的嵌入到自己的專案當中,使用別人的勞動成果是非常有效的一種方式,可以讓我們更高效的利用自己的時間,提升效率。
Constant.php 說明
<?php
// 字尾
define('EXT','.php');
// 路徑
define('ROOT',__DIR__.'/../');
define('APP',ROOT.'app/');
define('SYSTEM',ROOT.'system/');
1.0 版本只設定了上訴的常量,有兩個部分:字尾和路徑。
抽象的來看,路徑可以看成一個必需的大類,其他是另一大類。
字尾只是包含在其他當中的一個細分,除了字尾之外,還可以定義同級的常量,比如作業系統、分隔符等等,存在很多與字尾同級的其他常量,設不設定以及如何設定,並沒有定法。
在我看來,這完全取決於開發者(想怎麼玩就怎麼玩)。但是路徑,是一個必備的部分,無論哪一個框架都必然存在,好處很明顯,可以大大的簡化路徑書寫時的複雜度(寫的少),而且可以使用更具辨識度的名稱。
Function.php 說明
<?php
function vd($data)
{
echo '<pre>';
var_dump($data);
echo '</pre>';
}
function vde($data)
{
vd($data);
exit;
}
1.0 版本就設定了兩個框架級函式,照搬 TP 的命名,vd 和 vde。
在 1.0 demo 中並不屬於必需,列舉在這裡,只是說明,框架中會有一個或多個檔案用來存放框架級的函式。
只所以說一個或多個,取決於框架的複雜程度以及開發者的喜好,列舉 TP 的做法來進行一個對比,TP 中的框架級函式都在一個檔案中,按類別進行了區分,寫法如下:
<?php
//時間函式
...
//檔案函式
...
//字串函式
...
如果函式庫豐富(幾十上百個函式),即使是在 IDE 中,也是相當難使用的,尤其是在學習使用的時候。針對這種情況,可以進行進一步的劃分。
如上,將時間函式放到一個檔案當中,將檔案函式放到另一個檔案當中,每一個檔案存放一個類別,可以更好的幫助定位,而且單個檔案並不會很大。
究竟使用何種方式,還是取決於開發者的那靈光一閃。(很多的設計,並沒有那麼多的為什麼,就像 Python 取名一樣,作者喜歡看這個肥皂劇,所以就有了這麼個名字,事實往往就是這麼的平淡無奇)
Url.php 說明
這裡介紹 url.php 這個功能元件,下面會列舉具體的程式碼,並詳細說明為什麼這樣設計,以及是否還有其他的方案。
<?php
/**
* url解析器
*/
class url
{
// 模組
private static $module = 'admin';
// 控制器
private static $controller = 'index';
// 方法
private static $func = 'index';
// 需要載入的檔案路徑
private static $path = 'admin/controller/index';
/**
* 從超全域性變數獲取模組/控制器/方法
*/
public static function analyse($data = [])
{
// module,controller,function one of them exists
if(!empty($data['PATH_INFO'])){
$arr = explode('/',ltrim($data['PATH_INFO'],'/'));
$num = count($arr);//module,controller,function,can exists or not
switch($num){
case 1:
self::$module = $arr[0];// the rest use index
self::$path = $arr[0].'/controller/index';
break;
case 2:
self::$module = $arr[0];
self::$controller = $arr[1];
self::$path = $arr[0].'/controller/'.$arr[1];
break;
case 3:
self::$module = $arr[0];
self::$controller = $arr[1];
self::$func = $arr[2];
self::$path = $arr[0].'/controller/'.$arr[1];
break;
}
}
}
/**
* get param
*/
public function get($parse_name)
{
return self::$$parse_name;
}
/**
* set param
*/
public function set($parse_name,$parse_value)
{
self::$$parse_name = $parse_value;
}
}
首先是功能模組的命名,也就是類名,準確的反應功能是非常重要的。
好的命名,在使用過程中會減少許多無謂的消耗,能夠從名字上判斷出功能的,就不需要進入檔案檢視具體的實現,這只是看起來不起眼但實際上是相當提升效率的操作。
往下是類的屬性定義部分,我這裡的設計將模組,控制器以及方法完全分開,單獨進行了處理,並且設計了一個變數用來儲存應用檔案的路徑。
這裡的設計,要關注的點是 url.php 這個功能元件被設計用來幹什麼,其內部的具體實現都是基於這個目的來的,1.0 demo 中,url.php 被設計用來解析超全域性陣列得到具體的路由資訊,這是我們的目的。
明確了這個目的之後,再往下就是對抽象目標的具體建模。
為了達到拿到路由資訊的目的,屬性定義可以有各種各樣的實現方法,如將所有的變數都可以放到一個陣列當中,而不是拆開來做。具體選擇哪種做法,需要根據場景來進行設計。
比如我現在做的這個框架 demo,只需要跑通流程就可以,並需要考慮的太多,如果是商用的,情況肯定是不一樣的,需要考慮實際的使用情況,如何設計會更便捷、更易於使用,從這樣的一個角度出發,可能用陣列來進行組織可能是更好的解決方法,使用時,不會像這種設計這樣麻煩,後面說明方法設計時,會進一步解釋。
繼續往下就到了方法部分。
其中 get 和 set 我認為應該成為一種標配,類中所有屬性值的設定和取用都應該通過對外統一的介面來操作,統一、規範和標準的目的是為了工程實踐中的便捷,所有的類屬性的取用設定都使用同樣的方式,會減少學習成本,這是採用這種設計的主要理由。
接下來就到了本功能元件最重要的部分,function analyse。
關注點依然要放在其目的上,而不是其實現細節,因為實現的細節會因屬性設計部分的變更而變更,目前呈現的這種形式是由屬性設計而決定的,這一點一定要特別清楚,當在學習其他框架時無需太關注於其中的細節,只需要弄明白當前功能元件實現的功能是什麼即可。
在這裡,analyse 所做的就是根據請求拿到需要執行檔案的路由。
最後還有一點要進行說明:
pubic static function analyse($data = []){
...
}
這裡使用的是 static,只所以這樣設計,是為了使用靜態的一個特性,在執行靜態方法時不需要建立一個物件,然後藉助物件來呼叫方法,會讓指令碼變得簡潔、易懂。
比如,index.php 中就可以這樣寫:
...
// 解析陣列拿到路徑
url::analyse($_SERVER);
...
看起來非常的簡潔,less is more。
PS:本系列文章最佳閱讀方式,IDE+本地執行環境,IDE中閱讀可配合 demo 執行增進理解,GitHub地址