読者です 読者をやめる 読者になる 読者になる

バカンス駆動開発

この前バカンスって言ったら「古っ」って言われました

Dispatcherまで辿りつけない編 - CakePHPをもくじとしてPHPを学ぶシリーズ01

CakePHP php CakePHPをもくじとしてPHPを学ぶシリーズ

パーフェクト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があるんですか?

調べてみると、PHPRubyなどと違って単体で成立する言語ではないようです。これはそうですね、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を使っているかどうかでパスの値を変えているようですね。

PHP: ビルトインウェブサーバー - Manual

僕の環境は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_SEPARATORLinux環境では:の意。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を見てみましょう。

やばいもう何書いてるかわからへん。。

続きは次回に。。

カシュッ

参考