Heim > Backend-Entwicklung > PHP-Tutorial > yii-Quellcode-Analyse 4 – Importregistrierung von Nicht-Kernklassen

yii-Quellcode-Analyse 4 – Importregistrierung von Nicht-Kernklassen

WBOY
Freigeben: 2016-08-08 09:26:38
Original
804 Leute haben es durchsucht

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

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

<span>public</span> <span>static</span> <span>function</span> autoload(<span>$className</span><span>) {
        </span><span>include</span> self::<span>$_coreClasses</span> [<span>$className</span><span>];
     }</span>
Nach dem Login kopieren

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

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

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

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

这要从CApplication说起。

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

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

<?<span>php
</span><span>return</span> <span>array</span><span> (
    </span>'basePath' => <span>dirname</span> ( <span>__FILE__</span> ) . DIRECTORY_SEPARATOR . '..',
    'import' => <span>array</span><span> (
        </span>'application.utils.*'<span>
    )
    );
</span>?> 
Nach dem Login kopieren

然后在父类CModule

    <span>public</span> <span>function</span> configure(<span>$config</span><span>) {
        </span><span>if</span> (<span>is_array</span> ( <span>$config</span><span> )) {
            </span><span>foreach</span> ( <span>$config</span> <span>as</span> <span>$key</span> => <span>$value</span><span> )
                </span><span>$this</span>-><span>$key</span> = <span>$value</span><span>;
        }
    }</span>
Nach dem Login kopieren

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

    <span>public</span> <span>function</span> __set(<span>$name</span>,<span>$value</span><span>)
    {
        </span><span>$setter</span>='set'.<span>$name</span><span>;
        </span><span>if</span>(<span>method_exists</span>(<span>$this</span>,<span>$setter</span><span>))
            </span><span>return</span> <span>$this</span>-><span>$setter</span>(<span>$value</span><span>);
        </span><span>else</span>....<span>
    }</span>
Nach dem Login kopieren

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

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

    <span>public</span> <span>function</span> setImport(<span>$aliases</span><span>)
    {
        </span><span>foreach</span>(<span>$aliases</span> <span>as</span> <span>$alias</span><span>)
            Yii</span>::import(<span>$alias</span><span>);
    }</span>
Nach dem Login kopieren

然后是YiiBase里面的import()

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

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

include在autoload()

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

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

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

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

以上就介绍了yii源码分析4——非核心类的导入注册,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage