datafilter/datafilter

此包已被废弃,不再维护。未建议替代包。

数据过滤和验证

dev-master 2013-04-27 13:50 UTC

This package is not auto-updated.

Last update: 2020-01-18 05:52:51 UTC


README

DataFilter 是一个 PHP 的数据验证(净化)模块。

编写此模块的动机主要源于需要有一个通用的验证模块,该模块既不与任何类型的 ORM 逻辑交织,也不与控制器(在 MVC 术语中)逻辑交织。

第二个目标是创建一个可以通过元语言(JSON、YAML 等)配置的验证规则,无需“内联”规则定义 - 简而言之:将验证规则与业务逻辑分离。

需要 PHP 5.3(尽管示例中使用的语法是 5.4 数组符号)。

通过 Composer 安装

创建一个最小的 composer.json

{
    "require": {
        "datafilter/datafilter": "dev-master"
    }
}

运行 composer update 或 install

composer.phar install --dev

入门

一个基本示例,期望两个 POST 参数,都是必需的

<?php

require 'vendor/autoload.php';

// ..

$profile = new \DataFilter\Profile([
    'attribs' => [
        'username' => true,
        'password' => true
    ]
]);
if ($profile->check($_POST)) {
    $data = $profile->getLastResult()->getValidData();
}
else {
    error_log("Failed, required params not given");
}

深入

配置文件是 DataFilter 验证和过滤(也是净化)的核心。创建这些配置文件有三种可能的方法:使用内联 PHP 定义、使用元语言(如 JSON)编写的外部定义或使用编程方法在运行时编写定义。

根据您的验证问题的复杂程度,我认为使用内联 PHP 定义或将配置文件放入 JSON 文件中是个好主意。在运行时,您可以相应地修改加载的配置文件。

验证配置文件

验证配置文件是一组具有(可能多个)规则的属性。您可以将其视为单个表单,也可以视为模型(ORM)的一般属性验证。

以下将展示内联 PHP 方法(见上文)。

结构

