FuelPHP の Validation を読んでみた

## FuelPHP の Validation を読んでみた
最近 業務で PHP を使うことになったので、趣味で FuelPHP を使い始めました。その中で FuelPHP の Validation を使おうと思うのに腰が重かったので、使う前に実装を読んでみようと思いたち、フォーム の Validation 部分の実装を読んでみました。

ただ、FuelPHP は core の開発が最近あまり進んでいなさそうなのが何とかならないですかね。。。
~~※ 趣味で読んでいる Mocha が難しすぎて、少し気休めが欲しかったのもありますが...~~

## FuelPHP の Validation とは？
FuelPHP の Validation とは、フォーム を使う時に、フォーム の内容が正しいかをチェックする機能で、下のような ファイルで書きます([こちらのサイト](https://sil.hatenablog.com/entry/fuelphp-default-validation-rule-specification)から借りてきました)。

```php:app/classes/controller/validation.php
<?php

class Controller_Validation extends Controller
{
    public function action_index()
    {
      $form = Fieldset::forge();
      $form->add('name', 'Name')->add_rule('required');
      $form->add('age', 'Age')
                   ->add_rule('required')
                   ->add_rule('numeric_between',0,200);
      $form->add('submit', '', array('type'=>'submit', 'value'=>'Submit'));

      if (Input::method() === 'POST') {
        $val = $form->validation();
        if ($val->run()) {
          print "入力を受付けました";
        } else {
          print "不正な入力です";
        }
        $form->repopulate();
      }
      $view = View::forge('index/get');
      $view->set_safe('form', $form->build('/validation/'));
      return $view;
    }
}
```

コードの上の方の `$form = Fieldset::forge()` からの6行は フォームの定義を、コードの真ん中の方の `if (Input::method() === 'POST') {` からの 9行は フォームのバリデーションを行っています。下の方で、`$form->build` しているので、フォームの入力画面と更新が１つになった画面になっています。

コード自体はそんなには難しくないと思います。

では、中身はどうなっているのでしょうか？見てみましょう。

## 読む順番
今回、コードを読む順番としては、まず

```php
$form = Fieldset::forge();
```

の `Fieldset` の初期化の部分から始め、次に、

```php
$form->add('name', 'Name')->add_rule('required');
```

の `add` と `add_rule` を見ていって、次に、

```php
$val = $form->validation();
```

と

```php
$val->run()
```

の 'validation()' と `run()` を見ていきたいと思います。

## FuelPHP Validation を読んでみる

#### Fieldset の初期化

初期化のコードは[こちら](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/fieldset.php#L44)にあります。ここ単体でやっていることは、`new static` で自身の `__construct` を呼び出し、それを instances の配列に入れているだけです。実体は、`__construct` の初期化の部分(コードは[ここ](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/fieldset.php#L154))にありますが、その中の

```php
$this->validation($config['validation_instance']);
```

で、バリデーションの初期化を、

```php
$this->form($config['form_instance']);
```

でフォームの初期化を行っています。
内容が気になったら、中身を見てみるのもいいでしょう。今回は少しはしょって、次に行きます。

#### add と add_rule

[add の実装](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/fieldset.php#L289)は結構簡単で、普段は string しか渡されないので、主に

```php
$this->fields[$name] = new \Fieldset_Field($name, $label, $attributes, $rules, $this);
```

の部分のみを行っています。
この `$this->fields[$name]` が最後の return になっているので、この `\Fieldset_Field` だけを見れば大丈夫そうですね。

この `Fieldset_Field` は[ここ](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/fieldset/field.php#L23)にあって、その中に add_rule があります（コードだと[ここ](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/fieldset/field.php#L329)）。add_rule の内容は

```php
		$this->rules[] = array($callback, $args);
```

で、配列に追加しているだけなのが分かりますね。
次に、validation を読んでいきます。

#### $form->validation() と validation->run()

validation は 前にも少し出ましたが、[ここ](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/fieldset.php#L184)に実装があって 初期化では `\Validation::forge($this)` しているのが分かります（コードは[ここ](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/fieldset.php#L194)）。
この [forge](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/validation.php#L44) も __construct を呼び出している形になっていますが、この [__construct](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/validation.php#L137) も 再度 `$fieldset->validation($this);` を呼び出しているくらいの簡単な処理になりますね。

次に、`validation->run()` を読んでみます。
詳細ははしょりますが、まずは [ここ](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/validation.php#L350) で 先ほどの `add` で出てきた `fields` を for文で回します。
次に [ここ](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/validation.php#L357) の `$this->input($name);` で `Input::param` の内容を取得します（また、run の最初の部分で `Input::method` が `POST` か判定もしている ）。
次に [ここ](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/validation.php#L365) で 先ほどの `add_rule` した内容を for文で回し、`_run_rule` します。
この [_run_rule](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/validation.php#L467) では、最初の _find_rule_ で rule (add_ruleの第一引数が入っている。required や numeric_between など...) を `_validation_` が先頭についた形にして、それを `call_fuel_func_array` (詳細は[こちら](https://fuelphp.com/forums/discussion/15171/call_fuel_func_array/p1)。基本は call_user_func_array と同じ) で呼び出しています。
この call_fuel_func_array の結果の $output で 

```php
			throw new \Validation_Error($field, $value, $rule, $params);
```

しているのが分かると思います。
この結果が、[catch され](https://github.com/fuel/core/blob/97401c1298096b542ebb53858ddee77852d229e3/classes/validation.php#L380) 最終的に空であるかが返されています。

## 最後に
FuelPHP も一部の機能だけなら、意外と簡単に読めました。
Laravel を使う可能性もあるんですが、FuelPHP を使い続けるなら、もう少しコードを読んでみたいと思います。