Tudor Barbu's blog

Ramblings about software development

While working on a Zend Framework project, I’ve found myself in need of a “same as” validator. If you’re familiar with Zend Framework’s form system, then you know what a validator is, if not, enhance your knowledge on the topic by reading this page from ZF’s manual.

My validator was supposed to check if the same value was entered into two password fields, to avoid the user’s typos in passwords. You know, those annoying “please re-type your password” fields. At the time, I couldn’t find a good validator for this purpose, so I’ve decided to write my own. The source code is available here:

/**
 * Checks if a field has the same value as another,
 * very useful in validating passwords
 *
 * @package Generic Validate
 * @author Tudor Barbu <miau@motane.lu>
 * @copyright MIT
 */

class Generic_Validate_SameAs extends Zend_Validate_Abstract {
    /**
     * Validation failure message key for when the values are not
     * the same
     */
    const NOT_THE_SAME = 'not_the_same';

    /**
     * the external element that we check the value against
     *
     * @var Zend_Form_Element
     */
    protected $_element;

    /**
     * Validation failure message template definitions
     *
     * @var array
     */
    protected $_messageTemplates = array(
        self::NOT_THE_SAME => 'The two values are not identical',
    );

    /**
     * Can receive a Zend_Form_Element parameter that will be used
     * into the validation process
     *
     * @param Zend_Form_Element
     */
    public function __construct(Zend_Form_Element $element = null) {
        if(null !== $element) {
            $this->setElement($element);
        }
    }

    /**
     * Set the element
     *
     * @param Zend_Form_Element $element
     * @return void
     */
    public function setElement(Zend_Form_Element $element) {
        $this->_element = $element;
    }

    /**
     * gets the element
     *
     * @return Zend_Form_Element
     */
    public function getElement() {
        return $this->_element;
    }

    /**
     * overrides isValid from Zend_Validate_Interface
     *
     * @param string $value
     * @return bool
     */
    public function isValid($value) {
        if(null === $this->_element) {
            require_once 'Zend/Exception.php';
            throw new Zend_Exception('You must add a Zend_Form_Element to the SameAs validator prior to calling the isValid() method');
        }

        if($value != $this->_element->getValue()) {
            $this->_error(self::NOT_THE_SAME);
            return false;
        }

        return true;
    }
}

// EOF

