この時点で、いよいよCIフレームワークの中核に入ります。これは「ガイダンス」ファイルであるため、ユーザーのリクエストやデータ フローが正しいルートをたどるように、ユーザーのリクエストやパラメーターなどをガイドします。たとえば、ユーザーのリクエスト URL:
リーリーブートファイルの後、実際にはアプリケーションのUsrControllerコントローラーのregメソッドに渡されて処理されます。 CodeIgniter.php はどのような働きをしますか?段階的に見てみましょう。
以前のブログ(CIフレームワークのソースコード読解メモ2、全入口 Index.php)では、Index.phpファイルがフレームワークの環境、アプリケーション、システムなどを定義し、セキュリティチェックしていることを見てきました
(1)。事前定義された定数をロードします。
環境が定義されており、その環境に事前定義された定数ファイルが存在する場合は、その環境の定数定義ファイルが最初にロードされ、それ以外の場合は、config ディレクトリ内の定数定義ファイルがロードされます。 リーリー
その理由は、以前にも紹介しましたが、アプリケーションのコアコードを変更せずに、環境と対応するパラメーターをすばやく切り替えることができるためです。
(2). カスタムエラー処理関数を設定します。
_Exception_handler関数です。この関数の定義と説明については、以前のブログ(http://www.cnblogs.com/ohmygirl/p/CIRead-3.html)を参照してください。マニュアルの一文を再度引用しますが、次のレベルのエラーはユーザー定義関数では処理できないことを皆さんに思い出していただきたいと思います:<p>E_ERROR<code><strong>E_ERROR</strong>
、 <strong>E_PARSE</strong>
、 <strong>E_CORE_ERROR</strong>
、 <strong>E_CORE_WARNING</strong>
、 <strong>E_COMPILE_ERROR</strong>
、 <strong>E_COMPILE_WARNING</strong>
,和在 调用 set_error_handler() 函数所在文件中产生的大多数 <strong>E_STRICT</strong>
、E_PARSE<p></p>
、<strong> E_CORE_ERROR<span></span></strong>
、E_CORE_WARNING
、E_COMPILE_ERROR<p></p>
、E_COMPILE_WARNING<p></p>
、および set_error_handler の場所()
のほとんどがファイル内に生成されます。
🎜🎜🎜(3). コアクラスが拡張されているかどうかを確認します🎜🎜🎜 リーリー 🎜 このうち、$assign_to_config は、エントリーファイル Index.php で定義された構成配列でなければなりません。通常、CI のコアコンポーネントの名前は「CI_」で始まり、CI のコアコンポーネントが変更または拡張された場合は、次のようにする必要があります。 MY_ などの別の subclass_prefix プレフィックスを使用します。この場合、CI のローダー コンポーネントがクラスをロードしやすくするために、$assign_to_config['subclass_prefix'] を通じて拡張コアのプレフィックス名を指定する必要があります。または、ファイルをロードできないというエラーが発生します。発見される可能性があります。さらに、subclass_prefix 構成項目はデフォルトで APPPATH/Config/config.php 構成ファイルにあります。また、このコードは、index.php ファイル内の subclass_prefix の優先順位が高いことも示しています (つまり、両方が subclass_prefix に設定されている場合)。 Index.php の構成項目は、構成ファイル Config.php の構成を上書きします)。 🎜 🎜この時点で、CI フレームワークの基本的な環境構成の初期化が完了しました。次に、CodeIgniter は一連のコンポーネントを使用して、さらなる要件を満たします。 🎜 🎜2. コアコンポーネントをロードする🎜 通常、CI フレームワークのさまざまな機能は、さまざまなコンポーネントによって実行されます (たとえば、Log コンポーネントは主にログの記録に使用され、Input コンポーネントはユーザーの GET、POST およびその他のデータの処理に使用されます)。コンポーネント間の結合が低いため、拡張も容易になります。 CI の主なコアコンポーネントは次のとおりです: 🎜 BM 内線 CFG ユニ URI RTR 外 SECその中に:
BM:は、CIのベンチマークコンポーネントであるBenchMarkを指し、主にパフォーマンスのテストと追跡を容易にするために、さまざまな時点をマークし、メモリ使用量やその他のパラメーターを記録するために使用されます。
EXT: 以前紹介したCIの拡張コンポーネントは、CIコアを変更せずにシステムのコア動作機能を変更または追加するために使用されます。フック フックを使用すると、pre_system、pre_controller、post_controller、その他の事前定義されたフック ポイントなど、システム内で実行されているさまざまなフック ポイント (フック ポイント) にカスタム関数と追跡を追加できます。次のすべての $EXT->_call_hook("xxx"); は、特定のフック ポイント (存在する場合) を呼び出すプログラムです。
CFG: Config構成管理コンポーネント。主に設定ファイルの読み込みや設定項目の取得・設定などに使用されます。
UNI: UTF-8 文字セット処理の関連サポートに使用されます。 INPUT コンポーネントなどの他のコンポーネントは、コンポーネントをサポートするように変更する必要があります。
URI: URI (Uniform Resource Identifier) パラメーターなどを解析します。このコンポーネントは RTR コンポーネントと密接に関連しています。 (URI と Router はどこへ行っても仲良しのようです)。
RTR: ルーティングコンポーネント。 URI コンポーネントのパラメータ解析を通じてデータ フローの方向 (ルーティング) を決定します。
OUT: 最終出力管理コンポーネントは、CI(カスタム)の最終出力を担当します。
SEC: 安全处理组件。毕竟安全问题永远是一个大问题。
以BM组件为例,核心组件的加载方式是:
<span>$BM</span> =& load_class('Benchmark', 'core');
调用了load_class函数获取core目录下的相应组件。(load_class的实现和具体介绍见之前的博客:CI框架源码阅读笔记3 全局函数Common.php)
各组件的功能和具体实现之后会有详细的分析, 这里我们只需要知道该组件的基本功能即可。
调用很简单,只有一句话:
<span>$RTR</span>->_set_routing();
调用Router组件的_set_routing()函数来设置路由,具体的实现细节,我们这里暂且不管(之后的部分会有详细介绍),我们只需要知道,通过_set_routing的处理,我们可以获得实际请求的Controller,URI的segment参数段等信息。
值得注意的是,CI允许在index.php中配置routing,且会覆盖默认的routing设置(如共享CI的安装目录的多个应用程序可能有不同的routing):
<span>if</span> (<span>isset</span>(<span>$routing</span><span>)) { </span><span>$RTR</span>->_set_overrides(<span>$routing</span><span>); }</span>
设置完路由之后,可以通过该组件的:fetch_diretory() , fetch_class(), fetch_method()等分别获取目录、类、和方法。
到了这一步,CI会先检查是否有cache_override这个钩子(默认情况下没有配置,也就是返回FALSE),如果没有注册,则调用_display_cache方法输出缓存(这种说法并不准确,准确来说应该是,如果有相应的缓存,则输出缓存且直接退出程序,否则返回FALSE,这里我们暂时不去思考实现细节):
<span>if</span> (<span>$EXT</span>->_call_hook('cache_override') === <span>FALSE</span><span>) { </span><span>if</span> (<span>$OUT</span>->_display_cache(<span>$CFG</span>, <span>$URI</span>) == <span>TRUE</span><span>) { </span><span>exit</span><span>; } }</span>
能够走到这里,说明之前的缓存是没有命中的(实际上,任何页面都是应该先走到这一步,然后才会有设置缓存,之后的访问检查缓存才会命中)。这一步会require Controller基类和扩展的Controller类(如果有的话)及实际的应用程序控制器类:
<span>require</span> BASEPATH.'core/Controller.php'<span>; </span><span>if</span> (<span>file_exists</span>(APPPATH.'core/'.<span>$CFG</span>->config['subclass_prefix'].'Controller.php'<span>)) { </span><span>require</span> APPPATH.'core/'.<span>$CFG</span>->config['subclass_prefix'].'Controller.php'<span>; } </span><span>if</span> ( ! <span>file_exists</span>(APPPATH.'controllers/'.<span>$RTR</span>->fetch_directory().<span>$RTR</span>->fetch_class().'.php'<span>)) { show_error(</span>'xxx'<span>); } </span><span>include</span>(APPPATH.'controllers/'.<span>$RTR</span>->fetch_directory().<span>$RTR</span>->fetch_class().'.php');
之前我们已经说过,在Router组件_set_routing之后,可以通过fetch_directory(), fetch_class(), fetch_method()等分别获取请求的文件目录、控制器和方法。
现在对请求的控制器和方法做验证,我们看一下CI的主要验证:
<span>if</span> ( ! <span>class_exists</span>(<span>$class</span><span>) OR </span><span>strncmp</span>(<span>$method</span>, '_', 1) == 0<span> OR </span><span>in_array</span>(<span>strtolower</span>(<span>$method</span>), <span>array_map</span>('strtolower', <span>get_class_methods</span>('CI_Controller'<span>))) )</span>
这里简单解释一下,CI认为不合法的情况有:
(1).请求的class不存在:! class_exists($class)
(2).请求的方法以_开头(被认为是私有的private的方法,之所以这么做事因为php并不是一开始就支持private,public的访问权限的):strncmp($method, '_', 1) == 0
(3).基类CI_Controller中的方法不能直接被访问:in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller'))
如果请求的条件满足上面3个中的任何一个,则被认为是不合法的请求(或者是无法定位的请求),因此会被CI定向到404页面(值得注意的是,如果设置了404_override,并且404_override的class存在,并不会直接调用show_404并退出,而是会像正常的访问一样,实例化:$CI = new $class();)
走到这里,CI的Controller总算是加载完了(累趴)。不过且慢,还有不少事情要做:
(1). 检查_remap。
_remap这个东西类似于CI的rewrite,可以将你的请求定位到其他的位置。这个方法是应该定义在你的应用程序控制器的:
<span>public</span> <span>function</span> _remap(<span>$method</span><span>){ </span><span>$this</span>-><span>index(); }</span>
现在,所有的请求都会被定位到改控制器的index()中去了。如果_remap不存在,则调用实际控制器的$method方法:
<span>call_user_func_array</span>(<span>array</span>(&<span>$CI</span>, <span>$method</span>), <span>array_slice</span>(<span>$URI</span>->rsegments, 2));
(2).最终输出
$this->load->view()之后,并不会直接输出,而是放在了缓存区。$Out->_display之后,才会设置缓存,并最终输出(详细参考Output.php和Loader.php)
(3)若有使用了数据库,还要关闭数据库连接:
<span>if</span> (<span>class_exists</span>('CI_DB') AND <span>isset</span>(<span>$CI</span>-><span>db)) { </span><span>$CI</span>->db-><span>close(); }</span>
注意,如果在Config/database.php中设置了开启pconnect,则建立的连接是长连接,这种长连接是不会被close关闭的。所以,请谨慎使用pconnect.
到现在,CI的核心流程总算是走完了(虽然还有很多细节的问题,但不管怎么说,大树的枝干已经有了,树叶的细节,可以慢慢添加)。在结束本文之前,我们来梳理一下CI的核心执行流程:
回顾之前我们引用的官方给出的流程图,是不是基本一致的:
最後に、ファイル全体のソースコードを貼り付けます:
if ( ! define('BASEPATH')) exit('直接のスクリプトアクセスは許可されていません'); /** * CodeIgniter のバージョン */ 定義('CI_VERSION', '2.1.4'); /** * CodeIgniter ブランチ (コア = TRUE、リアクター = FALSE) */ 定義('CI_CORE', FALSE); require(BASEPATH.'core/Common.php'); /* 対応する環境設定ファイルがある場合はその設定配下の定数定義ファイルを優先して読み込み、そうでない場合はconfigディレクトリ配下の定数定義ファイルを読み込みます */ if (定義('環境') および file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php')) { require(APPPATH.'config/'.ENVIRONMENT.'/constants.php'); } その他 { require(APPPATH.'config/constants.php'); } set_error_handler('_例外_ハンドラー'); if ( ! is_php('5.3')) { @set_magic_quotes_runtime(0); // 魔法の引用符をキル } /* * subclass_prefix を設定します */ if (isset($assign_to_config['subclass_prefix']) AND $assign_to_config['subclass_prefix'] != '') { get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix'])); } /* * スクリプトの実行時間制限を自由に設定する */ //if(function_exists("set_time_limit") && @!ini_get("safe_mode")) if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0) { @set_time_limit(300); } $BM =&load_class('ベンチマーク', 'コア'); $BM->mark('total_execution_time_start'); $BM->mark('loading_time:_base_classes_start'); /* * フッククラスをインスタンス化する */ $EXT =&load_class('フック', 'コア'); /* * 「pre_system」フックはありますか? */ $EXT->_call_hook('pre_system'); /* * 設定クラスをインスタンス化する */ $CFG =&load_class('Config', 'core'); // Index.php ファイルに手動で設定する構成項目はありますか? if (isset($assign_to_config)) { $CFG->_assign_to_config($assign_to_config); } /* * UTF-8クラスをインスタンス化します。 */ $UNI =&load_class('Utf8', 'コア'); /* *------------------------------------------------ - ---- * URIクラスをインスタンス化する *------------------------------------------------ - ---- */ $URI =&load_class('URI', 'コア'); /* * ルーティングクラスをインスタンス化してルーティングを設定する */ $RTR =&load_class('ルーター', 'コア'); $RTR->_set_routing(); // メインインデックスファイルに存在する可能性のあるルーティングオーバーライドを設定します if (isset($ルーティング)) { $RTR->_set_overrides($routing); } /* * 出力クラスをインスタンス化する */ $OUT =&load_class('出力', 'コア'); /* * 有効なキャッシュ ファイルはありますか? 存在する場合は、完了です... */ if ($EXT->_call_hook('cache_override') === FALSE) { if ($OUT->_display_cache($CFG, $URI) == TRUE) { 終了; } }/* * xss および csrf サポートのセキュリティ クラスをロードします */ $SEC =&load_class('セキュリティ', 'コア'); /* * 入力クラスをロードし、グローバルをサニタイズします */ $IN =&load_class('入力', 'コア'); /* * 言語クラスをロードします */ $LANG =&load_class('Lang', 'core'); /* * アプリコントローラーとローカルコントローラーをロードします */ // 基本コントローラークラスをロードします require BASEPATH.'core/Controller.php'; 関数 &get_instance() { return CI_Controller::get_instance(); } if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php')) { require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'; } if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php')) { show_error('デフォルトのコントローラーをロードできません。Routes.php ファイルで指定されたコントローラーが有効であることを確認してください。'); } include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'); $BM->mark('loading_time:_base_classes_end'); /* * セキュリティーチェック */ $class = $RTR->fetch_class(); $method = $RTR->fetch_method(); if ( ! class_exists($class) または strncmp($method, '_', 1) == 0 または in_array(strto lower($method), array_map('strto lower', get_class_methods('CI_Controller'))) ) { if ( ! 空($RTR->ルート['404_override'])) { $x = explode('/', $RTR->ルート['404_override']); $クラス = $x[0]; $method = (isset($x[1]) ? $x[1] : 'インデックス'); if ( ! class_exists($class)) { if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) { show_404("{$クラス}/{$メソッド}"); } include_once(APPPATH.'controllers/'.$class.'.php'); } } その他 { show_404("{$クラス}/{$メソッド}"); } } /* * 「pre_controller」フックはありますか? */ $EXT->_call_hook('pre_controller'); /* * 要求されたコントローラーをインスタンス化します */ // コントローラーのベンチマークを実行できるように開始点をマークします $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start'); $CI = 新しい $class(); /* * 「post_controller_constructor」フックはありますか? */ $EXT->call_hook('post_controller_constructor'); /* * 要求されたメソッドを呼び出す */ // 「リマップ」機能はありますか?その場合は、代わりにそれを呼び出します if (method_exists($CI, '_remap')) { $CI->_remap($method, array_slice($URI->rsegments, 2)); }その他 { if ( ! in_array(strto lower($method), array_map('strto lower', get_class_methods($CI)))) { if ( ! 空($RTR->ルート['404_override'])) { $x = explode('/', $RTR->ルート['404_override']); $クラス = $x[0]; $method = (isset($x[1]) ? $x[1] : 'インデックス'); if ( ! class_exists($class)) { if ( ! file_exists(APPPATH.'controllers/'.$class.'.php')) { show_404("{$クラス}/{$メソッド}"); } include_once(APPPATH.'controllers/'.$class.'.php'); 設定解除($CI); $CI = 新しい $class(); } } その他 { show_404("{$クラス}/{$メソッド}"); } } call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2)); } // ベンチマークの終点をマークします $BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end'); /* * 「post_controller」フックはありますか? */ $EXT->_call_hook('post_controller'); /* * 最終的にレンダリングされた出力をブラウザに送信します */ if ($EXT->_call_hook('display_override') === FALSE) { $OUT->_display(); } /* * 「post_system」フックはありますか? */ $EXT->_call_hook('post_system'); /* * DB接続が存在する場合は閉じます */ if (class_exists('CI_DB') および isset($CI->db)) { $CI->db->close(); } /* ファイルの終わり CodeIgniter.php */ コードを表示作業促進のため、問題が発生する可能性はありません。
参考文献: