PHP命名空间 namespace 如何实现自动加载
测试
namespace 会自动加载,但是我测试的两个文件并没有自动加载:
#/DB/MySql.class.php
namespace DB;
class MySql
{
public function __construct()
{
var_dump(__FILE__);
}
}
#/index.php
namespace Home;
use DB\MySql;
$mysql = new MySql();
报错
Fatal error: Class 'DB\MySql' not found in D:\localhost\demo\space\index.php on line 23
修改
修改 index.php 改成如下,可以了,但是为什么不能自动加载?
php
namespace Home; use DB\MySql; spl_autoload_register(function ($class) { if ($class) { $file = str_replace('\\', '/', $class); $file .= '.class.php'; if (file_exists($file)) { include $file; } } }); $mysql = new MySql();
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
用Composer吧!
理由聽我娓娓道來!
PHP最早讀取套件的方法
初學PHP時,最早會面對的問題之一就是require與include差別何在?
require_once與include_once又是什麼?
弄懂這些問題之後,如果不使用framework,直接開發,便常出現類似這樣的code:
然後在其他檔案會出現:
這樣的結果,會產生至少兩個問題:
1. 許多檔案用到同樣幾個class,於是在不同地方都需要載入一次。
2. 當類別多了起來,會顯得很亂、忘記載入時還會出現error。
那麼,不如試試一種懶惰的作法?
寫一個php,負責載入所有類別:
然後在其他檔案都載入這支檔案即可:
結果新問題又來了:當類別很多的時候,隨便一個web page都會載入一堆code,吃爆記憶體,怎麼辦呢?
__autoload
為了解決這個問題,PHP 5開始提供__autoload這種俗稱「magic method」的函式。
當你要使用的類別PHP找不到時,它會將類別名稱當成字串丟進這個函式,在PHP噴error投降之前,做最後的嘗試:
也因為PHP這種「投降前最後一次嘗試」的行為,有時會讓沒注意到的人困惑「奇怪我的code怎麼跑得動?我根本沒有require啊..」,所以被稱為「magic method」。
如此一來,問題似乎解決了?
可惜還是有小缺點..,就是這個__autoload函式內容會變得很巨大。以上面的例子來說,一下會去根目錄找、一下會去other_library資料夾、一下會去my_library資料夾尋找。在整理檔案的時候,顯得有些混亂。
spl_autoload_register
於是PHP從5.1.2開始,多提供了一個函式。
可以多寫幾個autoload函式,然後註冊起來,效果跟直接使用__autoload相同。
現在可以針對不同用途的類別,分批autoload了。
每個loader內容可以做很多變化。可以多寫判斷式讓它更智慧、可以進行字串處理…。
自動載入類別的問題終於解決了…。
但是光上面的code也有15行,而且在每個project一定都會寫類似的東西。有沒有辦法自動產生這15行呢?
我的願望很簡單,我告訴你,反正我有my_library資料夾跟other_library資料夾,你自己進去看到什麼類別就全部載入好不好…?
阿不對,全部載入剛又說效能不好,那你進去看到什麼就全部想辦法用spl_autoload_register記起來好不好…?
我懶得打15行了,我只想打這幾個字:
$please_autoload = array( 'my_library', 'other_library');
可不可以發明一個工具,去吃$please_autoload這個變數,然後自己想辦法載入一切啊…?
ㄟ等等,我連php程式碼都懶得打了,在web領域JSON格式更簡潔。允許我這樣打,好嗎?
然後誰來個工具幫我產生一大串autoload相關的php程式碼吧…,可以嗎?
可以。
Composer登場
首先,裝好composer(本文不介紹如何安裝。)
再來,建立一個composer.json檔,裡面輸入這些:
比原本希望的多打了一些字,不過差不多。
再來,在terminal輸入
composer install
執行成功之後,你會看到一個vendor資料夾,內含一個autoload.php。
沒錯,跟你夢想的一樣。你只要載入這個檔案:
require 'vendor/autoload.php';
你需要的所有類別,都會在適當的時候、以適當的方式自動載入。
php再也不會噴error說你「類別尚未定義」了!
這vendor資料夾裡面的一切,都只是php code而已,並沒有特別神奇的地方。只要去看autoload.php的原始碼,就能知道composer到底寫了哪些php code給你。
ㄟ等等,我寫的類別都放在my_library裡面了,other_library都是網路上copy下來的現成類別。我想要用Google API的Client類別、Doctrine資料庫管理抽象層類別、還有guzzlehttp的發送request類別。
我連去下載這些檔案、然後丟進這個資料夾都懶得做了,我根本不想手動建立other_library這個資料夾。composer真那麼神…不如連下載都幫我自動下載?可以嗎?
可以。
查詢一下那幾個套件在「https://packagist.org/」的名稱、還有你需要的版本號。
把剛剛的composer.json改成這樣:
然後’composer install’指令除了自動載入你的類別之外、還會自動下載你需要的類別、然後自動載入它們。
一樣require ‘vendor/autoload.php’就可以了。composer實在是太棒了
我的部落格
http://blog.turn.tw/?p=1039
http://blog.turn.tw/?p=1122
命名空间跟文件加载并无直接关系,
只是有些语言,将命名空间结构和文件结构对应起来了。
以php为例,
一般的命名空间结构,跟php文件结构是存在映射关系的,通过命名空间名称,就能算出该类的实际存储位置,
然后实例化的时候,会触发用设置的spl自动加载函数将文件引入。
传送门
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader-examples.md
希望能有帮助
命名空间跟目录没半毛钱关系,只是为了自动加载文件方便,
首先题主要搞清楚一个概念,PHP命名空间中的 fooAnother 并不一定是指:当前目录下有个foo目录,里面有个Another 文件,文件内有个 Another Class 这个概念是完全错误的!(或者说这是一种习惯或规范)。
!!PHP命名空间的注册仅仅是一个字符符号注册而已,和任何目录、文件没有关系!!
所以命名空间的操作 一般会借助于 spl_autoload_register 或 autoload来自动加载文件,说白了还是需要 require 文件的。
只不过这个过程被 autoload 消化掉了,至于 autoload 怎么去引入文件 那要看你怎么编写 autoload 函数了。
例如:当autoload接收到参数为 fooAnother 时,把 foo 替换成 fooPlugin 然后去目录找文件 fooPlugin/Another.php 也很正常。
然后进行 require 文件,当 require 成功时(Another.php 内肯定要声明自己的命名空间,达到注册的目的), 此时你使用 fooAnother 系统会在已注册的命名空间符号内搜索(上面已经注册了):fooAnother直接实例化。
总结:
1、namespace 仅仅是符号注册! 和目录,文件没关系,具体引入文件需要依赖于 autoload。
2、补充autoload知识:当用户去实例化一个不存在的类/命名空间时,会触发用户已经注册的 autoload 函数,在此函数内可以进行字符串目录、文件拼接,最后require进来相应文件。此时 这个类就堂而皇之的存在了。So... 你 new 成功了。
实际执行代码的时候是
new \DB\MySql();
找不到这个文件的时候就会调用你的autoload函数 并file_exists("DB/MySql.class.php")
,在当前目录下查找这个文件,在找不到的情况下然后就是你看到的 错误命名空间可以理解成一种虚拟路径,比如:
即使
a.class.php
和b.class.php
存在于同一个文件夹下,在使用他们的时候也需要加入用命名空间。比如现在有一个c.php
, 它和a.class.php
与b.class.php
在同一文件夹下:其中,两个
include
的目的是把两个文件中的内容引入,这样才可以正常使用文件中的内容 (类、变量、函数等) 。而命名空间的作用在于:即使引入了类所在的文件,由于它们被加上了一层「虚拟路径」,因而在实例化的时候也需要加入各自的命名空间才行。而自动载入,目的是在于将「虚拟路径」和「真实文件路径」相对应,使得在实例化
new A\B\C()
就相当于在使用./A/B/C.php
文件中的类。有了这样一个约定,我们就可以在使用类时,只关注命名空间,而不用再繁琐地引入文件了。这是因为有这么一段代码(就是你写的spl_autoload_register
那段),按照某一约定(PSR0/PSR4,即命名空间路径与实际文件路径相匹配) 帮我们把文件引入的工作给做了。至于为什么非得写这么一段
spl_autoload_register
,那是因为这种对应约定还没被写入 PHP 的语言机制里。不然我们就可以像 Java 中定义和使用「包」的方式一样对待 PHP 项目代码了。