Christer’s blog o’ fun

April 30, 2008

Use a SOCKS proxy to connect to MSN Messenger in Pidgin

Filed under: Personal, Technology — Tags: , , , , , — christer @ 9:06 am

At work I’ve been using the HTTP Method in Pidgin to connect to the MSN Messenger service because of some rules in our firewall. Since the firewall is not that stable I was disconnected all the time which was kinda annoying. A coworker gave me a tip on just setting up the MSN account in Pidgin to connect to MSN via a SOCKS proxy. Since I have a computer running Linux at home with a decent enough connection I opened up a proxy from my machine at work to my home computer just using the bind-port option of the ssh command:

ssh -v -D 1080 -C <user>@<host>

Now all connections on port 1080 on my machine at work will be forwarded to my home computer.

The -v option generates some debug messages so the connection doesn’t time out. I’m not really sure if this is needed, but it doesn’t hurt to enable it.The -C option enables (gzip) compression.

The last thing that must be done is to change the MSN account in Pidgin to use localhost:1080 as SOCKS proxy (see image below)

I can’t remember being disconnected a single time after I did this. :)

April 28, 2008

5 cool PHP 5 features

Filed under: PHP, Technology — Tags: — christer @ 9:26 am

Marco Tabini has written about his 5 favourite PHP 5 features:

  1. SimpleXML
  2. JSON and SOAP
  3. PDO
  4. SPL
  5. SQLite

Personally I haven’t used SQLite too much from PHP but I agree with the other features being very nice.

April 26, 2008

Small PHP XML-RPC server implementation

Filed under: PHP, Technology — Tags: , — christer @ 10:35 am

My good friend Mats has relased his small (but oh so good) XML-RPC component over at his blog. Here’s what he says about it:

Usage is as simple as requiring the php-file into your “gateway” page (the URL you’ll be calling from your XML-RPC clients), and then creating the server object with the functions you want to expose as the argument to the constructor.

Links to a bz2 archive and a phps version of the component is available from the blog post.

April 25, 2008

Quick and dirty custom Zend_Form_Decorator_Label

Filed under: PHP, Technology — Tags: , , , — christer @ 5:04 pm

Someone on the zf-mvc list asked the following question:

how to add elements like the description between label and input, for example:


<dt>
    <label>Your E-Mail: *</label><span class="Description">- won't be published</span>
</dt>
<dd>
    <input value="" id="email" class="" type="text" />
</dd>

This is currently not possible out of the box with Zend_Form. I put on my problem-solving-hat and quickly made a rather dirty implementation of a custom decorator to solve the problem specified above. I made a class called My_Form_Decorator_LabelWithDescription that extends the Zend_Form_Decorator_Label class. My custom label class looks like this:


<?php
require_once 'Zend/Form/Decorator/Label.php';

class My_Form_Decorator_LabelWithDescription extends Zend_Form_Decorator_Label {
    /**
     * Description decorator
     *
     * @var Zend_Form_Decorator_Description
     */
    protected $_description = null;

    /**
     * Set the description decorator
     *
     * @param Zend_Form_Decorator_Description $description
     * @return My_Form_Decorator_LabelWithDescription
     */
    public function setDescription(Zend_Form_Decorator_Description $description) {
        $this->_description = $description;
        return $this;
    }

    /**
     * Get the description decorator
     *
     * @return Zend_Form_Decorator_Description
     */
    public function getDescription() {
        return $this->_description;
    }

    public function render($content) {
        $element = $this->getElement();
        $view    = $element->getView();
        if (null === $view) {
            return $content;
        }

        $label     = $this->getLabel();
        $separator = $this->getSeparator();
        $placement = $this->getPlacement();
        $tag       = $this->getTag();
        $id        = $this->getId();
        $class     = $this->getClass();
        $options   = $this->getOptions();

        if (empty($label) &amp;&amp; empty($tag)) {
            return $content;
        }

        if (!empty($label)) {
            $options['class'] = $class;
            $label = $view->formLabel($element->getName(), trim($label), $options);
        }

        if (null !== $this->_description) {
            $this->_description->setElement($element);
            $label = $this->_description->render($label);
        }

        if (null !== $tag) {
            require_once 'Zend/Form/Decorator/HtmlTag.php';
            $decorator = new Zend_Form_Decorator_HtmlTag();
            $decorator->setOptions(array('tag' => $tag));
            $label = $decorator->render($label);
        }

        switch ($placement) {
            case self::APPEND:
                return $content . $separator . $label;
            case self::PREPEND:
                return $label . $separator . $content;
        }
    }
}

