PHP自动加载是一个很有用的技巧,我们应该在项目中尽量使用autoload来减少维护类加载的工作。
使用__autoload 在使用PHP的项目中,如何实现自动加载对于新人总是一个很疑惑的问题,一般写过的一些PHP,或者用过一些框架的同学都知道通过实现__autoload()函数来实现简单的自动加载:
function __autoload($classname) { $filename = "./". $classname .".php"; include_once($filename); }
不过这样的方式缺点也能明显就是只能定义一种加载策略,或者把这个函数实现的很复杂来实现多样的自动加载策略。
使用spl_autoload_register 现在推荐使用的是spl_autoload_register()
来注册自动加载器,它可以用来注册多个自动加载策略。它的函数签名是:
bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
一般来说,我们只关注第一个参数,他是一个callable类型的function,一般简单的传入一个函数名称的字符串,也可以传入类名和方法名的数组,也可以传入一个匿名函数(PHP 5.3之后)。
function my_autoloader($class) { include 'classes/' . $class . '.class.php'; } spl_autoload_register('my_autoloader'); // Or, using an anonymous function as of PHP 5.3.0 spl_autoload_register(function ($class) { include 'classes/' . $class . '.class.php'; });
在PHP 5.3 引入命名空间后,我们还可以使用一种更方便的自动加载策略
spl_autoload_extensions(".php"); // comma-separated list spl_autoload_register(); // 等价于 function my_autoload ($pClassName) { include(__DIR__ . "/" . $pClassName . ".php"); } spl_autoload_register("my_autoload");
据说第一种可以效率更高。
使用SplClassLoader SplClassLoader是Jonathan H. Wage,Roman S. Borschel等人开发的一个用于PHP 5.3根据命名空间和类名实现自动加载的类。使用比较方便,这里推荐使用。其实现并不复杂使用一个类,源码如下(去掉了注释):
class SplClassLoader { private $_fileExtension = '.php'; private $_namespace; private $_includePath; private $_namespaceSeparator = '\\'; public function __construct($ns = null, $includePath = null) { $this->_namespace = $ns; $this->_includePath = $includePath; } public function setNamespaceSeparator($sep) { $this->_namespaceSeparator = $sep; } public function getNamespaceSeparator() { return $this->_namespaceSeparator; } public function setIncludePath($includePath) { $this->_includePath = $includePath; } public function getIncludePath() { return $this->_includePath; } public function setFileExtension($fileExtension) { $this->_fileExtension = $fileExtension; } public function getFileExtension() { return $this->_fileExtension; } public function register() { spl_autoload_register(array($this, 'loadClass')); } public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); } public function loadClass($className) { if (null === $this->_namespace || $this->_namespace.$this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace.$this->_namespaceSeparator))) { $fileName = ''; $namespace = ''; if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) { $namespace = substr($className, 0, $lastNsPos); $className = substr($className, $lastNsPos + 1); $fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; } $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension; // @add wuxu 验证文件是否存在,要不可能会抛出异常 $fileName = ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName; if (file_exists($fileName)) require $fileName; } } }
完整的代码见 GitHub Gist 。 (还有一些在此基础上的修改版本,比如:https://gist.github.com/jwage/221634)
它的使用方式如下:
// Example which loads classes for the Doctrine Common package in the // Doctrine\Common namespace. $classLoader = new SplClassLoader('Doctrine\Common', '/path/to/doctrine'); $classLoader->register();
这是一个实现使用命名空间的PHP项目的自动加载的方式(PSR-0),使用上面的注册代码,可以注册一个Doctrine\Common的namespace,它对应的根目录是/path/to/doctrine。
总结 这样对于实现一个使用命名空间的项目非常重要。 接下来会写一篇如何在一个项目中使用namespace(PSR-0)来使项目的结构更加科学。