0

0

YII 的源码分析(二)

php中文网

php中文网

发布时间:2016-08-08 09:32:45

|

994人浏览过

|

来源于php中文网

原创

上一篇简单分析了一下yii的流程,从创建一个应用,到屏幕上输出结果。这一次我来一个稍复杂一点的,重点在输出上,不再是简单的一行"hello world",而是要经过view(视图)层的处理。

依然是demos目录,这次我们选择hangman,一个简单的猜字游戏。老规则,还是从入口处开始看。

index.php:

<?<span>php

</span><span>//</span><span> change the following paths if necessary</span>
<span>$yii</span>=<span>dirname</span>(<span>__FILE__</span>).'/../../framework/yii.php'<span>;
</span><span>$config</span>=<span>dirname</span>(<span>__FILE__</span>).'/protected/config/main.php'<span>;

</span><span>//</span><span> remove the following line when in production mode
// defined('YII_DEBUG') or define('YII_DEBUG',true);</span>

<span>require_once</span>(<span>$yii</span><span>);
Yii</span>::createWebApplication(<span>$config</span>)->run();

和helloworld应用相比,这次多了main.php,打开main看下源码:

<?<span>php

</span><span>return</span> <span>array</span><span>(
    </span>'name'=>'Hangman Game',
    'defaultController'=>'game',
    'components'=><span>array</span><span>(
        </span>'urlManager'=><span>array</span><span>(
            </span>'urlFormat'=>'path',
            'rules'=><span>array</span><span>(
                </span>'game/guess/<g:\w>'=>'game/guess',<span>
            )</span>,<span>
        )</span>,<span>
    )</span>,<span>
);</span>

在我们以后的实际项目中,也是经常要用到配置文件的,所以我觉得有必要了解一下yii的配置文件--main.php

'name'=>'这里通常是定义网站的标题',也就是我们打开index.php时,在网页上显示的标题。

'defaultController'=>'这里是默认的控制器',也就是我们的index.php后面没有指定控制器时系统采用的控制器,如果我们这里没有指出来,默认就是site

'components'=>'这里是组件的参数,用多维数组进行配置。' 具体的参数可以查看yii手册。

Yii::createWebApplication($config)->run(); 上一次我们已经详细分析过它了,这里再简单的走一遍:

CWebApplication.php -> CApplication.php -> __construct($config) :

<span>$this</span>-><span>preinit();

        </span><span>$this</span>-><span>initSystemHandlers();
        </span><span>$this</span>-><span>registerCoreComponents();

        </span><span>$this</span>->configure(<span>$config</span><span>);
        </span><span>$this</span>->attachBehaviors(<span>$this</span>-><span>behaviors);
        </span><span>$this</span>-><span>preloadComponents();

        </span><span>$this</span>->init();

上次我们没有配置过程,所以$this->configure($config)什么也没有做,但是这次有配置参数,所以我们进去看看yii做了哪些操作:

CApplication自己没有实现configure方法,是继承于CModule.php的:

    <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>

代码非常简单,就是把配置参数的键做为类的属性名,value做为类的属性值进行了扩展。完成这一过程就运行CApplication 上的run方法了。

    <span>public</span> <span>function</span><span> run()
    {
        </span><span>if</span>(<span>$this</span>->hasEventHandler('onBeginRequest'<span>))
            </span><span>$this</span>->onBeginRequest(<span>new</span> CEvent(<span>$this</span><span>));
        </span><span>register_shutdown_function</span>(<span>array</span>(<span>$this</span>,'end'),0,<span>false</span><span>);
        </span><span>$this</span>-><span>processRequest();
        </span><span>if</span>(<span>$this</span>->hasEventHandler('onEndRequest'<span>))
            </span><span>$this</span>->onEndRequest(<span>new</span> CEvent(<span>$this</span><span>));
    }</span>

我们前面说过,这里只要关注 $this->processRequest(); 就可以了。运行的结果就是执行$this->runController('');  

    <span>public</span> <span>function</span> runController(<span>$route</span><span>)
    {
        </span><span>if</span>((<span>$ca</span>=<span>$this</span>->createController(<span>$route</span>))!==<span>null</span><span>)
        {
            </span><span>list</span>(<span>$controller</span>,<span>$actionID</span>)=<span>$ca</span><span>;
            </span><span>$oldController</span>=<span>$this</span>-><span>_controller;
            </span><span>$this</span>->_controller=<span>$controller</span><span>;
            </span><span>$controller</span>-><span>init();
            </span><span>$controller</span>->run(<span>$actionID</span><span>);
            </span><span>$this</span>->_controller=<span>$oldController</span><span>;
        }
        </span><span>else</span>
            <span>throw</span> <span>new</span> CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".',
                <span>array</span>('{route}'=><span>$route</span>===''?<span>$this</span>->defaultController:<span>$route</span><span>)));
    }</span>

由于url是index.php,后面没有任何参数,所以都是走的默认控制器,也就是我们在main.php中设定的game. 所以$controller 就等于 controllers/gameController.php, 通过上次的源码分析我们可以知道,在gameController.php中没有init方法时,都是走的父类中定义的默认方法(实际上是一个空方法),

$controller->run($actionID<span>); == gameController</span>->run(''); gameController上没有实现run方法,于是又是去父类中找run

从class GameController extends CController 可以看出,父类是CController , 找到相应的run方法:

