相信有在用PHP的朋友近年來常聽到composer這個套件管理工具。它究竟是作什麼用的?又是爲了解決什麼問題而存在呢?javascript
要了解這個,得先從歷史開始提及…。php
初學PHP時,最先會面對的問題之一就是require與include差異何在?
require_once與include_once又是什麼?java
弄懂這些問題以後,若是不使用framework,直接開發,便常出現相似這樣的code:web
// whatever.php // 這檔案須要用到幾個類別 require 'xxx_class.php'; require 'yyy_class.php'; require 'zzz_class.php'; // ...
而後在其餘檔案會出現:json
// another.php // 這檔案須要用到幾個類別 require 'yyy_class.php'; require 'zzz_class.php'; // ...
這樣的結果,會產生至少兩個問題:api
那麼,不如試試一種懶惰的做法?composer
寫一個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';
而後在其餘檔案都載入這支檔案便可:ui
require 'load_everything.php'
結果新問題又來了:當類別不少的時候,隨便一個web page都會載入一堆code,佔用大量內存,怎麼辦呢?google
爲解決這個問題,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資料夾尋找。在整理檔案的時候,顯得有些混亂。
因而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內容能夠作不少變化。能夠多寫判斷式讓它更智慧、能夠進行字串處理…。
自動載入類別的問題終於解決了…。
可是光上面的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": [ "my_library", "other_library" ] }
而後誰來個工具幫我產生一大串autoload相關的php程式碼吧…,能夠嗎?
能夠。
首先,裝好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給你。
等等,我寫的類別都放在my_library裏面了,other_library都是網路上copy下來的現成類別。我想要用Google API的Client類別、Doctrine資料庫管理抽象層類別、還有guzzlehttp的發送request類別。
我連去下載這些檔案、而後丟進這個資料夾都懶得作了,我根本不想手動創建other_library這個資料夾。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’就能夠了。composer實在是太棒了。
其實composer解決的問題不僅這樣。
類別多了起來以後,各類程式語言都提供namespace功能協助分類。
在有namespace的狀況下,PHP社羣與composer是如何解決自動載入的問題呢?
這些比較進階的內容,下回分曉。
http://blog.turn.tw/?paged=4&cat=2