Ethna v2.5.0のソースコードを読み解く Part1 【Ethna_ActionForm.php】
ethnaで書いてて、ふと思った
$hoge = $this->af->get('hoge');
で、hogeが渡されてこなかった場合、$hogeにはnullが入るのか?空が入るのか?
書けばすぐに分かるけど、せっかくなのでEthnaの元コード読んで見る事に
download
404 Not Found - Ethna
download && install 情報は公式に詳しくて書いてあります。
とりあえず、wgetして落として解凍
[ryoff@ryoff ~/ethna]$ tree -L 2 . |-- Ethna-2.5.0 | |-- CHANGES | |-- Ethna.php | |-- LICENSE | |-- README | |-- bin | |-- class | |-- misc | |-- skel | |-- test | `-- tpl `-- package.xml
ControllerやActionClass、ActionFormなどは、class以下にあります。
Ethna_ActionForm.php
$this->af
とあるように、afはActionFormの略です。
なので、
を読みます。
get
132 /** 133 * フォーム値のアクセサ(R) 134 * 135 * @access public 136 * @param string $name フォーム値の名称 137 * @return mixed フォーム値 138 */ 139 function get($name) 140 { 141 return $this->_getVarsByFormName($this->form_vars, $name); 142 }
$nameを受け取って、 $this->form_vars と $name を _getVarsByFormName関数に渡していますね。
その引数を返す、と。
form_vars
_getVarsByFormName関数を見るまえに、form_varsという変数を覗きましょう。
form_varsは40行目に初期値が定義されてます。
39 /** @var array フォーム値 */ 40 var $form_vars = array();
この変数に値をsetしているのは、setFormVars関数です。
299 /** 300 * ユーザから送信されたフォーム値をフォーム値定義に従ってインポートする 301 * 302 * @access public 303 */ 304 function setFormVars() 305 { 306 if (isset($_SERVER['REQUEST_METHOD']) == false) { 307 return; 308 } else if (strcasecmp($_SERVER['REQUEST_METHOD'], 'post') == 0) { 309 $http_vars =& $_POST; 310 } else { 311 $http_vars =& $_GET; 312 } 313 314 // 315 // ethna_fid というフォーム値は、フォーム定義に関わらず受け入れる 316 // これは、submitされたフォームを識別するために使われる 317 // null の場合は、以下の場合である 318 // 319 // 1. フォームヘルパが使われていない 320 // 2. 画面の初期表示などで、submitされなかった 321 // 3. {form name=...} が未設定である 322 // 323 $this->form_vars['ethna_fid'] = (isset($http_vars['ethna_fid']) == false 324 || is_null($http_vars['ethna_fid'])) 325 ? null 326 : $http_vars['ethna_fid']; ...... ......
なのですが、この関数はEthna_ActionForm内からは呼ばれていません。
呼び出し元は、同ディレクトリ内になるEthna_Controllerからです。
./class/Ethna_Controller.php
_trigger_WWW関数内でsetFormVars関数が呼び出されています。
897 /** 898 * フレームワークの処理を実行する(WWW) 899 * 900 * 引数$default_action_nameに配列が指定された場合、その配列で指定された 901 * アクション以外は受け付けない(指定されていないアクションが指定された 902 * 場合、配列の先頭で指定されたアクションが実行される) 903 * 904 * @access private 905 * @param mixed $default_action_name 指定のアクション名 906 * @param mixed $fallback_action_name アクション名が決定できなかった場合に実行されるアクション名 907 * @return mixed 0:正常終了 Ethna_Error:エラー 908 */ 909 function _trigger_WWW($default_action_name = "", $fallback_action_name = "") 910 { ..... ..... 949 // アクションフォーム初期化 950 // フォーム定義、フォーム値設定 951 $form_name = $this->getActionFormName($action_name); 952 $this->action_form =& new $form_name($this); 953 $this->action_form->setFormDef_PreHelper(); 954 $this->action_form->setFormVars(); 955 $backend->setActionForm($this->action_form);
_trigger_WWW関数はethnaのメインの処理を行います。
こちらはまた別途読み解いてみます。
今回は、
Controller 内で関数が呼び出されて、htmlのformに入力された値がform_varsにセットされるんだな
ぐらいに思っておきます。
前置きが長くなりましたが、これで、_getVarsByFormName関数に渡す、$this->form_varsという値には、htmlのFormに入力された値がセットされていることが分かりました。
_getVarsByFormName
206 /** 207 * 配列の中からキーで指定された要素を取り出す 208 * 209 * @access private 210 * @param array &$target 対象とする配列 211 * @param string $nane キー 212 * @return string 指定された要素 213 */ 214 function _getVarsByFormName(&$target, $name) 215 { 216 $keys = $this->_getFormNameArray($name); 217 return $this->_getVarsByKeys($target, $keys); 218 }
_getFormNameArray関数に$nameを渡して$keysを受け取り、
_getVarsByKeys関数に$targetと$keysを渡して、戻り値を返しています。
_getVarsByKeys関数で返される値がそのまま
$this->af->get('hoge');
で返される値なので、この二つの関数を読み解けば、戻り値のパターンが分かりそうです。
_getFormNameArray
185 /** 186 * フォーム名に対応するキーの配列を返す 187 * 188 * @access private 189 * @param string $name フォーム名 190 * @return array キーの配列 191 */ 192 function _getFormNameArray($name) 193 { 194 // 多次元配列を指定した場合 195 if (preg_match('/^.*\[[^\]]+\]$/', $name)) { 196 $buff = preg_replace('/\]\[/', '[', $name); // hoge[foo][bar] => hoge[foo[bar] 197 $buff = preg_replace('/\]/', "", $buff); // hoge][foo[bar] => hoge[foo[bar 198 $ret = explode('[', $buff); // hoge[foo[bar => array('hoge', 'foo', 'var') 199 } else { 200 // 多次元配列を指定していない場合 201 $ret = array($name); 202 } 203 return $ret; 204 }
多次元配列を指定した場合と指定していない場合で、処理が分かれています。
多次元配列を渡すとは、具体的にどんな事をしてくれるのか。
公式サイトに答えが書いてあります。
フォーム定義を以下のように [] 付きのキーで分類して定義するだけです。例を以下に示します。 [phone][home]や、[phone][mobile] のように、複数の階層を使って定義することも可能です。 ※テンプレートの例 <form method="post"> <input type="text" name="User[name]" value="宮崎あおい" /> <input type="text" name="User[phone][home]" value="01-2345-6789" /> <input type="text" name="User[phone][mobile]" value="090-1234-5678" /> <input type="submit"> </form>
サンプルに宮崎あおい、と使用するあたり、非常に好感が持てます。
そして、この方法で、User[name] User[phone]などと階層の途中からでも値を取得できます。
$this->af->get('User[name]') $this->af->get('User[phone]')
こんな感じ。
196〜198行目で多次元配列をexplodeして、配列にしてますね。
_getVarsByKeys
引き続き、_getVarsByKeys関数です。
_getFormNameArray関数で配列になって戻った$keysを順次処理していきます。
235 /** 236 * 配列の中からキーで指定された要素を取り出す 237 * 238 * @access private 239 * @param array &$target 対象とする配列 240 * @param array $keys キーの配列 241 * @return string 指定された要素 242 */ 243 function _getVarsByKeys(&$target, $keys) 244 { 245 $count = count($keys); 246 if ($count == 0) { // 探索完了 247 return $target; 248 } elseif ($this->max_form_deps + 1 <= $count) { // 深すぎる配列を制限する 249 return null; 250 } 251 252 // まだ探索するキーが残っている 253 $curval = array_shift($keys); 254 if (is_array($target) && array_key_exists($curval, $target)) { 255 return $this->_getVarsByKeys($target[$curval], $keys); 256 } 257 return null; 258 }
max_form_deps はデフォルトでは10階層となっているようです。
$target(=form_vars) 内から、$keysに相当するデータを取得しています。
$keysがなくなるまで、再帰処理してますね。
つまり結果は、
それぞれ帰ります。
関数まねして動かしてみると、
1 <?php 2 3 $param = array( 4 'a' => 1, 5 'b' => array( 6 'c' => 2, 7 'd' => array( 8 'e' => 3, 9 ), 10 ), 11 ); 12 function getVarsByKeys(&$param, $keys) { 13 $count = count($keys); 14 if ($count == 0) { 15 return $param; 16 } 17 18 $curval = array_shift($keys); 19 if (is_array($param) && array_key_exists($curval, $param)) { 20 return getVarsByKeys($param[$curval], $keys); 21 } 22 return null; 23 } 24 25 var_dump(getVarsByKeys($param, array('a'))); 26 // int(1) 27 var_dump(getVarsByKeys($param, array('b'))); 28 // array(2) { 29 // ["c"]=> 30 // int(2) 31 // ["d"]=> 32 // array(1) { 33 // ["e"]=> 34 // int(3) 35 // } 36 // } 37 var_dump(getVarsByKeys($param, array('b','d','e'))); 38 // int(3) 39 var_dump(getVarsByKeys($param, array('b','d','f'))); 40 // NULL 41 42 ?>
な感じ。