Home >Backend Development >PHP Tutorial >yii源码分析四——非核心类的导入注册

yii源码分析四——非核心类的导入注册

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOriginal
2016-06-13 12:17:28841browse

yii源码分析4——非核心类的导入注册

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

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

<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">function</span> autoload(<span style="color: #800080;">$className</span><span style="color: #000000;">) {        </span><span style="color: #0000ff;">include</span> self::<span style="color: #800080;">$_coreClasses</span> [<span style="color: #800080;">$className</span><span style="color: #000000;">];     }</span>

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

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

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

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

这要从CApplication说起。

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

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

<span style="color: #000000;">php</span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">array</span><span style="color: #000000;"> (    </span>'basePath' => <span style="color: #008080;">dirname</span> ( <span style="color: #ff00ff;">__FILE__</span> ) . DIRECTORY_SEPARATOR . '..',    'import' => <span style="color: #0000ff;">array</span><span style="color: #000000;"> (        </span>'application.utils.*'<span style="color: #000000;">    )    );</span>?> 

然后在父类CModule

    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> configure(<span style="color: #800080;">$config</span><span style="color: #000000;">) {        </span><span style="color: #0000ff;">if</span> (<span style="color: #008080;">is_array</span> ( <span style="color: #800080;">$config</span><span style="color: #000000;"> )) {            </span><span style="color: #0000ff;">foreach</span> ( <span style="color: #800080;">$config</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$key</span> => <span style="color: #800080;">$value</span><span style="color: #000000;"> )                </span><span style="color: #800080;">$this</span>-><span style="color: #800080;">$key</span> = <span style="color: #800080;">$value</span><span style="color: #000000;">;        }    }</span>

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

    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> __set(<span style="color: #800080;">$name</span>,<span style="color: #800080;">$value</span><span style="color: #000000;">)    {        </span><span style="color: #800080;">$setter</span>='set'.<span style="color: #800080;">$name</span><span style="color: #000000;">;        </span><span style="color: #0000ff;">if</span>(<span style="color: #008080;">method_exists</span>(<span style="color: #800080;">$this</span>,<span style="color: #800080;">$setter</span><span style="color: #000000;">))            </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>-><span style="color: #800080;">$setter</span>(<span style="color: #800080;">$value</span><span style="color: #000000;">);        </span><span style="color: #0000ff;">else</span>....<span style="color: #000000;">    }</span>

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

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

    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setImport(<span style="color: #800080;">$aliases</span><span style="color: #000000;">)    {        </span><span style="color: #0000ff;">foreach</span>(<span style="color: #800080;">$aliases</span> <span style="color: #0000ff;">as</span> <span style="color: #800080;">$alias</span><span style="color: #000000;">)            Yii</span>::import(<span style="color: #800080;">$alias</span><span style="color: #000000;">);    }</span>

然后是YiiBase里面的import()

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

 

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

include在autoload()

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

 

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

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

如果您觉得本文的内容对您有所帮助,您可以打赏我:

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn