FoilPHP / Foil

PHP template engine for native PHP templates
https://foilphp.github.io/Foil/
MIT License
170 stars 24 forks source link

Question about template variables #4

Closed skyosev closed 9 years ago

skyosev commented 9 years ago

Hi again!

I'm curious about the need to use _$this->myvar while accessing data in templates. Is there any particular reason (performance, escaping etc.) for not using locally scoped variables like _$myvar ? Plates does allow it AFAIK.

I didn't dig into Foil that much, so my question could sound improper.

Regards, Stoyan

gmazzap commented 9 years ago

Your question is not improper, and thanks again for your interest.

Reason are:

If you are intersted in a more in depth comparison with Plates features I did a blog post.

skyosev commented 9 years ago

I see. Thanks for your explanation.

While I agree with your points, I have some concerns about the 'On Plates data access' section.

Having worked countless hours with native php templates, I made the following observations:

About autoescaping - I guess there is no difference for the end user between escaping a variable on demand, when accessing it from the template with $this->my_var, and escaping all of the variables that are passed to the render function, thus providing escaped scoped vars to the template. Do you see room for an option like 'use_scoped_variables', that would allow to access the template variables without 'this' ?

gmazzap commented 9 years ago

variables in the templates are meant to be there

I've worked countless hours with templates engines, not native php one, but in this regard there is no difference between native or compiled, and I encountered a lot of occasions where variables may be there or not, expecially when data comes from external sources, like API, file parsing...

Moreover, if you know that undefined variables equals empty string, you can use undefined variable by design.

Let's assume that by design a page may has or may not a subtitle. Page template in Plates will probably contain:

<?= isset($subtile) ? $subtile : '' ?>

or a more verbose if statement. In Foil you just <?= $this->subtile ?> obtaining same effect.

An alternative would be always assign subtile var to page, but in that case the control structure is moved from template code to data generation code (controller or whatever), that is sure better, but no need for it is even better IMO.

actually getting notices for undefined variables is sometimes useful

I'm pretty agree that 'strict_variables' would by a nice addition I'm going to add myself an issue for it.

I guess there is no difference for the end user between escaping a variable on demand...

Plates documentation itself encourages people to do $this->e('var_name'). You can do exactly the same in Foil, but you can also to $this->var_name (so less typing) when autoescape is turned on.

If we agree that unescaped output is bad (and I agree) Foil gives an easier access to escaped data while unescaped data must be accessed intentionally with $this->raw(). Plates does the countrary: unescaped data is easily accessed, while escaped data must be required. I feel this is bad and I can say recently even the Laravel Blade do the same thing moving from unescaped-by-default to escaped-by-default.

Do you see room for an option like 'use_scoped_variables'

Yes, I do. Generally speacking I write open source code for things that I like to have but I can't find. This means the default behavior of my code reflects my preferences. But I don't see reason why I have to force people on my preferences. I have to say that option will destroy the autoescape thing. So I can allow it only if autoescape is off (there is already an option for that). If you want to add this feature and do a PR it will be merged. If you have no time for that, add a specific issue, I'll handle it when I can. (for the name I feel we can make it less verbose as 'scoped_variables').

skyosev commented 9 years ago

Thanks for the detailed explanation.

My experience with templates is 95% connected with html markup, that's why in my cases $subtitle would be wrapped by span (div, paragraph etc.) and I would use conditional structure to omit this markup regardless of how I access $subtitle. I see this is not valid for the cases you mention (API, file parsing), so I am biased here.

Another attempt to explain my view about autoescaping.

$engine->render('a-template', $template_variables_array);

Why can't the template engine just escape and then extract $template_variables_array before including the template file, so all of the $template_variables_array items would appear as escaped scoped variables in the template ? The original unescaped $template_variables_array can be kept in order to be able to access the raw variables with $this->raw('my_var');. Does this makes sense or I miss the point ?

I didn't mean to question your purposes for creating Foil, just discussing my views on the topic. I will think about a PR.

gmazzap commented 9 years ago

Why can't the template engine just escape and then extract $template_variables_array before including the template file, so all of the $template_variables_array items would appear as escaped scoped variables in the template

This is the biggest issue with autoescape in native PHP templates.

Variables passed to templates are not always strings. Strings can be easily escaped. Array can as well, by recursively parse them.

Problem are objects. While you can get object properties using get_object_vars() this fails if object implement __get() overloading method.

Example:

class Post {
    private $data;

    public function __construct(array $data) {
        $this->data = $data;
    }

    public function __get($var) {
        return isset($this->data[$var]) ? $this->data[$var] : '';
    }
}

$post = new Post(array('foo' => 'Bar'));
echo $post['foo'];   // echo 'Bar'

If the $post object is passed to templates, there is no way to autoescape post properties.

This problem does not exists for compiled template engines, because they first echo the value (in a sandboxed environment), then escape the echoed value.

The point of arraize Foil method is to convert all objects to array, so they don't suffer of this issue and can be easily autoescaped.

If the scoped_variable option is added to Foil, maybe, besides true / false values for it is possible to add support for "arraize" value: when used, if autoescape is true Foil passes template variable to arraize() before extract them...