>  기사  >  백엔드 개발  >  YII 소스코드 분석(2)

YII 소스코드 분석(2)

WBOY
WBOY원래의
2016-08-08 09:32:45871검색

이전 글에서는 Yii의 애플리케이션 제작부터 결과를 화면에 출력하기까지의 과정을 간략하게 분석했습니다. 이번에는 출력에 초점을 맞춰 조금 더 복잡한 작업을 수행하겠습니다. 이는 더 이상 단순한 "hello world" 라인이 아니라 뷰 레이어에서 처리해야 합니다.

여전히 데모 디렉토리에서 이번에는 간단한 추측 게임인 행맨을 선택합니다. 오래된 규칙은 입구에서 시작하는 것입니다.

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가 있습니다. 메인을 열고 소스 코드를 살펴보세요.

<?<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 뒤에 지정된 컨트롤러가 없을 때 시스템에서 사용하는 컨트롤러입니다. 여기서 지정하지 않으면 기본값은 사이트입니다.

'comComponents'=>'다음은 다차원 배열을 사용하여 구성된 구성요소의 매개변수입니다. '특정 매개변수에 대해서는 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 자체는 구성 메소드를 구현하지 않지만 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>

코드는 매우 간단합니다. 즉, 구성 매개변수의 키를 클래스의 속성 이름으로 사용하고, 값을 클래스의 속성 값으로 사용합니다. 이 프로세스를 완료한 후 CApplication에서 실행 메서드를 실행합니다.

    <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에 설정한 게임인 기본 컨트롤러를 사용합니다. 따라서 $controller는 위를 통해 Controllers/gameController.php와 동일합니다. 소스 코드 분석을 통해 gameController.php에 init 메소드가 없을 경우 상위 클래스에 정의된 기본 메소드가 사용되는 것을 알 수 있습니다(실제로는 빈 메소드).

$controller->run($actionID<span>); == gameController</span>->run(''); gameController上没有实现run方法,于是又是去父类中找run
GameController 클래스에서 CController를 확장하면 상위 클래스가 CController이고 해당 실행 메서드를 찾을 수 있습니다.

<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>
많은 과정을 거치고 나니 헬로월드의 과정과 비슷하네요. 마지막 분석에 따르면 여기서

가 실행되는 것을 알 수 있다

$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>
$output=$this->renderPartial($view,$data,true);를 echo하면 현재 $output이 이미 최종 결과를 얻었음을 알 수 있습니다. 해당 파일은 views/game/play.php

드디어 index.php에서 본 내용입니다. 이번 렌더링은 상대적으로 단순하기 때문에 프로그램이 더 적은 프로세스를 거치지만, 소스 코드에서 볼 수 있듯이 테마 등 많은 처리가 이루어졌습니다. 이번에는 이것을 먼저 분석하겠습니다. 안녕히 주무세요!

<br>
위 내용은 YII(2)의 소스코드 분석을 내용적인 측면까지 포함하여 소개하고 있는데, PHP 튜토리얼에 관심이 있는 친구들에게 도움이 되기를 바랍니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.