ZendFramework2ガイド

機能編

バリデータ

バリデータクラスの基本的な使い方

フォームから入力を受け付けるようなWebアプリケーションの場合、入力に対する検証は必須です。
検証は必須入力チェック、数値チェック、文字数チェックなど、たくさんの種類がありますが、それらについていちいちif文などでやっていては面倒です。
そこでZend2で用意されているバリデータを利用します。

バリデータは、Zend\Validator名前空間またはZend\i18n\Validator名前空間に全て存在しており、検証の種類ごとにクラスになっています。
たとえば必須入力の検証にはZend\Validator\NotEmptyクラス、文字数の検証にはZend\Validator\StringLengthクラスなど。
これらはすべてZend\Validator\AbstractValidator抽象クラスを継承しており、setOptionsメソッドで検証の詳細な設定、isValidで検証の実行など、同じメソッドで検証が行えるようになっています。

一部のバリデータはZend\i18n\Validator名前空間に属していますが、これの利用にはPHPのINTLモジュール(国際化モジュール)が必要です。
PHP5.3以降は標準となっていますが、入っていない場合はインストールする必要があります。
i18nはinternationalizationの略で、iとnの間に18文字あるからi18nと略されます。
たとえばAlnumは半角英数のバリデータですが、標準的なアルファベットは国によって異なるため、それに対応する意味でZend\i18nの下にあるわけです。

バリデータ単体での検査

バリデータクラスはいろいろな利用方法がありますが、まずは各バリデータを単体で単純に入力検証を行う方法です。

use Zend\Validator;

$empty = '';

$validator = new Validator\NotEmpty();
if (!$validator->isValid($empty)) {
    $messages = $validator->getMessages();
}
foreach ($messages as $message) {
    echo $message . '<br>';
}
Value is required and can't be empty

isValidメソッドに検査対象の値を渡すと、結果をbooleanで返してくれます。
結果がNGだった場合はfalseが返ってくるので、その場合にはgetMessagesメソッドでエラーメッセージが得られます。

検証条件の設定

バリデータの中には検証条件の設定が必要な物があります。例えば文字の長さチェックを行うStringLengthなどです。
StringLengthは、最大文字数や最小文字数を指定した上で検証する必要があります。
これはコンストラクタや専用の設定メソッド、またはsetOptionsメソッドで指定することが出来ます。

$string = 'abcdef';
$validator = new Validator\StringLength(array('max' => 5));
if (!$validator->isValid($string)) {
    $messages = $validator->getMessages();
}
foreach ($messages as $message) {
    echo $message . '<br>';
}
The input is more than 5 characters long

この例ではコンストラクタで指定していますが、他にも以下のように指定することも出来ます。

// コンストラクタで指定
$validator = new Validator\StringLength(array('max' => 5));

// setOptionsメソッドで設定
$validator = new Validator\StringLength();
$validator->setOptions(array('max' => 5));

// 専用のメソッドで設定
$validator = new Validator\StringLength();
$validator->setMax(5);

エラーメッセージを任意に設定する

デフォルトのエラーメッセージは英語ですが、これを好きなメッセージに変えることが出来ます。

$validator = new Validator\NotEmpty();
$validator->setMessage('入力してください');
if (!$validator->isValid($empty)) {
    $messages = $validator->getMessages();
}
foreach ($messages as $message) {
    echo $message . '<br>';
}
入力してください。

setMessageメソッドで指定するだけです。
ただ、バリデータによっては、検証結果でメッセージが変わるものがあります。
例えばStringLengthではminとmaxのどちらがNGになるかでメッセージが変わります。

$validator = new Validator\StringLength();
$validator->setOptions(array('max' => 5, 'min' => 3));

if (!$validator->isValid('abcdef')) {
    $messages = $validator->getMessages();
    echo reset($messages) . '<br>';
}
if (!$validator->isValid('ab')) {
    $messages = $validator->getMessages();
    echo reset($messages) . '<br>';
}
The input is more than 5 characters long
The input is less than 3 characters long

setMessageメソッドでメッセージを設定する場合、これら全てのメッセージが指定したメッセージになってしまいます。
このような場合、setMessagesメソッドでそれぞれのメッセージを別々に指定することができます。

use Zend\Validator\StringLength;

$validator = new StringLength();
$validator->setOptions(array('max' => 5, 'min' => 3));
$validator->setMessages(array(
    StringLength::TOO_SHORT => '%min% 文字以上で入力してください',
    StringLength::TOO_LONG => '%max% 文字以下で入力してください',
));

