Valitron

バリデーションライブラリPHP軽量依存関係なしカスタムバリデーション国際化

ライブラリ

Valitron

概要

Valitronは、依存関係のないシンプルで軽量なPHPバリデーションライブラリです。Vance Lucasによって開発され、読みやすく簡潔な構文でバリデーションを実行できることが特徴です。他のフレームワークの大きなコンポーネントに依存しないため、非常に軽量で効率的。一つの関数呼び出しですべてのバリデーションを実行でき、カスタムバリデーションルールの追加や国際化にも対応しています。小規模から中規模のPHPプロジェクトで手軽にバリデーション機能を導入したい場合に最適な選択肢です。

詳細

Valitron 1.4.xは2025年現在の最新版で、PHPの様々なバリデーションニーズに対応する軽量ライブラリです。required、numeric、email、date、lengthなど30以上の組み込みバリデーションルールを提供し、日常的なWebアプリケーション開発で必要な検証機能を網羅。シンプルなメソッドチェーンによる直感的なAPI設計で、学習コストを最小限に抑制しています。外部依存関係を持たないため、既存のプロジェクトへの導入が容易で、パフォーマンスへの影響も最小限。カスタムルールの追加、国際化対応、詳細なエラーメッセージカスタマイズなど、実用的な機能も充実しています。

主な特徴

  • 依存関係なし: 他のフレームワークやライブラリに依存せず、軽量で高速
  • シンプルな構文: 読みやすいメソッドチェーンによる直感的なバリデーション定義
  • 豊富な組み込みルール: 30以上の一般的なバリデーションルールを標準搭載
  • カスタムルール対応: 独自のバリデーションロジックを簡単に追加可能
  • 国際化サポート: 多言語エラーメッセージとロケール固有の検証
  • 軽量設計: 最小限のメモリ使用量とパフォーマンスオーバーヘッド

メリット・デメリット

メリット

  • 学習コストが低く、すぐに使い始められる
  • 依存関係がないため導入が簡単
  • 軽量でパフォーマンスへの影響が最小限
  • シンプルで読みやすいコード記述が可能
  • カスタムバリデーションの実装が容易
  • 小規模から中規模プロジェクトに最適

デメリット

  • 複雑なバリデーションロジックには不向き
  • 大規模エンタープライズ機能は限定的
  • アノテーションや属性による定義は非対応
  • オブジェクトのネストした検証機能が基本的
  • Symfonyなどの高機能ライブラリと比較すると機能が限定的
  • コミュニティとエコシステムが比較的小規模

参考ページ

書き方の例

インストールと基本セットアップ

# Composerでインストール
composer require vlucas/valitron

# 特定バージョンを指定
composer require vlucas/valitron:^1.4

基本的なバリデーション実装

<?php

require_once 'vendor/autoload.php';

use Valitron\Validator;

// シンプルなバリデーション例
$data = [
    'name' => '',
    'email' => 'invalid-email',
    'age' => 15,
    'website' => 'not-a-url'
];

$v = new Validator($data);

// バリデーションルールを定義
$v->rule('required', ['name', 'email'])
  ->rule('email', 'email')
  ->rule('integer', 'age')
  ->rule('min', 'age', 18)
  ->rule('url', 'website');

// バリデーションを実行
if ($v->validate()) {
    echo "バリデーション成功!\n";
} else {
    echo "バリデーションエラー:\n";
    foreach ($v->errors() as $field => $messages) {
        foreach ($messages as $message) {
            echo "- {$field}: {$message}\n";
        }
    }
}

// 個別の値のバリデーション
$email = '[email protected]';
$emailValidator = new Validator(['email' => $email]);
$emailValidator->rule('required', 'email')->rule('email', 'email');

if ($emailValidator->validate()) {
    echo "メールアドレスは有効です\n";
} else {
    echo "無効なメールアドレス\n";
}

より詳細なバリデーション例

<?php

use Valitron\Validator;

// ユーザー登録フォームのバリデーション
$userData = [
    'username' => 'user123',
    'email' => '[email protected]',
    'password' => 'secret123',
    'password_confirm' => 'secret123',
    'age' => 25,
    'website' => 'https://example.com',
    'phone' => '090-1234-5678',
    'birthday' => '1998-05-15',
    'tags' => ['php', 'web', 'development']
];

$v = new Validator($userData);

// 必須フィールド
$v->rule('required', ['username', 'email', 'password', 'password_confirm']);

// 文字列長のバリデーション
$v->rule('lengthBetween', 'username', 3, 20);
$v->rule('lengthMin', 'password', 8);