<span>public</span> <span>function</span> run(<span>$actionID</span><span>)
    {
        </span><span>if</span>((<span>$action</span>=<span>$this</span>->createAction(<span>$actionID</span>))!==<span>null</span><span>)
        {
            </span><span>if</span>((<span>$parent</span>=<span>$this</span>->getModule())===<span>null</span><span>)
                </span><span>$parent</span>=Yii::<span>app();
            </span><span>if</span>(<span>$parent</span>->beforeControllerAction(<span>$this</span>,<span>$action</span><span>))
            {
                </span><span>$this</span>->runActionWithFilters(<span>$action</span>,<span>$this</span>-><span>filters());
                </span><span>$parent</span>->afterControllerAction(<span>$this</span>,<span>$action</span><span>);
            }
        }
        </span><span>else</span>
            <span>$this</span>->missingAction(<span>$actionID</span><span>);
    }</span>

前面已经分析过了,没有指定时,都是默认参数。那么此时的$actionID为空,actionID就是gameController中定义的默认动作:public $defaultAction='play'; 

runActionWithFilters --->  runAction --> $action->runWithParams<br />这里的$action 需要从CAction -> CInlineAction中去找<br />
<span>public</span> <span>function</span> runWithParams(<span>$params</span><span>)
    {
        </span><span>$methodName</span>='action'.<span>$this</span>-><span>getId();
        </span><span>$controller</span>=<span>$this</span>-><span>getController();
        </span><span>$method</span>=<span>new</span> ReflectionMethod(<span>$controller</span>, <span>$methodName</span><span>);
        </span><span>if</span>(<span>$method</span>->getNumberOfParameters()>0<span>)
            </span><span>return</span> <span>$this</span>->runWithParamsInternal(<span>$controller</span>, <span>$method</span>, <span>$params</span><span>);
        </span><span>else</span>
            <span>return</span> <span>$controller</span>-><span>$methodName</span><span>();
    }</span>

走了这么多过程,和hello world的流程是差不多的。据上次的分析可以知道,这里执行了

$controller->$methodName<span>(); 也就是GameController->actionPlay()<br />到此,我们本节的重点才真正开始:<br /></span>
    <span>public</span> <span>function</span><span> actionPlay()
    {
        </span><span>static</span> <span>$levels</span>=<span>array</span><span>(
            </span>'10'=>'Easy game; you are allowed 10 misses.',
            '5'=>'Medium game; you are allowed 5 misses.',
            '3'=>'Hard game; you are allowed 3 misses.',<span>
        );

        </span><span>//</span><span> if a difficulty level is correctly chosen</span>
        <span>if</span>(<span>isset</span>(<span>$_POST</span>['level']) && <span>isset</span>(<span>$levels</span>[<span>$_POST</span>['level'<span>]]))
        {
            </span><span>$this</span>->word=<span>$this</span>-><span>generateWord();
            </span><span>$this</span>->guessWord=<span>str_repeat</span>('_',<span>strlen</span>(<span>$this</span>-><span>word));
            </span><span>$this</span>->level=<span>$_POST</span>['level'<span>];
            </span><span>$this</span>->misses=0<span>;
            </span><span>$this</span>->setPageState('guessed',<span>null</span><span>);
            </span><span>//</span><span> show the guess page</span>
            <span>$this</span>->render('guess'<span>);
        }
        </span><span>else</span><span>
        {
            </span><span>$params</span>=<span>array</span><span>(
                </span>'levels'=><span>$levels</span>,
                <span>//</span><span> if this is a POST request, it means the level is not chosen</span>
                'error'=>Yii::app()->request->isPostRequest,<span>
            );
            </span><span>//</span><span> show the difficulty level page</span>
            <span>$this</span>->render('play',<span>$params</span><span>);
        }
    }</span>
<span>显然走的是else的逻辑,重点请看</span> $this->render('play',$params); 这个render方法这么面熟,很多框架中都有类似的方法,比如discuz,smarty,CI 等等. 纵观yii框架,rnder 在它整个MVC模式中,是V得以实现的重要骨干。所以有必要把它翻个底朝天。<br />在CController.php中有这个方法:
    <span>public</span> <span>function</span> render(<span>$view</span>,<span>$data</span>=<span>null</span>,<span>$return</span>=<span>false</span><span>)
    {
        </span><span>if</span>(<span>$this</span>->beforeRender(<span>$view</span><span>))
        {
            </span><span>$output</span>=<span>$this</span>->renderPartial(<span>$view</span>,<span>$data</span>,<span>true</span><span>);
            </span><span>if</span>((<span>$layoutFile</span>=<span>$this</span>->getLayoutFile(<span>$this</span>->layout))!==<span>false</span><span>)
                </span><span>$output</span>=<span>$this</span>->renderFile(<span>$layoutFile</span>,<span>array</span>('content'=><span>$output</span>),<span>true</span><span>);

            </span><span>$this</span>->afterRender(<span>$view</span>,<span>$output</span><span>);

            </span><span>$output</span>=<span>$this</span>->processOutput(<span>$output</span><span>);

            </span><span>if</span>(<span>$return</span><span>)
                </span><span>return</span> <span>$output</span><span>;
            </span><span>else</span>
                <span>echo</span> <span>$output</span><span>;
        }
    }</span>

当我们echo $output=$this->renderPartial($view,$data,true);的时候,就发现,此时的$output已经就拿到我们最终的结果了。它对应的文件是views/game/play.php

也就是我们在index.php上最终看到的内容了。由于本次渲染比较简单,所以程序经过的流程也较少,但是从源码中可以看到,里边进行了许多的处理,比如主题什么的。本次就先分析到这。晚安!

<br />

以上就介绍了YII 的源码分析(二),包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

38

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

83

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

97

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

223

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

458

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

169

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

246

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

34

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
JavaScript高级框架设计视频教程
JavaScript高级框架设计视频教程

共22课时 | 3.7万人学习

AngularJS教程
AngularJS教程

共24课时 | 4.1万人学习

CSS3实现按钮特效视频教程
CSS3实现按钮特效视频教程

共15课时 | 3.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号