likes
comments
collection
share

PHP Composer 的 自动加载[译]

作者站长头像
站长
· 阅读数 11

原文地址:medium.com/tech-tajawa…

tips:源于一次面试,问自动加载的底层,回来查了查也没查到什么好的文章来说,知道看到这篇文章,我真的觉得讲的很好,希望大家都能学习学习吧。第一次翻译技术文,如有不准确的各位同学请大胆提出,谢谢大家~

斗胆总结-到底是怎么自动加载的:

1.入口文件必然会引入composer中的autoload.php

2.autoload文件会调用classLoadergetLoader方法,方法中使用spl_autoload_registerloadClass注册成自动加载的方法

3.当使用类名的时候,类名就会传入loadClass方法中,findFile找这个文件,通过classmap里找,psr0和psr4前缀拼去找这个文件进行加载

PHP Composer 的 自动加载[译]

Composer 对于任何 PHP 开发人员来说都是最棒的工具,它让管理任何扩展包的依赖变得简单。

下面的章节里,我不打算谈论Composer的主要功能“管理依赖关系”,而我会去关注在Composer中是如何管理加载大量的包。

  1. 什么是自动加载
  2. 一起创建自动加载的程序
  3. Composer是怎么做自动加载的呢
  4. Classmap VS PSR-0 VS PSR-4
  5. 结论
  6. 参考

1. 什么是自动加载

最大的烦恼之一是在每个PHP脚本文件的开头都必须要引入一长串的PHP类文件

问题:假设你正在开发一个应用程序,你有很多库需要加载进来

解决方案:在脚本的开头你去引入所有的类文件,你将可以在任何位置使用任何一个类

更好的解决方案:每当我要用一个类时,我再去加载他,这样我的应用程序就不必在每个请求中加载所有的类,这就是“自动加载”。但是如何实现呢?让我们搞清楚

2. 创建自动加载的程序

一个简单的方法,我们可以引入所有文件

<?php
// Classes/A.php
class A {} 

<?php
// Classes/B.php
class B {} 

<?php
// index.php
include_once 'Classes/A.php' ;
include_once 'Classes/B.php' ; 

// load A class
$a = new A(); 

// check the list of all loaded files
var_dump(get_included_files());

脚本将会输出:

array(3) {  
  [0]=> string(20) "/path/to/root/index.php"  
  [1]=> string(24) "/path/to/root/Classes/A.php"  
  [2]=> string(24) "/path/to/root/Classes/B.php"  
}

这意味着无论用不用B类,都会加载所有文件,随着你的项目越来越大,这样肯定是不行的。

自动加载 的出现解决了这个问题

<?php
// index.php
// my custom autoloader
function my_autoloader($class) {
    include 'Classes/' . $class . '.php';
}

// register the autoloader
spl_autoload_register('my_autoloader');

// load A class
$a = new A();

// check the list of all loaded files
var_dump(get_included_files());

这种方式相当于在my_autoloader函数中给类名和文件路径做了“映射”,而且注册了这个函数,因此你的脚本在任何时刻都可以对类进行实例化,只需要通过这个方法加载他。现在看看脚本中加载的文件:

array(2) {  
  [0]=> string(50) "/path/to/demos/autoload-2/index.php"  
  [1]=> string(54) "/path/to/demos/autoload-2/Classes/A.php"  
}

现在你的脚本就只加载了需要的文件。

PHP is telling you “I will give you the chance to go and load your class even if you didn’t load it before your statement $a = new A(); after that, I will throw an error if it didn’t work”.

PHP告诉你“即使你在$a = new A();的时候没有加载这个类,我也会去加载你的类,之后如果它不起作用,我会抛出一个异常”。

上面的例子都没啥问题,但你需要注意下面这几个事:

  1. 文件名应与类名相同
  2. 每个文件应该只有一个类
  3. 类名和文件名都应该区分大小写

在你的程序中你可以去处理这些情况,任何应用程序都可以根据它自己的文件结构做映射,但是为了使用这个自动加载器,你必须遵守上面的注意事项。

Composer提出了一种适合大多数结构的方式(或者说它强制在应用程序中做了选择 that it forces the applications to choose between ),让我们深入挖掘一下。

3. Composer是怎么做自动加载的呢

在脚本目录下添加composer.json文件

{  
  "autoload" : {  
    "classmap" : [  
      "Classes/"  
    ]  
  }  
}

更新你的index.php文件

require __DIR__ . '/vendor/autoload.php';  
  
$a = new A();

