OCS: The tracing of the Routing Page Process

  • 1772
  • 0

摘要:OCS: The tracing of the Routing Page Process

OCS: The tracing of the Routing Page Process

以 OCS2 版本為例, 在 ocs 系統的根目錄下中, index.php 是整個網站的超始點, 如下引言:

Bootstrap code for OCS site. Loads required files and then calls the dispatcher to delegate to the appropriate request handler.

在起始點中會引用 bootstrap.inc.php 來生成一項網站應用程式, 其類別是繼承 PKPApplication 的 Application; 接著啟動網站應用程式, 啟動的函式 execute 位於 PKPApplication 類別中, 它會負責生成派送器 ( Dispatcher ) 並處理客戶端的 request.

ocs2\index.php:

$application =& PKPApplication::getApplication();
$application->execute();

ocs2\lib\pkp\classes\core\PKPApplication.inc.php:

    // Dispatch the request to the correct handler
    $dispatcher =& $this->getDispatcher();
    $dispatcher->dispatch($this->getRequest());
}

function &getDispatcher() {
    $dispatcher =& Registry::get('dispatcher', true, null);

    if (is_null($dispatcher)) {
        import('core.Dispatcher');

        // Implicitly set dispatcher by ref in the registry
        $dispatcher = new Dispatcher();

        // Inject dependency
        $dispatcher->setApplication($this->getApplication());

        // Inject router configuration
        $dispatcher->addRouterName('core.PKPComponentRouter', ROUTE_COMPONENT);
        $dispatcher->addRouterName('core.PageRouter', ROUTE_PAGE);
    }

    return $dispatcher;
}

派送器中含有路由器, 是用來決定客戶端要求是由哪一個處理器 ( Handler ) 來處理. 在 getDispatcher 函式中可發現預設的兩種路由器, PKPComponentRouter 和 PageRouter; 網頁頁面是用頁面路由器來決定需求相對應的處理器. 派送器在派送要求時, 會去找到適合的路由器, 並把要求轉送給它處理. 客戶端要求即是 HTTP Request, 在 OCS 中是由 Request 類別實現, 它繼承 PKPRequest 類別. 兩者的差別是, PKPRequest 僅提供 HTTP requests 相關的操作; Request 則包含了額外的頁面邏輯, 像是 conference id, sched conf id, 頁面名稱, 操作名稱和頁面參數. Request 位於 ocs2\classes\core\Request.inc.php; PKPRequest 位於 ocs2\lib\pkp\classes\core\PKPRequest.inc.php

ocs2\lib\pkp\classes\core\Dispatcher.inc.php:

    $routerNames = $this->getRouterNames();     

    foreach($routerNames as $shortcut => $routerCandidateName) {
        $routerCandidate =& $this->_instantiateRouter($routerCandidateName, $shortcut);

        if ($routerCandidate->supports($request)) {             
            $request->setRouter($routerCandidate);
            $router =& $routerCandidate;
            $this->_router =& $router;
            break;
        }
    }
    ...
    $router->route($request);
}

PageRouter 繼承 PKPPageRouter, 大部份的頁面路由功能在 PKPPageRouter 中實作, PageRouter 僅只有一個 redirectHome 方法, 用於導回使用者首頁. 頁面路由時, 會先取得面頁路徑與操作名稱, 接著取得頁面操作的索引頁, 指派到 $sourceFile 變數中; 索引頁會被檢查是否存在, 如果存在就會引用它. 在所有的索引頁中, 會自行判斷客戶端要求的操作是需要哪一種處理器, 並把 HANDLER_CLASS 定義為處理器的名稱, 且匯入該處理器的檔案, 這些會在索引頁引入後完成. 然後, 在 route 函式中會生成出處理器, 並由處理器負責完成客戶端要求的操作.

ocs2\lib\pkp\classes\core\PKPPageRouter.inc.php:

    $page = $this->getRequestedPage($request);
    $op = $this->getRequestedOp($request);
    ... 
    $sourceFile = sprintf('pages/%s/index.php', $page);

    if (!HookRegistry::call('LoadHandler', array(&$page, &$op, &$sourceFile))) {
        if (file_exists($sourceFile) || file_exists('lib/pkp/'.$sourceFile)) require($sourceFile);
        elseif (empty($page)) require(ROUTER_DEFAULT_PAGE);
        else {
            $dispatcher =& $this->getDispatcher();
            $dispatcher->handle404();
        }
    }
    ...
    // Instantiate the handler class
    $HandlerClass = HANDLER_CLASS;
    $handler = new $HandlerClass($request);
    ...
    // Route the request to the handler operation
    $handler->$op($this->getRequestedArgs($request), $request);
}

以 http://{IP Address}/ocs2/index.php/itsa/itsa_c/author/submit/3?paperId=16 為例, 頁面名稱是 author, 操作是 submit; 操作之後的都是參數, 3 代表步驟三, 16 是指論文 ID. 在路由器路由此要求時, 會判斷頁面操作索引頁為 ocs2\pages\author\index.php; 引入此頁後會判斷操作所需要的處理器, 如下, submit 操作的處理器是 SubmitHandler 類別. 路由器生成 SubmitHandler, 並呼叫該操作函式, submitHandler->submit, 呼叫 submit 後會決定接到此要求的反應; 呼叫時會傳入 URL 上的參數, 參數由陣列儲存.