// フォーマットバリデーション
$v->rule('email', 'email');
$v->rule('url', 'website');
$v->rule('date', 'birthday');

// 数値範囲バリデーション
$v->rule('integer', 'age');
$v->rule('min', 'age', 18);
$v->rule('max', 'age', 120);

// 確認フィールドのバリデーション
$v->rule('equals', 'password_confirm', 'password');

// 正規表現バリデーション
$v->rule('regex', 'phone', '/^\d{3}-\d{4}-\d{4}$/');
$v->rule('alphaNum', 'username');

// 配列のバリデーション
$v->rule('array', 'tags');

// カスタムメッセージ
$v->message('required', '{field}は必須項目です');
$v->message('lengthMin', '{field}は{param}文字以上で入力してください');
$v->message('email', '有効なメールアドレスを入力してください');
$v->message('equals', 'パスワードが一致しません');

if ($v->validate()) {
    echo "ユーザー登録データは有効です\n";
    // 登録処理を実行
} else {
    echo "バリデーションエラー:\n";
    foreach ($v->errors() as $field => $messages) {
        echo "フィールド: {$field}\n";
        foreach ($messages as $message) {
            echo "  - {$message}\n";
        }
    }
}

カスタムバリデーションルールの作成

<?php

use Valitron\Validator;

// 日本の郵便番号バリデーション
Validator::addRule('japanesePostalCode', function($field, $value, $params, $fields) {
    return preg_match('/^\d{3}-?\d{4}$/', $value);
}, '有効な郵便番号を入力してください(例:123-4567)');

// 強いパスワードのバリデーション
Validator::addRule('strongPassword', function($field, $value, $params, $fields) {
    // 最低8文字、英大文字、英小文字、数字、特殊文字を含む
    return preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/', $value);
}, 'パスワードは8文字以上で、英大文字、英小文字、数字、特殊文字を含む必要があります');

// 日本語文字のバリデーション
Validator::addRule('japanese', function($field, $value, $params, $fields) {
    return preg_match('/^[ひらがなカタカナ漢字ー]+$/u', $value);
}, '日本語で入力してください');

// 年齢制限のバリデーション(生年月日から計算)
Validator::addRule('ageRange', function($field, $value, $params, $fields) {
    $birthdate = new DateTime($value);
    $today = new DateTime('today');
    $age = $birthdate->diff($today)->y;
    
    $minAge = $params[0] ?? 0;
    $maxAge = $params[1] ?? 120;
    
    return $age >= $minAge && $age <= $maxAge;
}, '年齢が範囲外です');

// 重複チェック(データベース接続が必要)
Validator::addRule('unique', function($field, $value, $params, $fields) {
    $table = $params[0];
    $column = $params[1] ?? $field;
    $excludeId = $params[2] ?? null;
    
    // PDOなどでデータベースチェック
    // これは疑似コードです
    $query = "SELECT COUNT(*) FROM {$table} WHERE {$column} = ?";
    if ($excludeId) {
        $query .= " AND id != ?";
        // $result = $pdo->prepare($query)->execute([$value, $excludeId]);
    } else {
        // $result = $pdo->prepare($query)->execute([$value]);
    }
    
    // 実際の実装では PDO を使用してデータベースをチェック
    return true; // 疑似コードのため常にtrue
}, 'この{field}は既に使用されています');

// カスタムルールの使用例
$data = [
    'postal_code' => '123-4567',
    'password' => 'SecurePass123!',
    'name' => 'たなか太郎',
    'birthday' => '1990-05-15',
    'email' => '[email protected]'
];

$v = new Validator($data);

// カスタムルールを適用
$v->rule('required', ['postal_code', 'password', 'name', 'birthday', 'email']);
$v->rule('japanesePostalCode', 'postal_code');
$v->rule('strongPassword', 'password');
$v->rule('japanese', 'name');
$v->rule('ageRange', 'birthday', 18, 65); // 18歳から65歳まで
$v->rule('email', 'email');
// $v->rule('unique', 'email', 'users', 'email'); // データベースが必要

if ($v->validate()) {
    echo "すべてのカスタムバリデーションが成功しました\n";
} else {
    foreach ($v->errors() as $field => $messages) {
        foreach ($messages as $message) {
            echo "- {$field}: {$message}\n";
        }
    }
}

国際化とローカライゼーション

<?php

use Valitron\Validator;

// 言語ファイルの設定
Validator::langDir(__DIR__ . '/lang'); // 言語ファイルディレクトリを設定

// 日本語に設定
Validator::lang('ja');

