Composer 不是一个包管理器。是的,它涉及 "packages" 和"libraries",但它在每个项目的基础上进行管理,在你项目的某个目录中(例如 vendor)进行安装。默认情况下它不会在全局安装任何东西。因此,这仅仅是一个依赖管理。
这种想法并不新鲜,Composer 受到了 node's npm 和 ruby's bundler 的强烈启发。而当时 PHP 下并没有类似的工具。
Composer 将这样为你解决问题:
a) 你有一个项目依赖于若干个库。
b) 其中一些库依赖于其他库。
c) 你声明你所依赖的东西。
d) Composer 会找出哪个版本的包需要安装,并安装它们(将它们下载到你的项目中)。
(1)安装composer
a)linux系统安装
你只需要下载 composer.phar 可执行文件
要检查 Composer 是否正常工作,只需要通过 php 来执行 PHAR:
php composer.phar
提示:此方法同样适用于windows系统安装
b)windows安装
下载并且运行 Composer-Setup.exe,它将安装最新版本的 Composer ,并设置好系统的环境变量,因此你可以在任何目录下直接使用 composer 命令。
c)使用 Composer
在我们的项目的根目录下,建立一个composer.json文件,里面有所需要的一些信息和所依赖的库
在这里就不在详细说明了。
配置好composer.json文件后,进入到项目的根目录下执行php composer.phar install 就可以执行初始化composer,把所要依赖的类库下载下来,会产生一个vendor文件夹,一个composer.lock锁文件,以后每次依赖下载的类库都会放在vendor的文件夹下。
d)分析vendor中的文件
vendor文件夹中包含composer文件夹,依赖的各种类库文件夹,一个autoload.php文件。
其中autoload.php文件是一个自动加载类,只要在一个文件中包含这个文件就可以通过命名空间的方式实现自动的加载功能,把需要的vendor中文件包含进去之后,再创建实例。
例如:
//根据路径将vendor下的autoload.php包含
require 'path/to/vendor/autoload.php'
new \kucha\ueditor\UEditor([]);
那么这个过程将会发生什么,整个原理又是怎么样的?
下面分析一下:
在vendor/composer文件夹下有4个aotoload_*.php的文件和一个ClassLoader.php
其中autoload_real.php类实现一个php的自定义autoload的加载函数。
autoload_real.php具体代码:
class ComposerAutoloaderInit32b8eb537f8e12e57c5e7bade69d01f0
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit32b8eb537f8e12e57c5e7bade69d01f0', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit32b8eb537f8e12e57c5e7bade69d01f0', 'loadClassLoader'));
//.......其他代码
/**
*
*/
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
/**
*注册一个自定义加载函数,在ClassLoader.php函数中
*
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
*
*/
参数为true,代表把loadClass()放置堆栈之首
$loader->register(true);
$includeFiles = require __DIR__ . '/autoload_files.php';
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire32b8eb537f8e12e57c5e7bade69d01f0($fileIdentifier, $file);
}
return $loader;
}
}
当require 'path/to/vendor/autoload.php'文件时,就会执行getLoader()这个函数,这个函数通过spl_autoload_register函数自定义自动加载函数,注册loadClassLoader()函数。所以当self::$loader = $loader = new \Composer\Autoload\ClassLoader();将ClassLoader.php包含进来,并创建一个$loader实例。由于$loader实例是一个static(静态)的变量对象,所以会缓存在整个过程中,所以spl_autoload_unregister()将从spl的堆栈中移除loadClassLoader()加载函数,因为它的任务已经完成,就是产生并且缓存一个$loader的对象。
然后$loader对象可以执行里面的各种函数,prs0,classMap,prs4,includepath的自动加载规则,把命名空间和对应的实际路径设置好,同时通过$loader->register(true)注册一个loadClass()的自动加载函数,并放在spl堆栈的首位,所以在文件中new \kucha\ueditor\UEditor([])时,代码就会到spl堆栈中找到loadClass()函数,然后将根据命名空间去寻找对应的文件包含,最后创建一个实例。所以在开发的整个过程中,我们很多时候不在需要通过include或者require去包含一个文件,然后再创建实例,而是利用composer创建好的规则,直接new namespace 的方式就可以轻松完成整个过程,创建实例。