php是使用require(require_once)和include(include_once)關鍵字加載類檔案,但是在實際的開發工程中我們基本上不會去使用這些關鍵字去加載類, 因為這樣做會使得代碼的維護相當的困難,實際的開發中我們會在檔案的開始位置用use關鍵字使用類,然后直接new這個類就可以了. 至于類是怎么加載的,一般都是框架或者composer去實作的,
<?php
use Illuminate\Container\Container;
$container = new Container();
自動加載
我們可以通過一段偽代碼來模擬一下在類的實體化工程中類是如何作業的
function instance($class)
{
// 如果類已加載則回傳其實體
if (class_exists($class, false)) {
return new $class();
}
// 查看 autoload 函式是否被用戶定義
if (function_exists('__autoload')) {
__autoload($class); // 最后一次加載類的機會
}
// 再次檢查類是否存在
if (class_exists($class, false)) {
return new $class();
} else { // 系統:我實在沒轍了
throw new Exception('Class Not Found');
}
}
php在語言層面提供了__autoload 魔術方法給用戶來實作自己的自動加載邏輯,當用戶去new一個類的時候,如果該類沒有被加載,php會在拋出錯誤前呼叫__autoload方法去加載類,下面的例子中的__autoload方法只是簡單的輸出要加載類的名稱, 并沒有去實際的加載對應的類, 所以會拋出錯誤,
<?php
use Illuminate\Container\Container;
$container = new Container();
function __autoload($class)
{
/* 具體處理邏輯 */
echo $class;// 簡單的輸出要加載類的名稱
}
/**
*
運行結果
Illuminate\Container\Container
Fatal error: Uncaught Error: Class 'Illuminate\Container\Container' not found in D:\project\php\laravel_for_ci_cd\test\ClassLoader.php:5
Stack trace:
#0 {main}
thrown in D:\project\php\laravel_for_ci_cd\test\ClassLoader.php on line 5
*/
明白了 __autoload 函式的作業原理之后,我們來用它去實作一個最簡單自動加載,我們會有index.php和Person.php兩個檔案在同一個目錄下,
//index.php
<?php
function __autoload($class)
{
// 根據類名確定檔案名
$file = './'.$class . '.php';
if (file_exists($file)) {
include $file; // 引入PHP檔案
}
}
new Person();
/*---------------------分割線-------------------------------------*/
//Person.php
class Person
{
// 物件實體化時輸出當前類名
function __construct()
{
echo '<h1>' . __CLASS__ . '</h1>';
}
}
/**運行結果
* 輸出 <h1>Person</h1>
*/
命名空間
命名空間并不是什么新鮮的事務,很多語言都早就支持了這個特性(只是叫法不相同),它主要解決的一個問題就是命名沖突! 就好像日常生活中很多人都會重名,我們必須要通過一些標識來區分他們的不同,比如說現在我們要用php介紹一個叫張三的人 ,他在財務部門作業,我們可以這樣描述,
namespace 財務部門;
class 張三
{
function __construct()
{
echo '財務部門的張三';
}
}
這就是張三的基本資料 , namespace是他的部門標識,class是他的名稱. 這樣大家就可以知道他是財務部門的張三而不是工程部門的張三,
非限定名稱,限定名稱和完全限定名稱
1.非限定名稱,或不包含前綴的類名稱,例如 $comment = new Comment(); 如果當前命名空間是Blog\Article,Comment將被決議為、\Blog\Article\Comment,如果使用Comment的代碼不包含在任何命名空間中的代碼(全域空間中),則Comment會被決議為\Comment,
注意: 如果檔案的開頭有使用use關鍵字 use one\two\Comment; 則Comment會被決議為 *one\two\Comment*,
2.限定名稱,或包含前綴的名稱,例如 $comment = new Article\Comment(); 如果當前的命名空間是Blog,則Comment會被決議為\Blog\Article\Comment,如果使用Comment的代碼不包含在任何命名空間中的代碼(全域空間中),則Comment會被決議為\Article\Comment,
3.完全限定名稱,或包含了全域前綴運算子的名稱,例如 $comment = new \Article\Comment(); 在這種情況下,Comment總是被決議為\Article\Comment,
spl_autoload
接下來讓我們要在含有命名空間的情況下去實作類的自動加載,我們使用 spl_autoload_register() 函式來實作,這需要你的 PHP 版本號大于 5.12,spl_autoload_register函式的功能就是把傳入的函式(引數可以為回呼函式或函式名稱形式)注冊到 SPL __autoload 函式佇列中,并移除系統默認的 __autoload() 函式,一旦呼叫 spl_autoload_register() 函式,當呼叫未定義類時,系統就會按順序呼叫注冊到 spl_autoload_register() 函式的所有函式,而不是自動呼叫 __autoload() 函式,
現在, 我們來創建一個 Linux 類,它使用 os 作為它的命名空間(建議檔案名與類名保持一致):
<?php
namespace os; // 命名空間
class Linux // 類名
{
function __construct()
{
echo '<h1>' . __CLASS__ . '</h1>';
}
}
接著,在同一個目錄下新建一個 index.php檔案,使用 spl_autoload_register 以函式回呼的方式實作自動加載:
<?php
spl_autoload_register(function ($class) { // class = os\Linux
/* 限定類名路徑映射 */
$class_map = array(
// 限定類名 => 檔案路徑
'os\\Linux' => './Linux.php',
);
/* 根據類名確定檔案路徑 */
$file = $class_map[$class];
/* 引入相關檔案 */
if (file_exists($file)) {
include $file;
}
});
new \os\Linux();
這里我們使用了一個陣列去保存類名與檔案路徑的關系,這樣當類名傳入時,自動加載器就知道該引入哪個檔案去加載這個類了,但是一旦檔案多起來的話,映射陣列會變得很長,這樣的話維護起來會相當麻煩,如果命名能遵守統一的約定,就可以讓自動加載器自動決議判斷類檔案所在的路徑,接下來要介紹的PSR-4 就是一種被廣泛采用的約定方式
PSR-4規范
PSR-4 是關于由檔案路徑自動載入對應類的相關規范,規范規定了一個完全限定類名需要具有以下結構:
<頂級命名空間>(<子命名空間>)*<類名>
PSR-4 規范中必須要有一個頂級命名空間,它的意義在于表示某一個特殊的目錄(檔案基目錄),子命名空間代表的是類檔案相對于檔案基目錄的這一段路徑(相對路徑),類名則與檔案名保持一致(注意大小寫的區別),
舉個例子:在全限定類名 \app\view\news\Index 中,如果 app 代表 C:\Baidu,那么這個類的路徑則是 C:\Baidu\view\news\Index.php.我們就以決議 \app\view\news\Index 為例,撰寫一個簡單的 Demo:
<?php
$class = 'app\view\news\Index';
/* 頂級命名空間路徑映射 */
$vendor_map = array(
'app' => 'C:\Baidu',
);
/* 決議類名為檔案路徑 */
$vendor = substr($class, 0, strpos($class, '\\')); // 取出頂級命名空間[app]
$vendor_dir = $vendor_map[$vendor]; // 檔案基目錄[C:\Baidu]
$rel_path = dirname(substr($class, strlen($vendor))); // 相對路徑[/view/news]
$file_name = basename($class) . '.php'; // 檔案名[Index.php]
/* 輸出檔案所在路徑 */
echo $vendor_dir . $rel_path . DIRECTORY_SEPARATOR . $file_name;

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/84974.html
標籤:PHP
上一篇:檔案管理系統
