Updated:

What is it?

Validator is a full-featured class for implementing FORM data validation. Based on PEAR QuickForm functionality and Cake's basic validation features, Validator provides an easy approach to validating FORM submissions. In addition, Validator can be used to filter FORM data (trimming extra spaces, for example).

Key features

Overview

Validator operates on a single-failure approach, meaning that validation of a field stops when a rule fails to validate. There are two reasons for this:

  1. Often validation rules depend on each other. Suppose you have an email_address field is must be a non-empty, valid e-mail address. The first validation rule should be to ensure that the field is non-empty, and the second rule would be to validate the field's value to determine if it's a properly formed e-mail address. If the first rule fails, the second is sure to fail - the second rule depends on the success of the first rule (non-empty).
  2. Simpler error messages. It is not very friendly to display error messages from ten different validation rules that may have failed simply because the field was left empty. The error messages should guide the user in case of problems, but if a field requires very precisely formatted data, the user should be informed of the necessary instructions apart from error messages.

Another important aspect of Validator to keep in mind is that optional fields which are empty are not validated. Empty fields will only be validated if they have the VALID_NOT_EMPTY rule assigned to them.

Validation rules

Validator provides a rule registry, consisting of registered validation rules. A number of rules are predefined:

VALID_NOT_EMPTY Validates presence of a value
VALID_COMPARE Validates whether two fields contain the same value
Extra parameter(s):
name of field to compare with
VALID_EMAIL Validates that the value is a properly formed e-mail address
VALID_NUMBER Checks to see if value is a number
VALID_RANGE Checks to see if value is within a specified range
Extra parameter(s):
(optional) minimum value for range
(optional) maximum value for range
VALID_RANGE_LENGTH Checks to see if length of value is within a specified range
Extra parameter(s):
(optional) minimum value for range
(optional) maximum value for range
VALID_LETTERS_ONLY Checks to see if value consists only of letters
VALID_NO_PUNCTUATION Checks to see if value does not contain any special punctuation characters
VALID_ALPHANUMERIC Checks to see if value consists only of letters and numbers
VALID_NONZERO Checks to see if value is non-zero

Usage

Validator's primary use is with the Cake MVC framework, though it can also be used as a general purpose validator.

Using Validator with Cake

Enabling Validator support

To use Validator with Cake, you will first need to place the validator.php script somewhere that will be easily accessible to your controllers. The easiest place to put it is in the /libs folder.

To include Validator support in all controllers, simply add the following line to the /app/app_controller.php file: uses('validator'); Once you have enabled support for Validator, you are ready to add the validation rules.

NOTE: In an effort to make the switch from Cake's validator to the Validator class, the constants have been re-defined in Validator. As a result, Cake's validators cannot be used. This requires the removal of any references to the validators.php file. So far, the only location I am aware of is in libs/model.php, in a uses() call.

Adding validation rules

Validator supports the array-based approach of Cake's validation, with a number of enhancements, as well as a programmatic approach. Both of these will be covered in the sections that follow.

Adding single rule validations using an array

Within the action (function) that will receive FORM data, rules can be assigned using an array: $this->validate = array( 'username' => VALID_NOT_EMPTY, 'password' => VALID_NOT_EMPTY ); The validate[] array uses field names for keys, and validation rule constants for values. In the above example, 'username' is a field name used as a key, and the value is a single validation rule named VALID_NOT_EMPTY.

The previous example demonstrates that Validator can be used as a drop-in replacement for single validation rulesets previously created for Cake. However, Validator can do much more.

Adding multiple rule validations using an array

In the previous example, 'username' and 'password' only have a single validation rule assigned to them. However, multiple rules can easily be assigned by passing an array for the value: $this->validate = array( 'username' => array(VALID_NOT_EMPTY, VALID_ALPHANUMERIC), 'password' => VALID_NOT_EMPTY ); Now, 'username' must not only be non-empty, but must also consist only of alphanumeric characters. Note that the array of rules is essentially unlimited.

Adding rules programmatically

In addition to providing support for array-based rulesets, Validator provides a simple way of assigning rules programmatically. Consider the previous example, created using an array. The same validation rules could be created using the addRule() function of Validator (assume $v is an instance of class Validator): $v->addRule('username', VALID_NOT_EMPTY); $v->addRule('username', VALID_ALPHANUMERIC); $v->addRule('password', VALID_NOT_EMPTY); If you want to assign a particular rule to multiple fields, simply pass an array of field names as the first argument: $v->addRule(array('username', 'password'), VALID_NOT_EMPTY); $v->addRule('username', VALID_ALPHANUMERIC);

Custom error messages

