1. 新建目录composer_test
后续的所有操作都在该目录下执行。所以现在该目录为空。
2. 使用Composer下载第一个包(monolog/monolog)
方法一
在当前目录下直接执行require命令: composer require monolog/monolog
。
- 该命令首先会在目录下寻找composer.json文件,如果没有的话则自动创建composer.json并将require的依赖包写入该文件。
- 然后再去composer源上下载所需要的依赖包,放到vendor目录下。
- 将依赖包的顶级命名空间与其源码目录的对应关系写入到/vendor/composer目录下的autoload_*文件中。
运行完命令后目录结构变成如下图所示。
- /composer.json文件 是composer进行项目依赖管理的配置文件。
- /composer.lock文件 是composer下载依赖包时候创建的文件,它记录了当前下载到的所有依赖包的版本。
- /vendor目录 是composer下载到的依赖包所存放的目录。(为什么当前/vendor目录下除了monolog还有composer与psr文件夹呢?因为monolog依赖于psr/log包,而composer文件夹则是存放实际负责类自动加载的函数。)
- /vendor/autoload.php文件 是composer的自动加载入口,它可以加载 vendor目录下所有依赖包的类文件。为了使用composer的自动加载,我们需要将这个autoload.php引入到项目的根引导文件中(index.php)
方法二
在空的项目目录下创建一个composer.json文件。(其中^2.0的意思是取大版本号为2的最新稳定版本)
{ "require": { "monolog/monolog": "^2.0" } }
然后执行命令 composer install
。然后composer就会根据composer.json文件去下载所需要的依赖包。结果同方法一中的截图一样。
3. 使用Composer的自动加载
在项目根目录下新建一个index.php文件。
<?php require __dir__.'/vendor/autoload.php'; $log = new Monolog\Logger('mylog'); $log->pushHandler(new Monolog\Handler\StreamHandler('php://stderr', Monolog\Logger::DEBUG)); $log->warning('what\'s up');
在第一行引入composer的autoload.php,它是composer实现自动加载的入口。
然后在后续的代码中,直接调用Monolog\Logger类即可。不需要再去手工require。
究其原因,我们打开/vendor/autoload.php文件看一下。
它实际上是调用了/vendor/composer/autoload_real.php文件,autoload_real.php文件要做的事情是:
- 调用./autoload_static.php,该文件包含了依赖包命名空间与源码目录的对应关系。并将这些对应关系写入./ClassLoader实例中。(autoload_static.php的作用等同于另外三个文件autoload_classmap.php, autoload_namespaces.php, autoload_psr4.php之和。根据php版本的不同,也有可能是调用那三个文件。)
- 调用php的spl_autoload_register函数,将./ClassLoader实例的loadClass方法注册为一个__autoload函数。这样当系统找不到某个类的实现源码时,就会调用这个ClassLoader实例。该实例就可以通过在上一步骤中获知的命名空间与源码目录的对应关系来找到类的实现源码,并include这个源码文件。(所以,Composer是懒加载的,只有在需要某个类时才会真正去加载该类的源码)
4. 自动加载自己编写的类
假设我们把自己项目的代码放在/composer_test/src目录下。比如说
<?php namespace CTest; class User { function hi() { echo 'hello, world'; } }
那么在没有让Composer自动加载识机制别我们的CTest命名空间之前,我们要想在/composer_test/index.php中引用User类。就必须直接引入该文件:
<?php // require __dir__.'/vendor/autoload.php'; require __dir__.'/src/User.php'; $u = new CTest\User(); $u->hi();
为了让Composer的自动加载能够识别我们自定义的类,首先需要在项目根目录的composer.json文件中加上autoload字段。
{ "require": { "monolog/monolog": "^2.0" }, "autoload": { "psr-4": { "CTest\\": "src/" } } }
然后在项目根目录下执行命令 composer dump-autoload
,可以看到该命令执行结果就是生成了autoload files。
我们可以观察到/vendor/composer/目录下的autoload_static.php与autoload_psr4.php都加入了刚写的命名空间。
这时候,就可以在/index.php代码中通过Composer的自动加载来调用刚写的Funway\User类了。
<?php require __dir__.'/vendor/autoload.php'; // require __dir__.'/src/User.php'; $u = new CTest\User(); $u->hi();
5. require与require-dev的区别
在composer.json配置文件中,有两个类似的字段require与require-dev。相对应的它们命令为:composer require vendor/package
与 composer require vendor/package --dev
。这两者有什么区别呢?
- require-dev是root-only的,是指只有在项目根目录下那个composer.json文件中的require-dev字段涉及的依赖包会被下载。而这些依赖包自己的composer.json中所需要的require-dev依赖是会被忽略的。这是require-dev唯一与require字段不同的地方。
- 在composer install 或者update 的时候,“root包”的require-dev是默认会被安装的。除非在install 或者update 的时候加上–no-dev 参数来指明不下载require-dev字段涉及的依赖包。
所以对于一个非package项目而言(即项目不打算作为可下载的包来发布,只是作为比如网站这样的一个项目),把依赖放在require还是require-dev中都是OK的。而对于打算作为包来发布的项目,那么就要考虑清楚哪些依赖是必须的,哪些依赖只是在开发测试时候才需要的。肯定不能把必须的依赖放在require-dev字段;也最好不要把非必须的依赖放在require字段。