PS: The formatting plugin I’m using might not display everything correctly :( Aside from that, It’s quite easy to use:

// create some filters
$stringTrimFilter = new Zend_Filter_StringTrim();
$stripTagsFilter = new Zend_Filter_StripTags();

// other validators
$notEmptyValidator = new Zend_Validate_NotEmpty();
$notEmptyValidator->setMessage('This field is required');

// create the SameAs validator
$sameAsValidator = new Generic_Validate_SameAs();
$sameAsValidator->setMessage('The 2 fields do not coincide', Generic_Validate_SameAs::NOT_THE_SAME);

// create the main password field
$password = new Zend_Form_Element_Password('password');
$password->setOptions(
    array(
        'label'       => 'Password',
        'required'   => true,
        'filters'      => array($stringTrimFilter, $stripTagsFilter),
        'validators' => array($notEmptyValidator),
    )
);    

// add this field to the SameAs validator
$sameAsValidator->setElement($password);

// create the new
$validateNewPassword = new Zend_Form_Element_Password('validateNewPassword');
$validateNewPassword->setOptions(
    array(
        'label'        => 'Re-type the password',
        'required'   => true,
        'filters'      => array($stringTrimFilter, $stripTagsFilter),
        'validators' => array($notEmptyValidator, $sameAsValidator), // add it as a validator to the second field
    )
);

If the value entered in the second field isn’t the same as the value entered in the first field, the validator will signal an error. Simple as that.

Zend Framework has some weird “features” so to say, which pop out from time to time and get the programmers really annoyed. The latest thing quoteInto() method of Zend_Db_Adapter_Abstract class. This is supposed to quote input variables prior to entering them into a SQL command to avoid SQL injection attacks. Sounds cool, doesn’t it? Well…not quite. Because, because it can only quote one argument.

You can’t write:

// note: I usually extend my models from the Zend_Db_Table_Abstract class
// this code should be executed in a model of a such model
$select = new Zend_Db_Select($this->_db);
$select->from($this->_name);
$select->where($this->_db->quoteInto('(a = ? AND b = ?) OR (c != ?)', $a, $b, $c);

…as this won’t work. Of course, there’s a really lame approach, to concatenate the output from multiple quoteInto() calls. Like such:

$select->where(
    $this->_db->quoteInto('(a =? AND ', $a) .
    $this->_db->quoteInfo('b = >)', $b) .
    $this->_db->quoteInfo(' OR ( c != ?)', $c)
);

As I said, extremely lame. What’s to do then. Well, when I’ve hit this roadblock, I’ve opted for prepared statements. Of course, they brake the OOP encapsulation of SQL queries but if you don’t need to alter the query later it’s okay. So:

$stmt = $this->_db->prepare( 'SELECT * FROM ' . $this->_name . ' WHERE (a = ? AND b = ?) OR (c != ?)');
$stmt->execute(array($a, $b, $c));

This also works when you need to write custom sql queries:

$stmt = $this->_db->prepare('INSERT INTO ' . $this->_name . ' (first, second) VALUES (?, ?) ON DUPLICATE KEY UPDATE first = first + 1');
$stmt->execute(array($first, $second));

A decent workaround to the quoteInto() problem…

20 May

Zend_Tool

Posted by Tudor. Tags: ,

Zend Framework I’ve downloaded the 1.8 version of Zend Framework and looked over Zend_Tool these last days. What can I say. What is there to say. Zend_Tool is cvasi-useless. You can use it to create a new project, as a shortcut to cp -r :) . It also generates a xml file with all the project’s data, which is really useless, I mean, you can’t do anything with that file, there aren’t any tools that use it. You can use it to add methods and controllers (it also creates the associated views) – again nothing that a 10 lines python script can’t do.

It lacks all the “good” features such a tool should have. What I expect from the next release of Zend_Tool is:

  • automatic form generation based on a database table’s structure, like CakePHP’s bake and scaffolding
  • easier customisation – I don’t want to write a Zend_Tool based application and use it to write my Zend Framework app
  • allow usage of a custom coding standard (I use tabs for indenting, not 4 spaces)
  • much, much better documentation

And now for something positive, I really like the new bootstrapping system they’ve introduced in the 1.8 release.

24 Apr

Styling Zend_Form

Posted by Tudor. Tags: , ,

As a Zend Framework user I use a lot the Zend_Form component to render my forms. I like it, it’s easy to use, pretty versatile and comes with lots of ready built filters and validators meant to make life easier. But the problems with Zend_Form begin when one tries to style it. Problem partially solved by the developers of ZF with the use of the decorator pattern.

Using decorators, the form’s elements can be wrapped in custom HTML tags and alter the way the form displays.

But in some cases, for example when you want each element of the form to have its custom markup, to have 3 elements on the first line and 2 on the second or you want to have all the errors grouped in a single area like in Drupal, the decorator pattern fails. Bad. What to do then? Stop using Zend_Form? Or hacking it? If the second choice sounds better, then keep reading :P Read the rest of this entry »

22 Mar

PHP Geek Meet

Posted by Tudor. Tags: , , ,

I’ve attended the first PHP GeekMeet in Cluj (for those of you that are geographically impaired, this is Cluj). I’ve held a presentation on Zend Framework, a long yet not as boring as I thought, 2 hours long presentation. My colleague Alex Novac from Zitec also held a presentation about optimisation techniques in PHP and disassembled a simple == operator call to see what happens below . The other presentation, although not strictly PHP related, was held by Andrei Gheorghe on scalability techniques.

I consider this edition of GeekMeet to be a succes and I congratulate Mihai for it.

PS: my presentation can be downloaded here.

02 Mar

Geek Meet

Posted by Tudor. Tags: , ,

geekmeetI’m going to hold a presentation about Zend Framework at PHP Geek Meet in Cluj, Romania. Everyone’s invited.

Read more about the meeting here – link available only in Romanian.

17 Feb

Zend Framework and hidden fields

Posted by Tudor. Tags: , ,

One thing I really hate about forms in Zend Framework is the fact that the hidden fields are displayed as blank lines – sounds stupid but this is what really happens – and if you have more than one – let’s say 4 or 5 – their presence can become really annoying and ruin the layout. This happens because although the hidden fields aren’t shown by the browser, their wrapping tags are. ZF wraps all the form elements in additional markup with the help of decorators. The default XHTML for a hidden field in a Zend_Form looks something like this:

<dt>&nbsp;</dt>
<dd><input type="hidden" id="myElement" name="myElement" /></dd>

The definition tags have display:block, so they insert a lot of empty spaces in the layout. Which in the end will make the designer very unhappy. The solution is to remove all the definition tags or to hide them. Since this is presentational matter, it should fall to a view helper. The first idea was to remove the tags completely for hidden fields. Like such:

class Motanelu_View_Helper_FormFixer {
    public function formFixer( Zend_Form $_form ) {
        foreach( $_form->getElements() as $element ) {
            if( $element instanceof Zend_Form_Element_Hidden ) {
                $element->removeDecorator( 'HtmlTag' );
                $element->removeDecorator( 'Label' );
            }
        }
        return $_form;
    }
}

…and in the view (assuming that the helper is the the right path and will be picked out automatically by Zend Framework)…

echo $this->formFixer( $this->form );

Should do the trick. And it does. A lame trick :) If there are more elements in the form, the resulting markup will look something like this:

<form enctype="application/x-www-form-urlencoded" action="" method="post">
    <dl class="zend_form">
        <dt><label for="name" class="optional">Enter your name</label></dt>
        <dd><input type="text" name="name" id="name" value="" /></dd>
        <input type="hidden" name="id" value="" id="id" />
        <dt>&nbsp;</dt>
        <dd><input type="submit" name="submit" id="submit" value="Go go go!" /></dd>
    </dl>
</form>

Yep, that’s right! Invalid code. Not good. Another approach would be to hide the dt and dd tags that wrap around hidden fields with CSS. First attempt:

