用Composer吧! 理由听我娓娓道来!
PHP最早读取套件的方法
初学PHP时,最早会面对的问题之一就是require与include差别何在?
require_once与include_once又是什麽?
弄懂这些问题之后,如果不使用framework,直接开发,便常出现类似这样的code:
1 2 3 4 5 6 |
// whatever.php // 这档案需要用到几个类别 require 'xxx_class.php'; require 'yyy_class.php'; require 'zzz_class.php'; // ... |
然后在其他档案会出现:
1 2 3 4 5 |
// another.php // 这档案需要用到几个类别 require 'yyy_class.php'; require 'zzz_class.php'; // ... |
这样的结果,会产生至少两个问题:
1. 许多档案用到同样几个class,于是在不同地方都需要载入一次。
2. 当类别多了起来,会显得很乱、忘记载入时还会出现error。
那麽,不如试试一种懒惰的作法? 写一个php,负责载入所有类别:
1 2 3 4 5 6 7 |
// 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投降之前,做最后的尝试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// autoload.php function __autoload($classname) { if ($classname === 'xxx.php'){ $<a class="zem_slink" title="Filename" href="http://en.wikipedia.org/wiki/Filename" target="_blank" rel="wikipedia noopener">filename</a> = "./". $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了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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格式更简洁。允许我这样打,好吗?
1 2 3 4 5 6 |
{ "autoload": [ "my_library", "other_library" ] } |
然后谁来个工具帮我产生一大串autoload相关的php程式码吧…,可以吗?
可以。
Composer登场
首先,装好composer(本文不介绍如何安装。)
再来,建立一个composer.json档,裡面输入这些:
1 2 3 4 5 6 7 8 |
{ "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改成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "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实在是太棒了