专业编程基础技术教程

网站首页 > 基础教程 正文

PHP自动加载学习记录

ccvgpt 2024-11-23 11:53:41 基础教程 2 ℃

1. 什么是自动加载?

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

2. 如何实现?

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

PHP自动加载学习记录

<?php
function __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的方式调用,注意:不能使用use
spl_autoload_register("\\App\\Loading::autoload");//Loading文件使用了namespace这里也就只能使用全路径调用
// 调用三个namespace类
//定位到Lib目录下的Name.php
use Lib\Name;
Name::test();
//定位到App目录下Android目录下的Name.php
Android\Name::test();
//定位到App目录下Ios目录下的Name.php
\Ios\Name::test();

Loading.php

<?php
namespace 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

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

Ios/Name.php

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

Lib/Name.php

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

输出:

Lib name
Android name
Ios 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关键字加载,或者绝对路径加载

Tags:

最近发表
标签列表