PHP自动加载学习记录

1. 什么是自动加载?

自动加载就是在实例化类的时候,PHP自动帮我们把需要用到的文件给require进来,不需要我们手动一个一个去require;
自动加载可以说是现代PHP框架的核心基础,就像地基一样,像Laravel,thinkphp,yii2等框架都是基于自动加载实现的文件自动载入。

2. 如何实现?

1. __autoload()方法实现自动加载
index.php

<?phpfunction __autoload($classname){ require $className . '.php';}$test = new Test();//在index.php同级目录下加载Test.php文件

小项目这样做当然没问题,但是项目变大了之后就无法满足需求了,比如需要加载多个不同目录的文件__autoload就无法实现了,因为一个项目只能有一个__autoload,不能申明多个。那怎么解决呢?就是下面的spl_autoload_register了。

2. spl_autoload_register方法实现自动加载
spl_autoload_register就是设计用来取代__autoload的,可以多次调用注册,使用方法如下:

<?php//多种形式sql_autoload_resister('load_function'); //函数名sql_autoload_resister(array('load_object', 'load_function')); //类和静态方法sql_autoload_resister('load_object::load_function'); //类和方法的静态调用//php 5.3之后,也可以像这样支持匿名函数了。spl_autoload_register(function($className){    if (is_file('./lib/' . $className . '.php')) {        require './lib/' . $className . '.php';    }});

需要注意的是,当项目中同时存在spl_autoload_register和__autoload时,__autoload会失效,想要同时工作,只能把__autoload作为函数注册到spl_autoload_register中。
多个spl_autoload_register注册后,调用的顺序是按照注册的顺序执行,直到找到可以加载的文件为止。

function load1($className){ echo 1; if (is_file($className . '.php')) { require $className . '.php'; }}function load2($className){ echo 2; if (is_file('./app/' . $className . '.php')) { require './app/' . $className . '.php'; }}function __autoload($className){ echo 3; if (is_file('./lib/' . $className . '.php')) { require './lib/' . $className . '.php'; }}//注册了3个spl_autoload_register('load1');spl_autoload_register('load2');spl_autoload_register('__autoload'); $config= new Config(); //Config就在本目录下$test= new Test(); //Test在/app/Test.php//打印结果//1Hello DB//123Hello Info

当我们想知道项目中注册了多少自动加载函数的时候,可以使用spl_autoload_functions函数

var_dump(spl_autoload_functions());//数组的形式输出array (size=3)  0 => string 'load1' (length=5)  1 => string 'load2' (length=5)  2 => string '__autoload' (length=10)

3. spl_autoload_register namespace实现文件规范化自动加载
根据PSR-0规范,namespace的命名就可以很方便的直接找到对应文件,而自动加载方法的类名是包含namespace的名称的,因此可以直接加载
index.php

<?php//定义当前的目录绝对路径define('DIR', dirname(__FILE__));//加载这个文件require DIR . './Loading.php';//采用`命名空间`的方式注册。php 5.3 加入的//也必须是得是static静态方法调用,然后就像加载namespace的方式调用,注意:不能使用usespl_autoload_register('\\App\\Loading::autoload');//Loading文件使用了namespace这里也就只能使用全路径调用// 调用三个namespace类//定位到Lib目录下的Name.phpuse Lib\Name;Name::test();//定位到App目录下Android目录下的Name.phpAndroid\Name::test();//定位到App目录下Ios目录下的Name.php\Ios\Name::test();

Loading.php

<?phpnamespace App;class Loading{    public static function autoload($className)    {        //根据PSR-O的第4点 把 \ 转换层(目录风格符) DIRECTORY_SEPARATOR ,        //便于兼容Linux文件找。Windows 下(/ 和 \)是通用的        //由于namspace 很规格,所以直接很快就能找到        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, DIR . '\\' . $className) . '.php';        if (is_file($fileName)) {            require $fileName;        } else {            echo $fileName . ' is not exist';            die;        }    }}

Android/Name.php

<?phpnamespace Android;class Name{ public static function test(){ echo __NAMESPACE__.' name'.PHP_EOL; }}

Ios/Name.php

<?phpnamespace Ios;class Name{    public static function test(){        echo __NAMESPACE__.' name'.PHP_EOL;    }}

Lib/Name.php

<?phpnamespace Lib;class Name{ public static function test(){ echo __NAMESPACE__.' name'.PHP_EOL; }}

输出:

Lib nameAndroid nameIos name

4. 同根命名空间下的相互调用
在同一命名空间下相互调用可能会存在意想不到的问题,假如在Lib/Name.php想调用Lib/Driver/Config.php

new Lib\Driver\Config();//报错C:\Users\53101\Desktop\study\php\autoload_study\Lib\Lib\Driver\Config.php is not exist

这种命名空间的调用实际上是一个相对路径,当前已经在Lib命名空间下了,所以不需要再重复调用Lib命名空间,正确的做法是:

//同级别调用new Config();//下一级别目录调用new Driver\Config();//或者使用use关键字,use引入的namespace是绝对路径use Lib\Driver\Config;new Config();//也可以直接写绝对路径命名空间调用new \Lib\Driver\Config();//在Lib前加\即调用绝对路径

如果反过来在Lib/Driver/Config.php中想调用Lib/Name.php就只能使用绝对路径引用的方式,因为相对路径已经到了下级目录Lib/Driver,无法找到上级目录Lib的文件。

//use绝对路径use Lib\Name;Name::test();//直接绝对路径调用\Lib\Name::test();

总结:

实现自动加载有两种方式

  1. __autolaod函数
  2. spl_autoload_register函数注册

实际使用时是配合namespace使用的,因为PSR-0规范可以方便地找到需要加载的文件,命名空间在调用时实际上就已经把路径写出来了,只需要把对应class加载进来就可以了。
同根命名空间下相互调用要注意可能出现的问题。实际使用时尽量使用use关键字加载,或者绝对路径加载

(0)

相关推荐