>> 在进行 php 模块开发的时候,不可或缺的会用到 composer ,通过查看 composer 加载的代码和网上查找,整理文档。 ### Composer 简介:Composer 是 PHP5.3以上 的一个依赖管理工具。它允许你声明项目所依赖的代码库,它会在你的项目中为你安装他们。Composer 不是一个包管理器。是的,它涉及 "packages" 和 "libraries",但它在每个项目的基础上进行管理,在你项目的某个目录中(例如 vendor)进行安装。默认情况下它不会在全局安装任何东西。因此,这仅仅是一个依赖管理。 ### 自动加载 对于库的自动加载信息,Composer 生成了一个 vendor/autoload.php 文件。你可以简单的引入这个文件,你会得到一个免费的自动加载支持。 `require 'vendor/autoload.php'` 这使得你可以很容易的使用第三方代码。 ##### 常用方式 - PSR-0 autoloading - PSR-4 autoloading - classmap generation - files includes 引用别人的一句话:这几种自动加载都会用到,理论上来说,项目代码用 psr-4 自动加载, helper 用 files 自动加载,development 相关用 classmap 自动加载。 psr-0 已经被抛弃了,不过有些历史遗留依然在用,所以偶尔也会看到。 PSR-4是推荐的方式,因为它提供了更大的易用性(当您添加课程时不需要重新生成自动加载器)。 ##### classmap classmap 引用的所有组合,都会在 install/update 过程中生成,并存储到 vendor/composer/autoload_classmap.php 文件中。这个 map 是经过扫描指定目录(同样支持直接精确到文件)中所有的 .php 和 .inc 文件里内置的类而得到的。 你可以用 classmap 生成支持支持自定义加载的不遵循 PSR-0/4 规范的类库。要配置它指向需要的目录,以便能够准确搜索到类文件。 实例: { "autoload": { "classmap": ["src/", "lib/", "Something.php"] } } 配置后 composer 会读取这个文件夹中所有的文件,然后再 vendor/composer/autoload_classmap.php 中将所有的 class 的 namespace + classname 生成成一个 key => value 的 php 数组 return array( 'Something' => $vendorDir . '/src/lib/Something.php', ); 然后用 spl_autoload_register 做自动加载。 ##### psr-0 在 psr-0 key 下你定义了一个命名空间到实际路径的映射(相对于包的根目录)。注意,这里同样支持 PEAR-style 方式的约定(与命名空间不同,PEAR 类库在类名上采用了下划线分隔)。 请注意,命名空间的申明应该以 \\ 结束,以确保 autoloader 能够准确响应。例: Foo 将会与 FooBar 匹配,然而以反斜杠结束就可以解决这样的问题, Foo\\ 和 FooBar\\ 将会被区分开来。 在 `install/update` 过程中,PSR-0 引用都将被结合为一个单一的键值对数组,存储至 `vendor/composer/autoload_namespaces.php` 文件中。 实例: { "autoload": { "psr-0": { "Monolog\\": "src/", "Vendor\\Namespace\\": "src/", "Vendor_Namespace_": "src/" } } } 如果你需要搜索多个目录中一个相同的前缀,你可以将它们指定为一个数组,例: { "autoload": { "psr-0": { "Monolog\\": ["src/", "lib/"] } } } PSR-0 方式并不仅限于申明命名空间,也可以是精确到类级别的指定。这对于只有一个类在全局命名空间的类库是非常有用的(如果 php 源文件也位于包的根目录)。例如,可以这样申明: { "autoload": { "psr-0": { "UniqueGlobalClass": "" } } } 如果你想设置一个目录作为任何命名空间的备用目录,你可以使用空的前缀,像这样: { "autoload": { "psr-0": { "": "src/" } } } ##### psr-4 PSR-4在psr-4key下,您可以定义从命名空间到路径的映射,相对于软件包根。当自动加载类似Foo \\ Bar \\ Baza名称空间前缀Foo \\的类指向src /目录时,意味着自动加载器将查找名为src / Bar / Baz.phpand的文件(如果存在)。请注意,与旧的PSR-0类型相反,前缀(Foo \\)不存在于文件路径中。 命名空间前缀必须以\\结尾以避免类似前缀之间的冲突。例如,Foowould与FooBarnamespace中的类匹配,所以尾随的反斜杠可以解决问题:Foo \\和FooBar \\是不同的。 在安装/更新期间,PSR-4引用全部组合到一个key =>值数组中,该数组可以在生成的文件vendor / composer / autoload_psr4.php中找到。 Example: { "autoload": { "psr-4": { "Monolog\\": "src/", "Vendor\\Namespace\\": "" } } } 如果您需要在多个目录中搜索相同的前缀,则可以将它们指定为一个数组,如下所示: { “autoload”:{ “psr-4”:{“Monolog \\”:[“src /”,“lib /”]} } } 如果您希望有一个后备目录,其中将查找任何命名空间,则可以使用如下的空前缀: { "autoload": { "psr-4": { "": "src/" } } } ##### Files 如果你想要明确的指定,在每次请求时都要载入某些文件,那么你可以使用 'files' autoloading。通常作为函数库的载入方式(而非类库)。 实例: { "autoload": { "files": ["src/MyLibrary/functions.php"] } } ### 加载机制 查看 `autoload.php` 代码如下: = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { require_once __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit5f13dbbb0f33353132d5160420b7b74c::getInitializer($loader)); } else { $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); } } $loader->register(true); if ($useStaticLoader) { $includeFiles = Composer\Autoload\ComposerStaticInit5f13dbbb0f33353132d5160420b7b74c::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } foreach ($includeFiles as $fileIdentifier => $file) { composerRequire5f13dbbb0f33353132d5160420b7b74c($fileIdentifier, $file); } return $loader; } } function composerRequire5f13dbbb0f33353132d5160420b7b74c($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; } } 文件的主要功能是: - 查看 `Composer\ClassLoader` 实例是否存在,如果不存在则生成示例,并放在 `ComposerAutoloaderInit5f13dbbb0f33353132d5160420b7b74c` 中。 - 然后将 `composer cli` 生成的各种 `autoload_psr4`, `autoload_classmap`, `autoload_namespaces(psr-0)` 全都注册到 `Composer\ClassLoader` 中。 - 直接 `require` 所有在 `autoload_files` 中的文件。 在文件中有如下代码: function composerRequire5f13dbbb0f33353132d5160420b7b74c($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { require $file; $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; } } 一个定义在类外部的函数,为什么这个不直接用 require 而是定义在了类的外边? 调查 `Composer\ClassLoader` 发现了这么一个注释: /** * Scope isolated include. * * Prevents access to $this/self from included files. */ function includeFile($file) { include $file; } 原因是:范围隔离include,阻止从包含文件访问 `$ this / self`。