if (!$validator->isValid('abcdef')) {
    $messages = $validator->getMessages();
    echo reset($messages) . '<br>';
}
if (!$validator->isValid('ab')) {
    $messages = $validator->getMessages();
    echo reset($messages) . '<br>';
}
5 文字以下で入力してください
3 文字以上で入力してください

主なバリデータクラス

以下にバリデータクラスの主なものを紹介します。

NotEmpty

必須入力検査を行います。

$validator = new NotEmpty();
$validator->setMessages(array(
    NotEmpty::IS_EMPTY => '入力してください'
));

StringLength

文字列の長さの検査を行います。
オプションで指定可能な項目は以下のとおりです。

min 最小の長さを指定します
max 最大の長さを指定します。
encoding 入力文字列の文字コードを指定します。デフォルトは'UTF-8'です。入力文字列がUTF-8以外の場合は正しく指定しないと正しい検証結果が得られません。
$validator = new StringLength();
$validator->setOptions(array(
    'min' => 4,
    'max' => 8,
    'encoding' => 'UTF-8',
));
$validator->setMessages(array(
    StringLength::TOO_SHORT => '%min% 文字以上で入力してください',
    StringLength::TOO_LONG => '%max% 文字以下で入力してください',
));

Digits

数値検査を行います。
数字文字列ではなく数値です。1.5など少数の数値や-3など負の数値など、PHPが数値として扱える物はtrueになります。

$validator = new Digits();
$validator->setMessages(array(
    Digits::NOT_DIGITS=> '数値で入力してください',
    Digits::STRING_EMPTY=> '入力してください',
));

Alnum

半角英数、つまり、半角のアルファベットまたは数字のみで構成された文字列であるかを検査します。
このバリデータはZend\Validator名前空間ではなく、Zend\I18n\Validatorにあります。

allowWhiteSpace ホワイトスペース文字(スペースやタブなど)を許可するか否かをbooleanで指定します。デフォルトはfalseです。
use Zend\I18n\Validator\Alnum;

$validator = new Alnum();
$validator->setOptions(array(
    'allowWhiteSpace' => false,
));
$validator->setMessages(array(
    Alnum::NOT_ALNUM => '半角英数で入力してください',
    Alnum::STRING_EMPTY => '入力してください',
));

Alpha

半角のアルファベットのみで構成された文字列であるかを検査します。
このバリデータはZend\Validator名前空間ではなく、Zend\I18n\Validatorにあります。

allowWhiteSpace ホワイトスペース文字(スペースやタブなど)を許可するか否かをbooleanで指定します。デフォルトはfalseです。
use Zend\I18n\Validator\Alpha;

$validator = new Alpha();
$validator->setOptions(array(
    'allowWhiteSpace' => false,
));
$validator->setMessages(array(
    Alpha::NOT_ALPHA => '半角英数で入力してください',
    Alpha::STRING_EMPTY => '入力してください',
));

Regex

正規表現にマッチするかを検証します。

pattern 正規表現を指定します。指定する正規表現はpreg_match関数で指定するのと同じ形式のPerl互換の正規表現です。
$validator = new Regex();
$validator->setOptions(array(
    'pattern' => '/^[0-9]{4}$/',
));
$validator->setMessages(array(
    Regex::NOT_MATCH => '数字4桁で入力してください',
));

Between

条件の2つの数値の間の範囲の数値であるかを検査します。

min 許容する範囲の最小の数値を指定します
max 許容する範囲の最大の数値を指定します
inclusive minとmaxで指定した値とイコールの値を許容するか否かを指定します
$validator = new Between();
$validator->setOptions(array(
    'min' => 4,
    'max' => 8,
    'inclusive' => false,
));
$validator->setMessages(array(
    Between::NOT_BETWEEN => '%min% 以上 %max% 以下 で入力してください',
    Between::NOT_BETWEEN_STRICT => '%min% より大きく %max% より小さい数値 で入力してください',
));

Identical

条件で指定した値と、検査対象が一致するか否かを検証します。
パスワードの入力で確認入力があるような場合などの検証に利用できます。

token 検査対象値と比較したい値を指定します。
$validator = new Identical();
$validator->setOptions(array(
    'token' => $data['password'],
));
$validator->setMessages(array(
    Identical::NOT_SAME => 'パスワードが一致しません',
));
$validator->isValid($data['password_confirm']);

InArray

検査対象値が、条件で指定した配列に含まれているか否かを検査します。

haystack 許可する値の配列を指定します
recursive haystackが多次元配列になっている場合、2階層目以降も検査するか否かを指定します。
strict 型を検査対象とするか否かを指定します。以下の3つの定数のいずれかを指定します。デフォルトはInArray::COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITYです。