Using classmap, we are telling composer that this is the way to do mapping, and it is a very basic way to map namespaces to paths and if you want more details, just check vendor/composer/autoload_classmap.php after you run the composer install:

使用classmap,我们告诉Composer这是进行映射的方法,这是将命名空间映射到路径的一种基本方式,如果你想获得更多的信息,在运行composer install之后,可以去看vendor/composer/autoload_classmap.php,如下:

  return array(  
    'A' => $baseDir . '/Classes/A.php',  
    'B' => $baseDir . '/Classes/B.php',  
  );

每当你实例化A,他就会去加载$baseDir . '/Classes/A.php'这个文件。

这个流程很容易理解。在程序中它总是从下面这行开始:

require __DIR__ . '/vendor/autoload.php';

而且你会发现到ComposerAutoloaderInitXXXX:getLoader()有所有的逻辑,这是在添加了不同的映射,在末尾注册成了自动加载:

// \Composer\ComposerAutoloaderInitXXXX:getLoader()
public static function getLoader() {
    ...
    $loader->register(true); // \Composer\ClassLoader::register()
    return $loader;
}

这是我们注册自动加载器的地方:

// \Composer\ClassLoader::register()
public function register($prepend = false) {
    spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}

每当我们实例化一个类的时候都会通过ClassLoader::loadClass()寻找相应的文件并加载他。

4. Classmap VS PSR-0 VS PSR-4

他们都是将命名空间及其对应路径映射的不同方式,一旦选择其中之一,你就需要一直遵循他们的规则,Composer知道如何基于您所遵循的映射来寻找相应的文件并加载他。

  1. Classmap:是最简单、最直接的,它输出的逻辑在vendor/composer/autoload_classmap.php中,当Composer读到我们正在使用classmap加载时,它会扫描composer.jsonclassmap的所有文件,而且会去创建一个命名空间和其路径映射的数组。

注: 添加新文件后,需要使用composer dumpautoload命令重新生成对应关系。

  1. PSR-0 :逻辑代码在这vendor/composer/autoload_namespaces.php,而且他的名字遵循了PSR-0的规则。

  2. PSR-4:代码vendor/composer/autoload_psr4.php,遵循了PSR-4规则。

PSR-0和PSR-4有几乎相同的规则。

注:

  • 这两个都不需要去执行composer dumpautoload,因为每次新加PHP文件时,“搜索文件路径”是动态执行的
  • 你需要去use namespace,尤其是PSR-4,因为命名空间附加到文件路径上。

子目录名称必须与子命名空间名称大小写一致。 - PSR-4

  • PSR-0是将下划线转换为目录分隔符,而PSR-4不同。

类名中的每个_都转换为一个DIRECTORY_SEPARATOR(目录分隔符)。在命名空间中_没有特殊含义。 - PSR-0

因此,$a = new Classes_A();会加载下面的文件:

<?php  
// path /Classes/A.php  
class Classes_A {} 

注意这里我们并没用use namespace

  • 以下composer.json正常工作,而且命名空间前缀为App的,会在Classes目录中寻找文件,但是PSR-0相反,前缀是不会存在于文件路径中。
{
  "autoload" : {
    "psr-4" : {
      "App\\" : "Classes"
    }
  }
}

-----以下是我自己加进来的哈,就是我们常见的一种引入的方式,更好的解释上面的配置---

Classes中建个C

<?php
namespace App;

class C
{
}

在index.php中use使用

<?php

require __DIR__ . '/vendor/autoload.php';

use App\C;

$a = new C();

var_dump(get_included_files());

---- 截止 ----

5. 结论

我们开始在PHP中定义了自动加载的文件,它正在解决它的问题,我们也定义了简单的例子,我们讨论了Composer,以及如何用不同的方式进行映射。

我介绍这个的主要原因是我们大多数应用程序和包中都用了Composer,我们应该去了解autoload是如何工作的。

我尽量写的很简单了,可以通过下面的链接做进一步了解。

6. 参考

  1. php.net/manual/en/l…
  2. php.net/manual/en/f…
  3. php.net/manual/en/f…
  4. getcomposer.org/doc/04-sche…
  5. www.php-fig.org/psr/psr-0
  6. www.php-fig.org/psr/psr-4

这有一篇我发现的类似的文章,可以一起看看哦

  1. www.884358.com/php-autoloa…

真是没想到有一天能自己翻译一篇文章呢,希望多点面试,赶紧找份工作吧。面试的过程也总是自我怀疑的过程,好好学习知识,加油啦 还有,能不能升到四级,我好难,我感觉三级这个等级已经很久了,虽然我确实写的很少。。。