實現 PSR-0和PSR-4的類自動載入器並帶案例說明
大家在閱讀文件 或者使用一些第三方的框架或者軟體的時候,都聽過或者看過裡面要求說實現了psr0或者psr4的規範。
我也一直在查資料,找痕跡。現在我的理解是,其實這2個規範就是對類的裝載,實現自動尋路徑。
首先我們看下 PSR0
我寫程式碼實現了它的自動載入器
這是載入器程式碼
<?php /** * Created by PhpStorm. * User: Administrator * Date: 2018/4/14 * Time: 22:27 */ class Psr0{ /* psr-0 規範說明 * 1.一個完整的標準的類檔案格式是這樣的 \vendor\namespace\class * 2.每個名稱空間必須有一個頂級名稱空間 子名稱空間可以有多個 或者沒有 * 3.載入檔案的時候 名稱空間分隔符會被轉換為 DIRECTORY_SEPARATOR * 4.類名如果帶'_',都會轉換為DIRECTORY_SEPARATOR,_的拼接部分必須是類目錄的子目錄部分並且是一一對應 * 打個比方 Tik_Tb_Order.php 其中 Tik和Tb必須是該類檔案所在的子目錄。 * 5.載入的檔案字尾必須以.php結尾 * 6.verdor namespace class必須由大小寫字母組合而成 */ public static function autoload($className) { //去掉最左邊的\ $className = ltrim($className,'\\'); //獲取名稱空間 $position = strrpos($className,'\\'); $strnamespaces = substr($className,0,$position); //獲取類名 $class = substr($className,$position+1); $namespaces = explode('\\',$strnamespaces); //組裝類路徑。。psr0規定 名稱空間分隔符要被DIRECTORY_SEPARATOR替換。 $file_path = ''; foreach ($namespaces as $namespace) { $file_path .= $namespace.DIRECTORY_SEPARATOR; } //類名下劃線處理 $class_file = str_replace('_',DIRECTORY_SEPARATOR,$class); //最終的file $file = './vendor/'.$file_path.$class_file.'.php'; include $file; } }
然後我的檔案目錄
我的呼叫程式碼 是 start.php
<?php /** * Created by PhpStorm. * User: Administrator * Date: 2018/4/14 * Time: 22:22 */ header("Content-type: text/html; charset=utf-8"); require 'Psr0.php'; //定義類的自動載入器 spl_autoload_register('Psr0::autoload'); //測試 $three = new \Com\Three(); $one = new \Com\One\One(); $two = new \Com\One\Fki_Tb_Two(); $three->sh(); $one->sh(); $two->sh();
這是執行效果
看懂了沒 沒的話 我帶大家分析一波。
1.首先 psr0規定 必須有個組織名 我這裡是vendor 然後得有個頂級名稱空間 我這裡是Com
開啟我的three.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:49
*/
namespace Com;
class Three{
public function sh()
{
echo '我是three'.'<br>';
}
}
我這個類就在當前頂級名稱空間下 且沒有子名稱空間 當然也可以被載入到。
再看one.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 10:30
*/
namespace Com\One;
class One{
public function sh()
{
echo '我是one'.'<br>';
}
}
我這裡是設定了子名稱空間 為One 根據psr0規範。子名稱空間必須是類的檔案系統載入的子目錄。所以也能通過。
我們再看fki_Tb_two.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 10:34
*/
namespace Com\One;
class Fki_Tb_Two{
public function sh(){
echo '我是tow'.'<br>';
}
}
這個類名帶下劃線 psr0規範說 下劃線最後的一部分才是類名。其餘部分充當該類被載入的子目錄部分,並且和子目錄名字母大小一一對應 在本例中。根據我上面目錄結構圖,它是這個類的2個上一級目錄名。所以也能被載入。
我們再看psr4
我先貼出載入器程式碼
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:27
*/
//單名稱空間
class Psr4_1{
public static function autoload($className)
{
/*
* Psr-4規範說明
* 1.合法的類的完全限定名格式是 \namespacename\subnamespace\class
* 2.必須有一個頂級的名稱空間
* 3.必須有一個終止類名
* 4.下劃線在類中無特殊意義
* 5.子名稱空間必須對於從檔案系統載入類檔案的一個子目錄
* 6.子目錄和子名稱空間必須大小寫和字母一一對應。子名稱空間分割符表示子目錄分隔符
* 7.
*/
//設定名稱空間目錄對映
$namespace_prefix='Lib1\\Red1';
$base_dir='./Lib1/Red';
//去掉最左邊的\
$className = ltrim($className,'\\');
//獲取最右邊的分隔符位置 用來做名稱空間和class的分界點
$position = strrpos($className,'\\');
//獲取名稱空間部分
$strnamespaces = substr($className,0,$position);
//判斷字首是否存在 。0表示是合法的,false直接返回錯誤
$is_exsists = strpos($strnamespaces,$namespace_prefix);
if($is_exsists!==0)
{
return;
}
//獲取子名稱空間部分
$Len = strlen($namespace_prefix);
$str_sub_namespace = substr($strnamespaces,$Len);
$str_sub_namespace_path = str_replace('\\',DIRECTORY_SEPARATOR,$str_sub_namespace);
//獲取類名
$class = substr($className,$position+1);
//拼接目錄
$file = $base_dir.$str_sub_namespace_path.DIRECTORY_SEPARATOR.$class.'.php';
include $file;
}
}
在貼出呼叫程式碼
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:22
*/
header("Content-type: text/html; charset=utf-8");
require 'Psr4_1.php';
//定義類的自動載入器
spl_autoload_register('Psr4_1::autoload');
$red = new \Lib1\Red1\Red();
$green = new \Lib1\Red1\Sub\Green();
$red_green = new \Lib1\Red1\Red_Green();
$red->sh();
$green->sh();
$red_green->sh();
效果圖
再是目錄結構圖
我分析一下吧。
首先 psr4是要求名稱空間字首和base_dir有個設定好的對應關係。
我這個psr4_1是一個單名稱空間的載入器。稍後我發一個批量的。先拿這個單的說明
我們開啟red.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 14:05
*/
namespace Lib1\Red1;
class Red{
public function sh(){
echo '我是red<br>';
}
}
這裡 我們的名稱空間 符合之前預設定好的對映關係。使用能被載入。
再看一個帶子名稱空間的例子
green.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 15:24
*/
namespace Lib1\Red1\Sub;
class Green{
public function sh(){
echo '我是green<br>';
}
}
這裡的子名稱空間的意思 就是他不在預設值的名稱空間字首裡面 也就是沒包含他。
那麼要讓載入器載入到他 我們必須把這個Sub也就是子名稱空間 對應一個子目錄。也就是在設定好的那個對映關係basedir後面新增一個子目錄 要求字母大小一一對應即可。
詳情請看我的目錄結構圖
psr4規範說明 類名的下劃線格式 並沒有實際的意義..這個和psr0是區別的。
看red_green.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 14:05
*/
namespace Lib1\Red1;
class Red_Green{
public function sh(){
echo '我是red_green<br>';
}
}
把這個當成是一個普通的類檔案進行載入
明白這個原理後 我貼出多名稱空間支援的psr4載入器 也就是我的psr4_2.php
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:27
*/
//支援多名稱空間部署
class Psr4_2{
private static $_classMapper = '';
public static function addClassMapper($prefix,$dir)
{
self::$_classMapper[$prefix] = $dir;
}
public static function autoload($className)
{
//去掉最左邊的\
$className = ltrim($className,'\\');
//獲取最右邊的分隔符位置 用來做名稱空間和class的分界點
$position = strrpos($className,'\\');
//獲取名稱空間部分
$strnamespaces = substr($className,0,$position);
//判斷字首是否存在 。0表示是合法的,false直接返回錯誤
foreach (self::$_classMapper as $mapperkey=> $classmapper)
{
$namespace_prefix = $mapperkey;
$is_exsists = strpos($strnamespaces,$namespace_prefix);
if($is_exsists!==false)
{
//獲取子名稱空間部分
$Len = strlen($namespace_prefix);
$str_sub_namespace = substr($strnamespaces,$Len);
$str_sub_namespace_path = str_replace('\\',DIRECTORY_SEPARATOR,$str_sub_namespace);
//獲取類名
$class = substr($className,$position+1);
//拼接目錄
$base_dir = self::$_classMapper[$namespace_prefix];
$file = $base_dir.$str_sub_namespace_path.DIRECTORY_SEPARATOR.$class.'.php';
include $file;
return;
}
}
}
}
我在貼出我的呼叫程式碼
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/14
* Time: 22:22
*/
header("Content-type: text/html; charset=utf-8");
require 'Psr4_2.php';
spl_autoload_register('Psr4_2::autoload');
//定義類的自動載入器
Psr4_2::addClassMapper('Lib1\Red1','./Lib1/Red');
Psr4_2::addClassMapper('Lib2','./Lib2');
Psr4_2::addClassMapper('Lib3\Hei','./lib3/hei/src');
$red = new \Lib1\Red1\Red();
$red->sh();
$green = new \Lib1\Red1\Sub\Green();
$green->sh();
$red_green = new \Lib1\Red1\Red_Green();
$red_green->sh();
$bule = new \Lib2\Bule();
$bule->sh();
$ming = new \Lib1\Red1\sub\Subb\Ming();
$ming->sh();
$fi_cc = new \Lib2\Fi_CC();
$fi_cc->sh();
$hei = new \Lib3\Hei\Hei();
$hei->sh();
$req = new \Lib3\Hei\Req\Req();
$req->sh();
效果圖
抽幾個具有代表性的分析一下 給大家看。
看我的req.php
檔案程式碼
<?php
/**
* Created by PhpStorm.
* User: Administrator
* Date: 2018/4/15
* Time: 17:56
*/
namespace Lib3\Hei\Req;
class Req{
public function sh(){
echo '我是req<br>';
}
}
也是存在一個子名稱空間的問題 我們只要指定對應的子目錄就能實現對他的載入。
總結一下 :根據psr4的自動載入規則 我們可以根據配置好的名稱空間字首。快速定位類的檔案位置。還有一點,為了開發的便捷性 我們可以配置多個名稱空間字首 ,使類檔案避免深度索引。