toma-stkのCakePHP自習室

2008-03-04basics.php - stripslashes_deep($value) (CakePHP解析 #10) このエントリーを含むブックマーク

今回はdispatcher.phpで呼び出されているstripslashes_deep関数を解析します。

この関数PHPのmagic_quotes_gpcがonの場合でも問題なく動作させるために使用されています。

(※<?phpハイライトカラーを有効にするために記述しています)

<?php
if (ini_get('magic_quotes_gpc') == 1) {
    if (!empty($_POST)) {
        $params['form'] = stripslashes_deep($_POST);
    }
} else {
    $params['form'] = $_POST;
}
?>

ここで、magic_quotes_gpcがonの場合に、裏でどんな処理が行われるのかをおさらいしておきます。

マニュアルによると

オンの場合、全ての' (シングルクオート), " (ダブルクオート), \ (バックスラッシュ)および
NULL 文字がバックスラッシュで自動的にエスケープされます。 これは、addslashes() の機能と同じです。

つまり、「Is your name O'reilly?」は、「Is your name O\'reilly?」に自動的に変換されるわけです。

この変換処理はHTTPリクエストデータ(GET, POST, そして COOKIE)に対して行われます。


そして、この変換されてしまったデータを元に戻すために使用されるのがstripslashes_deep()関数です。

この関数は「cake/basics.php」内に定義されています。

<?php
function stripslashes_deep($value) {
    if (is_array($value)) {
        $return = array_map('stripslashes_deep', $value);
        return $return;
    } else {
        $return = stripslashes($value);
        return $return ;
    }
}
?>

引数配列で無い場合と、配列の場合で処理が分かれています。


まずは配列で無い場合ですが、stripslashes関数で変換した値を返して終了しています。

stripslashes関数はmagic_quotes_gpcが行う変換処理の反対の変換を、つまり

「Is your name O\'reilly?」であれば、「Is your name O'reilly?」に変換します。

次は配列の場合の処理を見てみます。


まずarray_mapですが、これは第一引数で指定されている関数を、第二引数配列の各値を引数にして呼び出す関数です。

ここでポイントになるのはarray_map内でstripslashes_deep関数を、つまり自分自身を関数内で呼び出している点です。

これは再帰関数と呼ばれるものになりますが、初めはなぜ「stripslashes」でなく「stripslashes_deep」を呼び出す必要があるのか疑問でした。

ただし、よくよく考えてみると配列の中身も配列、更にその中身も配列の可能性が…と考えると「なるほど再帰関数ってこういう

使い方をするのか」と感心してしまいました。


この関数を作った人が関数名に「_deep」と付けたのもうなづけます。

再帰関数を理解するのにうってつけのお手本ではないでしょうか。

2008-02-29dispatcher.php - $this->parseParams($url) (CakePHP解析 #9) このエントリーを含むブックマーク

今回は$Route->parseから戻ってparseParamsメソッドの解析をします。

以前解析したように$Route->parse('users/login/aaaa/bbbb/cccc/dddd?a=A&b=B')の戻り値

下記のような配列になります。

Array
(
    [pass] => Array
        (
            [0] => aaaa
            [1] => bbbb
            [2] => cccc
            [3] => dddd
        )
    [controller] => users
    [action] => login
)

この結果を$params変数が受け取っているわけですが、この$params変数は「$this->data」を保持する非常に重要な変数です。

下記のようにその後の処理でform値やurl値を取り込んでいます。

(※<?phpハイライトカラーを有効にするために記述しています)

<?php
if (ini_get('magic_quotes_gpc') == 1) {
    if (!empty($_POST)) {
        $params['form'] = stripslashes_deep($_POST);
    }
} else {
    $params['form'] = $_POST;
}
?>

次回はこの処理の中で使用されている「stripslashes_deep」関数を解析します。

2008-02-18dispatcher.php - $Route->parse【その弐】 (CakePHP解析 #8) このエントリーを含むブックマーク

仕事が急に忙しくなって日が開いてしまいました…。今回は $Route->parseを更に解析する予定でしたが、正直何をしたいのかがいまいち分からない。どうやら$Routeオブジェクトの$routes配列にいろいろ突っ込んでいるようです。

一体どんな値を入れているのかだけ、出力して終了(>_<)

CakePHPのよく理解している人なら見当がつくのかな。

Router Object
(
 [routes] => Array
  (
   [0] => Array
    (
     [0] => /
     [1] => /^[\/]*$/
     [2] => Array
      (
      )

     [3] => Array
      (
       [controller] => schedules
       [action] => index
      )
    )

   [1] => Array
    (
     [0] => /pages/*
     [1] => #^/pages(?:\/(.*))?[\/]*$#
     [2] => Array
      (
      )
     [3] => Array
      (
       [controller] => pages
       [action] => display
      )
    )

   [2] => Array
    (
     [0] => /tests
     [1] => #^/tests[\/]*$#
     [2] => Array
      (
      )
     [3] => Array
      (
       [controller] => tests
       [action] => index
      )
    )

   [3] => Array
    (
     [0] => /bare/:controller/:action/*
     [1] => #^/bare(?:\/([^\/]+))?(?:\/([^\/]+))?(?:\/(.*))?[\/]*$#
     [2] => Array
      (
       [0] => controller
       [1] => action
      )
     [3] => Array
      (
       [bare] => 1
      )
    )

   [4] => Array
    (
     [0] => /ajax/:controller/:action/*
     [1] => #^/ajax(?:\/([^\/]+))?(?:\/([^\/]+))?(?:\/(.*))?[\/]*$#
     [2] => Array
      (
       [0] => controller
       [1] => action
      )
     [3] => Array
      (
       [bare] => 1
      )
    )

   [5] => Array
    (
     [0] => /:controller/:action/* (default)
     [1] => /^(?:\/(?:([a-zA-Z0-9_\-\.\;\:]+)(?:\/([a-zA-Z0-9_\-\.\;\:]+)(?:[\/\?](.*))?)?))[\/]*$/
     [2] => Array
      (
       [0] => controller
       [1] => action
      )
     [3] => Array
      (
      )
    )
  )

 [__admin] => 
 [_log] => 
)

追記

$route配列をよくよく観察してみると、CakePHPの遷移処理を管理しているように見えますね。

下記の例だと

[0] => /
[1] => /^[\/]*$/
[2] => Array
    (
    )
[3] => Array
    (
        [controller] => schedules
        [action] => index
    )

おそらく[0]がルートパスを表していて、[1]が遷移に関する正規表現?、[2]はひとまず置いといて

[3]は実行するコントローラーとアクション。

[1]の正規表現は何を意味しているんだろう?

「/^[\/]*$/」にマッチするのって「/」とか「///」みたいに、ひたすらスラッシュの文字列だけのように思うんだけど。

もう少しCakePHP理解しないと、分からないかな。

2008-02-05dispatcher.php - $Route->parse【その壱】 (CakePHP解析 #7) このエントリーを含むブックマーク

今回は「/cake/dispatcher.php」を解析してます。(めちゃめちゃ手強そう…)

このファイルはbootstrap.phpでrequireされた後に、「app/webroot/index.php」で

インスタンス化され、dispatchメソッドにURLリクエスト変数を渡しています。

(※<?phpハイライトカラーを有効にするために記述しています)

<?php
$Dispatcher = new Dispatcher();
$Dispatcher->dispatch($url);
?>

で、dispatcher.phpでは引数の$url変数をプライベートメソッドで何かしています。

<?php
$params = array_merge($this->parseParams($url), $additionalParams);
?>

parseParams()メソッドの動きを追ってみます。

すると今度はRouteクラスのparseメソッドに引き渡されました。

<?php
$Route = new Router();
include CONFIGS.'routes.php';
$params = $Route->parse ($from_url);
?>

しつこく、Routeクラスが定義されている「cake/libs/router.php」を追ってみます。

これまた、複雑そうな処理がずらずら出てきてしまいました。

なんか、面倒になってきたので一旦この辺で区切ろうと思います。

ちなみに適当なファイルに下記のような処理をほどこしたところ…

<?php
$Route = new Router();
include CONFIGS.'routes.php';
$params = $Route->parse ('users/login/aaaa/bbbb/cccc/dddd?a=A&b=B');

pr($params);    //結果出力
?>

このような結果が返ってきました。

Array
(
    [pass] => Array
        (
            [0] => aaaa
            [1] => bbbb
            [2] => cccc
            [3] => dddd
        )
    [controller] => users
    [action] => login
)

次回は、更につっこんで解析してみます。

2008-01-29basics.php - setUri() (CakePHP解析 #6) このエントリーを含むブックマーク

前回、Configureクラスをもうちょっと突っ込んでと書いてしまいましたが、それほど良いネタがありませんでした。なので、予定を変更して「cake/bootstrap.php」に戻ってそのすぐ後に呼び出されている関数「setUri」を解析してみます。

setUri()

(※<?phpハイライトカラーを有効にするために記述しています)

<?php
function setUri() {
	if (env('HTTP_X_REWRITE_URL')) {
		$uri = env('HTTP_X_REWRITE_URL');
	} elseif (env('REQUEST_URI')) {
		$uri = env('REQUEST_URI');
	} else {
		if (env('argv')) {
			$uri = env('argv');

			if (defined('SERVER_IIS')) {
				$uri = BASE_URL . $uri[0];
			} else {
				$uri = env('PHP_SELF') . '/' . $uri[0];
			}
		} else {
			$uri = env('PHP_SELF') . '/' . env('QUERY_STRING');
		}
	}
	return $uri;
}
?>

簡単なif文のプログラムですね。

ポイントになるのは「env」が何を返しているかということでしょう。この「env」も「basics.php」内に定義されており、引数に応じて$_SERVERや$_ENVの値を返しています。

ここで、setUriはリクエストされたURIを環境に応じて返しているようです。環境というのは、「mod_rewriteを使える/使えない」とか「サーバーApacheIIS」とか。

$uriと$_GET['url']の値をセット

最終的に呼び出しもとの「bootstrap.php」で「?=」以降の値を$uri変数と$_GET['url']変数に格納しているようですね。

<?php
$uri = setUri();

if ($uri === '/' || $uri === '/index.php' || $uri === '/'.APP_DIR.'/') {
	$_GET['url'] = '/';
	$url = '/';
} else {
	if (strpos($uri, 'index.php') !== false) {
		$uri = r('?', '', $uri);
		$elements=explode('/index.php', $uri);
	} else {
		$elements = explode('/?', $uri);
	}

	if (!empty($elements[1])) {
		$_GET['url'] = $elements[1];
		$url = $elements[1];
	} else {
		$_GET['url'] = '/';
		$url = '/';
	}
}
?>

この後、$uriをコントローラー名、アクション名に分解していろいろやっていくんだろうなぁ。


次回は「cake/dispatcher.php」を解析する予定です。