龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > web编程 > php编程 >

php类自动加载

时间:2014-07-22 14:52来源: 作者: 点击:
分享到:
span style=border-collapse:separate;color:#000000;font-family:AR PL UKai CN;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:normal;orphans:2;text-align:-webkit-auto;text-indent:0px;text-transform:no

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;
    }
}
精彩图集

赞助商链接