// カスタム言語ファイル(lang/ja.php)の例
/*
<?php
return [
    'required'      => '{field}は必須です',
    'email'         => '{field}は有効なメールアドレスではありません',
    'lengthMin'     => '{field}は{param}文字以上で入力してください',
    'lengthMax'     => '{field}は{param}文字以下で入力してください',
    'lengthBetween' => '{field}は{param}文字から{param}文字の間で入力してください',
    'min'           => '{field}は{param}以上である必要があります',
    'max'           => '{field}は{param}以下である必要があります',
    'integer'       => '{field}は整数である必要があります',
    'numeric'       => '{field}は数値である必要があります',
    'url'           => '{field}は有効なURLではありません',
    'date'          => '{field}は有効な日付ではありません',
    'equals'        => '{field}は{param}と一致する必要があります',
    'regex'         => '{field}の形式が正しくありません',
    'alphaNum'      => '{field}は英数字のみ使用できます',
    'array'         => '{field}は配列である必要があります'
];
*/

// 多言語対応のバリデーション例
class MultiLangValidator
{
    private $supportedLangs = ['ja', 'en'];
    private $currentLang = 'ja';
    
    public function __construct($lang = 'ja')
    {
        if (in_array($lang, $this->supportedLangs)) {
            $this->currentLang = $lang;
            Validator::lang($lang);
        }
    }
    
    public function validateUserData($data)
    {
        $v = new Validator($data);
        
        // フィールド名のローカライゼーション
        $fieldLabels = $this->getFieldLabels();
        $v->labels($fieldLabels);
        
        // バリデーションルール
        $v->rule('required', ['name', 'email', 'age']);
        $v->rule('email', 'email');
        $v->rule('integer', 'age');
        $v->rule('min', 'age', 18);
        $v->rule('lengthBetween', 'name', 2, 50);
        
        return $v;
    }
    
    private function getFieldLabels()
    {
        $labels = [
            'ja' => [
                'name' => '名前',
                'email' => 'メールアドレス',
                'age' => '年齢',
                'password' => 'パスワード',
                'phone' => '電話番号'
            ],
            'en' => [
                'name' => 'Name',
                'email' => 'Email Address',
                'age' => 'Age',
                'password' => 'Password',
                'phone' => 'Phone Number'
            ]
        ];
        
        return $labels[$this->currentLang] ?? $labels['ja'];
    }
}

// 使用例
$userData = [
    'name' => '',
    'email' => 'invalid-email',
    'age' => 15
];

// 日本語バリデーション
$jaValidator = new MultiLangValidator('ja');
$validator = $jaValidator->validateUserData($userData);

if (!$validator->validate()) {
    echo "日本語エラーメッセージ:\n";
    foreach ($validator->errors() as $field => $messages) {
        foreach ($messages as $message) {
            echo "- {$message}\n";
        }
    }
}

// 英語バリデーション
Validator::lang('en');
$enValidator = new MultiLangValidator('en');
$validator = $enValidator->validateUserData($userData);

if (!$validator->validate()) {
    echo "\nEnglish error messages:\n";
    foreach ($validator->errors() as $field => $messages) {
        foreach ($messages as $message) {
            echo "- {$message}\n";
        }
    }
}

高度な使用例とベストプラクティス

<?php

use Valitron\Validator;

// バリデーションクラスでの抽象化
class FormValidator
{
    protected $data;
    protected $validator;
    protected $errors = [];
    
    public function __construct(array $data)
    {
        $this->data = $data;
        $this->validator = new Validator($data);
        $this->setupRules();
    }
    
    protected function setupRules()
    {
        // サブクラスでオーバーライド
    }
    
    public function validate(): bool
    {
        if (!$this->validator->validate()) {
            $this->errors = $this->validator->errors();
            return false;
        }
        
        return $this->customValidation();
    }
    
    protected function customValidation(): bool
    {
        // サブクラスでカスタムバリデーションを実装
        return true;
    }
    
    public function getErrors(): array
    {
        return $this->errors;
    }
    
    public function getFirstError(): ?string
    {
        foreach ($this->errors as $fieldErrors) {
            return $fieldErrors[0] ?? null;
        }
        return null;
    }
}

// ユーザー登録フォームバリデーター
class UserRegistrationValidator extends FormValidator
{
    protected function setupRules()
    {
        $this->validator
            ->rule('required', ['username', 'email', 'password', 'password_confirm'])
            ->rule('lengthBetween', 'username', 3, 20)
            ->rule('alphaNum', 'username')
            ->rule('email', 'email')
            ->rule('lengthMin', 'password', 8)
            ->rule('equals', 'password_confirm', 'password');
        
        // フィールドラベル
        $this->validator->labels([
            'username' => 'ユーザー名',
            'email' => 'メールアドレス',
            'password' => 'パスワード',
            'password_confirm' => 'パスワード確認'
        ]);
    }
    