ocs2\pages\author\index.php:

case 'saveSubmit':
case 'submitSuppFile':
case 'saveSubmitSuppFile':
case 'deleteSubmitSuppFile':
    define('HANDLER_CLASS', 'SubmitHandler');
    import('pages.author.SubmitHandler');
    break;

SubmitHandler 處理器中對應到的 submit 操作, 如下, 會先取得一些資料, 如使用者, 會議, 步驟, 論文編號等, 並驗証這些資料; 接著準備要回應給客戶端看的頁面, 即 AuthorSubmitStep3Form, 位於 ocs2\classes\author\form\submit\ 目錄下. 最後, 生成頁面並注入資料模型, 並呼叫顯示頁面 display 函式, 它會準備頁面樣版上所需要的資料.

ocs2\pages\author\SubmitHandler.inc.php:

    $user =& Request::getUser();
    $schedConf =& Request::getSchedConf();

    ...

    $step = isset($args[0]) ? (int) $args[0] : 1;
    $paperId = (int) Request::getUserVar('paperId');

    $this->validate($paperId, $step);
    $this->setupTemplate(true);

    $paper =& $this->paper;

    $formClass = "AuthorSubmitStep{$step}Form";
    import("author.form.submit.$formClass");

    $submitForm = new $formClass($paper);
    if ($submitForm->isLocaleResubmit()) {
        $submitForm->readInputData();
    } else {
        $submitForm->initData();
    }
    $submitForm->display();
}

在頁面顯示這裡, display 函式最後會呼叫到 Form 類別中的 display 函式, 因為 AuthorSubmitStep3Form 繼承 AuthorSubmitForm, 而 AuthorSubmitForm 繼承 Form 類別. 在生成 AuthorSubmitStep3Form 時就決定了樣版頁面的路徑, 如下:

ocs2\classes\author\form\submit\AuthorSubmitForm.inc.php:

    parent::Form(sprintf('author/submit/step%d.tpl', $step));

    $this->addCheck(new FormValidatorPost($this));

    $this->step = $step;
    $this->paper = $paper;
    $this->paperId = $paper ? $paper->getId() : null;
}

Form 的 display 函式中會呼叫 TemplateManager 的 display 函式, 並傳入樣版來顯示面頁, 如下; TemplateManager 用於配接 Smarty 框架, 它繼承 PKPTemplateManager, 而 PKPTemplateManager 繼承 Smarty 類別. 由 TemplateManager 管理與設定樣版, 設定顯示資料可以使用其下的 assign_by_ref 或 assign 來指派變數與其值; 在 Smarty 框架中, 這些變數資料會被保存並於樣版頁中使用.

ocs2\lib\pkp\classes\form\Form.inc.php:

    ...

    // modifier vocabulary for creating forms
    ...

    // Determine the current locale to display fields with
    $formLocale = Request::getUserVar('formLocale');
    if (empty($formLocale)) $formLocale = AppLocale::getLocale();
    if (!in_array($formLocale, array_keys(AppLocale::getSupportedFormLocales()))) {
        $formLocale = AppLocale::getPrimaryLocale();
    }
    $templateMgr->assign('formLocale', $formLocale);

    if ($fetch) {
        return $templateMgr->fetch($this->_template);
    } else {
        $templateMgr->display($this->_template);
        return null;
    }
}

function display($request = null, $fetch = false) {
    ...
    $templateMgr->assign('formLocales', AppLocale::getSupportedFormLocales());
    ...
}

ocs2\templates\author\submit\step3.tpl:

<div id="locales">
<table width="100%" class="data">
    <tr valign="top">
        <td width="20%" class="label">{fieldLabel name="formLocale" key="form.formLanguage"}</td>
        <td width="80%" class="value">
            {url|assign:"submitFormUrl" op="submit" path="3" paperId=$paperId escape=false}
            {* Maintain localized author info across requests *}
            {foreach from=$authors key=authorIndex item=author}
                {foreach from=$author.biography key="thisLocale" item="thisBiography"}
                    {if $thisLocale != $formLocale}<input type="hidden" name="authors[{$authorIndex|escape}][biography][{$thisLocale|escape}]" value="{$thisBiography|escape}" />{/if}
                {/foreach}
            {/foreach}
            {form_language_chooser form="submit" url=$submitFormUrl}
            <span class="instruct">{translate key="form.formLanguage.description"}</span>
        </td>
    </tr>
</table>
</div>
{/if}

以 Form 類別為例, 在 display 函式中指派了 formLocales 這個變數給樣版頁, 其值是 formLocale 的個數. 而在樣版頁中, 可以看到直接在大括號中以 $formLocales 來使用. 樣版頁語法和相關內容在 Smarty 官網上能找到.