class Motanelu_View_Helper_FormFixer {
    public function formFixer( Zend_Form $_form ) {
        foreach( $_form->getElements() as $element ) {
            if( $element instanceof Zend_Form_Element_Hidden ) {
                foreach( $element->getDecorators() as $decorator ) {
                    $decorator->setOption( 'class', 'hidden' );
                }
            }
        }
        return $_form;
    }
}

But this doesn’t work as expected. The code above will produce:

<dt>&nbsp;</dt>
<dd class="hidden"><input type="hidden" name="id" value="" id="id" /></dd>

The dt doesn’t have the hidden class added to it. After some debugging, I’ve found why. It’s not a bug, it’s an…well…undocumented feature of Zend Framework.

// Zend_Form_Decorator_Label - line 306
if (!empty($label)) {
    $options['class'] = $class;
    $label = $view->formLabel($element->getFullyQualifiedName(), trim($label), $options);
} else {
    $label = '&nbsp;';
}

I’m very curious who is the rocket scientist that came up with this idea and more important why. Why shouldn’t I be allowed to change the class of an empty label? Well, never mind…But since it’s a very bad practice to change a framework’s source code, I’ve looked for a workaround. And found one: if the element doesn’t have a label, I’ll add a blank one. Like such:

class Motanelu_View_Helper_FormFixer {
    public function formFixer( Zend_Form $_form ) {
        foreach( $_form->getElements() as $element ) {
            if( $element instanceof Zend_Form_Element_Hidden ) {
                $label = $element->getLabel();
                if( empty( $label ) ) {
                    $element->setLabel( '&nbsp;' );
                }
                foreach( $element->getDecorators() as $decorator ) {
                    $decorator->setOption( 'class', 'hidden' );
                }
            }
        }
        return $_form;
    }
}

After adding the following code to my CSS file

.hidden {
    display: none;
}

…I can finally say that it works, that all the hidden fields stay hidden and that the resulting markup will validate even against the draconian XHTML Strict specification of the W3C.

31 Jan

Zend Framework base url

Posted by Tudor. Tags: ,

I’m a new Zend Framework user, coming from CakePHP. I like ZF’s approach on things, it’s much more flexible and allows a greater degree of control on things. (this doesn’t mean I’ve given up on CakePHP, I have a post comparing these two on the way). But still, Zend Framework lacks some – quite basic – features, such as the ability to easily get the application’s root url.

I’ve googled around for a solution and found this post on Thijs Lensselink’s blog. His first implementation was:

class Zend_View_Helper_BaseUrl
{
    function baseUrl()
    {
        $base_url = substr($_SERVER['PHP_SELF'], 0, -9);
        return $base_url;
    }
}

Quite an interesting approach, that works most of the times, because no matter how complicated the url is (www.example.com/controller/action/first/value1/second/value2/and-so/on) it will still be rewritten to the index.php file by Apache’s mod_rewrite. Thus, the value stored in the $_SERVER['PHP_SELF'] superglobal will always be the site’s URL in the format www.example.com/index.php. The string “index.php” is 9 characters long, so the substr will return the correct base url.

But if the project was launched in a hurry and still has some bugs, it’s a good idea to call it a beta version. It’s quite a common practice nowadays, in our web2.0 world to hold a project in a perpetual beta. So, instead of www.example.com, one might want to rewrite all the urls to www.example.com/beta. This is quite simple to achieve with ZF, with some minor changes in the bootstrap.php file:

$front_controller = Zend_Controller_Front::getInstance();
// your bootstrap
$request = new Zend_Controller_Request_Http();
$request->setBaseUrl( $request->getBaseUrl() . '/beta' );
$front_controller->dispatch( $request );

Now, the first approach – with substr – won’t work as expected. That leaves us with one bullet proof option: to write a helper that will return the base url that is encapsulated in the front controller object.

/**
 * Helper that retuns the base url
 *
 * @package View Helpers
 */

class Generic_View_Helper_BaseUrl {
    /**
     * returns the base url
     *
     * @return string
     */
    public function baseUrl() {
        return Zend_Controller_Front::getInstance()->getBaseUrl();
    }
}
// EOF

And this won’t create overhead, because Zend_Controller_Front::getInstance() returns a static singleton object, just as the user called Gary said on the original post.

21 Jan

Survive the deep end

Posted by Tudor. Tags: , , ,

While browsing the web, I’ve found this book on PHP, called Survive the deep end. I’ve thoroughly read some chapters and I can honestly say that I’m impressed. This is a very good book, one of the best books I’ve read lately. It’s not just another “copy & paste this script and it should do the trick” book. It features advanced concepts that are actually useful, it promotes best practices and you can easily see that the author is a seasoned PHP developer, willing to share his knowledge with world. The book is authored by Pádraic Brady, a developer Ireland and active contributor to the Zend Framework code base. He also maintains a blog at blog.astrumfutura.com – another good read, as he has some interesting stuff posted there.

Final recommendation: even if you’re not a Zend Framework user, you can still read it and learn more about things how to design a MVC application and apply that knowledge to your framework of choice. Survive the deep end.