I have added a protected $_description property and a set/get method for it.

The render method is simply copied from the parent class with a small modification:


if (null !== $this->_description) {
    $this->_description->setElement($element);
    $label = $this->_description->render($label);
}

This will render the description and add it to the label.

The last part of this post will show you how to enable this marvelous new decorator. I will use some code from my Translating Zend_Form error messages and more post, so if you haven’t read that yet you should catch up by doing so. :)

In the mentioned post I had some custom elements that added decorators. Lets add this new decorator to the custom text fields. The My_Form_Element_Text class looked like this:


<?php
require_once 'Zend/Form/Element/Text.php';

class My_Form_Element_Text extends Zend_Form_Element_Text {
    public function init() {
        $this->setDecorators(array(
             array('ViewHelper'),
             array('Description', array('escape' => false,
                                        'class' => 'fieldDescription')),
             array('Errors'),
             array('HtmlTag', array('tag' => 'dd')),
             array('Label', array('requiredSuffix' => ' *',
                                  'tag' => 'dt',
                                  'escape' => false))
        ));
    }
}

As you can see both the Label and Description decorators are added, but since my decorator includes the Description within the Label we don’t need both! After a little modification, the class now looks like:


<?php
require_once 'Zend/Form/Element/Text.php';
require_once 'Zend/Form/Decorator/Description.php';
require_once 'My/Form/Decorator/LabelWithDescription.php';

class My_Form_Element_Text extends Zend_Form_Element_Text {
    public function init() {
        $description = new Zend_Form_Decorator_Description(array(
            'tag' => 'span',
            'escape' => false,
            'class' => 'fieldDescription'));

        $label = new My_Form_Decorator_LabelWithDescription(array(
            'requiredSuffix' => ' *',
            'tag' => 'dt',
            'escape' => false));

        // Add the description decorator to the label
        $label->setDescription($description);

        $this->setDecorators(array(
             array('ViewHelper'),
             array('Errors'),
             array('HtmlTag', array('tag' => 'dd')),
             array($label)
        ));
    }
}

And voila! You now have a fully customizable Description decorator that will be rendered inside the Label. Pretty neat eh?

This is not exactly the best implementation of such a decorator but it gets the job done. It would be cool if some hooks where added to the default decorator that child classes could implement. That way I wouldn’t have to copy the entire render method from the Label decorator.

Using objects with Zend_View’s PartialLoop helper

Filed under: PHP, Technology — Tags: , , , — christer @ 2:23 pm

I just experienced some problems using an iteratable object like Zend_Db_Table_Rowset with Zend_View’s PartialLoop helper. What happens internally is that the Partial helper (that PartialLoop uses) converts the object we want to iterate on to an array using the object’s toArray method (if it exists). Now, that’s fine if you only want to access the properties of the object. The methods simply disappear…

I looked in the docs about this but found nothing. After some searching on the fw-mvc list I found a link to ZF’s issue tracker: http://framework.zend.com/issues/browse/ZF-2431

Let’s say we want to loop through a list of names and phone numbers. We have a view script called index.phtml that contain the following code:


<h1>People</h1>
<table>
<?= $this->partialLoop('partials/_person.phtml', $this->people) ?>
</table>

$this->people is in this case a Zend_Db_Table_Rowset object that contain several Person objects (which extends Zend_Db_Table_Row). The _person.phtml script looks like this:


<tr>
    <td><?= $this->name ?></td>
    <td><?= $this->phoneNumber ?></td>
</tr>

So far so good. Now, if the Person object has a method called getFather() that returns another Person object, and want to use that in _person.phtml we will experience some problems. If we change the _person.phtml script to look like:


<tr>
    <td><?= $this->name ?></td>
    <td><?= $this->phoneNumber ?></td>
    <td>Father: <?= $this->getFather()->name ?></td>
</tr>

and refresh the page we will get the following error:

helper ‘GetFather’ not found in path

As you can see the Person::getFather method has disappeared and since $this refers to the current view object, it thinks getFather is a view helper. I can’t remember when my father helped me with my view scripts, but that’s a different story!

