PHP命名空间namespace使用之spl_autoload
在github上很多项目都不会告诉你如何include或require他们的项目,有的项目会提供一个autoload.php文件直接引用即可(如Predis),有的项目什么也不会给,甚至在README中都不会给出require的代码(如Gaufrette)。毕竟国外的程序员都已经习以为常,使用autoload来包含项目非常容易。
下面,以自己写的一个小项目为例循序渐进讲解一下如果autoload项目文件。
项目地址:https://github.com/shuiguang/IpWatch
主要项目文件介绍:
IpWatch.class.php:类文件,IpWatch是主要用到的类。
FileCache.class.php:类文件,FileCache在IpWatch内部实例化。
RedisCache.class.php:类文件,RedisCache在IpWatch内部实例化。
其余的后面再说,传统方式使用只需要引用这三个文件即可使用本项目的所有功能,代码如下。
传统:
<?php include 'IpWatch.class.php'; include 'FileCache.class.php'; include 'RedisCache.class.php'; use Shuiguang\IpWatch; use Shuiguang\FileCache; use Shuiguang\RedisCache; $file_config = array( 'interval' => 60, //最后一次请求到现在的间隔时间(s),如果大于此值则清除统计缓存 'mode' => 'file', //可选file(文件记录),redis(需安装redis扩展) 'type' => 'time', //可选number(数值记录),time(访问时间记录) 'file_dir' => 'ip', //缓存文件路径,建议放在/tmp目录下或定时清除 'prefix' => '', //缓存文件名前缀 'suffix' => '.ip', //缓存文件名后缀 ); $ipWatcher = Shuiguang\IpWatch::getInstance($file_config); //允许单个IP单位时间内最大访问次数 $max_request = 10; //获取当前IP访问次数 $current = $ipWatcher->get(); //超过这个次数则退出脚本 if($current >= $max_request) { die($current); }else{ $add = $ipWatcher->incr(); } echo 'ok, go on';
使用传统的include或require方式:当项目文件数较小的时候,多处逻辑代码引用了这3个文件,如果后来该项目增加了其他很多的类文件,需要修改所有的逻辑代码,非常不便。
如果使用__autoload来定义include规则自动include类文件将会非常轻松。
进阶:
<?php //自定义自动加载函数 function autoload($classname) { $dir = './'; if(is_file($dir.'/'.$classname.'.class.php')) { include $dir.'/'.$classname.'.class.php'; }else{ throw new Exception($dir.'/'.$classname.'.class.php not exists'); } } //注册自动加载函数 spl_autoload_register('autoload'); use Shuiguang\IpWatch; use Shuiguang\FileCache; use Shuiguang\RedisCache; $file_config = array( 'interval' => 60, //最后一次请求到现在的间隔时间(s),如果大于此值则清除统计缓存 'mode' => 'file', //可选file(文件记录),redis(需安装redis扩展) 'type' => 'time', //可选number(数值记录),time(访问时间记录) 'file_dir' => 'ip', //缓存文件路径,建议放在/tmp目录下或定时清除 'prefix' => '', //缓存文件名前缀 'suffix' => '.ip', //缓存文件名后缀 ); $ipWatcher = Shuiguang\IpWatch::getInstance($file_config); //允许单个IP单位时间内最大访问次数 $max_request = 10; //获取当前IP访问次数 $current = $ipWatcher->get(); //超过这个次数则退出脚本 if($current >= $max_request) { die($current); }else{ $add = $ipWatcher->incr(); } echo 'ok, go on';
然而这样的方式并不是非常合适,一方面autoload函数将会作用于全局这样并不好,另一方面autoload函数中的.class.php文件路径是固定的,而且无法使用命名空间、子空间的优势。
从Predis项目中可以找到一个这样的类:Autoloader.php。(本项目中已经将类文件重命名为Autoloader.class.php)。
优化:
<?php /* * This file is part of IpWatch. * * Licensed under The MIT License * For full copyright and license information, please see the MIT-LICENSE.txt * Redistributions of files must retain the above copyright notice. * IpWatch for php * @author shuiguang * @link https://github.com/shuiguang/IpWatch * @license http://www.opensource.org/licenses/mit-license.php MIT License */ namespace Shuiguang; class Autoloader { private $directory; private $prefix; private $prefixLength; /** * @param string $baseDirectory Base directory where the source files are located. */ public function __construct($baseDirectory = __DIR__) { $this->directory = $baseDirectory; $this->prefix = __NAMESPACE__ . '\\'; $this->prefixLength = strlen($this->prefix); } /** * Registers the autoloader class with the PHP SPL autoloader. * * @param bool $prepend Prepend the autoloader on the stack instead of appending it. */ public static function register($prepend = false) { spl_autoload_register(array(new self, 'autoload'), true, $prepend); } /** * Loads a class from a file using its fully qualified name. * * @param string $className Fully qualified name of a class. */ public function autoload($className) { if (0 === strpos($className, $this->prefix)) { $parts = explode('\\', substr($className, $this->prefixLength)); $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.class.php'; if (is_file($filepath)) { require($filepath); } } } }
该类中主要使用了上一篇文章《PHP命名空间namespace使用》中介绍的__NAMESPACE__自动获取命名空间(文件夹名)和子命名空间(子文件夹名),然后通过autoload.php文件中的Shuiguang\Autoloader::register();进行注册。原理大致相同,但是实现方式非常优雅,所有的逻辑程序文件可引入autoload.php后直接使用即可,代码如下。
<?php /* * This file is part of IpWatch. * * Licensed under The MIT License * For full copyright and license information, please see the MIT-LICENSE.txt * Redistributions of files must retain the above copyright notice. * IpWatch for php * @author shuiguang * @link https://github.com/shuiguang/IpWatch * @license http://www.opensource.org/licenses/mit-license.php MIT License */ require __DIR__ . '/src/Shuiguang/Autoloader.class.php'; Shuiguang\Autoloader::register();
逻辑代码如下:
include 'autoload.php'; use代码 调用代码
如果以后需要添加类文件,放在src/Shuiguang下然后use即可,无需手动导入。
案例:
从github上下载最新版的Gaufrette,官方未给出任何自动加载的教程,需要我们自己实现。
解压之后,项目文件存在于Gaufrette-master\src\Gaufrette目录下,将predis中的Autoloader.php文件复制到Gaufrette-master\src\Gaufrette目录下,并且修改为namespace Gaufrette;
<?php namespace Gaufrette; /** * Implements a lightweight PSR-0 compliant autoloader for Predis. * * @author Eric Naeseth <eric@thumbtack.com> * @author Daniele Alessandri <suppakilla@gmail.com> */ class Autoloader { private $directory; private $prefix; private $prefixLength; /** * @param string $baseDirectory Base directory where the source files are located. */ public function __construct($baseDirectory = __DIR__) { $this->directory = $baseDirectory; $this->prefix = __NAMESPACE__.'\\'; $this->prefixLength = strlen($this->prefix); } /** * Registers the autoloader class with the PHP SPL autoloader. * * @param bool $prepend Prepend the autoloader on the stack instead of appending it. */ public static function register($prepend = false) { spl_autoload_register(array(new self(), 'autoload'), true, $prepend); } /** * Loads a class from a file using its fully qualified name. * * @param string $className Fully qualified name of a class. */ public function autoload($className) { if (0 === strpos($className, $this->prefix)) { $parts = explode('\\', substr($className, $this->prefixLength)); $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php'; if (is_file($filepath)) { require $filepath; } } } }
然后到Gaufrette-master目录建立一个autoload.php文件,该文件位置可任意放置,只需要保证require的路径正确即可。
<?php require __DIR__ . '/src/Gaufrette/Autoloader.php'; Gaufrette\Autoloader::register();
最后,到逻辑代码处引用这个autoload.php文件即可,例如。
<?php include 'autoload.php'; use Gaufrette\Filesystem; use Gaufrette\Adapter\Local as LocalAdapter; $adapter = new LocalAdapter(__DIR__); $filesystem = new Filesystem($adapter); $content = 'Hello I am the new content'; $filesystem->write('myFile.txt', $content);
完成引入该项目。
PS:外国人不喜欢使用.class.php作为类文件的后缀,通常直接使用.php文件名,其关键代码在Autoloader.php文件的这一行,怎么选取决于自己的习惯:
$filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
案例下载:
教程链接:http://blog.zhengshuiguang.com/php/spl_autoload.html
随意转载~但请保留教程地址★
评论已关闭