发布日期:2013-02-20 09:59 来源:佚名 标签: 加载
 

前些日子研究了zendFramework 框架,想在项目中用其个别组件而不是全部框架,仅拿其当个类库用而已。


可是看到网上的某些用法感觉如果用zf中的类,可能牵扯到类加载问题,因为zf中的类多不是孤立的经常互相依赖,所以如果用到一个类,那么这个类依赖的类文件路径也应该被include或require进来(关于include或是require的用法请参考文档,个人推荐:php类用require/require_once,这样语义也明确,其他作为内嵌的东西用include/include_once,按效率来讲不带_once的方法高些————涉及预扫描问题,但带_once的用法会安全些,可被多次引用而不会导致程序出错,至于选取问题自己斟酌!)


自动加载核心技术是给一个类如何找到声明该类的文件而已 然后include_once 进来,这里要借助__autoload方法或spl_autoload方法,后者更好些,并且spl_autoload可以注册自己的加载函数,加载函数实际跟__autoload魔术方法做的事情是一样的,但可以注册多个,这样在某个加载函数失效时会调用另一个,这些函数一起形成一个调用栈,失败时会依次调用,如果文件中同时出现了(或者由于被包含的原因--而同时出现在一个容器文件中)__autoload和spl_autoloadXXX的用法前者会失效。

spl自动加载机制:是在找不到类(从include_path路径或所有的require/include路径中)时,会给程序开发者一个钩子(机会),让开发者来自己根据类名来查找对应的类所在文件,其实java中也有classLoader.而且可以有多个机会,这些钩子函数会形成一个调用栈,依次调用他们来查找类文件,在注册时会有一个布尔选项,表示如果未找到是否抛出异常,我们一般不希望抛异常,除非是最后一个加入的类加载函数,这样便于沿栈继续寻找(一抛异常程序就停止了!!)。还有spl一般提供了一个默认的自动加载类如果没有提供自己的加载器可用用这个默认的就行,并且他可以指定各种类文件后缀一般可以满足不变态的文件命名处理————类名一定是文件名的首部,至于后缀是什么一般不要求。以下看我的代码就明白了

关于从类名推导其所在文件的方法:这里一般类名所在文件通常跟类名有某种关联,比如类名跟文件同名但文件会多出个.php后缀,或者.class.php或者.inc.class.php或者其他风格,总之尽量符合大家默认的风格,不要标新立异,这样便于自动加载你的类。如果项目小不会觉得自动加载的好处,中大型时显得尤为重要,并且可能同时用到多个类库的类,我想你不会无聊到追踪每一个引用类依赖了其他那个文件夹下的类,并依次将其文件保含进主容器文件吧!!!这里要遵循最少惊奇原则,不要给你的类起一个怪异的名字,这在某些时候会付出代价的!!

谈谈ZendFramework类:

ZF类名有严格的要求,通常类名需要包含其相对于Zend根目录的所有路径最后才是有效的类名,这样在使用时当然不便,但对于自动加载来说,实现很简单,只需要把Zend目录所在的文件夹路径加入到include_path中,然后自定义一个autoload自动加载函数,把类名中下划线替换为文件夹分隔符即可以找到正确的文件。其实这种命名风格已被许多php开源项目所采用(即使他们不准备用ZF框架)。

ZF本身有一些自动加载的实现,也可以看看其源码,估计一般没人去看源码,比如初学者,东南西北都分不清!那个加载类还有名空间的支持,是更复杂的东东,我也就大概瞄了一下下

下面给出我的类,这个类已经用在项目中了,而且工作正常!以前有一篇关于自动加载的,那篇是给一个文件夹递归搜索其子文件夹,并将之包含在include_path类路径中,但发现如果文件目录过于多的话并不是太合适,所以就又写了一个类:

<?php

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

