Zend_Validate-WritingValidators.xml 11 KB

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