1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
|
<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<sect1 id="zend.validate.writing_validators">
<title>Writing Validators</title>
<para>
<classname>Zend_Validate</classname> supplies a set of commonly needed validators, but
inevitably, developers will wish to write custom validators for their particular needs. The
task of writing a custom validator is described in this section.
</para>
<para>
<classname>Zend_Validate_Interface</classname> defines two methods,
<methodname>isValid()</methodname> and <methodname>getMessages()</methodname>, that may
be implemented by user classes in order to create custom validation objects. An object that
implements <classname>Zend_Validate_Interface</classname> interface may be added to a
validator chain with <methodname>Zend_Validate::addValidator()</methodname>. Such objects
may also be used with <link
linkend="zend.filter.input"><classname>Zend_Filter_Input</classname></link>.
</para>
<para>
As you may already have inferred from the above description of
<classname>Zend_Validate_Interface</classname>, validation classes provided with Zend
Framework return a boolean value for whether or not a value validates successfully. They
also provide information about <emphasis>why</emphasis> a value failed validation. The
availability of the reasons for validation failures may be valuable to an application for
various purposes, such as providing statistics for usability analysis.
</para>
<para>
Basic validation failure message functionality is implemented in
<classname>Zend_Validate_Abstract</classname>. To include this functionality when creating a
validation class, simply extend <classname>Zend_Validate_Abstract</classname>. In the
extending class you would implement the <methodname>isValid()</methodname> method logic and
define the message variables and message templates that correspond to the types of
validation failures that can occur. If a value fails your validation tests, then
<methodname>isValid()</methodname> should return <constant>FALSE</constant>. If the value
passes your validation tests, then <methodname>isValid()</methodname> should return
<constant>TRUE</constant>.
</para>
<para>
In general, the <methodname>isValid()</methodname> method should not throw any exceptions,
except where it is impossible to determine whether or not the input value is valid. A few
examples of reasonable cases for throwing an exception might be if a file cannot be opened,
an <acronym>LDAP</acronym> server could not be contacted, or a database connection is
unavailable, where such a thing may be required for validation success or failure to be
determined.
</para>
<example id="zend.validate.writing_validators.example.simple">
<title>Creating a Simple Validation Class</title>
<para>
The following example demonstrates how a very simple custom validator might be written.
In this case the validation rules are simply that the input value must be a floating
point value.
</para>
<programlisting language="php"><![CDATA[
class MyValid_Float extends Zend_Validate_Abstract
{
const FLOAT = 'float';
protected $_messageTemplates = array(
self::FLOAT => "'%value%' is not a floating point value"
);
public function isValid($value)
{
$this->_setValue($value);
if (!is_float($value)) {
$this->_error(self::FLOAT);
return false;
}
return true;
}
}
]]></programlisting>
<para>
The class defines a template for its single validation failure message, which includes
the built-in magic parameter, <emphasis>%value%</emphasis>. The call to
<methodname>_setValue()</methodname> prepares the object to insert the tested value into
the failure message automatically, should the value fail validation. The call to
<methodname>_error()</methodname> tracks a reason for validation failure.
</para>
</example>
<example id="zend.validate.writing_validators.example.conditions.dependent">
<title>Writing a Validation Class having Dependent Conditions</title>
<para>
The following example demonstrates a more complex set of validation rules, where it is
required that the input value be numeric and within the range of minimum and maximum
boundary values. An input value would fail validation for exactly one of the following
reasons:
</para>
<itemizedlist>
<listitem>
<para>The input value is not numeric.</para>
</listitem>
<listitem>
<para>The input value is less than the minimum allowed value.</para>
</listitem>
<listitem>
<para>The input value is more than the maximum allowed value.</para>
</listitem>
</itemizedlist>
<para>
These validation failure reasons are then translated to definitions in the class:
</para>
<programlisting language="php"><![CDATA[
class MyValid_NumericBetween extends Zend_Validate_Abstract
{
const MSG_NUMERIC = 'msgNumeric';
const MSG_MINIMUM = 'msgMinimum';
const MSG_MAXIMUM = 'msgMaximum';
public $minimum = 0;
public $maximum = 100;
protected $_messageVariables = array(
'min' => 'minimum',
'max' => 'maximum'
);
protected $_messageTemplates = array(
self::MSG_NUMERIC => "'%value%' is not numeric",
self::MSG_MINIMUM => "'%value%' must be at least '%min%'",
self::MSG_MAXIMUM => "'%value%' must be no more than '%max%'"
);
public function isValid($value)
{
$this->_setValue($value);
if (!is_numeric($value)) {
$this->_error(self::MSG_NUMERIC);
return false;
}
if ($value < $this->minimum) {
$this->_error(self::MSG_MINIMUM);
return false;
}
if ($value > $this->maximum) {
$this->_error(self::MSG_MAXIMUM);
return false;
}
return true;
}
}
]]></programlisting>
<para>
The public properties <varname>$minimum</varname> and <varname>$maximum</varname> have
been established to provide the minimum and maximum boundaries, respectively, for a
value to successfully validate. The class also defines two message variables that
correspond to the public properties and allow <property>min</property> and
<property>max</property> to be used in message templates as magic parameters, just as
with <property>value</property>.
</para>
<para>
Note that if any one of the validation checks in <methodname>isValid()</methodname>
fails, an appropriate failure message is prepared, and the method immediately returns
<constant>FALSE</constant>. These validation rules are therefore sequentially dependent.
That is, if one test should fail, there is no need to test any subsequent validation
rules. This need not be the case, however. The following example illustrates how to
write a class having independent validation rules, where the validation object may
return multiple reasons why a particular validation attempt failed.
</para>
</example>
<example id="zend.validate.writing_validators.example.conditions.independent">
<title>Validation with Independent Conditions, Multiple Reasons for Failure</title>
<para>
Consider writing a validation class for password strength enforcement - when a user is
required to choose a password that meets certain criteria for helping secure user
accounts. Let us assume that the password security criteria enforce that the password:
</para>
<itemizedlist>
<listitem><para>is at least 8 characters in length,</para></listitem>
<listitem><para>contains at least one uppercase letter,</para></listitem>
<listitem><para>contains at least one lowercase letter,</para></listitem>
<listitem><para>and contains at least one digit character.</para></listitem>
</itemizedlist>
<para>
The following class implements these validation criteria:
</para>
<programlisting language="php"><![CDATA[
class MyValid_PasswordStrength extends Zend_Validate_Abstract
{
const LENGTH = 'length';
const UPPER = 'upper';
const LOWER = 'lower';
const DIGIT = 'digit';
protected $_messageTemplates = array(
self::LENGTH => "'%value%' must be at least 8 characters in length",
self::UPPER => "'%value%' must contain at least one uppercase letter",
self::LOWER => "'%value%' must contain at least one lowercase letter",
self::DIGIT => "'%value%' must contain at least one digit character"
);
public function isValid($value)
{
$this->_setValue($value);
$isValid = true;
if (strlen($value) < 8) {
$this->_error(self::LENGTH);
$isValid = false;
}
if (!preg_match('/[A-Z]/', $value)) {
$this->_error(self::UPPER);
$isValid = false;
}
if (!preg_match('/[a-z]/', $value)) {
$this->_error(self::LOWER);
$isValid = false;
}
if (!preg_match('/\d/', $value)) {
$this->_error(self::DIGIT);
$isValid = false;
}
return $isValid;
}
}
]]></programlisting>
<para>
Note that the four criteria tests in <methodname>isValid()</methodname> do not
immediately return <constant>FALSE</constant>. This allows the validation class to
provide <emphasis>all</emphasis> of the reasons that the input password failed to meet
the validation requirements. if, for example, a user were to input the string "#$%" as a
password, <methodname>isValid()</methodname> would cause all four validation failure
messages to be returned by a subsequent call to <methodname>getMessages()</methodname>.
</para>
</example>
</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
|