※ 本文為 ott 轉寄自 ptt.cc 更新時間: 2014-11-23 22:52:07
看板 PHP
作者 poopoo888888 (阿川)
標題 [心得] Composer設計原理與基本用法
時間 Thu Nov 20 17:43:02 2014


嗨,大家好。

小弟耳聞composer這個套件管理工具很久了,一直覺得很困難而沒有弄懂它。
最近終於掌握了它一些,也理解了它的創造理念。
分享這個理解的過程,希望對正要入門composer的朋友有幫助。
有寫錯的地方希望各位不吝指正。

感恩<( _ _ )>

網頁好讀版

http://blog.turn.tw/?p=1039

-------------------------------------------------------

相信有在用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.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實在是太棒了。

--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 36.229.213.31
※ 文章網址: http://www.ptt.cc/bbs/PHP/M.1416476586.A.C3E.html
crossdunk: 推1F 11/20 17:45
onininon: 有看有推2F 11/20 17:56
rickysu: 推3F 11/20 18:04
lucky1lk: 不錯4F 11/20 18:48
KawasumiMai: 目前還停留在require一個all_require的階段XDD5F 11/20 19:08
martin77: 推, 說明得很棒6F 11/20 23:38
ga013077: 推7F 11/21 07:22
alex0914: 有懂有推8F 11/21 22:48
xdraculax: 推 雖然我懶得安裝寧可自己寫@@9F 11/22 00:48
aoeeoak: 有看有懂,很喜歡這種風格的教學文10F 11/22 02:03

--
※ 看板: ott 文章推薦值: 0 目前人氣: 0 累積人氣: 218 
分享網址: 複製 已複製
guest
x)推文 r)回覆 e)編輯 d)刪除 M)收藏 ^x)轉錄 同主題: =)首篇 [)上篇 ])下篇