精华内容
下载资源
问答
  • 主要介绍了PHP面向对象自动加载机制原理与用法,结合实例形式分析了php面向对象自动加载机制的原理、相关函数及注意事项,需要的朋友可以参考下
  • 主要介绍了PHP进阶学习之类的自动加载机制,较为详细的分析了php使用__autoload方法实现类的自动加载原理与相关使用注意事项,需要的朋友可以参考下
  • 主要介绍了PHP自动加载机制,结合实例形式详细分析了php自动加载机制原理、实现方法及相关操作注意事项,需要的朋友可以参考下
  • PHP自动加载机制

    2020-03-11 11:00:07
    最初的文件加载机制是require/include把一个文件加载进来,但是随着开发规模的增大,复杂性也越来越高,可能造成遗漏或者冗余,因此在PHP5版本进入自动加载机制(autoload) require("/var/www/Person.php") $per = ...

    一、require / include
    最初的文件加载机制是require/include把一个文件加载进来,但是随着开发规模的增大,复杂性也越来越高,可能造成遗漏或者冗余,因此在PHP5版本进入自动加载机制(autoload)

    require("/var/www/Person.php")
    $per = new Person()
    
    //或者
    include("/var/www/Person.php")
    $per = new Person()
    
    

    二、PHP 自动加载函数 __autoload()
    在new一个class时,PHP系统如果找不到这个类,就会去自动调用本文件中的__autoload($classname)方法,去require对应路径的类文件,从而实现自动lazy加载,但是现在已经弃用。
    首先同目录新建一个Person类

    <?php
    class Person {
        public function hello(){
            echo "hello";
        }   
    }
    
    <?php
    
    function __autoload($classname) {
        $classpath="./".$classname.'.php';
        if (file_exists($classpath)) {
            require_once($classpath);
        } else {
            echo 'class file'.$classpath.'not found!';
        }   
    }
    
    $p = new Person();
    $p->hello();
    
    执行时会提示 PHP Deprecated:  __autoload() is deprecated, use spl_autoload_register() instead
    
    

    三、SPL Autoload Register
    可以注册任意数量的自动加载器,当然也可以单个加载,但是这样做,对于大型项目来说,极其不方便管理

    spl_autoload_register(function($className){
        if (is_file('./' . $className . '.php')) {
            require './' . $className . '.php';
        }                   
    });                     
    
    $p = new Person();
    $p->hello();
    
    输出 hello
    

    正确的做法如下:
    ①定义一个loader加载器

    <?php
    class Loader {
        public static function autoload($classname) {
            $file = "./".$classname.".php";
    
            if (file_exists($file)) {
                include $file;
            } else {
                echo 'class file'.$classname.'not found!';
            }   
        }   
    }
    

    ② 注册加载器

    //引入加载器
    include "./Loader.php";
    
    spl_autoload_register("Loader::autoload", true, true);
    
    $p = new Person();
    $p->hello();
    
    输出hello
    
    展开全文
  • 主要介绍了PHP MVC框架中类的自动加载机制,结合实例形式分析了MVC框架中类的自动加载机制原理、实现方法及相关操作注意事项,需要的朋友可以参考下
  •   这篇文章是对PHP自动加载功能的一个总结,内容涉及PHP的自动加载功能、PHP的命名空间、PHP的PSR0与PSR4标准等内容。 一、PHP自动加载功能 PHP自动加载功能的由来   在PHP开发过程中,如果希望从...

    文章转载出处:http://leoyang90.cn/2017/03/11/PHP-Composer-autoload/

    前言

      
      这篇文章是对PHP自动加载功能的一个总结,内容涉及PHP的自动加载功能、PHP的命名空间、PHP的PSR0与PSR4标准等内容。


    一、PHP自动加载功能

    PHP自动加载功能的由来

      在PHP开发过程中,如果希望从外部引入一个class,通常会使用include和require方法,去把定义这个class的文件包含进来。这个在小规模开发的时候,没什么大问题。但在大型的开发项目中,使用这种方式会带来一些隐含的问题:如果一个PHP文件需要使用很多其它类,那么就需要很多的require/include语句,这样有可能会造成遗漏或者包含进不必要的类文件。如果大量的文件都需要使用其它的类,那么要保证每个文件都包含正确的类文件肯定是一个噩梦, 况且require_once的代价很大。

      PHP5为这个问题提供了一个解决方案,这就是类的自动装载(autoload)机制。autoload机制可以使得PHP程序有可能在使用类时才自动包含类文件,而不是一开始就将所有的类文件include进来,这种机制也称为lazy loading。

      总结起来,自动加载功能带来了几处优点:

    1. 使用类之前无需include或者require
    2. 使用类的时候才会require/include文件,实现了lazy loading,避免了require/include多余文件。
    3. 无需考虑引入类的实际磁盘地址,实现了逻辑和实体文件的分离。

      
      如果想具体详细的了解关于自动加载的功能,可以查看资料:
      PHP的类自动加载机制
      PHP的autoload机制的实现解析

    PHP自动加载函数__autoload()

      通常PHP5在使用一个类时,如果发现这个类没有加载,就会自动运行_autoload()函数,这个函数是我们在程序中自定义的,在这个函数中我们可以加载需要使用的类。下面是个简单的示例:

    1
    2
    3
    function __autoload($classname) {
    require_once ($classname . "class.php");
    }

      在我们这个简单的例子中,我们直接将类名加上扩展名”.class.php”构成了类文件名,然后使用require_once将其加载。从这个例子中,我们可以看出autoload至少要做三件事情:

    1. 根据类名确定类文件名;
    2. 确定类文件所在的磁盘路径(在我们的例子是最简单的情况,类与调用它们的PHP程序文件在同一个文件夹下);
    3. 将类从磁盘文件中加载到系统中。

      
      第三步最简单,只需要使用include/require即可。要实现第一步,第二步的功能,必须在开发时约定类名与磁盘文件的映射方法,只有这样我们才能根据类名找到它对应的磁盘文件。

      当有大量的类文件要包含的时候,我们只要确定相应的规则,然后在__autoload()函数中,将类名与实际的磁盘文件对应起来,就可以实现lazy loading的效果。从这里我们也可以看出_autoload()函数的实现中最重要的是类名与实际的磁盘文件映射规则的实现。

    __autoload()函数存在的问题

      如果在一个系统的实现中,如果需要使用很多其它的类库,这些类库可能是由不同的开发人员编写的, 其类名与实际的磁盘文件的映射规则不尽相同。这时如果要实现类库文件的自动加载,就必须在__autoload()函数中将所有的映射规则全部实现,这样的话autoload()函数有可能会非常复杂,甚至无法实现。最后可能会导致autoload()函数十分臃肿,这时即便能够实现,也会给将来的维护和系统效率带来很大的负面影响。

      那么问题出现在哪里呢?问题出现在_autoload()是全局函数只能定义一次,不够灵活,所以所有的类名与文件名对应的逻辑规则都要在一个函数里面实现,造成这个函数的臃肿。那么如何来解决这个问题呢?答案就是使用一个_autoload调用堆栈,不同的映射关系写到不同的_autoload函数中去,然后统一注册统一管理,这个就是PHP5引入的SPL Autoload。

    SPL Autoload

      SPL是Standard PHP Library(标准PHP库)的缩写。它是PHP5引入的一个扩展库,其主要功能包括autoload机制的实现及包括各种Iterator接口或类。SPL Autoload具体有几个函数:

    1. spl_autoload_register:注册__autoload()函数
    2. spl_autoload_unregister:注销已注册的函数
    3. spl_autoload_functions:返回所有已注册的函数
    4. spl_autoload_call:尝试所有已注册的函数来加载类
    5. spl_autoload :__autoload()的默认实现
    6. spl_autoload_extionsions: 注册并返回spl_autoload函数使用的默认文件扩展名。

      
      这几个函数具体详细用法可见php中spl_autoload详解

      简单来说,spl_autoload就是SPL自己的定义__autoload()函数,功能很简单,就是去注册的目录(由set_include_path设置)找与$classname同名的.php/.inc文件。当然,你也可以指定特定类型的文件,方法是注册扩展名(spl_autoload_extionsions)。

      而spl_autoload_register()就是我们上面所说的autoload调用堆栈,我们可以向这个函数注册多个我们自己的_autoload()函数,当PHP找不到类名时,PHP就会调用这个堆栈,一个一个去调用自定义的_autoload()函数,实现自动加载功能。如果我们不向这个函数输入任何参数,那么就会注册spl_autoload()函数。

      好啦,PHP自动加载的底层就是这些,注册机制已经非常灵活,但是还缺什么呢?我们上面说过,自动加载关键就是类名和文件的映射,这种映射关系不同框架有不同方法,非常灵活,但是过于灵活就会显得杂乱,PHP有专门对这种映射关系的规范,那就是PSR标准中PSR0与PSR4。

      不过在谈PSR0与PSR4之前,我们还需要了解PHP的命名空间的问题,因为这两个标准其实针对的都不是类名与目录文件的映射,而是命名空间与文件的映射。为什么会这样呢?在我的理解中,规范的面向对象PHP思想,命名空间在一定程度上算是类名的别名,那么为什么要推出命名空间,命名空间的优点是什么呢


    二、Namespace命名空间

               要了解命名空间,首先先看看官方文档 中对命名空间的介绍:

      什么是命名空间?从广义上来说,命名空间是一种封装事物的方法。在很多地方都可以见到这种抽象概念。例如,在操作系统中目录用来将相关文件分组,对于目录中的文件来说,它就扮演了命名空间的角色。具体举个例子,文件 foo.txt 可以同时在目录/home/greg 和 /home/other 中存在,但在同一个目录中不能存在两个 foo.txt 文件。另外,在目录 /home/greg 外访问 foo.txt 文件时,我们必须将目录名以及目录分隔符放在文件名之前得到 /home/greg/foo.txt。这个原理应用到程序设计领域就是命名空间的概念。
      在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:
      1 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突
      2 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。

      PHP 命名空间提供了一种将相关的类、函数和常量组合到一起的途径。

      
      简单来说就是PHP是不允许程序中存在两个名字一样一样的类或者函数或者变量名的,那么有人就很疑惑了,那就不起一样名字不就可以了?事实上很多大程序依赖很多第三方库,名字冲突什么的不要太常见,这个就是官网中的第一个问题。那么如何解决这个问题呢?在没有命名空间的时候,可怜的程序员只能给类名起a_b_c_d_e_f这样的,其中a/b/c/d/e/f一般有其特定意义,这样一般就不会发生冲突了,但是这样长的类名编写起来累,读起来更是难受。因此PHP5就推出了命名空间,类名是类名,命名空间是命名空间,程序写/看的时候直接用类名,运行起来机器看的是命名空间,这样就解决了问题。

      另外,命名空间提供了一种将相关的类、函数和常量组合到一起的途径。这也是面向对象语言命名空间的很大用途,把特定用途所需要的类、变量、函数写到一个命名空间中,进行封装。

      解决了类名的问题,我们终于可以回到PSR标准来了,那么PSR0与PSR4是怎么规范文件与命名空间的映射关系的呢?答案就是:对命名空间的命名(额,有点绕)、类文件目录的位置两者映射关系做出了限制,这个就是标准的核心了。更完整的描述可见现代 PHP 新特性系列(一) —— 命名空间


    三、PSR标准

      
      在说PSR0与PSR4之前先介绍一下PSR标准。PSR标准的发明者和规范者是:PHP-FIG,它的网站是:www.php-fig.org。就是这个联盟组织发明和创造了PSR-[0-4]规范。FIG 是 Framework Interoperability Group(框架可互用性小组)的缩写,由几位开源框架的开发者成立于 2009 年,从那开始也选取了很多其他成员进来,虽然不是 “官方” 组织,但也代表了社区中不小的一块。组织的目的在于:以最低程度的限制,来统一各个项目的编码规范,避免各家自行发展的风格阻碍了程序设计师开发的困扰,于是大伙发明和总结了PSR,PSR是Proposing a Standards Recommendation(提出标准建议)的缩写,截止到目前为止,总共有5套PSR规范,分别是:

    PSR-0 (Autoloading Standard) 自动加载标准
    PSR-1 (Basic Coding Standard)基础编码标准
    PSR-2 (Coding Style Guide) 编码风格向导
    PSR-3 (Logger Interface) 日志接口
    PSR-4 (Improved Autoloading) 自动加载的增强版,可以替换掉PSR-0了。

      
      具体详细的规范标准可以查看PHP中PSR-[0-4]规范

    PSR0标准

      
      PRS-0规范是他们出的第1套规范,主要是制定了一些自动加载标准(Autoloading Standard)PSR-0强制性要求几点:

    1、 一个完全合格的namespace和class必须符合这样的结构:“\< Vendor Name>(< Namespace>)*< Class Name>”
    2、每个namespace必须有一个顶层的namespace(”Vendor Name”提供者名字)
    3、每个namespace可以有多个子namespace
    4、当从文件系统中加载时,每个namespace的分隔符(/)要转换成 DIRECTORYSEPARATOR(操作系统路径分隔符)
    5、在类名中,每个下划线(
    )符号要转换成DIRECTORYSEPARATOR(操作系统路径分隔符)。在namespace中,下划线\符号是没有(特殊)意义的。
    6、当从文件系统中载入时,合格的namespace和class一定是以 .php 结尾的
    7、verdor name,namespaces,class名可以由大小写字母组合而成(大小写敏感的)

      
       具体规则可能有些让人晕,我们从头讲一下。
      
      我们先来看PSR0标准大致内容,第1、2、3、7条对命名空间的名字做出了限制,第4、5条对命名空间和文件目录的映射关系做出了限制,第6条是文件后缀名。
      
       前面我们说过,PSR标准是如何规范命名空间和所在文件目录之间的映射关系?是通过限制命名空间的名字、所在文件目录的位置和两者映射关系。
      
      那么我们可能就要问了,哪里限制了文件所在目录的位置了呢?其实答案就是:
      

    限制命名空间名字 + 限制命名空间名字与文件目录映射 = 限制文件目录

      
      好了,我们先想一想,对于一个具体程序来说,如果它想要支持PSR0标准,它需要做什么调整呢?

    1. 首先,程序必须定义一个符合PSR0标准第4、5条的映射函数,然后把这个函数注册到spl_register()中;
    2. 其次,定义一个新的命名空间时,命名空间的名字和所在文件的目录位置必须符合第1、2、3、7条。

      
       一般为了代码维护方便,我们会在一个文件只定义一个命名空间。
       好了,我们有了符合PSR0的命名空间的名字,通过符合PSR0标准的映射关系就可以得到符合PSR0标准的文件目录地址,如果我们按照PSR0标准正确存放文件,就可以顺利require该文件了,我们就可以使用该命名空间啦,是不是很神奇呢?
      
       接下来,我们详细地来看看PSR0标准到底规范了什么呢?
      
       我们以laravel中第三方库Symfony其中一个命名空间/Symfony/Core/Request为例,讲一讲上面PSR0标准。
      

    1. 一个完全合格的namespace和class必须符合这样的结构:“\< Vendor Name>(< Namespace>)*< Class Name>”

      上面所展示的/Symfony就是Vendor Name,也就是第三方库的名字,/Core是Namespace名字,一般是我们命名空间的一些属性信息(例如request是Symfony的核心功能);最后Request就是我们命名空间的名字,这个标准规范就是让人看到命名空间的来源、功能非常明朗,有利于代码的维护。
      

    2 . 每个namespace必须有一个顶层的namespace(”Vendor Name”提供者名字)

      也就是说每个命名空间都要有一个类似于/Symfony的顶级命名空间,为什么要有这种规则呢?因为PSR0标准只负责顶级命名空间之后的映射关系,也就是/Symfony/Core/Request这一部分,关于/Symfony应该关联到哪个目录,那就是用户或者框架自己定义的了。所谓的顶层的namespace,就是自定义了映射关系的命名空间,一般就是提供者名字(第三方库的名字)。换句话说顶级命名空间是自动加载的基础。为什么标准要这么设置呢?原因很简单,如果有个命名空间是/Symfony/Core/Transport/Request,还有个命名空间是/Symfony/Core/Transport/Request1,如果没有顶级命名空间,我们就得写两个路径和这两个命名空间相对应,如果再有Request2、Request3呢。有了顶层命名空间/Symfony,那我们就仅仅需要一个目录对应即可,剩下的就利用PSR标准去解析就行了。
      

    3.每个namespace可以有多个子namespace

      这个很简单,Request可以定义成/Symfony/Core/Request,也可以定义成/Symfony/Core/Transport/Request,/Core这个命名空间下面可以有很多子命名空间,放多少层命名空间都是自己定义。

      

    4.当从文件系统中加载时,每个namespace的分隔符(/)要转换成 DIRECTORY_SEPARATOR(操作系统路径分隔符)

      现在我们终于来到了映射规范了。命名空间的/符号要转为路径分隔符,也就是说要把/Symfony/Core/Request这个命名空间转为\Symfony\Core\Request这样的目录结构。
      

    5.在类名中,每个下划线_符号要转换成DIRECTORYSEPARATOR(操作系统路径分隔符)。在namespace中,下划线\符号是没有(特殊)意义的。

      这句话的意思就是说,如果我们的命名空间是/Symfony/Core/Request_a,那么我们就应该把它映射到\Symfony\Core\Request\a这样的目录。为什么会有这种规定呢?这是因为PHP5之前并没有命名空间,程序员只能把名字起成Symfony_Core_Request_a这样,PSR0的这条规定就是为了兼容这种情况。
      剩下两个很简单就不说了。
      
      有这样的命名空间命名规则和映射标准,我们就可以推理出我们应该把命名空间所在的文件该放在哪里了。依旧以Symfony/Core/Request为例, 它的目录是/path/to/project/vendor/Symfony/Core/Request.php,其中/path/to/project是你项目在磁盘的位置,/path/to/project/vendor是项目用的所有第三方库所在目录。/path/to/project/vendor/Symfony就是与顶级命名空间/Symfony存在对应关系的目录,再往下的文件目录就是按照PSR0标准建立的:

       /Symfony/Core/Request => /Symfony/Core/Request.php

       一切很完满了是吗?不,还有一些瑕疵:

    1. 我们是否应该还兼容没有命名空间的情况呢?
    2. 按照PSR0标准,命名空间/A/B/C/D/E/F必然对应一个目录结构/A/B/C/D/E/F,这种目录结构层次是不是太深了?

    PSR4标准

      2013年底,新出了第5个规范——PSR-4。

      PSR-4规范了如何指定文件路径从而自动加载类定义,同时规范了自动加载文件的位置。这个乍一看和PSR-0重复了,实际上,在功能上确实有所重复。区别在于PSR-4的规范比较干净,去除了兼容PHP 5.3以前版本的内容,有一点PSR-0升级版的感觉。当然,PSR-4也不是要完全替代PSR-0,而是在必要的时候补充PSR-0——当然,如果你愿意,PSR-4也可以替代PSR-0。PSR-4可以和包括PSR-0在内的其他自动加载机制共同使用。
      
       PSR4标准与PSR0标准的区别:

    1. 在类名中使用下划线没有任何特殊含义。
    2. 命名空间与文件目录的映射方法有所调整。

      对第二项我们详细解释一下(Composer自动加载的原理):
      假如我们有一个命名空间:Foo/class,Foo是顶级命名空间,其存在着用户定义的与目录的映射关系:

    "Foo/" => "src/"
    

      按照PSR0标准,映射后的文件目录是:src/Foo/class.php,但是按照PSR4标准,映射后的文件目录就会是:src/class.php,为什么要这么更改呢?原因就是怕命名空间太长导致目录层次太深,使得命名空间和文件目录的映射关系更加灵活。

      再举一个例子,来源PSR-4——新鲜出炉的PHP规范
      PSR-0风格

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    vendor/
    vendor_name/
    package_name/
    src/
    Vendor_Name/
    Package_Name/
    ClassName.php # Vendor_Name\Package_Name\ClassName
    tests/
    Vendor_Name/
    Package_Name/
    ClassNameTest.php # Vendor_Name\Package_Name\ClassName

      PSR-4风格

    1
    2
    3
    4
    5
    6
    7
    vendor/
    vendor_name/
    package_name/
    src/
    ClassName.php # Vendor_Name\Package_Name\ClassName
    tests/
    ClassNameTest.php # Vendor_Name\Package_Name\ClassNameTest

      对比以上两种结构,明显可以看出PSR-4带来更简洁的文件结构。

    本文原作者推荐使用的一个markdown编辑器,很好用,个人也很推荐StackEdit

    展开全文
  • PHP类延迟加载机制原理

    千次阅读 2015-11-03 21:28:39
    PHP中加载一个类,必须使用require 或者 include把类文件包含进来,才可以实例化类,我们比较传统的方法在使用...如果没有提供任何参数,则自动注册autoload的默认实现函数,很多框架中也引入了这个类延迟加载机制,有
        PHP中加载一个类,必须使用require 或者 include把类文件包含进来,才可以实例化类,我们比较传统的方法在使用的时候就是一次性的把所需要的类文件全部进行require,PHP5之前是这么干的,PHP5之后引入了一个函数spl_autoload_register,欲注册的自动装载函数。如果没有提供任何参数,则自动注册autoload的默认实现函数,很多框架中也引入了这个类延迟加载机制,有效了节省了我们在使用类的时候的时间,下面我们就举例说明一下,自动装载类机制。

        代码很简单只是简单说明了这种装载机制的好处:

      

    <?php
    			
    		/**
    			类延迟加载原理测试
    
    		*/	
    	
    	//1.不使用类延迟加载
    	
    	require 'class/Class1.php';
    	require 'class/Class2.php';
    	
    	$c1 = new Class1;
    	$c2 = new Class2;
    	$c1->say();
    	$c2->say();
    	
    	//2.使用类延迟加载
    	
    	function autoload($class){
    		
    		require ('class/'.$class.'.php');
    		
    	}
    	spl_autoload_register("autoload");
    	
    	
    	$c1 = new Class1;
    	$c2 = new Class2;
    	$c1->say();
    	$c2->say();
    	
    
    
    
    ?>
    上述代码分别写出了使用延迟加载和不使用延迟加载的例子,主要说明延迟加载机制原理就是在我们需要实例化一个PHP类的时候,new class这个过程如果没有包含类文件活报出警告信息,没有包含该类文件,但是这个时候,会去执行spl_autoload_register这个函数,执行之后,会将我们new的类名传递过去,进行包含我们的类文件,这样就是我们在使用的时候才去加载需要的类,而不是无论是否使用都进行加载,这个原理跟java框架的Spring很像,需要类的时候去创建类对象,不需要的时候就不加载。

    YII2框架就使用了这个机制我们查看YII2框架的代码:

    找到vendor\yiisoft\yii2下面的Yii.php文件



    我们可以看到YII2框架就是使用了类延迟加载机制,在入口文件index.php中包含了Yii.php就会执行自动装载类的方法了,以上就是对PHP类自动装载以及延迟加载的说明,

    希望可以帮到大家。


    展开全文
  • php类文件的自动加载机制

    千次阅读 2014-09-28 11:07:42
    通常情况,在使用php面向对象开发的过程中,一个类的定义都是一个文件,这样子下来,当类与类之间需要...在php5之后已经有了类的自动加载机制,可以定义__autoload函数,在使用到某个未定义的类,执行php会出错,但是

    通常情况,在使用php面向对象开发的过程中,一个类的定义都是一个文件,这样子下来,当类与类之间需要相互引用的时候就需要include(require)相应的类文件,如此一来带来的一个不是问题的问题,就是每次需要用到某个类的时候就需要去手工include(require)。

    在php5之后已经有了类的自动加载机制,可以定义__autoload函数,在使用到某个未定义的类,执行php会出错,但是在此时php引擎在返回失败之前会去check下是否有定义__autoload去加载需要的类。

    目前可以是用php的__autoload机制或者SPL(Standard PHP Library)的spl_autoload机制来自动加载。

    0. php中自动加载机制的主要原理

    通过查阅php的源代码,在zend api中的可以发现php在初始化执行器时:
    1. 会先将autoload的注册函数清空;
    2. 然后执行过程中如果要获取某个类时通过zend_fetch_class函数获取;
    3. 而zend_fetch_class中通过zend_lookup_class_ex函数去查找对应的类是否存在,在zend_lookup_class_ex函数中实现了自动加载的功能。
    如以下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    // 初始化执行器
    voidinit_executor(TSRMLS_D)/* {{{ */
    {
        ....
        EG(in_autoload) = NULL;
        EG(autoload_func) = NULL;
        ....
    }
    // 获取类指针
    zend_class_entry *zend_fetch_class(constchar*class_name, uint class_name_len, intfetch_type TSRMLS_DC) /* {{{ */
    {
        zend_class_entry **pce;
        intuse_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) == 0;
        ...
        if(zend_lookup_class_ex(class_name, class_name_len, NULL, use_autoload, &pce TSRMLS_CC) == FAILURE) {
            ...
        }
        return*pce;
    }
    // 查找类是否存在
    ZEND_APIintzend_lookup_class_ex(constchar*name, intname_length,constzend_literal *key, intuse_autoload, zend_class_entry ***ce TSRMLS_DC) /* {{{ */
    {
        zval **args[1];
        zval autoload_function;
        ...
        /* The compiler is not-reentrant. Make sure we __autoload() only during run-time
        * (doesn't impact fuctionality of __autoload()
        */
        if(!use_autoload || zend_is_compiling(TSRMLS_C)) {
            ...
            returnFAILURE;
        }
        ...
        fcall_info.size = sizeof(fcall_info);
        fcall_info.function_table = EG(function_table);
        fcall_info.function_name = &autoload_function;      // 这里的autoload_function指定义的__autoload函数
        ...
        fcall_cache.initialized = EG(autoload_func) ? 1 : 0;
        fcall_cache.function_handler = EG(autoload_func);   // 这里的autoload_func指注册autoload_func函数
        ...
        retval = zend_call_function(&fcall_info, &fcall_cache TSRMLS_CC);
        ...
        returnretval;
    }

    也就可以发现php在自动加载类时最重要的异步是:zend_call_function(&fcall_info, &fcall_cache TSRMLS_CC);
    当中fcall_info指针里面的function_name是指php自带的“__autoload”函数
    而fcall_cache指针里面定义的function_handler是用户注册的autoload_func
    而在zend_call_function中执行过程有一个重要步骤是:
    查看fcall_cache是否定义:如果有定义,则执行fcall_cache的handler;如果没有定义,则才执行fcall_info.function_name,也就是__autoload函数。

    所以,综上可以结论:
    php的自动加载未定义类过程:
    1. 检查是否注册autoload_func
    2. 如果没有注册autoload_func,则检查是否定义了__autoload;如果有定义__autoload则执行用户定义的加载方式,如果没有定义__autoload则返回错误。
    3. 如果有注册autoload_func则执行注册的加载函数,不执行__autoload。

    php有两种自动加载方式:
    1. 通过定义__autoload函数来处理未定义类的加载;
    2. 通过注册autoload_func来处理未定义类的加载,即:通过spl_autoload_register来注册autoload_func
    当中注册autoload_func的方式的优先级高于使用__autoload方式,也就是说,假如同时定义__autoload和注册autoload_func,只会执行注册的autoload_func,不会继续执行__autoload。

    1.使用__autoload方式
    void __autoload ( string $class )
    如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /* Foo.class.php */
    classFoo{
        function__construct()
        {
            echo'constructed';
        }
    }
    /* test_autoload.php*/
    function__autoload($className)
    {
        include($className.'.class.php');
    }
    $foo=newFoo();// output:constructed

    在test.php中就不需要加载Foo.class.php了,__autoload会自动加载类Foo的定义。
    不过要注意的是:
    1. 在php 5.3.0 版之前,__autoload 函数抛出的异常不能被 catch 语句块捕获并会导致一个致命错误。从php 5.3.0+ 之后,__autoload 函数抛出的异常可以被 catch 语句块捕获,但需要遵循一个条件。如果抛出的是一个自定义异常,那么必须存在相应的自定义异常类。
    2. 一个项目中只能定义一个__autoload,假如说当项目中引用了第三方文件时,刚好第三方文件中也使用了__autoload;或者项目成员多的时候,定义了不同的__autoload;此时就会出错,因为__autoload函数拥有全局域,php也支不支持函数重载,只能将几个__autoload合并成一个。
    如此一来就有了更加灵活的spl_autoload_register()的自动加载机制,php官方也建议不要使用__autoload而使用spl_autoload_register()。

    2. 使用spl_autoload_register方式
    SPL(Standard PHP Library),是php的标准库,php5.1+ 支持。spl的自动加载机制主要原理就是通过spl_autoload_register注册函数,并且将php中autoload_func指向了注册的函数。如果通过spl_autoload_register来注册函数,spl会执行它默认的spl_autoload()函数。
    bool spl_autoload_register ([ callback $autoload_function ] )

    1
    2
    3
    4
    5
    6
    7
    /* test_autoload.php*/
    functionautoloadHandler($className)
    {
        include($className.'.class.php');
    }
    spl_autoload_register('autoloadHandler');
    $foo=newFoo();// output:constructed

    如果是在类中定义的handler函数,如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /* test_autoload.php*/
    classAutoloader
    {
        staticfunctioninit()
        {
            spl_autoload_register($this,'autoloadHandler');
        }
        publicfunctionautoloadHandler( $className)
        {
            include($className.'.class.php');
        }
    }
    Autoloader::init();
    $foo=newFoo();// output:constructed

    上面就是php类自动加载的主要两种方式,我觉得使用自动加载的好处有:

    1. 不在需要需要用到什么类文件就require(include)了
    2. 在设置autoload的过程添加一个map缓存已经加载过的类,也不再需要使用require_once(include_once)了
    3. 按需加载,避免require(include)不必要的文件


    补充:

    在PHP开发过程中,如果希望从外部引入一个class,通常会使用include和require方法,去把定义这个class的文件包含进来,但是这样可能会使得在引用文件的新脚本中,存在大量的include或require方法调用,如果一时疏忽遗漏则会产生错误,使得代码难以维护。

    自PHP5后,引入了__autoload这个拦截器方法,可以自动对class文件进行包含引用,通常我们会这么写: 

    复制代码代码如下:

    function __autoload($className) { 
    include_once $className . '.class.php'; 


    $user = new User(); 

    当PHP引擎试图实例化一个未知类的操作时,会调用__autoload()方法,在PHP出错失败前有了最后一个机会加载所需的类。因此,上面的这段代码执行时,PHP引擎实际上替我们自动执行了一次__autoload方法,将User.class.php这个文件包含进来。 

    在__autoload函数中抛出的异常不能被catch语句块捕获并导致致命错误。 

    如果使用 PHP的CLI交互模式时,自动加载机制将不会执行。 

    当你希望使用PEAR风格的命名规则,例如需要引入User/Register.php文件,也可以这么实现: 
    复制代码代码如下:

    //加载我 
    function __autoload($className) { 
    $file = str_replace('_', DIRECTORY_SEPARATOR, $className); 
    include_once $file . 'php'; 

    $userRegister = new User_Register(); 


    这种方法虽然方便,但是在一个大型应用中如果引入多个类库的时候,可能会因为不同类库的autoload机制而产生一些莫名其妙的问题。在PHP5引入SPL标准库后,我们又多了一种新的解决方案,spl_autoload_register()函数。 

    此函数的功能就是把函数注册至SPL的__autoload函数栈中,并移除系统默认的__autoload()函数。一旦调用spl_autoload_register()函数,当调用未定义类时,系统会按顺序调用注册到spl_autoload_register()函数的所有函数,而不是自动调用__autoload()函数,下例调用的是User/Register.php而不是User_Register.class.php: 
    复制代码代码如下:

    //不加载我 
    function __autoload($className) { 
    include_once $className . '.class.php'; 

    //加载我 
    function autoload($className) { 
    $file = str_replace('/', DIRECTORY_SEPARATOR, $className); 
    include_once $file . '.php'; 

    //开始加载 
    spl_autoload_register('autoload'); 
    $userRegister = new User_Register(); 


    在使用spl_autoload_register()的时候,我们还可以考虑采用一种更安全的初始化调用方法,参考如下: 
    复制代码代码如下:

    //系统默认__autoload函数 
    function __autoload($className) { 
    include_once $className . '.class.php'; 

    //可供SPL加载的__autoload函数 
    function autoload($className) { 
    $file = str_replace('_', DIRECTORY_SEPARATOR, $className); 
    include_once $file . '.php'; 

    //不小心加载错了函数名,同时又把默认__autoload机制给取消了……囧 
    spl_autoload_register('_autoload', false); 
    //容错机制 
    if(false === spl_autoload_functions()) { 
    if(function_exists('__autoload')) { 
    spl_autoload_register('__autoload', false); 



    奇技淫巧:在Unix/Linux环境下,如果你有多个规模较小的类,为了管理方便,都写在一个php文件中的时候,可以通过以ln -s命令做软链接的方式快速分发成多个不同类名的拷贝,再通过autoload机制进行加载。

    展开全文
  • PHP-自动加载原理分析

    千次阅读 2017-11-27 15:44:13
    那么PHP自动加载的前因后果到底是什么?PHP的内部原理又是怎么样的呢?接下来我就根据自己的理解进行一下分析总结: 为什么会有自动加载? 在PHP面向对象(OO)编程中,为了方便管理,我们都会把一个类写在一个单独的...
  • PHP的类自动加载机制

    万次阅读 2012-04-16 10:21:57
    PHP的类自动加载机制  在PHP开发过程中,如果希望从外部引入一个class,通常会使用include和require方法,去把定义这个class的文件包含进来。这个在小规模开发的时候,没什么大问题。但在大型的开发项目中,这么...
  • 自动加载类的方法: function userAutoload($class_name) { $framework_class_list = array( // '类名' => '类文件地址' 'Controller' => './framework/Controller.class.php', 'Model' => './framework/...
  • PHP 命名空间与自动加载机制介绍

    千次阅读 2017-12-11 17:02:27
    include 和 require 是PHP中引入文件的两个基本方法。在小规模开发中直接使用 include 和 require 没哟什么不妥,但在大型项目中会造成大量的 include 和 require 堆积。这样的代码既不优雅,执行效率也很低,而且...
  • PHP自动加载有两种,一种是__autoload,另外一种是spl_autoload_registed(),以下将对这两种加载机制进行详细讲述。 自动加载的原理 自动加载就是在我们new(实例化)一个class(类)的时候,PHP系统如果找不到你...
  • PHP5之后提供了类的自动加载机制,使类在加载时才被使用,即lazy loading,其主要实现原理见 http://www.nowamagic.net/php/php_Autoload.php PHP5提供的类加载机制分为二种,一种是用户自扩展的_autoload方式,...
  • Yii的类自动加载机制

    千次阅读 2016-05-30 15:03:37
    Yii借助了PHP的类自动加载机制高效实现了类的定位、导入,这一机制兼容 PSR-4 的标准。在Yii中,类仅在调用时才会被加载,特别是核心类,其定位非常快,这也是Yii高效高性能的一个重要体现。 自动加载机制
  • 详解composer的自动加载机制

    万次阅读 2017-09-27 23:10:21
    composer是一个用PHP开发的用来管理项目依赖的工具,当你在项目中声明了依赖关系后,composer可以自动帮你下载和安装这些依赖库,并实现自动加载代码。安装composercomposer的安装非常的容易1.下载安装脚本,这里把...
  • PHP autoload机制详解 <br /> (1) autoload机制概述 <br /> 在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护...
  • TP5 自动加载机制详解

    千次阅读 2018-11-15 13:46:16
    今天我们就从源码来研究下TP5自动加载的实现。   作为单入口框架,就从入口文件看起,按照tp5文档所示的规范,入口文件应该是放在public/ 下。 那么为什么大多数要把入口放到子文件夹下呢?这是一个小技巧。 第...
  • 如果想具体详细的了解关于自动加载的功能,可以查看资料: PHP 的类自动加载机制 PHP 的 autoload 机制的实现解析
  • 由于对于composer自动加载机制的记忆只剩下了”spl_auto???”和”根据命名空间来推导出文件路径”这两个了。。。还是残缺的。。 本想网上收藏一篇详解,奈何,没发现符合我觉得的”由浅入深”文章。 所以有了这篇...
  • 主要介绍了CodeIgniter配置之autoload.php自动加载用法,结合实例形式较为详细的分析了CodeIgniter自动加载机制原理与使用方法,需要的朋友可以参考下
  • PHP autoload机制详解 自动加载函数

    千次阅读 2014-02-24 17:15:26
    (1) autoload机制概述 在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护时也很便利。这也是OO设计的基本思想之一。在PHP5之前...
  • index.php require __DIR__.'/../vendor/autoload.php'; 跟踪:index.php > /vendor/autoload.php require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInit148a9da910429c7c0.....
  • 在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护时也很便利。这也是OO设计的基本思想之一。在PHP5之前,如果需要使用一个类,只...
  • 在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护时也很便利。这也是OO设计的基本思想之一。在PHP5之前,如果需要使用一个类,只...
  • PHP中的自动加载

    2016-11-02 11:26:35
    PHP中的自动加载 what is 自动加载? 或许你已经对自动加载有所了解。简单描述一下:自动加载就是我们在new一个class的时候,不需要手动去写require来导入这个class.php文件,程序自动帮我们加载导入进来。这是...
  • php7:自动加载的实现

    2020-07-10 11:12:16
    在实际项目中,各个类都放在单独的一个文件中,在实际项目的运行过程中需要调用许多类来共同协调,因此每次调用其它类文件都需要使用include或require结构来完成调用,为了减少代码的冗余度,PHP支持自动加载机制以...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,656
精华内容 6,662
关键字:

php自动加载机制原理