朝花夕拾|勿忘初心 朝花夕拾|勿忘初心

关于PHP中的_自动加载

in 安全研究,PHP语言 read (149) 1338汉字 站长Lucifaer 文章转载请注明来源!

在分析了一些国外的cms后,不得不承认的一点——国外的cms框架设计更加复杂。伴随着框架的复杂性,就是其功能的强大性。相比之下,国内的cms更趋于一种简化封装(甚至到达了简单粗暴的地步),比如ThinkPHP,相比于Symfony真的是好读太多了。

可以明显的看出,国外的cms在PHP的类调用方面封装的非常的完善,而且类调用让人眼花缭乱,非常容易跟丢。这边就首先先总结一下PHP中的自动加载方面的内容,之后会有对于Joomla的框架的简单跟踪。

0x00 自动加载

自动加载简单来说就是在new一个class的时候,不需要手动require来包含class.php文件,程序自动帮我们加载并导入进来。自动加载可以说是现代PHP框架的根基,基本上所有的框架都会用到自动加载类。

0x01 自动加载的原理

自动加载的原理,就是在我们new一个class的时候,PHP如果找不到我们的类,就会去自动调用文本文件中的__autoload($classname)方法,我们new的这个class_name就称为这个方法的参数。所以我们只需要在这个方法中根据我们需要new的class来判断和包含对应路径类文件,从而实现自动加载。

0x02 被抛弃的__autoload与新兴的spl_autoload_register

在目前最新的PHP版本(PHP7.2.0)已经将__autoload方法删除,官方文档推荐使用spl_autoload_register进行类的自动加载。

那我们就跳过__autoload直接说spl_autoload_register

当我们去new一个找不到的class时,PHP就会去自动调用spl_autoload_register注册的函数,这个函数通过它的参数传入:

spl_autoload_register('function_name'); //函数名
spl_autoload_register(array('object_name', 'function_name')); //类和静态方法
spl_autoload_register('load_object::load_function'); //类和方法的静态调用

index.php

class autoloading($className){
  echo 1;
  require $className.'.php';
}
spl_autoload_register('autoloading');
$db = new DB();

上面实现了自动加载的方式,我们同样也可以用类加载的方式调用,但是必须是static方法:

class autoloading{
    // 必须是静态方法
    public static function load($className){
        require $className.'.php';
    }
}
// 两种方法都可以
spl_autoload_register(array('autoloading', 'load'));
spl_autoload_register('autoloading::load');
$db = new DB();

**注意一点:若同时使用spl_autoload_register和__autoload,__autoload会失效。**

0x03 多个spl_autoload_register的使用

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'); 
$db = new DB(); //DB就在本目录下
$info = new Info(); //Info 在/lib/Info.php

执行结果:

1Hello DB
123Hello Info

注意,前面说过,spl_autoload_register使用时,__autoload会无效,有时候,我们希望它继续有效,就可以也将它注册进来,就可以继续使用。

可以继续看一下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)

0x04 spl_autoload_register自动加载+namespace命名空间的使用

直接上代码

AutoLoading\loading

<?php
namespace AutoLoading;
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;
       }
    }
} 

上面就是一个自动加载的核心思想方法。下面就利用spl_autoload_register来注册这个函数:

index.php

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

由于我们是采用PSR-O方式来定义namespace的命名的,所以很方便的定位到这个文件的在哪个目录下了。

APP\Android\Name

namespace App\Android;
class Name
{
    public function __construct()
    {
        echo __NAMESPACE__ . "<br>";
    }
    public static function test()
    {
        echo  __NAMESPACE__ . ' static function test <br>';
    }
}

输出结果为:

Lib static function test 
App\Android static function test 
App\Ios static function test

0x05 同命名空间下的相互调用

比如Lib\Factory.phpLib\Db\MySQL.php

如果是在同一个命名空间下平级的2个文件。可以直接调用,不用命名空间:

new MySQL(); //直接这样就可以了。
new Db\MySQL(); //如果有个Db文件夹,就这样。

还有一种方法就是使用use。使用use就可以带上Lib了。use使用的是绝对路径:

use Lib\Db\MySQL;
new MySQL();

如果想在Lib\Db\MySQL.php中调用Lib\Register.php

use Lib\Register;
Register::getInstance();

因为现在已经在Lib\Db这样一个命名空间了,如果你不用use,而是使用Lib\Register::getInstance()或者使用Register::getInstance()的话。将是在Lib\Db这个空间下进行相对路径的加载,是错误的。

0x06 总结一下

1. 其本身作为cms的core,所具有的优点:

  • 首先spl_autoload_register是一个队列,所有注册的自动加载类都会成为该队列中的一个成员。
  • 在实例化一个未被调用的对象时,会首先遍历注册队列,直到找到对应文件,此时再将相应文件包含进来。
  • 简单一点理解,在一个cms框架中,可以将类的自动加载作为一个处理节点,其他地方的所有类的实例化全部通过该节点来处理,实现了代码的高度重用。剩下需要考虑的所有问题,只剩下规范命名空间了,可以让开发者只专注与开发组件、模块及视图。

2. 自己的一点猜想:

  • 通过上面的分析,我们可以从该核心模块迅速的理清整个cms类之间调用的过程,也就是其运行的整体思路。
  • 只要清楚了整体运行思路,剩下的只需要关注代码部分即可,类调用部分,关注命名空间即可。
  • 将命名空间作为cms内部的别名,通过命名空间来进行回溯,极大的节省了时间。
php
最后由Lucifaer修改于2017-06-01 00:49
发表新评论
博客已萌萌哒运行
© 2017 由 Typecho 强力驱动.Theme by YoduLucifaer,文艺型小青年 才不是死宅.....哼 爱好:听歌、买书、咖啡 欢迎各大组织收留
PREVIOUS NEXT
雷姆
拉姆