怎样优化autoload

1. 概述

php的autoload机制就不再累述了,autoload机制确实能给开发带来便利,但是实现不好的话会很影响性能,比如:

function __autoload($class_name) {  
        include_once $class_name . '.php';  
}  
这是最简单的autoload实现函数,只要求类名和文件名保持一致,这实现起来很简单,但性能却不是很好,因为每次触发autoload函数会去遍历 include_path指定的文件夹查找匹配的文件。优化方案很简单,将类和文件地址的映射关系事先保存在一个文件里,autoload只需要在这个文件里根据类名查找到相应的文件地址,然后include文件,这样不用反复的去遍历文件夹,性能能得到一定的提升。下面来看看怎样实现。

2. 生成映射文件

在项目运行起来之前需要生成映射文件,代码如下:

class AssemblyBuilder                                             
{                                                                 
    private static $_skipFolders = array('.','..','.git');        
    private static $_classes = array();                           
    private static $_autoload_clspath = "_autoload_clspath";      
                                                                  
    public static function run($path)                             
    {                                                             
        $contents = "";                                           
        self::scanFolder($path);                                  
        if(!empty(self::$_classes))                               
        {                                                         
            foreach(self::$_classes as $k => $v)                  
            {                                                     
                $contents = $contents."$k,$v\n";                  
            }                                                     
            file_put_contents(self::$_autoload_clspath,$contents);
        }                                                         
    }                                                             
                                                                  
    public static function getAutoloadClspath()                   
    {                                                             
        return self::$_autoload_clspath;                          
    }
    
    public static function scanFolder($path)                                         
    {                                                                                
        $dirs = scandir($path);                                                      
        if(is_array($dirs) && !empty($dirs))                                         
        {                                                                            
            foreach($dirs as $dir)                                                   
            {                                                                        
                $fullPath = $path.'/'.$dir;                                          
                if(is_dir($fullPath))                                                
                {                                                                    
                    if(self::isSkipFolder($dir))                                     
                        continue;                                                    
                    self::scanFolder($fullPath);                                     
                }                                                                    
                elseif(is_file($fullPath))                                           
                {                                                                    
                    self::scanFile($fullPath);                                       
                }                                                                    
            }                                                                        
        }                                                                            
    }                                                                                
                                                                                 
    public static function scanFile($file)
    {
        $lastPointPos = strrpos($file, ".");                                     
        if($lastPointPos)                                                           
        {                                                                            
            $suffix = substr($file, $lastPointPos+1, strlen($file) - $lastPointPos); 
            if($suffix == 'php')
            {                                                                        
                self::extractClassName($file);                                       
            }
        }                                                                            
    }

    public static function isSkipFolder($dir)                                           
    {                                                                                   
         return in_array($dir,self::$_skipFolders);                                      
    }                                                                                   
                                                                                         
    public static function extractClassName($file)                                      
    {                                                                                   
         $content = file($file);                                                         
         foreach($content as $line)                                                      
         {                                                                               
             if(preg_match('/^\s*(abstract\s*)?(interface|class)\s*(\S+)/',$line,$match))
             {                                                                           
                 $className = $match[3];                                                 
                 self::$_classes[$className] = $file;                                    
             }
         }                                                                               
    }
 }
$path = getcwd();           
AssemblyBuilder::run($path);
装配器中的三个私有变量,skipFolders数组配置了在遍历文件夹时需要跳过的文件夹,classes数组保持了类名和文件的映射关系,autoload_clspath配置了映射文件名。

每次修改了类名或者添加了新类都需要重新运行上面的脚本以更新映射文件。

3. autoload实现

有了映射文件就可以根据类名来查找相应的文件,然后require文件,实现代码如下:

function __autoload($name)                                                     
{                                                                              
    $lines = file(dirname(__FILE__).'/'.AssemblyBuilder::getAutoloadClspath());
    if(is_array($lines)&&!empty($lines))                                       
        foreach($lines as $line)                                               
        {
            $arr = explode(',',$line);                                         
            $path = trim($arr[1]);
            if($arr[0] == $name)
            {
                require_once($path);                                           
                break;
            }   
        }   
}       
还可以优化,加一个全局缓存,这样第一次加载一个类时,查找到对应的文件,将文件名放入缓存,这样下次加载同一个类时可以从缓存里读取文件名,不同再遍历映射文件了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值