・InArray::COMPARE_STRICT:型を検査対象とし、値も型も一致して初めて検査結果trueとなります。
・InArray::COMPARE_NOT_STRICT:型は検査対象としません。例えば数値の1と文字列の'1'でも一致とみなします。
・InArray::COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY:数値と数字文字列のみ一致としてみなします。その他については型も検査対象です。
$validator = new InArray();
$validator->setOptions(array(
    'haystack' => array(1,2,3),
    'recursive' => false,
    'strict' => InArray::COMPARE_STRICT,
));
$validator->setMessages(array(
    InArray::NOT_IN_ARRAY=> '1,2,3の何れかで入力してください',
));

自作バリデータクラス

バリデータクラスは自作することが出来ます。
バリデータクラスとしては必要なことはZend\Validator\AbstractValidatorクラスを継承することです。
そしてAbstractValidatorクラスはZend\Validator\ValidatorInterfaceというインターフェイスを実装しており、このインターフェイスではisValidというメソッドの実装が強制されており、AbstractValidatorクラスではこのメソッドが未実装のため、AbstractValidatorを継承したクラスでは必ずisValidメソッドを実装する必要があります。
上で説明しているバリデータクラスの使い方を見ていただければ分かる通り、検査を実行するメソッドがisValidです。そのため、自作バリデータを作る場合でも、isValidメソッドで検査を実行するロジックを書き、適切にtrueまたはfalseを返すようにする必要があります。

例として、全て平仮名で入力されているかを検査するクラスを作ってみましょう。

namespace Zend\Validator;

class Hiragana extends AbstractValidator
{
    const NOT_HIRAGANA = 'notHiragana';

    protected $messageTemplates = array(
        self::NOT_HIRAGANA => '平仮名で入力してください',
    );

    public function isValid($value)
    {
        if (!preg_match('/^[ぁ-ゞ]+$/u')) {
            $this->error(self::NOT_HIRAGANA);
            return false;
        }
        return true;
    }
}

クラスの作りは標準のバリデータクラスを参考にしてもらえれば良いと思います。
isValidメソッドが必要なことは上述のとおりですが、その他にも標準のバリデータと同様に扱えるクラスとするにはいくつかやるべきことがあります。それらを下で説明します。

getMessagesでエラーメッセージを取得できるようにする

上の例のとおりですが、まず$messageTemplatesというクラス変数を設けます。この$messageTemplatesは配列とし、適当なキーでエラーメッセージを定義します。キーは定数にするとよいでしょう。
isValidメソッドで検査ロジックを書くわけですが、条件に合わなかった場合にはfalseを返す前にエラーメッセージをセットする必要があります。セットする方法は、$this->errorメソッドを実行し、引数に$messageTemplatesで定義したエラーメッセージのキーを指定します。
これでgetMessagesメソッドにより、エラーメッセージが取得できるようになります。

オプションを指定できるようにする

標準のバリデータではオプションとして検査条件が指定できるものがいくつか存在します。StringLengthにおける'max'や'min'の指定などです。これの実装方法です。
以下はその例です。上の平仮名バリデータを少し拡張し、片仮名まで含めた仮名バリデータに作り変えました。
そして片仮名を許容するか否かを、allowKatakanaというオプションで指定できるようにしました。

namespace Zend\Validator;

class Kana extends AbstractValidator
{
    const NOT_KANA = 'notKana';

    protected $messageTemplates = array(
        self::NOT_KANA => '仮名で入力してください',
    );

    protected $options = array(
        'allowKatakana' => true,
    );

    public function setAllowKatakana($allow)
    {
        $this->options['allowKatakana'] = $allow;
    }

    public function isValid($value)
    {
        $pattern = '/^[ぁ-ゞ]+$/u';
        if ($this->options['allowKatakana'] == true) {
            $pattern = '/^[ぁ-ゞァ-ヾ]+$/u';
        }
        if (!preg_match($pattern)) {
            $this->error(self::NOT_KANA);
            return false;
        }
        return true;
    }
}

まず、クラス変数として$optionsを設け、必要なオプションを配列として定義します。配列はオプションのキーとそのデフォルト値で定義します。例では'allowKatakana'を定義しました。
次に、そのオプションのキーの前に"set"を付けた名前のメソッドを定義し、オプションの設定を外側から行えるメソッドとして定義します。例ではallowKatakanaというオプションなので、'setAllowKatakana'です。例では単純にセットするロジックですが、実際はパラメータの型チェックなどを行うべきです。
そして、オプションの状態によって検査ロジックを切り分けます。例ではallowKatakanaがtrueであれば、片仮名も含めた正規表現チェックを行っています。
このような感じでオプションに対応することができます。