How to fix this you ask? This is where the Zend_View_Helper_Partial::setObjectKey() method comes into play! If you have set an object key using that method on the PartialLoop helper and loop an object, you have to access the object in the partial loop script using the same object key you specified in the setObjectKey() call. Confused? Nothing that a little code can’t fix!

If you want to use the same object key in all your partial loop scripts, you can set the object key in your bootstrapper doing something like this:


$view = new Zend_View();
$view->partialLoop()->setObjectKey('object');

$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$viewRenderer->setView($view);

Now we also need to change the _person.phtml script to access the object in a slightly different manner:


<tr>
    <td><?= $this->object->name ?></td>
    <td><?= $this->object->phoneNumber ?></td>
    <td>Father: <?= $this->object->getFather()->name ?></td>
</tr>

Refresh, and the people in the list will have a father!

Example Zend Framework Blog application

Filed under: PHP, Technology — Tags: , , , — christer @ 8:57 am

Pádraic Brady is running a series of articles on how to make a blogging application using Zend Framework. He starts off part one by saying:

So here is Part 1 of another lengthy PHP tutorial series on writing a blog application using the Zend Framework. To make things interesting, this will not be an articifial example - when development has ceased, I will deploy and put into production my marvelous new blog, and then proceed to modify it to the point of death, bending it to my will. Muahahahaha.

Part 1: Introductory Planning
Part 2: The MVC Application Architecture

April 24, 2008

Translating Zend_Form error messages and more

Filed under: PHP, Technology — Tags: , , , , , — christer @ 4:36 pm

Update: Fixed some stuff based on an email from Matthew Weier O’Phinney. Also updated the downloadable file. Changes include:

  • Removing calls to setDisableLoadDefaultDecorators() since they are not needed
  • Fixed typo in a comment
  • Added DtDdWrapper to the display group to generate valid markup

Zend_Form supports i18n using the Zend_Translate component. Here is a tutorial on how to make your (Zend_) forms display labels, descriptions and error messages in different languages. Just to be nice I’ll make the example a bit more complex by creating my own custom form elements and a display group for the form buttons. This tutorial includes alot of PHP code, so don’t say I didn’t warn you. This time I have made a sample application that you can download and look at while going through the tutorial. Download this tar.gz file and unpack it and configure apache to use the www directory as document root. You will also need mod_rewrite to be enabled on the apache server. You also need to put Zend Framework in the lib directory. I omitted it to keep the file size to a minimum.

Now, lets get on with the show!

Lets start by defining our form. This time I’ll make a “contact me” form with the following elements:

  • Name (Custom text element)
  • Email (Custom email text element)
  • Content (Custom textarea element)
  • Submit button (Custom submit element)
  • Reset button (Custom reset element)

The form ContactForm will extend Zend_Form and the class looks like this:


<?php
require_once 'Zend/Form.php';

class ContactForm extends Zend_Form {
    public function init() {
        $this->setMethod('post')
             ->setAction('/')
             ->setName('contactForm');

        $name = new My_Form_Element_Text('name');
        $name->setLabel('formLabels_Name')
             ->setDescription('formDescriptions_Name')
             ->setRequired(true);

        $email = new My_Form_Element_Text_Email('email');
        $email->setLabel('formLabels_Email')
              ->setDescription('formDescriptions_Email')
              ->setRequired(true);

        $content = new My_Form_Element_Textarea('content');
        $content->setLabel('formLabels_Content')
                ->setDescription('formDescriptions_Content')
                ->setRequired(true)
                ->setAttribs(array('rows' => 6, 'cols' => 50));

        $submit = new My_Form_Element_Submit('submit');
        $submit->setLabel('formLabels_Submit');

        $reset = new My_Form_Element_Reset('reset');
        $reset->setLabel('formLabels_Reset');

        $this->addElements(array($name, $email, $content, $submit, $reset));
        $this->addDisplayGroup(array('submit', 'reset'), 'buttons');

        $group = $this->getDisplayGroup('buttons');
        $group->setDecorators(array(
            array('FormElements'),
            array('HtmlTag', array('tag' => 'div',
                                   'class' => 'formButtons')),
            array('DtDdWrapper'),
        ));
    }
}

The first thing you might notice is the weird labels and descriptions in the form elements. We will cover those later on when setting up the Zend_Translate object. You don’t have to pay much attention to that aspect yet.

