zoukankan      html  css  js  c++  java
  • yii源码分析4——非核心类的导入注册

    转载请注明: TheViper http://www.cnblogs.com/TheViper 

    yii源码分析1中说到spl_autoload_register注册给定的函数作为 __autoload 的实现,在这里是autoload().

    public static function autoload($className) {
            include self::$_coreClasses [$className];
         }

    实际上这个autoload()是没有考虑非核心文件的引入的。比如,在app文件夹经常会有自定义的一些重要文件夹,比如'application.utils.*(工具类),'application.filters.*'(过滤类),'application.validators.*'(校验类)等。

    在实际用的时候,是不用一个一个include的,直接new就可以了,yii已经帮我们做了include的工作。而这个工作就是在autoload()里面做的。

    上面的代码很显然没有考虑非核心文件的引入,这是我的疏忽。

    那yii是怎么帮我们引入非核心文件的?

    这要从CApplication说起。

    abstract class CApplication extends CModule {
        public function __construct($config = null) {
            if (is_string ( $config ))
                $config = require ($config);
            Yii::setApplication ( $this );//保存整个app实例
            if (isset ( $config ['basePath'] )) {
                $this->setBasePath ( $config ['basePath'] );
                unset ( $config ['basePath'] );
            } else
                $this->setBasePath ( 'protected' );
            //设置别名,后面就可以用application表示basePath了
            Yii::setPathOfAlias ( 'application', $this->getBasePath () );
            //钩子,模块 预 初始化时执行,子类实现。不过这时,配置还没有写入框架
            $this->preinit ();
            $this->registerCoreComponents ();
            //父类实现
            $this->configure ( $config );
            //加载静态应用组件
            $this->preloadComponents ();
            //这才开始初始化模块
            $this->init ();
        }

    注意到里面的$this->configure ( $config );,$config是传入的配置文件,是一个数组,非核心文件的定义就是在这里面,比如引入工具类文件夹

    <?php
    return array (
        'basePath' => dirname ( __FILE__ ) . DIRECTORY_SEPARATOR . '..',
        'import' => array (
            'application.utils.*'
        )
        );
    ?> 

    然后在父类CModule

        public function configure($config) {
            if (is_array ( $config )) {
                foreach ( $config as $key => $value )
                    $this->$key = $value;
            }
        }

    这里yii很"狡猾",它在CModule的父类CComponent中重写了__set()

        public function __set($name,$value)
        {
            $setter='set'.$name;
            if(method_exists($this,$setter))
                return $this->$setter($value);
            else....
        }

    可以看到,如果CModule中如果有设置yii指定参数(比如import)的方法,就会调用它,而我之前裁剪的时候,把CModule中的setImport()删掉了。

    另外可以看到basePath, params, modules, import, components 是yii保留的参数名。

        public function setImport($aliases)
        {
            foreach($aliases as $alias)
                Yii::import($alias);
        }

    然后是YiiBase里面的import()

        public static function import($alias, $forceInclude = false) {
            if (isset ( self::$_imports [$alias] )) //是否已经存在路径
                return self::$_imports [$alias];
            
            if (class_exists ( $alias, false ) || interface_exists ( $alias, false ))//类是否已经定义,针对如urlManager这样的已定义于$_coreClasses[]的类
                return self::$_imports [$alias] = $alias;
            if (($pos = strrpos ( $alias, '.' )) === false)         //直接是文件名
            {
                // try to autoload the class with an autoloader if $forceInclude is true
                if ($forceInclude && (Yii::autoload ( $alias, true ) || class_exists ( $alias, true )))
                    self::$_imports [$alias] = $alias;
                return $alias;
            }
            
            $className = ( string ) substr ( $alias, $pos + 1 );
            $isClass = $className !== '*';
            //是否为路径+类名
            if ($isClass && (class_exists ( $className, false ) || interface_exists ( $className, false )))
                return self::$_imports [$alias] = $className;
            //获取真实路径
            if (($path = self::getPathOfAlias ( $alias )) !== false) {
                //是否以*结尾,如application.utils.*
                if ($isClass) {
                    if ($forceInclude) {
                        if (is_file ( $path . '.php' ))
                            require ($path . '.php');
                        else
                            throw new CException ( Yii::t ( 'yii', 'Alias "{alias}" is invalid. Make sure it points to an existing PHP file and the file is readable.', array (
                                    '{alias}' => $alias 
                            ) ) );
                        self::$_imports [$alias] = $className;
                    } else
                        self::$classMap [$className] = $path . '.php';
                    return $className;
                } else             // a directory
                {
                    if (self::$_includePaths === null) {
                        self::$_includePaths = array_unique ( explode ( PATH_SEPARATOR, get_include_path () ) );
                        if (($pos = array_search ( '.', self::$_includePaths, true )) !== false)
                            unset ( self::$_includePaths [$pos] );
                    }
                    
                    array_unshift ( self::$_includePaths, $path );
                    
                    if (self::$enableIncludePath && set_include_path ( '.' . PATH_SEPARATOR . implode ( PATH_SEPARATOR, self::$_includePaths ) ) === false)
                        self::$enableIncludePath = false;
                    return self::$_imports [$alias] = $path;
                }
            }
        }

    一系列的判断,最后走到最后的else,将path写入到$_imports,这时仍然没有include.

    include在autoload()

        public static function autoload($className)
        {
            // use include so that the error PHP file may appear
            if(isset(self::$classMap[$className]))
                include(self::$classMap[$className]);
            elseif(isset(self::$_coreClasses[$className]))
                include(self::$_coreClasses[$className]);
            else
            {
                // include class file relying on include_path
                if(strpos($className,'\')===false)  // class without namespace
                {
                    if(self::$enableIncludePath===false)
                    {
                        foreach(self::$_includePaths as $path)
                        {
                            $classFile=$path.DIRECTORY_SEPARATOR.$className.'.php';
                            if(is_file($classFile))
                            {
                                include($classFile);
                                break;
                            }
                        }
                    }
                    else
                        include($className.'.php');
                }
                return class_exists($className,false) || interface_exists($className,false);
            }
            return true;
        }

    如果需要include的是非核心文件,那这里的$className只是一个alias,即文件名的前缀。

    裁剪的yii http://files.cnblogs.com/TheViper/framework.zip

     

  • 相关阅读:
    js原生图片拼图Demo
    display:inline-block在ie7下的解决办法
    Apollo 配置中心部署注意事项
    chrony 时间同步配置
    IPv6基础介绍
    Rabbitmq 报错 nodedown
    Maven 私服你应该不陌生吧,可你会用 Artifactory 搭建吗?
    你 MySQL 中重复数据多吗,教你一招优雅的处理掉它们!
    MySQL 数据库的基本使用
    自建 yum 源
  • 原文地址:https://www.cnblogs.com/TheViper/p/4389822.html
Copyright © 2011-2022 走看看