PHP自动加载机制

概述

首先,为什么PHP需要自动加载呢?

在PHP面向对象(OO)编程中,为了方便管理,我们都会把一个类写在一个单独的文件中,那么如果想在A类中使用B类的功能,就需要把B类加载到A类。对于这样的需求在最原始的时候,我们是通过require 和 include 语法实现将文件加载到另一个文件中,include 和 require 是PHP中引入文件的两个基本方法。

在小规模开发中直接使用 include 和 require 不会有什么不妥,但在大型项目中会造成大量的 include 和 require 堆积。这样的代码既不优雅,执行效率也很低,而且维护起来也相当困难。

PHP的自动加载功能,框架实现自动加载的包括PHP规范中的PSR0和PSR4原则,Composer的自动加载功能等等。

php加载文件方式

  1. 常规加载:include,include_once,requice,requice_one
  2. 魔法方法:__autoload()
  3. SPL 自动加载:spl_autoload_register()

include()与require()

简单的文件加载方法:

  1. require()

    包含的意思,找不到文件时,会报warning的错误,然后程序继续往下执行。include语句只有在被执行时才会读入要包含的文件。在错误处理方便,使用include语句,如果发生包含错误,程序将跳过include语句,虽然会显示错误信息但是程序还是会继续执行!php处理器会在每次遇到include()语句时,对它进行重新处理,所以可以根据不同情况的,在条件控制语句和循环语句中使用include()来包含不同的文件。

  2. include()

    必须的意思,找不到文件时,会报fatal error(致命错误),程序停止往下执行。在php文件被执行之前,php解析器会用被引用的文件的全部内容替换require语句,然后与require语句之外的其他语句组成个新的php文件,最好后按新的php文件执行程序代码。

  3. require_once()

    类似于include(),系统会进行判断,如果已经包含,则不会再包含第二次。

  4. include_once()

    类似于require(),系统会进行判断,如果已经包含,则不会再包含第二次。

共同点:能包含位于独立文件中的代码,可以减少代码的重复,实现代码结构的模块化,方便调用。

  • 注意事项

    • 加载文件格式

      include/require 包含进来的文件必须要加<?php ?>因为在包含时,首先理解文件内容是普通字符串,碰到<?php ?>标签时,才去解释。

    • 路径要求

      可以用绝对路径,也可以用相对路径;windows下正反斜线都可以,linux下只认正斜线,所以最好用正斜线。

    • 如何选择

      比如是系统配置,缺少了,网站不让运行,自然用require,如果是某一段统计程序,少了,对网站只是少统计人数罢了,不是必须要的,可以用include而加不加once是效率上的区别,加上once,虽然系统帮你考虑了只加载一次,但系统的判断会是效率降低,因此,更应该在开发之初,就把目录结构调整好,尽量不要用_once的情况。

    • 特殊用法

      利用include/require返回被包含页面的返回值

      1
      2
      a.php页面中: ..... return $value; 
      b.php页面中:$v = include("a.php");

__autoload()自动加载

  • PHP5及之后的版本,使用尚未定义的类时会自动调用__autoload函数,所以我们可以通过编写__autoload函数来让php自动加载类,而不必写一个长长的包含文件列表。

    需明确的是对于__autoload()函数,PHP在找不到类的时候会自动执行,但是PHP内部并没有定义这个函数。

    这个函数需要开发者自定义,并且编写内部逻辑,PHP只负责在需要的时候自动调用执行。而且在调用的时候会自动传人要加载的类名作为参数。

  • 用法:首先需要在需要加载文件的代码中,定义__autoload()函数,并且编写内部逻辑。PHP在找不到类的时候会自动执行__autoload()函数。下面是A.php需要加载B.php的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //文件 B.php 不做修改
    //文件 A.php
    <?php
    class A{
    public function test(){
    $b_object = new B();
    $b_object->echo_info();
    }
    }
    function __autoload($classname){
    require $classname.'.php';//include 'b.php';
    }
    $a_object = new A();
    $a_oject->test();
    ?>
    命令行输入:#php a.php
    输出: “我是class B中的方法执行结果“
  • 缺陷:一个项目中仅能有一个这样的 __autoload() 函数,因为 PHP 不允许函数重名,也就是说你不能声明2个__autoload()函数文件,否则会报致命错误。

    如果项目比较大,加载每个文件都使用同样的规则显然是不现实的,那么我们可能就需要在__autoload()中编写复杂的规则逻辑来满足加载不同文件的需求。

    这同样会使得__autoload()函数变得复杂臃肿,难以维护管理

