A free and open-source book on ZF3 for beginners

8.7. Writing Your Own Filter

An alternative to using the Callback filter is writing your own filter class implementing the FilterInterface interface. Then, this filter may be used in forms of your web application (or, if you wish, outside a form).

To demonstrate how to create your own filter, we will write the PhoneFilter class encapsulating the phone filtering algorithm we used with the Callback filter example.

As you may remember, the base concrete class for all standard filters is the AbstractFilter class. By analogy, we will also derive our custom PhoneFilter filter from that base class.

We plan to have the following methods in our PhoneFilter filter class (see table 8.18):

Table 8.18. Public methods of the PhoneFilter filter
Method name Description
__construct($options) Constructor - accepts an optional argument $options, which is needed to set filter options at once.
setFormat($format) Sets the phone format option.
getFormat() Returns the phone format option.
filter($value) Runs the phone filter.

To start, create the PhoneFilter.php file in the Filter directory under the module's source directory 28. Put the following code into that file:

28) The PhoneFilter class may be considered as a service model because its goal is to process data, not to store it. By convention, we store all custom filters under the Filter directory.

namespace Application\Filter;

use Zend\Filter\AbstractFilter;

// This filter class is designed for transforming an arbitrary phone number to 
// the local or the international format.
class PhoneFilter extends AbstractFilter 
  // Phone format constants.
  const PHONE_FORMAT_LOCAL = 'local'; // Local phone format 
  const PHONE_FORMAT_INTL  = 'intl';  // International phone format 
  // Available filter options.
  protected $options = [
    'format' => self::PHONE_FORMAT_INTL
  // Constructor.
  public function __construct($options = null) 
    // Set filter options (if provided).
    if(is_array($options)) {
  // Sets phone format.
  public function setFormat($format) 
    // Check input argument.
    if( $format!=self::PHONE_FORMAT_LOCAL && 
       $format!=self::PHONE_FORMAT_INTL ) {            
      throw new \Exception('Invalid format argument passed.');
    $this->options['format'] = $format;

  // Returns phone format.
  public function getFormat() 
    return $this->format;
  // Filters a phone number.
  public function filter($value) 
    if(!is_scalar($value)) {
      // Return non-scalar value unfiltered.
      return $value;
    $value = (string)$value;
    if(strlen($value)==0) {
      // Return empty value unfiltered.
      return $value;
    // First, remove any non-digit character.
    $digits = preg_replace('#[^0-9]#', '', $value);
    $format = $this->options['format'];
    if($format == self::PHONE_FORMAT_INTL) {            
      // Pad with zeros if the number of digits is incorrect.
      $digits = str_pad($digits, 11, "0", STR_PAD_LEFT);

      // Add the braces, the spaces, and the dash.
      $phoneNumber = substr($digits, 0, 1) . ' (' . 
                     substr($digits, 1, 3) . ') ' .
                     substr($digits, 4, 3) . '-' . 
                     substr($digits, 7, 4);
    } else { // self::PHONE_FORMAT_LOCAL
      // Pad with zeros if the number of digits is incorrect.
      $digits = str_pad($digits, 7, "0", STR_PAD_LEFT);

      // Add the dash.
      $phoneNumber = substr($digits, 0, 3) . '-'. substr($digits, 3, 4);
    return $phoneNumber;                

From line 2, you can see that the filter class lives in the Application\Filter namespace.

In line 8, we define the PhoneFilter class. We derive our filter class from the AbstractFilter base class to reuse the functionality it provides. Line 4 contains the short alias for the AbstractFilter class.

In lines 11-12, for convenience, we define the phone format constants (PHONE_FORMAT_INTL for international format and PHONE_FORMAT_LOCAL for local format). These are the equivalents of the "intl" and "local" strings, respectively.

In lines 15-17, we define the $options private variable, which is an array having the single key named "format". This key will contain the phone format option for our filter.

In lines 20-28, we have the constructor method, which takes the single argument $options. When constructing the filter manually, you may omit this parameter. However, when the filter is constructed by the factory class, the factory will pass filter options to the filter's constructor through this argument.

In lines 31-40 and 43-46, we have the setFormat() and getFormat() methods that allow to set and retrieve the current phone format, respectively.

In lines 49-86, we have the filter() method. This method encapsulates the phone number filtering algorithm. It takes the $value parameter, transforms it by taking the selected phone format in account, and returns the formatted phone number.

8.7.1. Using the PhoneFilter Class

When the PhoneFilter filter class is ready, you can easily start using it in the feedback form (or in another form) as follows. It is assumed that you call the following code inside of the ContactForm::addInputFilter() method:

      'name'     => 'phone',
      'required' => true,                
      'filters'  => [                    				
          'name' => PhoneFilter::class,
          'options' => [
            'format' => PhoneFilter::PHONE_FORMAT_INTL
        // ...
      // ...

You can see how the PhoneFilter filter works in the Form Demo sample application bundled with this book. Open the "http://localhost/contactus" page in your web browser. If you enter some phone number in an incorrect format, the filter will fix the phone number.

If you wish, you can use the PhoneFilter outside of forms, as shown in the code example below:

use Application\Filter\PhoneFilter;

// Create PhoneFilter filter.
$filter = new PhoneFilter();

// Configure the filter.

// Filter a string.
$filteredValue = $filter->filter('12345678901');

// The expected filter's output is the '1 (234) 567-8901'.