65.9K
CodeProject 正在变化。 阅读更多。
Home

PHP 业务实体验证

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (3投票s)

2010年9月29日

CPOL

3分钟阅读

viewsIcon

16722

PHP 业务实体验证

PHP 验证功能

如果我说所有应用程序都使用验证功能,我不会泄露任何秘密。其中一些应用程序的验证功能非常简单,只是使用 if-else 条件来确保传入的数据是正确的,但有些应用程序需要高级功能,尤其是处理传入用户数据的业务应用程序。如果您开发一些基于 Web 的业务应用程序,并且您确信应用程序用户将设置正确的数据,您仍然需要一些验证,因为 HTTP 网络流量很容易被黑客攻击(当然如果不是 HTTPS),并且有人可能会发送不正确的数据到 ... 甚至只是为了开玩笑。

在这里,我将解释一个非常好的验证功能,我们现在在不同的 PHP 和 .NET 项目中使用它。 为什么我认为它很好? 因为它易于扩展和移动。 我们最后的 .NET 项目将验证作为解决方案的外部项目包含在内,不幸的是,PHP 没有像 .NET 这样的命名空间,我们将所有验证保存在项目中的单独文件夹中,然后将此文件夹复制到新项目中。

让我们开始...

例如,您开发了一些具有不同业务实例的业务应用程序。 假设您有 Employee 业务实例和相应的 DEmployee 数据实例。 它们具有不同的属性和函数,并实现一些 IBusinessIDataEnity 接口。 在将 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 函数,如果数据良好,则返回 TRUEValidationError 实例。

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 日:初始帖子
© . All rights reserved.