/**
* Description of MyAutoLoader
*
* @author Administrator
*/
class MyAutoLoader {

private static $includeDirArray = '';

/**
   * 从以下目录中查找类
   * @param mixed $dirs 目录数组 或者单个目录 路径必须是相对于当前php文件的
   * @param boolean $isZFClass 是否是ZendFramework类库 如果是 就要先
   */
public static function loadFromDir($dirs,$supportZFClass=false) {
    if(is_string($dirs) && is_dir($dirs)){
      self::$includeDirArray = array($dirs);
    }else if(is_array ($dirs)){
      foreach($dirs as $dir){
        if(!is_dir($dir)){
          throw new InvalidArgumentException("not a dir!");
        }
      }
      self::$includeDirArray = $dirs;
    }else{
      //忽略传递的参数
    }
    /**
     * 将所有目录添加到includePath中
     */
     self::setIncludePath();

    /**
     * spl_autoload_register 参一是加到自动加载栈中的函数 参二是是否抛异常
     * 如果不给参一 那么会用默认的那个加载函数spl_autoload()
     */
    spl_autoload_register();
    // spl_autoload_register(null, false); //这个 估计内部可能跟上面的等效
    spl_autoload_extensions('.php,.inc,.class,.interface,.class.php');

    /**
     * 如果在一个脚本中同时存在__autoload函数定义 那么如果不把他加到spl调用栈上那么他就被忽略了
     * 所以下面的方法是稳妥的
     * spl_autoload_register
     */
    /**
     * 在栈未初始化时在做这个 或者还没有注册函数时
     */
    if (false === spl_autoload_functions()) {
      if (function_exists('__autoload')) {
        spl_autoload_register('__autoload', false);
      }
    }

    //spl_autoload_register(__CLASS__.'::style1Loader', false);
    //spl_autoload_register(__CLASS__.'::style2Loader', false);
    //是否支持zend风格的类加载
    if($supportZFClass){
      spl_autoload_register(__CLASS__.'::Style4ZendFrameworkLoader', true);
    }
}

/**
   * 从指定的目录中查找类,如果传人一个bool值表示支持zend类
   * 加载
   */
public static function loadFrom() {
        $args=func_get_args();
        $argNum = func_num_args();
        //echo $argNum;
    // $argCount=func_num_args();
    // $nthArg=func_get_arg(0);
    /*
     * 测试用
     print_r($args);
     echo "参数数目:".$argCount;
     echo "<br>第一个参数是:".$nthArg;
     *
     */
        $includedDirs ;
        $supportZendFramework = false;
      if($argNum >0){
          foreach($args as $arg){
             if(is_dir($arg)){
               $includedDirs[] = $arg;
             }
             if(is_bool($arg)){
               $supportZendFramework = true;
              // echo "支持zf类";
             }
          }
          self::loadFromDir($includedDirs, $supportZendFramework);
        }else{
           //忽略参数;
          // echo "忽略参数";
          throw new Exception('need args!');
        }
       // print_r($includedDirs);
        unset ($includedDirs);
}

private static function setIncludePath() {
     set_include_path(get_include_path() . PATH_SEPARATOR . implode(PATH_SEPARATOR, self::$includeDirArray));
}

private static function style1Loader($class) {
    require_once 'class.' . $class . ".php";
}

private static function style2Loader($class) {
    require_once 'inc.' . $class . '.php.class';
}

/**
   * ZENDFramework风格的类名, 先把includePath设置一下
   * @param <type> $class
   */
private static function Style4ZendFrameworkLoader($class) {
    $classPath = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
    require_once $classPath;
}

/**
   * 支持zend类加载,只需要把zend根目录加到includepath中即可
   * 或者用上面的loadFrom做
   */
public static function supportZendClass() {
    spl_autoload_register(__CLASS__.'::Style4ZendFrameworkLoader', true);
}
}

?>

这个类的用法如:

require 'lib/MyAutoLoader.php';
MyAutoLoader::loadFromDir(array('lib', 'lib/logic','lib/dao'));

下面可以放心用这几个文件夹下的类了 如果需要支持ZendFramework类 那么把ZF库所在的目录添加进来即可

 

相关评论

专题信息
    PHP是目前最热门的Web开发语言,它简单高效、开源免费、跨平台等特性受到广大Web开发人员的欢迎,从1994年诞生至今已被2000多万个网站采用。PHP独特的语法混合了C、Java、Perl以及PHP自创新的语法。本教程从PHP是什么,PHP环境搭建,PHP基础知识,PHP文件,PHP数据库,PHP实例等知识点了解PHP这门语言。