標準のバリデータを更に継承する

上の例の自作バリデータは正規表現でチェックを行っています。
お気づきの方もあるかもしれませんが、標準のバリデータに正規表現チェックを行うバリデータがあります。Regexです。なので、このRegexを更に継承するともっと単純なクラスにすることが出来ます。

namespace Zend\Validator;

class Hiragana extends Regex
{
    public function __construct()
    {
        parent::__construct('/^[ぁ-ゞ]+$/u');
        $this->setMessage(array(Regex::NOT_MATCH => '平仮名で入力してください'));
    }
}

標準のRegexクラスはコンストラクタで正規表現パターンの指定が必要になっています。
今回は平仮名の検証のクラスとするため、このRegexにデフォルトで平仮名をチェックする正規表現を指定するような形にすれば目的は達せられることになります。Regexではコンストラクタでpatternの指定が必須ですが、継承したHiraganaクラスでは引数なしのコンストラクタを定義し、親クラスであるRegexのコンストラクタに平仮名チェックを行う固定の正規表現パターンを指定するようにします。
ついでにデフォルトのエラーメッセージもセットしておきます。

このように、標準で存在するバリデータを拡張するという方法も有効です。

バリデータチェーン

バリデータチェーンとは、ある一つの検証対象入力値に対して、復数のバリデーションを一度に行う仕組みです。
例えば、ある入力に対して、必須入力チェックと文字列長チェックとアルファベットのみチェックの3つの検証をを行うとしましょう。
3つのバリデータによるチェックを行うわけですから、普通に考えると以下のようになります。

use Zend\Validator\NotEmpty;
use Zend\Validator\StringLength;
use Zend\I18n\Validator\Alpha;

$input = 'hogehoge1';

$validator = new NotEmpty();
if (!$validator->isValid($input)) {
    echo reset($validator->getMessages()) . '<br>';
}
$validator = new StringLength(array('max' => 5));
if (!$validator->isValid($input)) {
    echo reset($validator->getMessages()) . '<br>';
}
$validator = new Alpha();
if (!$validator->isValid($input)) {
    echo reset($validator->getMessages()) . '<br>';
}

単純にNotEmptyとStringLengthとAlphaという3つのバリデータで順番に検証しています。しかしバリデータごとに同じようなコードが並んでおり、なんとなく無駄な感じがします。
そこで、3つの検証を一気にやってしまうクラスが存在します。それはZend\Validator\ValidatorChainです。
ValidatorChainを利用すると以下のようになります。

use Zend\Validator\ValidatorChain;
use Zend\Validator\NotEmpty;
use Zend\Validator\StringLength;
use Zend\I18n\Validator\Alpha;

$input = 'hogehoge1';

$validatorChain = new ValidatorChain();
$validatorChain->attach(new NotEmpty());
$validatorChain->attach(new StringLength(array('max' => 5)));
$validatorChain->attach(new Alpha());

if (!$validatorChain->isValid($input)) {
    foreach ($validatorChain->getMessages() as $message) {
        echo $message . '<br>';
    }
}
The input is more than 5 characters long
The input contains non alphabetic characters

よく見るとなんてことはない、ValidatorChainというクラスに、復数のバリデータを先に登録して、一度に実行できるというだけの仕組みです。

ValidatorChainでは登録した順番に検証を行いますが、上の例だと入力に対してStringLengthとAlphaでひっかかり、2つのメッセージが出力されています。ということは、2つめでNGとなっても続けて3つ目の検証も行っていることになります。でも場合によっては最初にNGとなった時点でそのエラーメッセージのみを返し、それ以降の検証に進まないで欲しい場合もあります。このような場合はattachメソッドの2つ目の引数にtrueを指定します。そうすると、そのバリデータでNGとなったら以降の検証は行なわれずに中断します。

$input = 'hogehoge1';

$validatorChain = new ValidatorChain();
$validatorChain->attach(new NotEmpty(), true);
$validatorChain->attach(new StringLength(array('max' => 5)), true);
$validatorChain->attach(new Alpha(), true);

if (!$validatorChain->isValid($input)) {
    foreach ($validatorChain->getMessages() as $message) {
        echo $message . '<br>';
    }
}
The input is more than 5 characters long

検証対象値は3つ目の検証に引っかかるはずですが、2つ目しか検証していないのがわかります。