2012年4月30日月曜日

cakePHPの構造と動作のまとめ(その3)

前回は基本的なフォルダ構成についておおよそまとめた。それらの構成を見てるとどういう動きをするのかもおおよそ解ってきた(つもりだ)けど、やっぱりつもりじゃ何となく嫌だしある程度調べてみたので今回はそのまとめ。
ただ、大まかな流れを書いてるサイトは沢山あっても、どこの何を参照しながらcakePHPが構築されていってるのかというような詳しい部分を書いてるところはあまり見当たらない。
そこで、まずcakePHP内の表示部分に以下のコードを貼って呼び出される関数を追いながら調べてみようと安易な気持ちで考えた。

print '<pre>';
print_r(debug_backtrace()); 
print '</pre>';
 debug_bactrace() <- 呼び出し元の関数やファイルをどんどんと追って行く
まずは簡単なページを作成

ちょっと行程を飛び越えちゃうんだけど、CookBook2.xのブログのチュートリアルを参考にしてモデルとコントローラーとビューを作成してみた。作成したものはチュートリアルのと全く同じだから割愛。

まずは「app/View/Posts/index.ctp」の一番最後に上記のソースを貼り付けて表示させてみることに。

ものすごいことになった

妙に表示が遅いなって思ったら…思わず馬鹿じゃないのって言いたくなる結果になっちゃった。

<pre>Array
(
    [0] => Array
        (
            [file] => /var/www/html/cake20/lib/Cake/View/View.php
            [line] => 908
            [function] => include
        )

    [1] => Array
        (
            [file] => /var/www/html/cake20/lib/Cake/View/View.php
            [line] => 872
            [function] => _evaluate
            [class] => View
            [object] => View Object
                (
                    [Helpers] => HelperCollection Object
                        (
                            [_View:protected] => View Object
 *RECURSION*
・
・
(その後75751行迄続く)

1000行程度なら頑張って解析する気にもなるけど、75751行は…。
もしかしたら、debug_backtraceの使い方をぼくは間違えてるのかな。。ひょっとして、ファイルに書き込まれてる関数全部吐き出すの?呼び出されたものだけじゃなくて?
と不安になって調べてみたけど、間違えてないっぽい(多分)。とすると、ひとつのページを呼び出すのに75751行分もあちこち参照してるのかぁ。
こんな長いのは初めてみたからびっくり。それだけぼくはちっぽけなのしか作ってこなかったってことなのかな。

方針転換

ひとまず、上のコードのprint_r(debug_backtrace());print count(debug_backtrace());に変更。どれくらいのファイル/関数が呼ばれてるのかだけ出力してみると、7つだけだった。
というわけで、更に変更して以下のコードを入れてみることにした。ついでにどこを呼び出してるかも見てみることに。

foreach(debug_backtrace() as $key => $r){
 print '[Key]'.$key.'<br/>';
 print !empty($r['file']) ?'[file]'.$r['file'].'<br/>';
 print !empty($r['line']) ?'[line]'.$r['line'].'<br/>';
 print !empty($r['class']) ?'[class]'.$r['class'].'<br/>':'';
 print !empty($r['function']) ?'[function]'.$r['function'].'<br/>':'';
 print !empty($r['args']) ?'[args]'.count($r['args']).'<br/>':'';
 print '<br />';
}

すると、非常にあっさりとした感じに。

[Key]0
[file]/var/www/html/cake20/lib/Cake/View/View.php
[line]908
[function]include

[Key]1
[file]/var/www/html/cake20/lib/Cake/View/View.php
[line]872
[class]View
[function]_evaluate
[args]2

[Key]2
[file]/var/www/html/cake20/lib/Cake/View/View.php
[line]463
[class]View
[function]_render
[args]1

[Key]3
[file]/var/www/html/cake20/lib/Cake/Controller/Controller.php
[line]959
[class]View
[function]render
[args]2

[Key]4
[file]/var/www/html/cake20/lib/Cake/Routing/Dispatcher.php
[line]110
[class]Controller
[function]render

[Key]5
[file]/var/www/html/cake20/lib/Cake/Routing/Dispatcher.php
[line]85
[class]Dispatcher
[function]_invoke
[args]3

[Key]6
[file]/var/www/html/cake20/app/webroot/index.php
[line]96
[class]Dispatcher
[function]dispatch
[args]2

これなら追えそうかもしれない。

とにかく追跡してみる

あとはこのfile情報やline情報をベースに、ネットでも調べながら、モデルコントローラーコンポーネントビヘイビアヘルパーエレメントレイアウトなどをあえて呼び出しつつ、色んな場所で実行しながらマッピングをしていった。
何だか非常に効率が悪い気もするしもっとスマートなやり方がある気もするけど、自分不器用なんで気にしない。

あちこちに貼ってみた結果

どうも、こんな風になってるらしい。

もうわけがわからないですが、これもかなり端折っちゃった。最初の目的がセキュリティ関連がどうなってるんだろうというのがあったんだけど、複雑すぎて結局わからないまま。
(もっと知識がついた段階で、改めて中を調べてみようかと思います) 簡単な流れをまとめると、
http://localhost/cake20/posts/を呼び出した場合

1)まずはcake20/index.php(またはcake20/app/webroot/index.php)が呼び出される

ini_set('include_path', ROOT . DS . 'lib' . PATH_SEPARATOR . ini_get('include_path'));によって参照パスが定義されてる。

2)様々な定義がされた後、Dispatcherに渡される

app/config/core.phpapp/config/bootstrap.phpの設定情報もこの時点で定義されるらしい。

3)ルーティング情報が読み込まれ、一連のコントローラーが読み込まれた後、$Dispatcher->_invoke()によってコントローラが呼び出される

ここがコアみたい。実際は_invoke()によって描写の最後まで実行される。

4)$controller->constructClasses()コンポーネントとヘルパー(モデルも?)が読み込まれる

あと記載はしてないけど、プラグインもこの時に読み込まれる(らしい)ただ、モデルが読み込まれるって書かれてるっぽいんだけど、それらしい動きをどうもしてない。
むしろモデルは$this->[モデル名]みたいな感じでコントローラ内で呼び出した最初の時に、__get()__isset()のマジックメソッドによって読み込まれてるっぽい。
(怪しいんで、図解にはモデルは書いてません)

5)beforeFilter()などはこの時に呼び出される

そういったもろもろのはこの時に定義される。

6)$this->_invokeAction()によってPostsController->index()が呼び出される

要はぼくが作ったファイルはこの時に呼び出され、実行される。
もちろんそのファイルないし関数が作成されてなかったらエラーとなる。

また、上にも書いたように、モデルは$this->[モデル名]みたいに呼び出された最初の際にマジックメソッドによって読み込まれてるみたい。
($usesにそのモデル名がなかった場合、エラーになる)

7)index()の読み込みが終わったら、$this->render()にて描写開始

なのかな?このあたりがちょっとあやふや。
なお、render()が実行される前にbeforeRender()が実行される。

8)レイアウト→ビュー→エレメントを使ってHTMLが構築される

この時点では、あとは渡された情報から作成されるだけ。

9)終了処理が行われた後、表示される

afterFilter()関数が作成されてあればそれが呼ばれる。
あと図には書かなかったけど、$controller->shutdownProces()が呼び出されて終了処理が行われ、そして表示される。


なんというか、複雑すぎて結局全部は追えなかった。図を作成するのに4日近くかかったけど、どういう動きをするかを実際に検証しながら知ることができたからやってみてよかったといえばよかったかも。
(間違えてるかもしれませんが)

0 件のコメント:

コメントを投稿