A couple of other things worth noticing in this class is the custom elements:

  • My_Form_Element_Text
  • My_Form_Element_Text_Email
  • My_Form_Element_Textarea
  • My_Form_Element_Submit
  • My_Form_Element_Reset

This is not really necessary but since I’m such a nice guy I’ll do it anyways! :p

The last thing I do in the form is to create a display group for the buttons in the form. I do this so I can wrap them in a div container. Later on you’ll se how I remove all the default decorators on the buttons so I only get two input tags in the div container.

Now that we have our form class set up, lets have a look at the custom elements.

My_Form_Element_Text


<?php
require_once 'Zend/Form/Element/Text.php';

class My_Form_Element_Text extends Zend_Form_Element_Text {
    public function init() {
        $this->setDecorators(array(
             array('ViewHelper'),
             array('Description', array('escape' => false,
                                        'class' => 'fieldDescription')),
             array('Errors'),
             array('HtmlTag', array('tag' => 'dd')),
             array('Label', array('requiredSuffix' => ' *',
                                  'tag' => 'dt',
                                  'escape' => false))
        ));
    }
}

Here I set the decorators I want and the order I want them to appear in. For the description and the label decorators you can see that I set the ‘escape’ attribute to false. This is so I can include HTML code/entities in the labels and descriptions. I also add a custom css class to the description decorator.

My_Form_Element_Text_Email


<?php
require_once 'My/Form/Element/Text.php';

class My_Form_Element_Text_Email extends My_Form_Element_Text {
    public function init() {
        // Call up our parents init method as well
        parent::init();

        // Add a validator
        $this->addValidator('EmailAddress');
    }
}

This is simply a text element with a validator attached. Yes, I know this is completely overkill but it might help you understand more on how to make custom elements. The validator could of course be added to the element in the form class, but hey, you already know how to do that! If not, have a look at my previous post on Zend_Form!

My_Form_Element_Textarea


<?php
require_once 'Zend/Form/Element/Textarea.php';

class My_Form_Element_Textarea extends Zend_Form_Element_Textarea {
    public function init() {
        $this->setDecorators(array(
             array('ViewHelper'),
             array('Description', array('escape' => false,
                                        'class' => 'fieldDescription')),
             array('Errors'),
             array('HtmlTag', array('tag' => 'dd')),
             array('Label', array('requiredSuffix' => ' *',
                                  'tag' => 'dt',
                                  'escape' => false))
        ));
    }
}

Almost identical to the custom text element. Nothing new here, move on!

My_Form_Element_Submit


<?php
require_once 'Zend/Form/Element/Submit.php';

class My_Form_Element_Submit extends Zend_Form_Element_Submit {
    public function init() {
        $this->addDecorator('ViewHelper');
    }
}

Here I only add the ViewHelper decorator that simply renders the input tag and nothing more.

My_Form_Element_Reset


<?php
require_once 'Zend/Form/Element/Reset.php';

class My_Form_Element_Reset extends Zend_Form_Element_Reset {
    public function init() {
        $this->addDecorator('ViewHelper');
    }
}

Identical to the submit element.

Now that we have made the classes for all the custom elements, lets include our form in a controller action and render it within a view.

To make it easy I’ll put it in the index action of my index controller:


class IndexController extends Zend_Controller_Action {
    public function indexAction() {
        require_once 'ContactForm.php';
        $contactForm = new ContactForm();

        $request = $this->getRequest();

        if ($request->isPost() && $contactForm->isValid($_POST)) {
            // do some processing of the form
            // ...
            // ...
        }

        $this->view->contactForm = $contactForm;
    }
}

In the view you could then simply do:


<?= $this->contactForm->render() ?>

If you have followed the tutorial you will end up with a form looking something like this:

As you see the form looks great, except for the weird labels and descriptions. This is where Zend_Translate enters the show!

From the ZF docs:

Zend_Translate is Zend Framework’s solution for multilingual applications.

Just what we need!

The Zend_Translate component is adapter based like many other ZF components. All of the supported adapters are listed in the docs. For this tutorial I will use the Array adapter. That means that we will store our language in php arrays. I usually structure my applications the following way:

  • /application
  • /application/controllers
  • /application/views
  • /application/models
  • /application/forms
  • /lib/My
  • /lib/Zend
  • /www
  • /language
  • /etc

For this simple application I will add two languages: English and Norwegian. I will store each language in its own script inside the language directory. The files will look like this:

