举例来说,假设我们的项目想要使用 monolog 这个日志工具,就需要在composer.json里告诉composer我们需要它:
{
"require": {
"monolog/monolog": "1.*"
}
}
之后执行:
php composer.phar install
好,现在安装完了,该怎么使用呢?composer自动生成了一个autoload文件,你只需要引用它
require '/path/to/vendor/autoload.php';
然后就可以非常方便的去使用第三方的类库了,是不是感觉很棒啊!对于我们需要的monolog,就可以这样用了:
use monologlogger;
use monologhandlerstreamhandler;
// create a log channel
$log = new logger('name');
$log->pushhandler(new streamhandler('/path/to/log/log_name.log', logger::warning));
// add records to the log
$log->addwarning('foo');
$log->adderror('bar');
在这个过程中,composer做了什么呢?它生成了一个autoloader,再根据各个包自己的autoload配置,从而帮我们进行自动加载的工作。(如果对autoload这部分内容不太了解,可以看我之前的 一篇文章
)接下来让我们看看composer是怎么做的吧。
对于第三方包的自动加载,composer提供了四种方式的支持,分别是 psr-0和psr-4的自动加载(我的一篇文章也有介绍过它们),生成class-map,和直接包含files的方式。
psr-4是composer推荐使用的一种方式,因为它更易使用并能带来更简洁的目录结构。在composer.json里是这样进行配置的:
{
"autoload": {
"psr-4": {
"foo\": "src/",
}
}
}
key和value就定义出了namespace以及到相应path的映射。按照psr-4的规则,当试图自动加载 "foo\bar\baz" 这个class时,会去寻找 "src/bar/baz.php" 这个文件,如果它存在则进行加载。注意, "foo\"
并没有出现在文件路径中,这是与psr-0不同的一点,如果psr-0有此配置,那么会去寻找
"src/foo/bar/baz.php"
这个文件。
另外注意psr-4和psr-0的配置里,"foo\"结尾的命名空间分隔符必须加上并且进行转义,以防出现"foo"匹配到了"foobar"这样的意外发生。
在composer安装或更新完之后,psr-4的配置换被转换成namespace为key,dir path为value的map的形式,并写入生成的 vendor/composer/autoload_psr4.php 文件之中。
{
"autoload": {
"psr-0": {
"foo\": "src/",
}
}
}
最终这个配置也以map的形式写入生成的
vendor/composer/autoload_namespaces.php
文件之中。
class-map方式,则是通过配置指定的目录或文件,然后在composer安装或更新时,它会扫描指定目录下以.php或.inc结尾的文件中的class,生成class到指定file path的映射,并加入新生成的 vendor/composer/autoload_classmap.php 文件中,。
{
"autoload": {
"classmap": ["src/", "lib/", "something.php"]
}
}
例如src/下有一个basecontroller类,那么在autoload_classmap.php文件中,就会生成这样的配置:
'basecontroller' => $basedir . '/src/basecontroller.php'
files方式,就是手动指定供直接加载的文件。比如说我们有一系列全局的helper functions,可以放到一个helper文件里然后直接进行加载
{
"autoload": {
"files": ["src/mylibrary/functions.php"]
}
}
它会生成一个array,包含这些配置中指定的files,再写入新生成的
vendor/composer/autoload_files.php
文件中,以供autoloader直接进行加载。
下面来看看composer autoload的代码吧
<?php
// autoload_real.php @generated by composer
class composerautoloaderinit73612b48e6c3d0de8d56e03dece61d11
{
private static $loader;
public static function loadclassloader($class)
{
if ('composerautoloadclassloader' === $class) {
require __dir__ . '/classloader.php';
}
}
public static function getloader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('composerautoloaderinit73612b48e6c3d0de8d56e03dece61d11', 'loadclassloader'), true, true);
self::$loader = $loader = new composerautoloadclassloader();
spl_autoload_unregister(array('composerautoloaderinit73612b48e6c3d0de8d56e03dece61d11', 'loadclassloader'));
$vendordir = dirname(__dir__); //verdor第三方类库提供者目录
$basedir = dirname($vendordir); //整个应用的目录
$includepaths = require __dir__ . '/include_paths.php';
array_push($includepaths, get_include_path());
set_include_path(join(path_separator, $includepaths));
$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);
$includefiles = require __dir__ . '/autoload_files.php';
foreach ($includefiles as $file) {
composerrequire73612b48e6c3d0de8d56e03dece61d11($file);
}
return $loader;
}
}
function composerrequire73612b48e6c3d0de8d56e03dece61d11($file)
{
require $file;
}
首先初始化classloader类,然后依次用上面提到的4种加载方式来注册/直接加载,classloader的一些核心代码如下:
/**
* @param array $classmap class to filename map
*/
public function addclassmap(array $classmap)
{
if ($this->classmap) {
$this->classmap = array_merge($this->classmap, $classmap);
} else {
$this->classmap = $classmap;
}
}
/**
* registers a set of psr-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix the prefix
* @param array|string $paths the psr-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackdirspsr0 = (array) $paths;
} else {
$this->prefixespsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* registers a set of psr-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix the prefix/namespace, with trailing '\'
* @param array|string $paths the psr-4 base directories
*
* @throws invalidargumentexception
*/
public function setpsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackdirspsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\' !== $prefix[$length - 1]) {
throw new invalidargumentexception("a non-empty psr-4 prefix must end with a namespace separator.");
}
$this->prefixlengthspsr4[$prefix[0]][$prefix] = $length;
$this->prefixdirspsr4[$prefix] = (array) $paths;
}
}
/**
* registers this instance as an autoloader.
*
* @param bool $prepend whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadclass'), true, $prepend);
}
/**
* loads the given class or interface.
*
* @param string $class the name of the class
* @return bool|null true if loaded, null otherwise
*/
public function loadclass($class)
{
if ($file = $this->findfile($class)) {
includefile($file);
return true;
}
}
/**
* finds the path to the file where the class is defined.
*
* @param string $class the name of the class
*
* @return string|false the path if found, false otherwise
*/
public function findfile($class)
{
//这是php5.3.0 - 5.3.2的一个bug 详见https://bugs.php.net/50731
if ('\' == $class[0]) {
$class = substr($class, 1);
}
// class map 方式的查找
if (isset($this->classmap[$class])) {
return $this->classmap[$class];
}
//psr-0/4方式的查找
$file = $this->findfilewithextension($class, '.php');
// search for hack files if we are running on hhvm
if ($file === null && defined('hhvm_version')) {
$file = $this->findfilewithextension($class, '.hh');
}
if ($file === null) {
// remember that this class does not exist.
return $this->classmap[$class] = false;
}
return $file;
}
private function findfilewithextension($class, $ext)
{
// psr-4 lookup
$logicalpathpsr4 = strtr($class, '\', directory_separator) . $ext;
$first = $class[0];
if (isset($this->prefixlengthspsr4[$first])) {
foreach ($this->prefixlengthspsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach ($this->prefixdirspsr4[$prefix] as $dir) {
if (file_exists($file = $dir . directory_separator . substr($logicalpathpsr4, $length))) {
return $file;
}
}
}
}
}
// psr-4 fallback dirs
foreach ($this->fallbackdirspsr4 as $dir) {
if (file_exists($file = $dir . directory_separator . $logicalpathpsr4)) {
return $file;
}
}
// psr-0 lookup
if (false !== $pos = strrpos($class, '\')) {
// namespaced class name
$logicalpathpsr0 = substr($logicalpathpsr4, 0, $pos + 1)
. strtr(substr($logicalpathpsr4, $pos + 1), '_', directory_separator);
} else {
// pear-like class name
$logicalpathpsr0 = strtr($class, '_', directory_separator) . $ext;
}
if (isset($this->prefixespsr0[$first])) {
foreach ($this->prefixespsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . directory_separator . $logicalpathpsr0)) {
return $file;
}
}
}
}
}
// psr-0 fallback dirs
foreach ($this->fallbackdirspsr0 as $dir) {
if (file_exists($file = $dir . directory_separator . $logicalpathpsr0)) {
return $file;
}
}
// psr-0 include paths.
if ($this->useincludepath && $file = stream_resolve_include_path($logicalpathpsr0)) {
return $file;
}
}
/**
* scope isolated include.
*
* prevents access to $this/self from included files.
*/
function includefile($file)
{
include $file;
}
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:)!