开发者

super-light templating system in PHP that doesn't allow php code inside templates or use eval

I'm searching for a very basic PHP templating system. Right now I'm using:

/**
 * Renders a single line. Looks for {{ var }}
 *
 * @param string $string
 * @param array $parameters
 *
 * @return string
 */
function renderString($string, array $parameters)
{
    $replacer = function ($match) use ($parameters)
    {
        return isset($parameters[$match[1]]) ? $parameters[$match[1]] : $match[0];
    };

    return preg_replace_callback('/{{\s*(.+?)\s*}}/', $replacer, $string);
}

(from here: PHP - Extremely light templating开发者_如何学编程 system)

but I can only assign and display variables. I also need a way to use conditions like IF and loop arrays.

I found Rain TPL - http://www.raintpl.com/Quick-Start/#if - which is very close to what I'm looking for, but there are a few things that I don't like it it:

  • it allows the dude who is writing the template to run PHP functions (inside the IF condition).
  • it writes cache and php files, which I don't want

So, is there anything out there similar to this, but even more "basic", strict, and more secure?


Twig might be for you.

It can do conditions, and has a sandbox mode for untrusted code.

It does compilation and caching, but that seems to be possible to turn off.


There's also a Mustache port for PHP. The PHP port is here. The syntax is similar to what you're already doing, and supports simple IF and FOREACH-type loops.

And, does it without eval.


Have a look at Twig or H2O.

  • http://www.twig-project.org/
  • http://www.h2o-template.org/


From your requirements I am guessing you are wanting your website users to write some basic php scripts. You might not find a free template engine that does that.

I think it's better for you if you change an existing template engine to your needs.

You can change Rain TPL to disable some of its features that you don't want. For example you can do...

  1. Disable function use in IF statements:
    a. Locate elseif( preg_match( '/\{if(?: condition){0,1}="([^"]*)"\}/', $html, $code ) ){

    b. Replace $this->function_check( $tag ); with a new method something like $this->ifcondition_function_check( $tag );

    c. Create the new method that will disable all functions in IF statements.

    private function ifcondition_function_check($code)
    {
        $preg = '/[a-zA-z0-9]+\((.*?)\)/';
        if (preg_match( $preg, $code, $match ) ){
            // find the line of the error
            $line = 0;
            $rows=explode("\n",$this->tpl['source']);
            while( !strpos($rows[$line],$code) )
                    $line++;
    
            // draw the error line
            $error = str_replace( array('<','>'), array( '&lt;','&gt;' ), array($code,$rows[$line]) );
            $error = str_replace( $code, "<font color=red>$code</font>", $rows[$line] );
    
            // debug the error and stop the execution of the script
            die( "<div>RainTPL Sandbox Error in template <b>{$this->tpl['tpl_filename']}</b> at line $line : <i>$error</i></b>" );
        }
    }
    

    d. Now functions are disabled.

    1. Remove the cache file. (The cache file in Rain TPL is a PHP file with the template tags replaced by PHP code)
      a. Go to method draw()
      b. Locate unset( $this->tpl );
      c. Just before this line remove the complied (cache) file @unlink($this->tpl['compiled_filename']);.
      d. Now the cache file is just a temporary file to execute the PHP code.

Hope this helps


very easy to use

http://www.smarty.net/


When you want it really small and flexible maybe the best is to stay with your own stuff? I like handcrafting ;-) You can extend your existing function. Following, your function plus if and loop statement and escaping of variables for security:

<?php
function renderString($str, $parms)
{
    // if
    $str = preg_replace_callback('/{{if (?P<name>\w+)}}(?P<inner>.*?){{endif}}/is', function($match) use ($parms) {
        if( isset($parms[$match['name']])) {
            // recursive
            return renderString($match['inner'], $parms);
        }
    }, $str);
    // loop
    $str = preg_replace_callback('/{{loop (?P<name>\w+)}}(?P<inner>.*?){{endloop}}/is', function($match) use ($parms) {
        if( isset($parms[$match['name']]) && is_array($parms[$match['name']])) {
            $str = '';
            foreach ($parms[$match['name']] as $value) {
                $parms['loop'] = $value;
                // recursive
                $str .= renderString($match['inner'], $parms);
            }
            return $str;
        }
    }, $str);
    // var
    $str = preg_replace_callback('/{{(?P<name>\w+)}}/is', function($match) use ($parms) {
        if( isset($parms[$match['name']])) {
            return htmlspecialchars($parms[$match['name']]);
        }
    }, $str);
    return $str;
}

$template = "<h1>{{title}}</h1>

{{if optional}}
<p>Optional: {{optional}}</p>
{{endif}}

{{if noop}}I'm not there{{endif}}

<ul>
{{loop numbers}}
<li>{{symbol}} {{loop}}</li>
{{endloop}}
</ul>";

echo renderString($template, array(
    'title'    => 'The Title',
    'optional' => 'I am optional',
    'numbers'  => array( 'one', 'two', 'three'),
    'symbol'   => '>',
));

This script is tested in PHP 5.3 and you can copy it 1:1 to a file to play with it.


try PHPTAL: http://phptal.org/

the syntax for TAL templates does not break html, so you - and the designers can check if they going to look good.

see also:
http://wiki.zope.org/ZPT/TALSpecification14
http://wiki.zope.org/ZPT/TAL

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