english.php


<?php
return array(
    'formLabels_Name' => 'Name',
    'formLabels_Email' => 'Email',
    'formLabels_Content' => 'Content',
    'formLabels_Submit' => 'Submit',
    'formLabels_Reset' => 'Reset',

    'formDescriptions_Name' => 'Your complete name',
    'formDescriptions_Email' => 'Your email address so I can contact you',
    'formDescriptions_Content' => 'Write something clever here',
);

norwegian.php


<?php
return array(
    'formLabels_Name' => 'Navn',
    'formLabels_Email' => 'Email',
    'formLabels_Content' => 'Innhold',
    'formLabels_Submit' => 'Send',
    'formLabels_Reset' => 'Nullstill',

    'formDescriptions_Name' => 'Skriv ditt fulle navn',
    'formDescriptions_Email' => 'Skriv din epostadresse her slik at jeg kan kontakte deg',
    'formDescriptions_Content' => 'Skriv noe morsomt til meg her',
);

Both the files simply returns an array. To store the content of these files in variables we simply do:


$english = require APP_ROOT . '/language/english.php';
$norwegian = require APP_ROOT . '/language/norwegian.php';

Since Zend_Translate lets you specify a filename as the source of a language you can easily skip the two lines above.

A good place to instantiate your translate object is in the bootstrapper (typically /www/index.php). The way to do this is as follows:


// Create the object and add a language
$translate = new Zend_Translate('Array', APP_ROOT . '/language/english.php', 'en_US');
// Add another translation
$translate->addTranslation(APP_ROOT . '/language/norwegian.php', 'nb_NO');
// Set nb_NO as default translation
$translate->setLocale('nb_NO');

In the first line we instantiate the $translate object and specify the English language file. The last argument is the locale of the language. A list of supported locales can be found in the Zend_Locale class.

Then we add a Norwegian translation and last we set the Norwegian translation as default. This should probably be specified in a ini file and maybe let the users viewing the site choose for themselves, but for now we’ll just set it in the bootstrapper.

Zend_Translate also supports automatic language detection by parsing the content of $_SERVER['HTTP_ACCEPT_LANGUAGE'] but I won’t go deeper into that here. Read more about it in the docs. For now we’ll settle on serving everyone a Norwegian version of the form. Everybody understands Norwegian right?

Now, let’s reload our page containing the form and see what happens!

Nothing! Well … what did you expect?! Zend_Form does not know anything about the global $translate object. What you need to do is store the $translate object in Zend_Registry with a given key as stated in the docs:


Zend_Registry::set('Zend_Translate', $translate);

And thats about it! If you reload the page containing the form you will see that the labels and descriptions have changed to Norwegian! Success! Now lets submit the empty form and see what happens!

Oops … the error messages are clearly still in English. And how do we fix that? We simply need to add the translation keys associated with the different error messages to the language we want. And where do we find them you ask? In the validator classes of course!

Since we have set the elements as required the NotEmpty validator is automatically added to the elements. Along with that we need to translate the EmailAddress validator. If we take a look in those classes we find the following message keys:


// Zend_Validate_NotEmpty
const IS_EMPTY = 'isEmpty';

// Zend_Validate_EmailAddress
const INVALID            = 'emailAddressInvalid';
const INVALID_HOSTNAME   = 'emailAddressInvalidHostname';
const INVALID_MX_RECORD  = 'emailAddressInvalidMxRecord';
const DOT_ATOM           = 'emailAddressDotAtom';
const QUOTED_STRING      = 'emailAddressQuotedString';
const INVALID_LOCAL_PART = 'emailAddressInvalidLocalPart';

All validators are built like this; with messages keys and a message template that holds the english translation of the error message.

The EmailAddress validator also does some validation of the hostname using Zend_Validate_Hostname so there are probably more message keys that needs to be translated but now that you know how it works I don’t have to do it for you. :)

So, if we want to translate the single message found in Zend_Validate_NotEmpty we add a key => value pair in the array in our norwegian.php file:


'isEmpty' => 'Felter mangler innhold.',

Now, if we leave the form elements empty and submit the form the Norwegian error message should appear.

And that’s about it I guess. Hopefully you have learned a thing or two about how to enable i18n in your Zend_Form’s! :) If you leave a comment it will make my day!

April 17, 2008

Please leave a message after the beep

Filed under: Personal — christer @ 9:25 am