SPL 自动加载

PHP在实例化一个对象时(实际上在实现接口,使用类常数或类中的静态变量,调用类中的静态方法时都会如此),首先会在系统中查找该类(或接口)是否存在,如果不存在的话就尝试使用autoload机制来加载该类。而autoload机制的主要执行过程为:

  • 检查执行器全局变量函数指针autoload_func是否是NULL;
  • 如果 autoload_func==NULL ,则查找系统是否定义 __autoload() 函数,如果定义了,则执行并返回加载结果。如果没有定义,则报错并退出;
  • 如果 autoload_func 不等于NULL,则直接执行 autoload_func 指向的函数加载类,此时并不检查 __autoload() 函数是否定义。

通过上述PHP自动加载流程,可知PHP实际上提供了两种方法来实现自动装载机制:(1)使用用户定义的__autoload()函数,这通常在PHP源程序中来实现;(2)设计一个函数,将autoload_func指针指向它,这通常使用C语言在PHP扩展中实现,即 SPL autoload机制,即本节中的SPL自动加载。如果两种方式都实现了,也就是 autoload_func 不等于NULL,程序只会执行第二种方式,__autoload() 函数是不会被执行的。

  • 用法

    通过spl_autoload_register('my_autoload'),实现了 当程序执行找不到类B时,会执行 自定义的 my_autoload()函数,加载B类。实际上 spl_autoload_register('my_autoload') 的作用就是 把autoload_func 指针指向 my_autoload()

    示例:

    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
    B.php文件不变
    A.php
    <?php
    class A{
    public function test(){
    $b_object = new B();
    $b_object->echo_info();
    }
    }

    function __autoload($classname){
    require $classname.'.php';//include 'b.php';
    }

    function my_autoload($classname){
    require $classname.'.php';//include 'b.php';
    echo 'my_autoload ';
    }

    spl_autoload_register('my_autoload');
    $a_object = new A();
    $a_object->test();

    结果:my_autoload 我是class B中的方法执行结果
    ?>
  • SPL 自动加载整个过程

    针对上述的示例,假如把spl_autoload_register('my_autoload')改成 spl_autoload_register()不添加任何参数,B类也能被加载。

    为什么呢?

    因为SPL扩展内部自己定义了一个自动加载函数 spl_autoload(),实现了自动加载的功能,如果我们不定义自己的自动加载函数,并且程序里写了spl_autoload_register()(如果不传参数,必须是第一次执行才会有效)或者 spl_autoload_register('spl_autoload'),那么autoload_func 指针就会指向内部函数 spl_autoload()。程序执行的时候如果找不到相应类就会执行该自动加载函数。

    SPL 是怎么实现autoload_func 指针指向不同的函数呢?

    在SPL内部定义了 一个函数 spl_autoload_call() 和 一个全局变量autoload_functionsautoload_functions本质上是一个HashTable,不过我们可以将其简单的看作一个链表,链表中的每一个元素都是一个函数指针,指向一个具有自动加载类功能的函数。

    spl_autoload_call()的作用就是按顺序遍历 autoload_functions,使得autoload_func指向每个自动加载函数,如果加载成功就停止,如果不成功就继续遍历下个自动加载函数,直到加载成功或者遍历完所有的函数。

    autoload_functions 这个列表是谁来维护的呢?

    spl_autoload_register() 这个函数维护。我们说的自动加载函数的注册,其实就是通过·spl_autoload_register()·把自动加载函数加入到autoload_functions 列表。

    相关SPL自动加载函数

    1
    2
    spl_autoload_functions() //打印autoload_functions列表
    spl_autoload_unregister() //注销自动加载函数

参考:

  1. PHP-自动加载原理分析
  2. php自动加载方式集合

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/242256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

操作系统知识——互斥和死锁

银行家算法银行家算法是最有代表性的避免死锁算法&#xff0c;是Dijkstra提出的。这是由于该算法能用于银行系统现金贷款的发放而得名。 我们可以把操作系统看作是银行家&#xff0c;操作系统管理的资源相当于银行家管理的资金&#xff0c;进程向操作系统请求分配资源相当于用户…

C#常用的控件

C#常用的控件 窗体 菜单、工具栏、对话框 用户控件及控件的继承

C++输入函数的应用