    protected function customValidation(): bool
    {
        // ユーザー名の重複チェック(疑似コード)
        if ($this->isUsernameTaken($this->data['username'] ?? '')) {
            $this->errors['username'][] = 'このユーザー名は既に使用されています';
            return false;
        }
        
        // メールアドレスの重複チェック(疑似コード)
        if ($this->isEmailTaken($this->data['email'] ?? '')) {
            $this->errors['email'][] = 'このメールアドレスは既に使用されています';
            return false;
        }
        
        return true;
    }
    
    private function isUsernameTaken(string $username): bool
    {
        // データベースチェックの疑似コード
        return false;
    }
    
    private function isEmailTaken(string $email): bool
    {
        // データベースチェックの疑似コード
        return false;
    }
}

// プロフィール更新バリデーター
class ProfileUpdateValidator extends FormValidator
{
    private $userId;
    
    public function __construct(array $data, int $userId)
    {
        $this->userId = $userId;
        parent::__construct($data);
    }
    
    protected function setupRules()
    {
        $this->validator
            ->rule('required', ['name', 'email'])
            ->rule('lengthBetween', 'name', 2, 50)
            ->rule('email', 'email')
            ->rule('url', 'website')
            ->rule('date', 'birthday')
            ->rule('lengthMax', 'bio', 500);
        
        // オプションフィールドの条件付きバリデーション
        if (!empty($this->data['password'])) {
            $this->validator
                ->rule('lengthMin', 'password', 8)
                ->rule('equals', 'password_confirm', 'password');
        }
    }
}

// APIレスポンス用バリデーター
class ApiValidator
{
    public static function validateAndRespond(FormValidator $validator): array
    {
        if ($validator->validate()) {
            return [
                'success' => true,
                'message' => 'バリデーション成功',
                'data' => null
            ];
        }
        
        return [
            'success' => false,
            'message' => 'バリデーションエラーが発生しました',
            'errors' => $validator->getErrors(),
            'first_error' => $validator->getFirstError()
        ];
    }
}

// 使用例
$registrationData = [
    'username' => 'user123',
    'email' => '[email protected]',
    'password' => 'password123',
    'password_confirm' => 'password123'
];

$validator = new UserRegistrationValidator($registrationData);
$result = ApiValidator::validateAndRespond($validator);

if ($result['success']) {
    echo "ユーザー登録バリデーション成功\n";
} else {
    echo "バリデーションエラー:\n";
    echo "最初のエラー: " . $result['first_error'] . "\n";
    
    foreach ($result['errors'] as $field => $messages) {
        foreach ($messages as $message) {
            echo "- {$field}: {$message}\n";
        }
    }
}

// バッチバリデーション例
class BatchValidator
{
    public static function validateMultiple(array $datasets, string $validatorClass): array
    {
        $results = [];
        
        foreach ($datasets as $index => $data) {
            $validator = new $validatorClass($data);
            $results[$index] = [
                'data' => $data,
                'valid' => $validator->validate(),
                'errors' => $validator->getErrors()
            ];
        }
        
        return $results;
    }
    
    public static function getValidData(array $results): array
    {
        return array_filter($results, fn($result) => $result['valid']);
    }
    
    public static function getInvalidData(array $results): array
    {
        return array_filter($results, fn($result) => !$result['valid']);
    }
}

// CSVインポート時のバッチバリデーション例
$csvData = [
    ['username' => 'user1', 'email' => '[email protected]', 'password' => 'pass123456', 'password_confirm' => 'pass123456'],
    ['username' => 'u2', 'email' => 'invalid-email', 'password' => 'weak', 'password_confirm' => 'weak'],
    ['username' => 'user3', 'email' => '[email protected]', 'password' => 'strong123', 'password_confirm' => 'strong123']
];

$batchResults = BatchValidator::validateMultiple($csvData, UserRegistrationValidator::class);
$validData = BatchValidator::getValidData($batchResults);
$invalidData = BatchValidator::getInvalidData($batchResults);

echo "有効なデータ: " . count($validData) . "件\n";
echo "無効なデータ: " . count($invalidData) . "件\n";

foreach ($invalidData as $index => $result) {
    echo "行 {$index}: エラーあり\n";
    foreach ($result['errors'] as $field => $messages) {
        foreach ($messages as $message) {
            echo "  - {$field}: {$message}\n";
        }
    }
}