I’m not sure why, but I really hate it when I have to leave a message on somebody’s answering machine. Whats even worse is when I get a message myself… It sometimes freaks me out.

I did try to remove that feature from my mobile phone subscription but only the administrators of the phone stuff at work is allowed to do that and I don’t think “because it freaks me out” is a good enough excuse to have it disabled! :p

April 16, 2008

Custom validators for Zend_Form_Element

Filed under: PHP, Technology — Tags: , , — christer @ 2:40 pm

I am checking out Zend_Form from the Zend Framework these days and on my way to work today I wrote a custom validator that checks if an element has the same value as other elements in the form (typically used to confirm a password or an email address).

This “tutorial” contains quite a lot of code and requires some understanding of how Zend Framework works and how OOP works in PHP5.

My “register user” form consists of four elements:

  • Email
  • Confirm email
  • Password
  • Confirm password

All elements are required and I have attached the EmailAddress validator to the two email elements to ensure correct addresses. The thing I also want to do is to ensure that the two email elements have the same value. The same goes for the two password elements. To do this I had to write a custom validator for these cases. A similar validator can be found in the Zend_Form docs but I wanted to be able to specify the names of the element(s) an element must be equal to when I add the validator instead of hardcoding them in the validator classes (like the example in the docs does).

The form class looks a little something like this:


<?php
require_once 'Zend/Form.php';

class FKK_RegisterUserForm extends Zend_Form {
    public function init() {
        /* Set the action and method. The action is generated using the view's url helper */
        $this->setAction($this->getView()->url(array(), 'User_Register'));
        $this->setMethod('post');

        $mail = new FKK_Form_Element_Text('mail');
        $mail->setLabel('Mail');
        $mail->setDescription('Your email address');
        $mail->setRequired(true);
        $mail->addValidator('EmailAddress', true);
        $mail->addValidator('EmailConfirmation', false, array('mailRepeat'));

        $mailRepeat = new FKK_Form_Element_Text('mailRepeat');
        $mailRepeat->setLabel('Mail (repeat)');
        $mailRepeat->setDescription('Please confirm your email address');
        $mailRepeat->setRequired(true);
        $mailRepeat->addValidator('EmailAddress', true);
        $mailRepeat->addValidator('EmailConfirmation', false, array('mail'));

        $password = new FKK_Form_Element_Password('password');
        $password->setLabel('Password');
        $password->setDescription('Your password. Must be between 4 and 20 characters.');
        $password->setRequired(true);
        $password->addValidator('StringLength', true, array(4, 20));
        $password->addValidator('PasswordConfirmation', false, array('passwordRepeat'));

        $passwordRepeat = new FKK_Form_Element_Password('passwordRepeat');
        $passwordRepeat->setLabel('Password (repeat)');
        $passwordRepeat->setDescription('Please confirm your password');
        $passwordRepeat->setRequired(true);
        $passwordRepeat->addValidator('StringLength', false, array(4, 20));
        $passwordRepeat->addValidator('PasswordConfirmation', false, array('password'));

        $submit = $this->createElement('submit', 'registerUser');
        $submit->setLabel('Register user');

        $this->addElements(array($mail, $mailRepeat, $password, $passwordRepeat, $submit));
    }
}

The class is just an extension of the Zend_Form class. The only method the class needs to implement is a public init() method which is called from Zend_Form’s constructor.

The init() method above creates some custom text (FKK_Form_Element_Text) and password (FKK_Form_Element_Password) elements. The reason I have made my own elements is so I can do specific stuff with only text and password fields (like adding the Description decorator and a custom plugin prefix. More on this later).

After creating an element I set some properties to it and then add some validators. When adding multiple validators I specify the second argument to the addValidator method ($breakChainOnFailure) to stop the rest of the validation when one of them fails. At least thats what I think $breakChainOnFailure does. :)

The interesting validator here is “EmailConfirmation” and “PasswordConfirmation”. They both to pretty much the same thing so I’ll only describe one of them.


$mail->addValidator('EmailConfirmation', false, array('mailRepeat'));

The code above means that the mail element must have the same value as the “mailRepeat” element. If there are other elements it needs to match as well you can just specify more element names in the array. The third parameter to the addValidator method is passed to the constructor of the validator which will be shown later on. Lets move on!

The custom element classes looks like this:


<?php
require_once 'Zend/Form/Element/Text.php';