$profile = [

    // attribute definitions
    'attribs' => [
        'attributeName' => [
            // rule definitions
        ],
        // list of attributes and rules
    ],

    // overwrite default invalid error message
    'errorTemplate' => "Attribute :attrib: violated rule :rule:",

    // overwrite default missing error message
    'missingTemplate' => "Attribute :attrib: is missing",

    // custom rule classes
    'ruleClasses' => [
        "\\MyRuleClass",
        // ..
    ],

    // custom filter classes
    'filterClasses' => [
        "\\MyFilterClass",
        // ..
    ],

    // custom, global pre-filters (before validating)
    'preFilters' => [
        function($in) {
            return $in;
        },
        "namedFilter",
        ["\\SomeClass", "someMethod"],
        // ..
    ],

    // custom, global pre-filters (after validating, only on valids)
    'postFilters' => [
        function($in) {
            return $in;
        },
        "namedFilter",
        ["\\SomeClass", "someMethod",
        // ..
    ]
];

规则格式

简单(必需、可选)

最简单的规则格式是 true(必需)或 false(可选)。

// ..
'attribs' => [
    'attribName'  => true,
    'attribName2' => false
],
// ..

命名检查函数

有一些预定义的命名函数可以使用。完整列表请参阅 \DataFilter\PredefinedRules\Basic

以下示例显示了正则表达式测试和最小长度测试。这两个属性都是隐式必需的。

// ..
'attribs' => [
    'attribName'  => 'Regex:/^a[0-9]+',
    'attribName2' => 'MinLen:5'
],
// ..

包含预定义规则的类可以添加。它们必须实现返回函数引用的公共、静态方法

class MyClass {
    public static function ruleMyTest($arg1, $arg2) {
        return function ($input, \DataFilter\Rule $rule = null, \DataFilter\Attribute $attrib = null, \DataFilter\Profile $profile = null) {
            return true;
        }
    }
}

自定义类需要注册,并可以使用方法名称使用

// ..
'ruleClasses' => ['\\MyClass'],
'attribs' => [
    'attribName'  => 'MyTest:foo:bar'
],
// ..

自定义检查函数

可以内联添加自定义检查函数(可以是 Closure 或可调用数组)

// ..
'attribs' => [
    'attribName'  => ['\\MyClass', 'myMethod'],
    'attribName2' => function($input, ..) {
        return true;
    }
],
// ..

复杂格式

复杂格式支持对每个属性和规则进行严格控制。也可以为每个属性使用多个规则。

'attribs' => [
    'attribName' => [

        // whether required
        'required' => true,

        // whether any (first) positive rule match validates argument (default: false)
        'matchAny' => false,

        // default value is set if attribute NOT given (empty input is still given!). Implies optional (not required)
        'default' => null,

        // default missing error text
        'missing' => 'This attribute is missing',

        // whether skip all (global and local) filters (default: false)
        'noFilter' => false,

        // list of ules
        'rules' => [
            'ruleName' => [

                // either a Closure (or callable array or a named function)
                'constraint' => $constraint,

                // custom error message if rule fails
                'error' => 'On error show this message',

                // whether ignore this rule on empty input
                'skipEmpty' => false,

                // whether this rule sets the result valid and stops further rules
                'sufficient' => false
            ]
        ],

        // dependencies: see explanation below
        'dependent' => [
            'onSomeInput' => ['otherField1', 'otherField2']
        ]
    ]
]

属性依赖关系

依赖关系可以通过常见的密码案例来解释。假设您有一个表单,其中包含一个名为 password 的输入和一个名为 password_new 的输入。如果提供了 password,则应同时提供 password_new。在这种情况下,您可以创建从 passwordpassword_new 的依赖关系,如下所示

'attribs' => [

    // the password input
    'password' => [

        // not required itself
        'required' => false,

        // ..
        'dependent' => [
            '*' => ['password_new']
        ]
        //..
    ],

    // default: password_new is optional
    'password_new' => false,
    // ..
]

dependent 的左侧值代表其他属性(右侧数组)所依赖的输入。 * 是一个特殊情况,表示:“任何输入”。如果提供了多个依赖关系,而输入没有其他匹配项时,则使用 *

此外,还有 dependentRegex,它的工作方式相同,但在左侧使用正则表达式。

'attribs' => [

    // the password input
    'password' => [
        // ..
        'dependentRegex' => [
            '/./' => ['password_new']
        ]
        //..
    ],

    // default: password_new is optional
    'password_new' => false,
    // ..
]

依赖关系可以在条件表单部分的情况下很有用(例如,如果一个单选输入开关表单的一部分开或关)。

使用 JSON

您可以将定义放入 JSON 文件中,并将它们加载到配置文件中。

def.json:

{
    "attribs": {
        "someAttrib": true,
        "otherAttrib": {
            "required": false,
            "rules": {
                "isEmail": "Email",
                ...
            }
        },
        ...
    },
    "preFilters" => [ ... ],
    ...
}

然后加载定义

$profile = \DataFilter\Profile::loadJson("def.json");

当然,使用 JSON 文件外部化的定义的问题是无法使用可调用函数(function() {..})。然而,使用下面的程序化方法,您可以在运行时添加这些规则/过滤器。

程序化

此选项应主要用于在需要时修改预定义配置文件。您也可以从头创建完整的配置文件,但据我看来,这只会使代码混乱。

// creating
$profile = new \DataFilter\Profile();
$profile->setAttrib('email');
$email = $profile->getAttrib('email');
$email->setRule('checkMail', 'Email');
$email->setRule('checkSomething', function($in) {
    return strlen($in) > 4 && preg_match('/aaa/', $in);
});
$email->addPostFilters([
    function($in){
        return ucfirst($in);
    }
]);
# ..

// manipulating
$profile = \DataFilter\Profile::fromJson("def.json");
$email = $profile->getAttrib('email');
# ..