PHP 业务实体验证
PHP 业务实体验证
PHP 验证功能
如果我说所有应用程序都使用验证功能,我不会泄露任何秘密。其中一些应用程序的验证功能非常简单,只是使用 if-else 条件来确保传入的数据是正确的,但有些应用程序需要高级功能,尤其是处理传入用户数据的业务应用程序。如果您开发一些基于 Web 的业务应用程序,并且您确信应用程序用户将设置正确的数据,您仍然需要一些验证,因为 HTTP 网络流量很容易被黑客攻击(当然如果不是 HTTPS),并且有人可能会发送不正确的数据到 ... 甚至只是为了开玩笑。
在这里,我将解释一个非常好的验证功能,我们现在在不同的 PHP 和 .NET 项目中使用它。 为什么我认为它很好? 因为它易于扩展和移动。 我们最后的 .NET 项目将验证作为解决方案的外部项目包含在内,不幸的是,PHP 没有像 .NET 这样的命名空间,我们将所有验证保存在项目中的单独文件夹中,然后将此文件夹复制到新项目中。
让我们开始...
例如,您开发了一些具有不同业务实例的业务应用程序。 假设您有 Employee 业务实例和相应的 DEmployee 数据实例。 它们具有不同的属性和函数,并实现一些 IBusiness 和 IDataEnity 接口。 在将 Employee 实例保存到数据库之前,我必须验证它。 我只是将 Validate 函数添加到我们的接口,添加到 IBusiness 以根据业务规则验证实例,例如员工姓名应以 Department 编号开头,添加到 IDataEnity 以根据数据库规则验证实例,例如员工姓名长度应大于 5 个符号。 为什么我们不能只进行一次员工验证? 以 Department 编号开头 employee 姓名是业务规则,我们要求员工姓名应以另一个业务属性的属性开头,我们应该在业务级别验证它。 员工长度应在 5 到 25 个符号之间是数据规则,因为字段长度是数据库限制,业务级别不考虑姓名符号,它使用姓名属性作为对象。 也许 Employee 姓名是一个复杂的实例,它根本没有符号,它是引用,但是字符串 Employee 表示应该从 Department 编号开始。
正如我们在 IBusiness 接口中声明了 Validation 函数,我们需要在 Employee 类中实现它,如下所示
public function Validate(Action $action) {
	$v = new Validator ( $action );
	$v
		->Validate ( new BusinessNamePattern ( ), 
		array ('Employee Name' => $this->getActualName () ) );
	if ($v->hasError ())
		return $v;
	return parent::Validate ( $action );
}
看起来很简单,我们创建 Validator 实例并运行带有模式实例和数据数组的函数进行验证。 如果数据错误,则返回 Validator 实例。
同样简单的 Validation 实现也在 DEmployee 类中。
public function Validate(BaseBusinessAction $action)
{
	$v = new Validator($action);
	$v->Validate(new DOBPatternYYYYMMDD(), array('dob' => $this->get_dob()));
	$v->Validate(new NamePattern(), array('Last Name'=>$this->get_last_name()));
	$v->Validate(new NamePattern(), array('First Name'=>$this->get_first_name()));
	if ($v->hasError())
		return $v;
	return parent::Validate($action);		
}
在这里,我们验证出生日期以及名字、姓氏。 完全一样,但看起来太复杂了,让我们更优雅地重写它,如下所示
public function Validate(BaseBusinessAction $action)
{
	$v = new Validator($action);
	$v
		->Validate(new DOBPatternYYYYMMDD(), array('dob' => $this->get_dob()))
		->Validate(new NamePattern(), array(
			'First Name'=>$this->get_first_name()
			, 'Last Name'=>$this->get_last_name()
			));
	if ($v->hasError())
		return $v;
	return parent::Validate($action);		
}
这是一样的。
验证已准备就绪,如果您有 Validator 和模式,那么一切就完成了。 在我们的项目中,一切都完成了。 简单吗? 是的。
快吗? 是的。
开发和支持成本低廉吗? 是的。
您没有验证器,让我们继续。 没什么困难的。 所有模式都扩展了 IPattern 接口并实现 isApproved 函数,如果数据良好,则返回 TRUE 或 ValidationError 实例。
class Validator
{
	private $_errors = array();
	/**
	 * 
	 * @var BaseBusinessAction
	 */
	private $_action;
	
	public function __construct($action)
	{
		$this->_action = $action;	
	}
	
