原文: https://zhuanlan.zhihu.com/p/133857206
---------------------------------
Composer是一個非常流行的PHP包依賴管理工具,已經取代PEAR包管理器,對於PHP開發者來說掌握Composer是必須的.
對於使用者來說Composer非常的簡單,通過簡單的一條命令將需要的代碼包下載到vendor目錄下,然后開發者就可以引入包並使用了.
其中的關鍵在於你項目定義的composer.json,可以定義項目需要依賴的包(可能有多個),而依賴的包可能又依賴其他的包(這就是組件的好處),這些都不用你煩心,Composer會自動下載你需要的一切,一切在於composer.json的定義.
Composer對於使用者來說是很透明,但是其背后的理念還是需要了解一下的,其的誕生也不是偶然的,得益於Github的快速發展,PHP語言也越來越現代化,顯得更高大上了.
要了解這個,得先從歷史開始說起…
PHP最早加載類的方法
初學PHP時,最早會面對的問題之一就是require與include差別何在?require_once與include_once又是什么?
弄懂這些問題之后,如果不使用framework,直接開發,便常出現類似這樣的code:
// whatever.php // 這檔案需要用到幾個類別 require 'xxx_class.php'; require 'yyy_class.php'; require 'zzz_class.php'; // ...
然后在其他檔案會出現:
// another.php // 這檔案需要用到幾個類別 require 'yyy_class.php'; require 'zzz_class.php'; // ...
這樣的結果,會產生至少兩個問題:
許多檔案用到同樣幾個class,於是在不同地方都需要載入一次。當類別多了起來,會顯得很亂、忘記載入時還會出現error。那么,不如試試一種懶惰的作法?
寫一個php,負責載入所有類別:
// load_everything.php require 'xxx_class.php'; require 'yyy_class.php'; require 'zzz_class.php'; require 'aaa_class.php'; require 'bbb_class.php'; require 'ccc_class.php';
然后在其他檔案都載入這支檔案即可:
require 'load_everything.php'
結果新問題又來了:當類別很多的時候,隨便一個web page都會載入一堆code,占用大量內存,怎么辦呢?
__autoload
為解決這個問題,PHP 5開始提供__autoload這種俗稱“magic method”的函式。
當你要使用的類別PHP找不到時,它會將類別名稱當成字串丟進這個函式,在PHP噴error投降之前,做最后的嘗試:
// autoload.php function __autoload($classname) { if ($classname === 'xxx.php'){ $filename = "./". $classname .".php"; include_once($filename); } else if ($classname === 'yyy.php'){ $filename = "./other_library/". $classname .".php"; include_once($filename); } else if ($classname === 'zzz.php'){ $filename = "./my_library/". $classname .".php"; include_once($filename); } // blah }
也因為PHP這種“投降前最后一次嘗試”的行為,有時會讓沒注意到的人困惑“奇怪我的code怎么跑得動?我根本沒有require啊..”,所以被稱為“magic method”。
如此一來,問題似乎解決了?
可惜還是有小缺點..,就是這個__autoload函式內容會變得很巨大。以上面的例子來說,一下會去根目錄找、一下會去other_library資料夾、一下會去my_library資料夾尋找。在整理檔案的時候,顯得有些混亂。
spl_autoload_register
於是PHP從5.1.2開始,多提供了一個函式。可以多寫幾個autoload函式,然后注冊起來,效果跟直接使用__autoload相同。現在可以針對不同用途的類別,分批autoload了。
spl_autoload_register('my_library_loader'); spl_autoload_register('other_library_loader'); spl_autoload_register('basic_loader'); function my_library_loader($classname) { $filename = "./my_library/". $classname .".php"; include_once($filename); } function other_library_loader($classname) { $filename = "./other_library/". $classname .".php"; include_once($filename); } function basic_loader($classname) { $filename = "./". $classname .".php"; include_once($filename); }
每個loader內容可以做很多變化。可以多寫判斷式讓它更智慧、可以進行字串處理…。自動載入類別的問題終於解決了… 但是一大串一大串的autoload,手動去寫這些,很麻煩,這個時候就會想到能不能用一種工具直接去生成呢?這個時候就有了composer了。
Composer
建立一個composer.json檔,里面輸入這些:
{ "autoload": { "classmap": [ "my_library", "other_library" ] } }
再來,在terminal輸入 composer install
執行成功之后,你會看到一個vendor資料夾,內含一個autoload.php。沒錯,跟你夢想的一樣。你只要載入這個檔案:
require 'vendor/autoload.php';
你需要的所有類別,都會在適當的時候、以適當的方式自動載入。php再也不會error說你“類別尚未定義”了!
這vendor資料夾里面的一切,都只是php code而已,並沒有特別神奇的地方。只要去看autoload.php的原始碼,就能知道composer到底寫了哪些php code給你。
最后提供一個擴展包下載,直接用composer就好了。
查詢一下那幾個套件在“https://packagist.org/”的名稱、還有你需要的版本號。
把剛剛的composer.json改成這樣:
{ "require": { "google/apiclient": "1.0.*@beta", "guzzlehttp/guzzle": "~4.0", "doctrine/dbal": "~2.4" }, "autoload": { "classmap": [ "my_library" ] } }
然后composer install指令除了自動載入你的類別之外、還會自動下載你需要的類別、然后自動載入它們。
一樣require vendor/autoload.php就可以了。
