首页 > 学院 > 开发设计 > 正文

Composer概述及其自动加载探秘

2019-11-15 01:35:49
字体:
来源:转载
供稿:网友
Composer概述及其自动加载探秘

composer概述

一开始,最吸引我的当属Composer了,因为之前从没用过Composer。

Composer 是php中用来管理依赖关系的工具,你只需在自己的项目中声明所依赖的外部工具库,Composer就会帮你安装这些依赖的库文件。运行Composer 需要 PHP 5.3.2+ 以上版本。

使用composer

第一步,声明依赖关系。比方说,你正在创建的一个项目需要一个库来做日志记录。你决定使用monolog。为了将它添加到你的项目中,你所需要做的就是创建一个composer.json文件,其中描述了项目的依赖关系。

{    "require": {        "monolog/monolog": "1.2.*"    }}

第二步,使用composer。在项目根目录,执行安装命令,执行完毕后,monolog就会被下载到vendor/monolog/monolog目录。

$ php composer.phar install

第三步,类的自动加载。除了库的下载,Composer 还准备了一个自动加载文件,它可以加载 Composer 下载的库中所有的类文件。使用它,你只需要将下面这行代码添加到你项目的引导文件中:

require 'vendor/autoload.php';

这使得你可以很容易的使用第三方代码。例如:如果你的项目依赖 monolog,你就可以像这样开始使用这个类库,并且他们将被自动加载。

$log = new Monolog/Logger('name');$log->pushHandler(new Monolog/Handler/StreamHandler('app.log', Monolog/Logger::WARNING));$log->addWarning('Foo');

Composer自动加载探秘

在现实世界中使用工具时,如果理解了工具的工作原理,使用起来就会更加有底气。对于一个第一次接触laravel,且是第一次接触 composer 的新手来说,如果理解Composer 是如何工作的,使用起来将会更加自如。

我的理解是,composer 根据声明的依赖关系,从相关库的 源 下载代码文件,并根据依赖关系在Composer目录下生成供类自动加载的 PHP 脚本,使用的时候,项目开始处引入 “/vendor/autoload.php” 文件,就可以直接实例化这些第三方类库中的类了。那么,Composer 是如何实现类的自动加载的呢?接下来,我们从laravel 的入口文件开始顺藤摸瓜往里跟进,来一睹 Composer 自动加载的奥妙。

1.代码清单laravel/public/index.php

#laravel/public/index.phPRequire __DIR__.'/../bootstrap/autoload.php';$app = require_once __DIR__.'/../bootstrap/start.php';$app->run();

第一行先是引入了laravel/bootstrap/autoload.php,不做解释,打开该文件

2.代码清单laravel/bootstrap/autoload.php

define('LARAVEL_START', microtime(true));require __DIR__.'/../vendor/autoload.php';if (file_exists($compiled = __DIR__.'/compiled.php')){    require $compiled;}Patchwork/Utf8/Bootup::initMbstring();

第一行定义了程序开始执行的时间点。紧接着第二行,引入了 laravel/vendor/autoload.php

第七行,前面说过,引入Composer的autoload.php之后就可以直接使用第三方类库中的类了,这里就是直接使用的Bootup 类。下面来看看/vendor/autoload.php 到底做了什么。

3.代码清单laravel/vendor/autoload.php

1 // autoload.php @generated by Composer2 3 require_once __DIR__ . '/composer' . '/autoload_real.php';4 5 return ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256::getLoader();

到这里,马上就进入自动加在的大门了。

这个文件很简单,第5行的函数名是不是看的一头雾水?别被吓到了,他就是个类名而已。这个类是在第3行引入的文件laravel/vendor/composer/autoload_real.php 里头声明的,接下来打开该文件看getLoader();