Validator provides default error messages for all pre-defined validation rules. However, it is likely that you'll want to override these with your own messages. The following examples allow you to do this: $this->validate = array( 'username' => array(VALID_NOT_EMPTY => 'Username is required'), 'password' => array(VALID_NOT_EMPTY => 'Password is required') ); $this->validate = array( 'username' => array(VALID_NOT_EMPTY => 'Username is required', VALID_ALPHANUMERIC => 'Username must contain only letters and/or numbers'), 'password' => array(VALID_NOT_EMPTY => 'Password is required') ); Notice that now the validation rule names are keys, and the error messages are the values.

Custom error messages can also be provided programmatically: $v->addRule('username', VALID_NOT_EMPTY, 'Username is required'); $v->addRule('password', VALID_NOT_EMPTY, 'Password is required');

Extra parameters

All of the previous examples used simple validation rules such as VALID_NOT_EMPTY which do not require extra parameters. However, some rules, such as VALID_COMPARE, need extra information in order to operate. These extra parameters are passed similiar to the way custom error messages are passed. Consider the case of having two fields, 'password' and 'confirm_pass', and you want to validate that the two contain the same value. This is accomplished using VALID_COMPARE (note: VALID_NOT_EMPTY is omitted in this example for clarity): $this->validate = array( 'password' => array(VALID_COMPARE => array('Passwords do not match', 'confirm_pass') ) ); Now, the custom error message is replace with an array, whose first value is the error message. All subsequent entries in the array are extra parameters used by the particular validation rule. VALID_COMPARE, for example, requires one extra parameter, which is the field name to compare against. In this case, 'confirm_pass' is the name of this second field.

Naturally, we can also do this programmatically: $v->addRule('password', VALID_COMPARE, 'Passwords do not match', 'confirm_pass'); Tip: If you need to pass extra parameters but don't want to provide a custom error message, simply pass null where the custom error message is required

Creating new validators

Validator provides a function known as registerValidator which allows for custom validators to be registered for use. There are three types of validators:

  1. Regular expression validators (regex)
  2. Internal callback validators (internal)
  3. External callback validators (callback)

Registration of any type of validator is performed with the registerValidator.

Creating a regex validator

Regular expression validators are the simplest type of validators, and as a result are quite easy to register. The VALID_LETTERS_ONLY validator is implemented with a simple regular expression: /^[a-zA-Z]+$/. Suppose that this rule was not already registered. The following code will register this validator: $v->registerValidator('VALID_LETTERS_ONLY', 'regex', '/^[a-zA-Z]+$/'); The first parameter is a string name for the validator. In the above example it was passed as a literal string. However, it's better to create a constant instead, to simplify the addition of validation rules: define('VALID_LETTERS_ONLY', 'VALID_LETTERS_ONLY'); The second parameter is the type of validator, in this case regex. The third parameter is the regular expression itself. An optional fourth parameter allows you to provide the default error message: define('VALID_LETTERS_ONLY', 'VALID_LETTERS_ONLY'); ... ... $v->registerValidator( VALID_LETTERS_ONLY, 'regex', '/^[a-zA-Z]+$/', 'This field must consist only of letters' );

Creating an internal callback validator

Internal callback validators are "internal" because they are defined within the Validator class. The VALID_COMPARE validator, for example, is a separate function within the Validator class, and as such is registered as internal. Internal callback validators can only be created by either modifiying the Validator class itself (not such a good idea), or by subclassing the Validator and then adding the additional callback functions and registration.

