Zend_Validate-WritingValidators.xml 10 KB

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