4.代码清单laravel/vendor/composer/autoload_real.php

 1 <?php 2  3 // autoload_real.php @generated by Composer 4  5 class ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256 6 { 7     private static $loader; 8  9     public static function loadClassLoader($class)10     {11         if ('Composer/Autoload/ClassLoader' === $class) {12             require __DIR__ . '/ClassLoader.php';13         }14     }15 16 17     public static function getLoader()18     {19         if (null !== self::$loader) {20             return self::$loader;21         }22 23         spl_autoload_register(array('ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256', 'loadClassLoader'), true, true);24         self::$loader = $loader = new /Composer/Autoload/ClassLoader();25         spl_autoload_unregister(array('ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256', 'loadClassLoader'));26 27         $vendorDir = dirname(__DIR__);        28         $baseDir = dirname($vendorDir);29 30         $includePaths = require __DIR__ . '/include_paths.php';        31 32         array_push($includePaths, get_include_path());33         set_include_path(join(PATH_SEPARATOR, $includePaths));34 35 36         $map = require __DIR__ . '/autoload_namespaces.php';37         foreach ($map as $namespace => $path) {38             $loader->set($namespace, $path);39         }40 41         $map = require __DIR__ . '/autoload_psr4.php';42         foreach ($map as $namespace => $path) {43             $loader->setPsr4($namespace, $path);44         }45 46         $classMap = require __DIR__ . '/autoload_classmap.php';47         if ($classMap) {48             $loader->addClassMap($classMap);49         }50         51 52         $loader->register(true);53 54         $includeFiles = require __DIR__ . '/autoload_files.php';55         foreach ($includeFiles as $file) {56             composerRequire9b2a1b1cf01c9a870ab98748dc5f1256($file);57         }58 59         return $loader;60     }61 }62 63 function composerRequire9b2a1b1cf01c9a870ab98748dc5f1256($file)及$loader->addClassMap()64 {65     require $file;66 }

第17行,getLoader()中先是判断当前类中的 $loader 值,如果不是 null 就返回,这个可以略过。接着实例化了ClassLoader 类给 $loader ,laravel/vendor/composer/ClassLoader.php

这里引入了几个文件,这些文件是由composer自动生成的,当依赖关系发生改变时不需要修改这些脚本,运行composer重新生成即可。

laravel/vendor/composer/autoloade_namespace.php

laravel/vendor/composer/autoloade_prs4.php

laravel/vendor/composer/autoloade_classmap.php

laravel/vendor/composer/autoloade_files.php

在设置完一堆的 path 信息后,执行了$loader->set()和$loader->setPsr4()及$loader->addClassMap(),然后 进行了$loader->register(true);现在我们一个个来看。

5.代码清单laravel/vendor/composer/ClassLoader.php

  1 <?php  2   3 /*  4  * This file is part of Composer.  5  *  6  * (c) Nils Adermann <naderman@naderman.de>  7  *     Jordi Boggiano <j.boggiano@seld.be>  8  *  9  * For the full copyright and license information, please view the LICENSE 10  * file that was distributed with this source code. 11  */ 12  13 namespace Composer/Autoload; 14  15 /** 16  * ClassLoader implements a PSR-0 class loader 17  * 18  * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md 19  * 20  *     $loader = new /Composer/Autoload/ClassLoader(); 21  * 22  *     // register classes with namespaces 23  *     $loader->add('Symfony/Component', __DIR__.'/component'); 24  *     $loader->add('Symfony',           __DIR__.'/framework'); 25  * 26  *     // activate the autoloader 27  *     $loader->register(); 28  * 29  *     // to enable searching the include path (eg. for PEAR packages) 30  *     $loader->setUseIncludePath(true); 31  * 32  * In this example, if you try to use a class in the Symfony/Component 33  * namespace or one of its children (Symfony/Component/Console for instance), 34  * the autoloader will first look for the class under the component/ 35  * directory, and it will then fallback to the framework/ directory if not 36  * found before giving up. 37  * 38  * This class is loosely based on the Symfony UniversalClassLoader. 39  * 40  * @author Fabien Potencier <fabien@symfony.com> 41  * @author Jordi Boggiano <j.boggiano@seld.be> 42  */ 43 class ClassLoader 44 { 45     // PSR-4 46     private $prefixLengthsPsr4 = array(); 47     private $prefixDirsPsr4 = array(); 48     private $fallbackDirsPsr4 = array(); 49  50     // PSR-0 51     private $prefixesPsr0 = array(); 52     private $fallbackDirsPsr0 = array(); 53  54     private $useIncludePath = false; 55     private $classMap = array(); 56  57     public function getPrefixes() 58     { 59         return call_user_func_array('array_merge', $this->prefixesPsr0); 60     } 61  62     public function getPrefixesPsr4() 63     { 64         return $this->prefixDirsPsr4; 65     } 66  67     public function getFallbackDirs() 68     { 69         return $this->fallbackDirsPsr0; 70     } 71  72     public function getFallbackDirsPsr4() 73     { 74         return $this->fallbackDirsPsr4; 75     } 76  77     public function getClassMap() 78     { 79         return $this->classMap; 80     } 81  82     /** 83      * @param array $classMap
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表