Extend Zend_View_Stream to easily escape view variables

Zend_View_Stream is used pretty much when ever you use Zend_View, and I’ve blogged about how handy it is before.  But as it’s a class like any other, you can extend it to give added functionality.  One such use is to add automatic escaping to your view variables when you want.  So instead of doing:

<?php echo $this->escape($this->var); ?>
<?= $this->escape($this->var); ?>

You could simply do:

<?=~ $this->var; ?>

That’s a lot simpler, isn’t it?

To do this, we need to do two things; 1) extend the stream class and 2) make sure we register it before Zend Framework registers Zend_View_Stream.

<?php

class My_Stream extends Zend_View_Stream
{
    /**
     * Opens the script file and converts markup.
     */
    public function stream_open($path, $mode, $options, &$opened_path)
    {
        // get the view script source
        $path        = str_replace('zend.view://', '', $path);
        $this->_data = file_get_contents($path);

        /**
         * If reading the file failed, update our local stat store
         * to reflect the real stat of the file, then return on failure
         */
        if ($this->_data === false) {
            $this->_stat = stat($path);
            return false;
        }

        /**
         * Convert <?= ?> to long-form <?php echo ?> and <? ?> to <?php ?>
         *
         */
        $this->_data = preg_replace(
            '/\<\?\=~ (.*?);? \?>/', 
            '<?php echo $this->escape($1); ?>', 
            $this->_data
        );
        $this->_data = preg_replace(
            '/\<\?\=/',
            '<?php echo ',
            $this->_data
        );
        $this->_data = preg_replace(
            '/<\?(?!xml|php)/s',
            '<?php ',
            $this->_data
        );
        
        /**
         * file_get_contents() won't update PHP's stat cache, so we grab a stat
         * of the file to prevent additional reads should the script be
         * requested again, which will make include() happy.
         */
        $this->_stat = stat($path);

        return true;
    }
}

The observant of you will notice that the above is almost exactly the same as Zend_View_Stream::stream_open(), but with this bit of code added to it:

        $this->_data = preg_replace(
            '/\<\?\=~ (.*?);? \?>/', 
            '<?php echo $this->escape($1); ?>', 
            $this->_data
        );

So if you don’t like using ~ to do the escape this is where you’d change it.

Now all you have to do is register your stream before Zend Framework does. You could do this in your bootstrap file, your application class, or whereever it makes sense for your applicaiton. Basically, you’d just be dropping in a line like this:

stream_register_wrapper('zend.view', 'My_Stream');

Hopefully that gives you some ideas on extending the stream wrapper even more!

Did you like this? Share it:

Leave a Reply