views:

126

answers:

6

In my View (using Zend_View so the the view is an object), I make calls to object properties and methods to populate the template like so:

<?= $this->user->name ?> // Outputs John Doe
<br/>
<?= $this->user->getCompany()->name ?> // Outputs Acme
<br/>
<?= $this->method() ?> // Outputs foobar

If I make it so that all property requests (like for 'user') go through __get() is there any way that I can catch the subsequent calls so that I can force a method call on the final outputted value? For example so that I could do automatic escaping of output.

As I see it right now, I either have to escape the input as it goes into the database or use compiled templates like Smarty does, or switch to assigning every variable to the View object so that it has direct control to force escaping before outputting the data.

A: 

I started out by saying No. Thats because you cant make the returned value go 'through' __get(), but after thinking realised you could get around it. Personally I think it would be inefficient and would also cause other problems such as getting to $name without the escaping.

To do it you need to use __get($var) in each class and then return the variable after escaping it.

probably very wasteful though I personally would use

<?= $this->user->escaped_name() ?>

in the class for the user and the class representing the company

__get($var){ 
  if ($var == 'name')
     return escape($this->name);
}

This is only one example and assumes $name is a non public member. if its an element in an array suitable indexing into the array will be required eg:

return escape($this->data['name']);

NB: I have no knowledge of zend_views but I assume it won't make any difference

DC

DeveloperChris
I disagree, the method `User::escaped_name()` does not belong with `User` but with something handling strings.
chelmertz
I agree, I was only trying to stick to the users example. personally if I had to I'd use <?php echo escape($this->user->name); ?>
DeveloperChris
A: 

Did you try using Zend_View_Abstract::addFilterPath() and Zend_View_Abstract::addFilter()?

See Output Filters in Zend_View

chelmertz
A: 

The manual way is to use the escape() view helper in the template directly like this:

<?= $this->escape($this->user->name) ?> // Outputs John Doe
<br/>
<?= $this->escape($this->user->getCompany()->name) ?> // Outputs Acme
<br/>
<?= $this->escape($this->method()) ?> // Outputs foobar

The easiest way to automate it is to use a stream wrapper. Zend Framework provides Zend_View_Stream which is used to automate conversion of short tags to long tags. This is enabled using:

$view->setUseStreamWrapper(true);

You would need to extend it to automatically escape any use of <?=. The code to use within the wrapper is something like this:

$find = '/<?=[ ]*([^;>]*?|[^;?]*?)[; ]*?>/';
$replace = "<?php echo $this->escape($1); ?>";
$this->data = preg_replace($find, $replace, $this->data);

Obviously, using a stream wrapper has some performance implications too.

Further details here:

Rob Allen
I think you missed the part about automatically escaping output. :P
Noah Goodrich
You're right :)
Rob Allen
A: 

You could extend the view object and then override the __get() method to escape all output as needed.

You can use:

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

to change the view object to the one that you create.

smack0007
That's what I want to do. What I am asking is if there is a way to force the call to escape() on name which is a property call on the object that Zend_View contains.
Noah Goodrich
A: 

Use PHPTAL as your view.

It escapes everything by default (compiles templates to PHP which has all neccessary htmlspecialchars() calls added automatically).

${this/user/getCompany/name} <!-- it's safe! -->
porneL
+1  A: 

You could use a decorator. See this simplified example:

class ViewObject_Decorator
{
    protected $_decoratedObject;

    protected $_view;

    public function __construct( $object, Zend_View view )
    {
        $this->_decoratedObject = $object;
        $this->_view = $view;
    }

    public function __get( $property )
    {
        return $this->_view->escape( $this->_decoratedObject->$property );
    }

    /*
        maybe implement __call to proxy to decoratedObject methods, etc...
    */
}

Then your proposed __get method (presuming this is a Zend_View method?) would be something like:

public function __get( $property )
{
    // decorate the requested object, and send the view along with it.
    return new ViewObject_Decorator( $this->$property, $this );
}
fireeyedboy