To register an internal callback validator, you first need to create the callback function. As an example, consider the VALID_COMPARE function: function validatorValidCompare($field, $compare) { $data =& $this->data; // if either field isn't set, exit if (!isset($data[$field]) || !isset($data[$compare])) { return false; } else { return ($data[$field] == $data[$compare]); } // end if } // end if

First, we obtain all the arguments with func_get_args(). Next, we ensure that the necessary number of parameters have been passed (two in this case). Then, we obtain a reference to the data. Notice that this step references $this->data, a clear indication that this is an internal callback function.

The next step is to actually pull out the parameters. Internal callback functions will always have the primary field being validated as the first parameter, with additional parameters following it. Note that any custom error messages defined for callback functions are NOT passed to the callback, as they are dealt with internally by the Validator.

The final step is the actual validation, which first checks to ensure that the fields are set in the data[].

Now that we have an internal callback function, we need to register it. This is performed with registerValidator: $v->registerValidator( VALID_COMPARE, 'internal', 'validatorValidCompare', 'The fields do not match' );

Notice that the third parameter is now the string name of the callback function to associate with the validator.

In the previous example, the validator was registered through the $v instance object that has been used throughout this documentation. In practice, however, the registration for internal callbacks would be performed in the constructor (this is in fact the case for all pre-defined validators in the Validator). The following example demonstrates fully how to subclass the validator and create and register a new internal callback function: define('VALID_ADMIN_PASS', 'VALID_ADMIN_PASS'); class MyValidator extends Validator() { function validatorAdminPass($field) { $data =& $this->data; return ($data[$field] == 'admin'); } // end function validator_admin_pass function MyValidator($obj=null, $rules=null, $data=null) { // call parent constructor $pclass = get_parent_class($this); $this->$pclass($obj, $rules, $data); $this->registerValidator( VALID_ADMIN_PASS, 'internal', 'validatorAdminPass', "The password must be 'admin'" ); } // end constructor } // end class MyValidator

Creating an external callback validator

A simpler approach to creating callbacks is to create external callbacks, which associate with regular functions defined outside of the Validator class. The only real difference is the way you access the data[] array, since it will not have access to the $this->data member. Rather, Validator provides a static function which will access a reference to the data, called using Validator::get_data(). Note that this function can also be used with internal callback functions, allowing you to easily move the function into or out of a class definition if necessary. The following example reproduces the previous example but with an external callback: define('VALID_ADMIN_PASS', 'VALID_ADMIN_PASS'); ... ... function validatorAdminPass($field) { $data =& Validator::getData(); return ($data[$field] == 'admin'); } // end function validator_admin_pass

Using filters

A filter is a simple way to "clean" field values before validating them. Validator implements filters via callbacks, and keeps the feature lightweight. Unlike validation rules, filters do not need registered in order to use. The callback used to filter can be any number of built-in PHP functions, or it can be a custom function.

The following example demonstrates the use of the PHP trim() function as a filter: $v->applyFilter('username', 'trim');

The previous example applies the trim() function to the value of the 'username' field.

If you want to apply a filter to every element, Validator provides a special constant to make it easy - simply pass ALL_ELEMENTS as the first argument: $v->applyFilter(ALL_ELEMENTS, 'trim');

Retrieving error messages

Validator provides two functions, hasError() and getError() for handling error messages. Both functions accept a field name as a parameter. The hasError() function returns true if the specified field failed to validate. The getError() function will return the error message of the validation rule that failed.

As an example, the following function could be created in the app/app_controller.php file, to be used in any view: function validationErrors($field) { if (isset($this->validator)) { // hasError returns true if the given field contains errors if ($this->validator->hasError($field)) { return sprintf('span>%s</span>', $this->validator->getError($field)); } // end if } // end if isset } Note that the example assumes that a Validator object is assigned to a member named validator of the controller (if using Cake).

Now, the new validationErrors() function can be used in a view: <div> <label>First name:</label> <?=$this->inputTag('first_name'); ?> <?=$this->validationErrors('first_name');?> </div>

One more example

Now, let's look at the code necessary to actually use Validator from a controller (if using Cake). Consider the following function register() in some controller (perhaps MembersController): function register() { $this->validate = array( 'first_name' => VALID_NOT_EMPTY, 'last_name' => VALID_NOT_EMPTY, 'username' => array(VALID_ALPHANUMERIC, VALID_RANGE_LENGTH => array("Username must be between 6 and 10 characters", 6, 10) ), 'password' => array(VALID_NOT_EMPTY, VALID_RANGE_LENGTH => array(null, 6, 10) ), 'confirm' => array(VALID_COMPARE => array(null, 'password')), 'age' => array(VALID_NUMBER, VALID_RANGE => array("You must be at least 13 to register", 13, null)) ); // create new validator $this->validator = new Validator(&$this); // trim() all the fields to remove spaces $this->validator->applyFilter(ALL_ELEMENTS, 'trim'); // perform validation $result = $this->validator->validates(); // if validation passed, show message if ($result) { die('Validation passed!'); } // end if } // end register()

Now, assume that the MembersController contains the validationErrors() function that was created before. The register.thtml view may be something like this: <?=$this->formTag('/members/register/'); ?> <div> <label>First name:</label> <?=$this->inputTag('first_name'); ?> <?=$this->validationErrors('first_name');?> </div> <div> <label>Last name:</label> <?=$this->inputTag('last_name'); ?> <?=$this->validationErrors('last_name');?> </div> <div> <label>Age: </label> <?=$this->inputTag('age'); ?> <?=$this->validationErrors('age');?> </div> <div> <label>Username:</label> <?=$this->inputTag('username'); ?> <?=$this->validationErrors('username');?> </div> <div> <label>Password:</label> <?=$this->passwordTag('password'); ?> <?=$this->validationErrors('password');?> </div> <div> <label>Confirm password:</label> <?=$this->passwordTag('confirm'); ?> <?=$this->validationErrors('confirm');?> </div> <div> <?=$this->submitTag('Submit'); ?> </div> </form>