add to hatena hatena.comment (31) add to del.icio.us (0) add to livedoor.clip (0) add to Yahoo!Bookmark (0) Total: 31

PHP5 配列をメソッドチェインで再帰処理するクラス

CakePHPを使っていると、データ構造が多階層の配列で管理されていることが多く、一括で処理をしたい場合などに度々再帰処理を実装することがあります。
通常であれば、array_map系の関数を使って再帰的に処理を記述することになりますが、

  • 毎回array_mapを使うのは面倒だし、もっと簡潔に記述したい
  • 個別のグローバル関数・各メソッドを連続して簡単に利用したい

という理由で、以下のように記述できるクラスを作成しました。
(クラスのソースコードは最後に記述しています)

PHP:
  1. // $arrの全ての要素にtrimと半角カナ変換を実施
  2. ArrayMap::create($arr)->trim()->mb_convert_kana('KV');
  3.  
  4. // $arrの全ての要素に「先頭:」という文字を追加
  5. ArrayMap::create($arr)->sprintf('先頭:%s');

※PHP5のマジックメソッドを使って実現していますので、PHP4系では使えません
※通常の手続き型で実装する場合は「array_walk_recursive」「array_map recursive」などで検索するといろいろ参考情報があります。

利用メリット

あくまで個人的な意見ですが、、 

  • PHPの標準関数をそのまま利用できる
  • 自作関数やメソッドを作成する際に再帰処理の考慮が不要になり、単純な処理だけを作成→利用時に合成とすることができる(より再利用できる)
  • ソース可読性の向上

利用書式

1.グローバル関数を実行する場合

ArrayMap::create(配列変数)->グローバル関数([引数1],[引数2],・・・)
->次の関数・メソッド

例:配列$arrの全ての要素にtrimと半角カナ変換を実施

PHP:
  1. ArrayMap::create($arr)->trim()->mb_convert_kana('KV');

2.静的クラスメソッドを実行する場合

ArrayMap::create(配列変数)->call(array(静的クラス名,メソッド名),[引数1],[引数2],・・・)
->次の関数・メソッド

例:配列$arrの全ての要素にInflectorクラスのcamelizeメソッドを実施(引数無し)

PHP:
  1. ArrayMap::create($arr)->call(array('Inflector', 'camelize'));

3.オブジェクトメソッドを実行する場合

ArrayMap::create(配列変数)->call(array(オブジェクト変数,メソッド名),[引数1],[引数2],・・・)
->次の関数・メソッド

例:配列$arrの全ての要素に$thisオブジェクトのconvメソッドを実施(引数1:'a'、引数2:'b')

PHP:
  1. ArrayMap::create($arr)->call(array($this, 'conv'), 'a', 'b');

※呼び出される関数・メソッドは第1引数に処理をする値、第2引数以降に必要であれば引数を設定します。
※ArrayMapクラスから呼び出す際は各要素が自動的に第1引数に設定されるので、第2引数以降を呼び出し時に指定します
※標準関数のstr_replace()やsprintf()などは処理を行う値が最後の引数になっています。
これらはArrayMapクラス内で定義しておけば自動的に最後の引数として設定します。
呼び出し時に動的に最後の引数として要素を渡したい場合は、実行前にp()メソッドを実行する必要があります。

PHP:
  1. ArrayMap::create($arr)->trim()->p()->要素を最後の引数とする関数();

CakePHPでの利用

前半にも記述しましたが、CakePHPでは各データが配列で管理されている為、
以下のような部分で適用できるかと思います。(すいません、未検証です)

  • コントローラーのbeforeFilterで入力値($this->dataや$this->params)の変換(trimや改行・文字コード統一など)
  • モデルのbeforeValidateなどで値の変換(trimや最大長による切り捨て)
  • ビュー内やコントローラーのbeforeRenderなどで値のエスケープ処理

ソースコード

ダウンロード

PHP:
  1. <!--p<br-->#########################################################################
  2. /**
  3. * 配列要素操作クラス
  4. */
  5. #########################################################################
  6. class ArrayMap {
  7. protected $value;
  8. protected $arg_push = false;
  9. protected $is_call = false;
  10.  
  11. // 値を最後の引数とする関数を追加する
  12. public static $auto_push_method = array( 'str_replace', 'preg_replace', 'sprintf', 'date');
  13.  
  14. // コンストラクタ
  15. public function __construct(&amp;$array = array()){
  16. $this-&gt;value =&amp; $array;
  17. return $this;
  18. }
  19. // セッター
  20. public function set($value) {
  21. $this-&gt;value = $value;
  22. return $this;
  23. }
  24. public function setByRef(&amp;$value) {
  25. $this-&gt;value =&amp; $value;
  26. return $this;
  27. }
  28.  
  29. // 値を返す
  30. public function v() {
  31. return $this-&gt;value;
  32. }
  33. // 次に実行される関数は引数の最後に値を渡す
  34. public function p() {
  35. $this-&gt;arg_push = true;
  36. return $this;
  37. }
  38. // 実行する関数・メソッドをグローバル以外から明示的に指定
  39. // 引数を指定する場合は第二引数以降に指定
  40. public function call($method){
  41. $this-&gt;is_call = true;
  42. $arguments = func_get_args();
  43. array_shift($arguments);
  44. return $this-&gt;__call($method, $arguments);
  45. }
  46.  
  47. // 関数実行メソッド
  48. public function __call($method, $arguments){
  49. // 自動的に引数位置を変更するメソッド
  50. if(!$this-&gt;arg_push &amp;&amp; in_array($method, self::$auto_push_method)){
  51. $this-&gt;p();
  52. }
  53. // 値が空の場合
  54. if(empty($this-&gt;value)){
  55. return $this;
  56. }
  57.  
  58. // 値がスカラー値の場合でも動くように
  59. if(is_scalar($this-&gt;value)){
  60. $arg = $arguments;
  61. if($this-&gt;arg_push){
  62. $arg[] = $this-&gt;value;
  63. }else{
  64. array_unshift($arg, $this-&gt;value);
  65. }
  66. $this-&gt;value = call_user_func_array($method, $arg);
  67. $this-&gt;arg_push = false;
  68. $this-&gt;is_call = false;
  69. return $this;
  70. }
  71.  
  72. // 再帰を含めた要素処理
  73. foreach($this-&gt;value as $k =&gt; $v){
  74. $arg = $arguments;
  75. if(is_array($v)){
  76. $arr = new ArrayMap($v);
  77. if($this-&gt;arg_push) $arr-&gt;p();
  78. if($this-&gt;is_call){
  79. array_unshift($arg, $method);
  80. call_user_func_array(array($arr, 'call'), $arg);
  81. }else{
  82. call_user_func_array(array($arr, $method), $arguments);
  83. }
  84. $this-&gt;value[$k] = $arr-&gt;v();
  85. continue;
  86. }
  87.  
  88. if($this-&gt;arg_push){
  89. $arg[] = $v;
  90. }else{
  91. array_unshift($arg, $v);
  92. }
  93. $this-&gt;value[$k] = call_user_func_array($method, $arg);
  94. }
  95. $this-&gt;arg_push = false;
  96. $this-&gt;is_call = false;
  97. return $this;
  98. }
  99.  
  100. #########################################################################
  101. /**
  102. * インスタンス生成用静的メソッド
  103. */
  104. #########################################################################
  105. public static function create(&amp;$array){
  106. return new ArrayMap($array);
  107. }
  108. }

関連するその他の記事

  • No Related Post

Comments

Leave a Reply