class FKK_Form_Element_Text extends Zend_Form_Element_Text {
    public function init() {
        $this->setDisableLoadDefaultDecorators(true);
        $this->addPrefixPath('FKK_Validate', 'FKK/Validate/', 'validate');

        $this->addDecorator('ViewHelper');
        $this->addDecorator('Errors');
        $this->addDecorator('Description', array('escape' => false, 'class' => 'fieldDescription'));
        $this->addDecorator('HtmlTag', array('tag' => 'dd'));
        $this->addDecorator('Label', array('requiredSuffix' => ' *', 'tag' => 'dt', 'class' => 'fieldLabel'));
    }
}

The FKK_Form_Element_Password class is exactly the same except that it extends the Zend_Form_Element_Password class instead so I’ll skip that one.

First I disable the loading of the default decorators and then add a custom prefix path that is used by the plugin loader (Zend_Loader_PluginLoader) that loads up the different decorators and validators. I need to do this so I can magically use my own validators.

Last I add the decorators I want and configure them the way I want them to appear. Most of the stuff is pretty close to the default (with the exception of the Description decorator). As you might notice I only override the public init() method (as I did when extending Zend_Form). The init() method is called from the constructor inherited from Zend_Form_Element_Text and Zend_Form_Element_Password.

And now onto the interesting part … the FKK_Validate_EmailConfirmation validator (as mentioned the PasswordConfirmation validator works exactly the same way so I only cover one of them).

The class looks a little something like this:


<?php
require_once 'Zend/Validate/Abstract.php';

class FKK_Validate_EmailConfirmation extends Zend_Validate_Abstract {
    const NOT_MATCH = 'emailConfirmationNotMatch';

    protected $_messageTemplates = array(
        self::NOT_MATCH => 'Email confirmation does not match'
    );

    /**
     * The fields that the current element needs to match
     *
     * @var array
     */
    protected $_fieldsToMatch = array();

    /**
     * Constructor of this validator
     *
     * The argument to this constructor is the third argument to the elements' addValidator
     * method.
     *
     * @param array|string $fieldsToMatch
     */
    public function __construct($fieldsToMatch = array()) {
        if (is_array($fieldsToMatch)) {
            foreach ($fieldsToMatch as $field) {
                $this->_fieldsToMatch[] = (string) $field;
            }
        } else {
            $this->_fieldsToMatch[] = (string) $fieldsToMatch;
        }
    }

    /**
     * Check if the element using this validator is valid
     *
     * This method will compare the $value of the element to the other elements
     * it needs to match. If they all match, the method returns true.
     *
     * @param $value string
     * @param $context array All other elements from the form
     * @return boolean Returns true if the element is valid
     */
    public function isValid($value, $context = null) {
        $value = (string) $value;
        $this->_setValue($value);

        $error = false;

        foreach ($this->_fieldsToMatch as $fieldName) {
            if (!isset($context[$fieldName]) || $value !== $context[$fieldName]) {
                $error = true;
                $this->_error(self::NOT_MATCH);
                break;
            }
        }

        return !$error;
    }
}

The validator extends the Zend_Validate_Abstract class and contains two methods:

  • public function __construct($fieldsToMatch = array())
  • public function isValid($value, $context = null)

The constructor is needed so we can store the parameter that is given to the addValidator() method (see above).

The isValid() method simply compares the value of the current element with the values of the other elements it needs to match. The $context parameter is an associative array that contains all the other elements in the form.

The error message follows the same pattern as the other validate classes so we can use Zend_Translate to have different translations of the error message (not covered here).

The only thing left to do now is to display the form and when validating using $form->isValid($_POST); the custom validator will run and display an error next to the fields that is not valid. The following code will do that:


<?php
// Create an instance of the form
$form = new FKK_RegisterUserForm();

// Initialize the array of values that we will populate the form with
$values = array();

// See if anyone has clicked the submit button
if (isset($_POST['registerUser'])) {
    $values = $form->getValues();

    // see if the form is valid
    if ($form->isValid($_POST)) {
        // do some clever stuff here
    }
}

// Populate the form with values
$form->populate($values);

// Display the form
print($form);

And thats about it. :) Please leave comments and if you see any mistakes I made don’t hesitate to tell me about it. :) I am new to Zend_Form so there are probably some stuff that can be done in a different (read: better) way.

Blog at WordPress.com.