composer 的設計原理及其基本用法


相信有在用PHP的朋友近年來常聽到composer這個套件管理工具。它到底是做什么用的?又是為了解決什么問題而存在呢?

要了解這個,得先從歷史開始說起…。

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'; // ... 

這樣的結果,會產生至少兩個問題:

  1. 許多檔案用到同樣幾個class,於是在不同地方都需要載入一次。
  2. 當類別多了起來,會顯得很亂、忘記載入時還會出現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內容可以做很多變化。可以多寫判斷式讓它更智慧、可以進行字串處理…。

自動載入類別的問題終於解決了…。

但是光上面的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安裝,及如何在天朝這種大局域網內使用。

再來,建立一個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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM