php类自动加载
rare内置了类自动装载功能,当使用一个类的使用直接使用而无须require(include) 类文件。
该类自动装载功能非常的独立,若你需要,可以直接在其他框架(任意php程序)中使用。
1.先引入 rareAutoLoad.class.php
2.注册功能
$option=array( 'dirs' => '/www/phplib/', //class 从那些目录中查找,多个目录使用,分割 'cache' => '/tmp/111111.php', //class path 缓存文件 'suffix' => '.class.php' //需要类自动装载的php类文件的后缀 ); rareAutoLoad::register($option);
为了提高效率,对class信息进行了缓存(类名=>路径),以保证只会扫描目录一遍。当加载时发现是一个新类名时,会尝试在指定位置重新扫描以加载该类,若类不存在也记录到缓存文件中。
如此,以让加载类和 class_exists($class_name,true)的效率达到最优。
<?php
/**
* 类自动装载
* http://raremvc.googlecode.com
* http://rare.hongtao3.com
* @example
* include 'rareAutoLoad.php';
* $option=array('dirs'=>'/www/lib/share/,/www/lib/api/',//class 从那些目录中查找
* 'cache'=>'/tmp/111111.php',//class path 缓存文件
* ‘suffix’=>'.class.php' //需要类自动装载的php类文件的后缀
* "hand"=>true, //是否手动更新class 路径文件 ,为false 时 缓存文件写入到指定的cache文件中去,
* //为true 是需要手动允许 autoLoad.php 文件
* );
* rareAutoLoad::register($option);
*
* 参考了symfony 的类自动装载
* 为了提供效率,将类的位置保存到缓存文件中,在第一次使用的时候会对dirs中的文件目录进行扫描
* 需要自动装载的类的文件命名 要求 必须 以 .class.php 结束,如文件名为 a.class.php 中定义的类可以被扫描到而 a.php的文件会忽略掉
* 类名 和 文件命名 可以没有关系 如 a.class.php 文件中 可以定义 class b{}
*
* @author duwei<duv123@gmail.com>
*
*/
class rareAutoLoad
{
private static $instance=null;
private static $registered=false;
private $cacheFile=null;
private $classes=array();//对应class 类名 和对应文件路径
private $option;
private $hand=false;//是否手动运行该脚本进行class路径扫描,
private $reloadCount=0;//reload操作的次数
/**
* @param array $option 需要参数 dirs:扫描目录 cache:缓存文件
*/
public function __construct($option){
if(!isset($option['suffix'])) $option['suffix']=".class.php";//文件后缀
$this->option=$option;
if(!isset($option['cache'])){
$trac=debug_backtrace(false);
$calFile=$trac[2]['file'];
$option['cache']="/tmp/rareautoLoad_".md5($calFile)."_".filemtime($calFile);
@unlink($option['cache']);
}
if(isset($option['hand']))$this->hand=(boolean)$option['hand'];
$this->cacheFile=$option['cache'].".php";
$this->getClasses();
}
/**
* 获取DAutoLoad 的单实例对象
* @param array $option
* @return DAutoLoad
*/
private static function getInstance($option){
if (!isset(self::$instance)){
self::$instance = new rareAutoLoad($option);
}
return self::$instance;
}
/**
* 注册自动装载
* @param array $option array('dirs'=>'/www/lib/share/,/www/lib/api/','cache'=>'/tmp/111111.php');
* @throws Exception
*/
public static function register($option) {
if (self::$registered)return;
// ini_set('unserialize_callback_func', 'spl_autoload_call');
if (false === spl_autoload_register(array(self::getInstance($option), 'autoload'))){
die(sprintf('Unable to register %s::autoload as an autoloading method.', get_class(self::getInstance())));
}
self::$registered = true;
}
/**
* spl_autoload_call 调用 load class
* 若缓存文件中的类的路径不正确,会尝试reload一次
* 对reload后还不存在的类 缓存中记录其key,标记为 false,以避免缓存文件多次无效的更新
* 对于使用 class_exists 进行判断时默认会进行autoload操作
* @param $class
* @return
*/
public function autoload($class){
if(class_exists($class, false) || interface_exists($class, false)) return true;
if ($this->classes && isset($this->classes[$class]) ){
$file=$this->classes[$class];
if(!$file)return false;
if(!file_exists($file) && !$this->hand){
$this->reload();
return $this->autoload($class);
}
require($file);
return true;
}{
$this->reload();
if(isset($this->classes[$class])){
$file=$this->classes[$class];
if(!$file)return false;
require($file);
return true;
}else{
$this->classes[$class]=false;
$this->saveCache();
}
}
return false;
}
/**
* 获取类名列表
* @return
*/
private function getClasses(){
if(file_exists($this->cacheFile)){
$this->classes=require($this->cacheFile);
if(is_array($this->classes))return true;
}
$this->classes=array();
$this->reload();
}
/**
* 重新扫描一次
* 并将类名的位置信息保存到cache 中
* @return
*/
private function reload(){
$this->reloadCount++;
if($this->hand)return;
$cachedir=dirname($this->cacheFile);
$this->directory($cachedir);
if(!is_writable($cachedir)) die('can not write cache!');
settype($this->classes, 'array');
$dirs=$this->option['dirs'];
if(!is_array($dirs)) $dirs=explode(",", $dirs);
$dirs=array_unique($dirs);
foreach($dirs as $dir){
if(!$dir || !file_exists($dir))continue;
$this->scanDir($dir);
}
$this->saveCache();
}
private function saveCache(){
if($this->hand)return;
$phpData="<?php\n/**\n*autoLoadCache\n*@since ".date('Y-m-d H:i:s')."\n*/\n";
if(!is_array($this->classes))$this->classes=array();
ksort($this->classes);
$phpData.="return ".var_export($this->classes,true).";";
file_put_contents($this->cacheFile, $phpData,LOCK_EX);
clearstatcache();
}
/**
* 扫描文件夹以及文件
* 只有 $this->option['suffix'] 命名的文件才会被扫描到
* @param $dir
* @return
*/
private function scanDir($dir){
$files=scandir($dir,1);
foreach($files as $fileName){
$file=rtrim($dir,DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$fileName;
if(is_dir($file) && strpos($fileName,'.')!==0){
$this->scanDir($file);
}else{
if($this->str_endWith($fileName,$this->option['suffix'])){
preg_match_all('~^\s*(?:abstract\s+|final\s+)?(?:class|interface)\s+(\w+)~mi', file_get_contents($file), $classes);
foreach ($classes[1] as $class){
$this->classes[$class] = $file;
}
}
}
}
}
private function directory($dir){
return is_dir($dir) or ($this->directory(dirname($dir)) and mkdir($dir, 0777));
}
function str_endWith($str,$subStr){
return substr($str, -(strlen($subStr)))==$subStr;
}
}
- 上一篇:PHP手写日历代码
- 下一篇:动态 bindVars 的用法






