php的__autoload魔术方法和spl_autoload_register()可以做到在不手动引入类文件的情况下自动加载类。
- 魔术方法__autoload()
__autoload()方法的用法如下:
- 自动类加载注册函数 spl_autoload_register
使用这个函数的时候,他会在内部创建一个SPL __autoload 函数队列,并将注册的 autoload_function放到这个函数队列中。
一旦调用未引入的类文件的时候,系统会按照顺序遍历执行这个队列中的所有函数直到成功引入这个类文件。
· autoload_function
这是一个函数【方法】名称,可以是字符串或者数组(调用类方法使用)。这个函数(方法)要做的就是,把要用到的类文件包含include(requeire)进来。
· throw
此参数设置了 autoload_function 无法成功注册时, spl_autoload_register()是否抛出异常。
· prepend
如果是 true,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部。
我们用 spl_autoload_register() 来重新实现以下刚刚的程序:
__autoload 的缺点是无法定义多个自动类加载方法而sql_autoload_register可以。
例如,我在一个入口文件想用自动加载类的功能去加载位于两个目录下的两个类文件 /a/a.php 和 /b/b.php 而且 我不知道a.php是位于a目录,b.php位于b目录。此时__autoload就要写一系列的if…else…去判断这位于哪个目录,如果类不存在a这个目录就尝试去b目录找。
而如果使用sql_autoload_register可以直接定义两个自定义的加载类方法,一个去include a目录,一个去include b目录。
- psr4规范
上述例子还不是 spl_autoload_register 的正确使用方式。应该结合psr4规范来进行类的自动引入。
PSR4是一种自动加载规范,老版本是PSR0,尽管thinkPHP支持PSR4和PSR0的自动加载方式,但是默认也是优先进行PSR4加载,如果失败,再进行PSR0的加载。
PSR4规范要求一个完整的类名应该具有完整的命名空间,这些命名空间和该类所在的目录路径对应。
具体要求如下:
a、使用一个类时(如new或者用一个类调用静态方法),需要使用完整的类名。完整的类名必须有一个根命名空间,可以有多个子命名空间 如
new \Vendor\Cake\Name\ClassName() 是对的;
new ClassName()是错的;
use \Vendor\Cake\Name\ClassName 再 new ClassName()是对的。
b、完整的类名最右边必须有一个类名,类名的下划线没有特殊含义,类名 必须 与对应的以 .php 为后缀的文件同名;
c、自动加载器(autoloader)的实现 一定不可抛出异常、一定不可触发任一级别的错误信息以及 不应该有返回值。
PSR-4和PSR-0最大的区别是对下划线(underscore)的定义不同。PSR-4中,在类名中使用下划线没有任何特殊含义。而PSR-0则规定类名中的下划线_会被转化成目录分隔符。
下面看看结合了 psr4 的 自动加载器(类)如何实现:
index.php 入口文件
主要逻辑有2点:
1、预定义所有允许自动加载的顶级命名空间与其基本目录的映射;
2、使用 sql_autoload_register()注册autoload方法。当外界使用某个类的时候,autoload方法根据该类(连带命名空间)的顶级命名空间找到对应的基本目录,根据该类的子命名空间找到类的路径。
特别注意:
开发者A在TP5项目中安装了一个composer的类M(例如是/vendor/phpmailer这个目录),开发者B在自己的本地TP5项目中直接拷贝类M到自己的相应目录中,结果运行失败,没有加载到类M。
运行失败是因为,B没有在/vendor/composer/autoload_static.php 这个自动加载类的顶级命名空间容器中添加 phpmailer这个目录路径与其顶级命名空间的映射。此时我们可以手动加上或者不用手动复制phpmailer目录,而是使用composer命令安装,他会自动在autoload_static.php做修改加上这个映射。