Dispatcherまで辿りつけない編 - CakePHPをもくじとしてPHPを学ぶシリーズ01
パーフェクトPHP でフレームワークを作ってみて、次にどうしようか考えていました。
結論としては、オープンソースのフレームワークをもくじとしてPHPを学ぶ事にしました。
理由としては、Webアプリケーションを作るのに必要な機能はフルスタックなフレームワークにつまっているので、手探りで一つ一つ調べながら自己流のうんこコードを産むより、オープンソースのコードを吸収したほうが綺麗にそしてすばやく学べる、というところです。
題材はタイトルにもある通りCakePHPを使います。有名ですし、解説記事もたくさんあるので。なんだかんだやり玉にあげられているのでみんなCakePHPのことが大好きなんだと思います。*1
CakePHPをもくじとしてPHPを学ぶシリーズ
なので、このシリーズでやることはCakePHPを使ってアプリケーションを作成することではなく、ひたすらCakePHPの処理を追っかけるということになります。ようござんすか?ようござんすね?
第一回はDispatcherについてです
フレームワークをもくじに、と言ってもおおきすぎて何からやれば効率的かわからないので、単純にリクエストを投げてレスポンスが帰ってくるそのまんまの流れ通りやっていこうと思います。
CakePHPのバージョンは2.4.3(stable)を使います。
まずは一番上位のディレクトリを見てみる
ここでは http://example.jp/以下にCakePHPのファイル群をおいているものとします。
example.jp/ .girignore .htaccess CONTRIBUTING.md README.md app/ build.properties build.xml index.php lib/ plugins/ vendors/
ディレクトリが4つ有ります。
app/
はアプリケーションごとのソースの置き場ですね。作ったファイルを放り込む場所です。
lib/
はCakePHPの本体ですね。確かマニュアルにもlib/
内は触らないと約束してくれ的なことが書いてありました。
plugins/
とvendors/
はまぁそんな感じですね。2つある意味がわかりませんが、そのうちわかるでしょう。今は置いときます。
他にもファイルがありますが、CakePHPがそれらを使う処理に差し掛かった時に読もうと思います。
さて、.htaccess
を見てみましょう。
<IfModule mod_rewrite.c> RewriteEngine on RewriteRule ^$ app/webroot/ [L] RewriteRule (.*) app/webroot/$1 [L] </IfModule>
mod_rewriteによって全てのアクセスをhttp://example.jp/app/webroot/$1
に転送しています。$1
は例えばhttp://example.jp/posts/view/13
というURLにアクセスした場合posts/view/13
の部分です。つまりこの例でいうとhttp://example.jp/app/webroot/posts/view/13
にアクセスしたことになります。
app/webroot/を見てみる
example.jp/app/webroot/ css/ files/ img/ js/ .htaccess favicon.ico index.php test.php
なんか見慣れた風景が出てきましたね。ここにも.htaccess
があるので見てみます。
<IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] </IfModule>
この.htaccess
はリクエストされたディレクトリやファイルが存在しない場合はindex.php
に転送しますという意味です。.htaccess
ファイルはそれが置かれているディレクトリ以下全てのディレクトリで有効です。
ここまでの流れをちょっと書いてみます。
(簡単な流れ) ①http://example.jp/posts/view/13 にアクセス! ↓ ②http://example.jp/app/webroot/posts/view/13 に転送!(最上階の.htaccessファイルによって) ↓ ③http://example.jp/app/webroot/index.php に転送!?(webroot/の.htaccessファイルによって)
つまりhttp://example.jp/以下にどんなURLでアクセスしても、http://example.jp/app/webroot/index.php
に転送されちゃいます。このような全てアクセスを最初に受け付けるファイルのことをフロントコントローラと呼ぶようです。(断定はしない)
フロントコントローラを用いるメリットはたくさんあって、それは後々わかりますが一例を言うと、全てのアクセスの入り口を一箇所にすることで、全ての処理で共通する処理をもれなく行えるということでしょうか。
さきほどの(簡単な流れ)の②から③でposts/view/13
が消えてます。どうするのかはwebroot/index.php
を見ればわかるようです。
/app/webroot/index.phpを見る
初めに色々定数を定義しています。
<?php if (!defined('DS')) { define('DS', DIRECTORY_SEPARATOR); } if (!defined('ROOT')) { define('ROOT', dirname(dirname(dirname(__FILE__)))); } //ROOT = 'http://example.jp' if (!defined('APP_DIR')) { define('APP_DIR', basename(dirname(dirname(__FILE__)))); } //APP_DIR = 'http://example.jp/app' if (!defined('WEBROOT_DIR')) { define('WEBROOT_DIR', basename(dirname(__FILE__))); } //WEBROOT_DIR = 'http://example.jp/app/webroot' if (!defined('WWW_ROOT')) { define('WWW_ROOT', dirname(__FILE__) . DS); } //WWW_ROOT = 'http://example.jp/app/webroot/'
DIRECTORY_SEPARATOR
はファイルパスの区切り文字を返す定数です。これ→/
。PHPマニュアルのコメントにはあんまり使う意味ないと書いてあります。*2dirname(__FILE__)
はそのファイルが物理的置いてあるディレクトリのことです。
ここではCakePHPはまず初めにCakePHP自身のディレクトリ構造を把握して定義していると思われます。続きを見ます。
<?php //毎回<?phpって書いているのは、書かないとmarkdown記法だとコードに色が付かないんです。 // for built-in server if (php_sapi_name() === 'cli-server') { if ($_SERVER['REQUEST_URI'] !== '/' && file_exists(WWW_ROOT . $_SERVER['PHP_SELF'])) { return false; } $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); }
php_sapi_name()
?ちょっとよくわからない関数が出てきたので、マニュアルを見ます。
string php_sapi_name ( void )
PHP が使用しているインターフェイス (サーバー API、SAPI) の型を小文字の文字列で返します。たとえば、CLI 版の PHP ではこの文字列は "cli" となります。Apache と組み合わせて使用している場合は、 実際に使用している SAPI によってさまざまな結果となります。 返されうる値の一覧を以下にあげます。
CLI版のPHPとは一体...?5.2とか5.5とかバージョン以外にもなんたら版のPHPがあるんですか?
調べてみると、PHPはRubyなどと違って単体で成立する言語ではないようです。これはそうですね、Webサーバー上で走ります。で、Webサーバーとのやりとりのためのインターフェース(まぁやりとりの仕方ですね)の型がいくつかあって、それがCLI版やCGI版みたいです。php_sapi_name()
はその型を調べています。
<?php if (php_sapi_name() === 'cli-server') { ...
ここではその型がcli-serverかどうかを調べています。cli-serverというのはPHP5.4からCLI版のPHPの内部に組み込まれたWebサーバーのことです(ビルトインウェブサーバー)。つまり、apacheやnginxを立てなくてもPHP単体でWeb環境を作ることが出来、開発用に使う事ができます。すげーな。cli-serverを使っているかどうかでパスの値を変えているようですね。
僕の環境はPHP5.3だし、ビルトインウェブサーバーとかいきなり言われても困るのでこの件については別エントリに譲ります。ちなみにphp -v
コマンドで何版か見れます。
$ php -v PHP 5.3.8 (cli) (built: Aug 23 2011 15:23:25) Copyright (c) 1997-2011 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2011 Zend Technologies
確かに、バージョンの後ろに書いてますね。気付かなかった。 あと長すぎるのでここでビール投入。次に進みます。
<?php if (!defined('CAKE_CORE_INCLUDE_PATH')) { if (function_exists('ini_set')) { ini_set('include_path', ROOT . DS . 'lib' . PATH_SEPARATOR . ini_get('include_path')); } if (!include 'Cake' . DS . 'bootstrap.php') { $failed = true; } } else { if (!include CAKE_CORE_INCLUDE_PATH . DS . 'Cake' . DS . 'bootstrap.php') { $failed = true; } } if (!empty($failed)) { trigger_error("CakePHP core could not be found. Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php. It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR); }
ini_set()
は指定した設定オプションの値を設定します。(PHP: ini_set - Manual)。ここで設定している'include_path'
はマニュアルによると
require、 include、 fopen()、 file()、 readfile() および file_get_contents() 関数がファイルを 探すディレクトリのリストを指定します。フォーマットは、システ ムの環境変数 PATHと同じです。つまり、UNIXでは コロンで、Windowsではセミコロンで区切ったディレクトリのリスト で指定します。 PHP は、インクルードするファイルを探す際に インクルードパスの各エントリを個別に調べます。 まず最初のパスを調べ、見つからなければ次のパスを調べ、…… というように、ファイルが見つかるか warning あるいは error が発生するまで続けます。
なるほど、よくinclude()
関数を使うときはファイルのパスはフルパスで書けとあちこちで言われますが、このinclude_path
という設定オプションでデフォルトで探しに行く場所が決められていたんですね。
PATH_SEPARATOR
はLinux環境では:
の意。ini_get()
は引数で指定した設定オプションの値を取得。つまりここでは、元々のrequire()
などで探すディレクトリよりも先にhttp://example.jp/lib
を探してね、というところでしょうか。そして読み込み失敗したらエラーだすよということですね。
trigger_error()
は第2引数にE_USER_ERROR
をしていると第1引数でしていた文字列とともにfatal errorを出します。(E_USER_ERROR)
まぁここのif文の中は/lib/Cake/bootstrap.php
を読み込んでるわけです。ビール2本目。
流れに従ってbootstrap.php
を見てみましょう。
やばいもう何書いてるかわからへん。。
続きは次回に。。
カシュッ