最近因为找工作的事情&#xff0c;在刷一些编程题&#xff0c;也陆陆续续参加了一些笔试&#xff0c;一般编程中使用的语言是C&#xff0c;因为编程题会有时间的限制&#xff0c;C的效率最高&#xff0c;不容易出现超时的问题所以使用最多的语言就是C。 在编程中&#xff0c;其…

C#绘图及图像

C#绘图及图像 绘图的基本方法 绘图的应用

PHP开发API

引言如何使用 PHP 开发 API&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09; 呢&#xff1f; 做过 API 的人应该了解&#xff0c;其实开发 API 比开发 WEB 更简洁&#xff0c;但可能逻辑更复杂&#xff0c;因为 API 其实就是数据输出&a…

热部署和热加载

2018-8-22今天入职第二天&#xff0c;看到老大在群里发了一个go热加载实现的链接&#xff0c;之前没有听说过热加载&#xff0c;所以搜索了一下&#xff0c;下面是关于热部署和热加载的相关整理。 引言在项目线上更新时&#xff0c;可能会遇到需要热部署的情况&#xff0c;虽然…

C#实现定时器

C#实现定时器 方法一 布局 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;namespace…

HTTP请求流程

hhtp请求流程首先http是一个应用层的协议&#xff0c;在这个层的协议&#xff0c;只是一种通讯规范&#xff0c;也就是因为双方要进行通讯&#xff0c;大家要事先约定一个规范。 http请求与响应的步骤如下&#xff1a; URL解析、域名解析 输入URL的时候&#xff0c;首先浏览器会…

PHP执行外部程序的方法

引言在一些特殊情况下&#xff0c;会使用PHP调用外部程序执行&#xff0c;比如&#xff1a;调用shell命令、shell脚本、可执行程序等等 PHP执行外部程序的方法 system()&#xff0c;system函数将执行的结果直接输出exec()&#xff1a;执行一个外部程序&#xff0c;将输出结果保…

C语言函数变量的引用

C语言函数变量的引用 有时候&#xff0c;你想引用函数中的某些变量。 #include <stdio.h>int main() {struct guoba{int pang;int piqi;};void tong(int *p){int month1 3;int month5 8;struct guoba xiaoguoba;xiaoguoba.pang month1 month5;*p xiaoguoba.pang ;…

php 终止程序的方法——return、exit()、die()

php中终止程序的运行有三种方式&#xff1a; return&#xff0c;主脚本程序中使用exit()&#xff0c;脚本中使用die()&#xff0c; 脚本中使用三者所在的脚本文件他们之后的代码都不会执行了。php开发模式下想直接看到变量的值使用exit()、die()。 returnreturn是语言结构而不是…

Eric Evans说DDD还未结束

在Explore DDD 2018大会上&#xff0c;Eric Evans做了“怀疑、乐观和实用主义”的主题演讲&#xff0c;他在演讲中表示&#xff0c;“DDD还没有结束”。在“领域驱动设计”这本书出版十五年之后&#xff0c;作者对这个想法长期以来如此流行表示了惊讶之情。他强调&#xff0c;D…

mark

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

C语言的进阶-指针的应用

指针的应用 #include <stdio.h> void swap(int *p,int *q); int main() {int a 5;int b 8;swap(&a,&b);printf("a%d,b%d\n",a,b);return 0; }void swap(int *p,int *q) {int t *p;*p *q;*q t; }函数只能返回一个值&#xff01; 可以通过参数返回多…

Go简单开始

Go是一门编译型语言&#xff0c;Go语言的工具链将源代码及其依赖转换成计算机的机器指令2。Go语言提供的工具都通过一个单独的命令 go 调用&#xff0c; go 命令有一系列子命令。最简单的一个子命令就是run。这个命令编译一个或多个以.go结尾的源文件&#xff0c;链接库文件&am…

C语言进阶-指针与数组

C语言进阶-指针与数组 q可以改变指向的内容&#xff0c;但不能改变指向的地址 p可以改变指向地址&#xff0c;但不能改变指向内容

测试下文章

这是一篇测试 编辑

发布文章测试

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

C语言进阶-指针运算

C语言进阶-指针运算 #include <stdio.h>int main() {char a[] {0,1,2,3,4,5,6,7,8,9};char *p a;printf("p%d\n",p);printf("p1%d\n",p1);int b[] {0,1,2,3,4,5,6,7,8,9};int *q b;printf("q%d\n",q);printf("q1%d\n",q1);r…