	/**
	 * Validate $data
	 * @param BasePattern $pattern
	 * @param $data it is key=>value array to validate
	 * @return Validator
	 * @uses 
	 * $v = new Validator();
	 * $v->Validate(new NotEmptyPattern(BasePattern::ACTION_INSERT), 
	 * array('name'=>$name, 'city'=>$city, 'address'=>$address))
	 * $v->Validate(new NamePattern(BasePattern::ACTION_INSERT), 
	 * array('name'=>$name))
	 * if ($v->hasError)
	 * 	throw new ValidationException($v);
	 */
	public function Validate(BasePattern $pattern, $data)
	{
		if (is_array($data))
		{
			$pattern->set_action($this->_action);
			foreach ($data as $key => $value)
				if (!(($error = $pattern->isApproved($key, $value)) 
					=== TRUE))
					$this->_errors[] = $error;
		}
		else
			throw new ApplicationException
			('Wrong argument parameters:'.print_r($data, true));
		return $this;
	}
	
	/**
	 * 
	 * @return true if there is some error
	 */
	public function hasError()
	{
		return (count($this->_errors) > 0);
	}
	/**
	 * @return the $_action
	 */
	public function get_action() {
		return $this->_action;
	}
	/**
	 * @return the $_errors
	 */
	public function get_errors() {
		return $this->_errors;
	}
	
	/**
	 * Return validation errors as string
	 * @return string
	 */
	public function toString()
	{
		$result = array();
		foreach ($this->_errors as $error)
			$result[] = $error->toString();
		return implode("\n", $result);		
	}
	
	public function __toString()
	{
		return $this->toString();
	}
}
您可以根据需要拥有任何模式,一些棘手的或困难的或容易的,您需要的一切。 下面我展示了 NamePattern 类
class NamePattern extends BasePattern
{
	/**
	 * Satisfy data with coded requirements
	 * Name can not have any numbers or any special symbols
	 * @param $field
	 * @param $value
	 * @return ValidationError or TRUE
	 */
	public function isApproved($field, $value)
	{
		$not_empty = new NotEmptyPattern($this->get_action());
		if (($res = $not_empty->isApproved($field, $value)) !== TRUE)
			return $res;
		//if (preg_match('/[\d~!@#$%^&\*\(\)\+={}\[\]|\\:;"<>\?\/]/', $value))
		if (preg_match('/[~!@#$%^&\*\(\)\+={}\[\]|\\:;"<>\?\/]/', $value))
			return new ValidationError($field, $value, 
				"$field has unavailable symbols: $value");
		$clear_value = preg_replace('/\s/', "", $value);
		$len = strlen($clear_value); 
		if ($len<5)
			return new ValidationError($field, $value, 
					"$field is too short.");
		if ($len>25)
			return new ValidationError($field, $value, 
					"$field is too long.");
		return TRUE;
	}
}
它使用另一个模式 NotEmptyPattern 来确保姓名不为空,查找未经批准的符号并检查姓名长度。 此模式不依赖于操作。 例如,实例 Id 验证模式可能依赖于操作,因为通常 INSERT 操作可以具有 id = 0,但更新不能。
class ValidationError
{
	private $_field;
	private $_value;
	private $_message;
	
	/**
	 * Create instance
	 * @param $field
	 * @param $value
	 * @param $message it is format of error string for sprintf.
	 */
	public function __construct($field, $value, $message='')
	{
		$this->_field = $field;
		$this->_value = $value;
		$this->_message = $message;
	}
	/**
	 * @return the $_message
	 */
	public function get_message() {
		return $this->_message;
	}
	/**
	 * @return the $_value
	 */
	public function get_value() {
		return $this->_value;
	}
	/**
	 * @return the $_field
	 */
	public function get_field() {
		return $this->_field;
	}
	public function __toString()
	{
		return $this->toString();
	}
	
	public function toString()
	{
		if (empty($this->_message))
			return "Field ".$this->_field." 
			has incorrect value '".$this->_value."'.";
		else
			return sprintf($this->_message, $this->_value);
	}
}
我想您明白了使用它是多么简单。 获取已填充的实例,验证它,检查结果,如果数据不正确则回滚,如果数据正确则保存。 看起来像这样
//......................................
	$employee = new Employee ( );
//...... populate employee instance......
	$v = $employee->Validate(BasePattern::ACTION_UPDATE);
	if ($v->hasError())
	{
		$transaction->rollback();
		HTTPFactory::sendJSONResponse ( array 
			('code'=>1, 'processStatus' => "Form is not saved 
			(Employee data validation error:\n".$v.')' ) );
	}
	$employee->save ();
//......................................
HTTPFactory::sendJSONResponse 将 json 响应发送到用户浏览器并退出。
就是这样。
历史
- 2010 年 9 月 29 日:初始帖子



