PHP Looping Template Engine - From Scratch

后端 未结 6 1374
鱼传尺愫
鱼传尺愫 2020-12-24 04:46

For a group project I am trying to create a template engine for PHP for the people less experienced with the language can use tags like {name} in their HTML and the PHP will

6条回答
  •  温柔的废话
    2020-12-24 05:02

    A simple approach is to convert the template into PHP and run it.

    $template = preg_replace('~\{(\w+)\}~', 'showVariable(\'$1\'); ?>', $template);
    $template = preg_replace('~\{LOOP:(\w+)\}~', 'data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
    $template = preg_replace('~\{ENDLOOP:(\w+)\}~', 'unwrap(); endforeach; ?>', $template);
    

    For example, this converts the template tags to embedded PHP tags.

    You'll see that I made references to $this->showVariable(), $this->data, $this->wrap() and $this->unwrap(). That's what I'm going to implement.

    The showVariable function shows the variable's content. wrap and unwrap is called on each iteration to provide closure.

    Here is my implementation:

    class TemplateEngine {
        function showVariable($name) {
            if (isset($this->data[$name])) {
                echo $this->data[$name];
            } else {
                echo '{' . $name . '}';
            }
        }
        function wrap($element) {
            $this->stack[] = $this->data;
            foreach ($element as $k => $v) {
                $this->data[$k] = $v;
            }
        }
        function unwrap() {
            $this->data = array_pop($this->stack);
        }
        function run() {
            ob_start ();
            eval (func_get_arg(0));
            return ob_get_clean();
        }
        function process($template, $data) {
            $this->data = $data;
            $this->stack = array();
            $template = str_replace('<', '', $template);
            $template = preg_replace('~\{(\w+)\}~', 'showVariable(\'$1\'); ?>', $template);
            $template = preg_replace('~\{LOOP:(\w+)\}~', 'data[\'$1\'] as $ELEMENT): $this->wrap($ELEMENT); ?>', $template);
            $template = preg_replace('~\{ENDLOOP:(\w+)\}~', 'unwrap(); endforeach; ?>', $template);
            $template = '?>' . $template;
            return $this->run($template);
        }
    }
    

    In wrap() and unwrap() function, I use a stack to keep track of current state of variables. Precisely, wrap($ELEMENT) saves the current data to the stack, and then add the variables inside $ELEMENT into current data, and unwrap() restores the data from the stack back.

    For extra security, I added this extra bit to replace < with PHP echos:

    $template = str_replace('<', '', $template);
    

    Basically to prevent any kind of injecting PHP codes directly, either , <%, or

提交回复
热议问题