在Yii Framework中集成Twig

Twig是一款快速、安全、灵活的PHP模板引擎,它内置了许多filter和tags,并且支持模板继承,能让你用最简洁的代码来描述你的模板。他的语法和Python下的模板引擎Jinjia以及Django的模板语法都非常像。
比如我们在PHP中需要输出变量并且将其进行转义时,语法比较累赘:

1
2
< ?php echo $var ?>
< ?php echo htmlspecialchars(\$var, ENT_QUOTES, 'UTF-8') ?>

但是在Twig中可以这样写:

1
2
3
{{ var }}
{{ var|escape }}
{{ var|e }}         {# shortcut to escape a variable #}

遍历数组:

1
2
3
4
5
{% for user in users %}
  * {{ user.name }}
{% else %}
  No user has been found.
{% endfor %}


但是要在Yii Framework集成Twig就会遇到点麻烦了,官方网站中已经有能够集成Twig的方案,所以这里我也不再赘述。但是由于Twig中是不支持PHP语法的,所以在有些表达上会遇到困难,比如我们在写Form的视图时,经常会这么写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
< ?php $form=$this->beginWidget('CActiveForm'); ?>
	<span>Login</span>
	<ul>
		<li>
			< ?php echo $form->label($model,'username'); ?>
			< ?php echo $form->textField($model,'username'); ?>
		</li>
		<li>
			< ?php echo $form->label($model,'password'); ?>
			< ?php echo $form->passwordField($model,'password'); ?>
		</li>
		<li class="last">
			<button type="submit">Login</button>
		</li>
	</ul>
	< ?php echo $form->error($model,'password'); ?>
< ?php $this->endWidget(); ?>

但是这样的语法是没法在twig中表达的,所以想去扩展下Twig的功能,让他能够支持我们自定义的widget标签,然后自动解析成我们需要的代码。
总共需要两个类:TokenParser和Node,下面直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
< ?php
/*
 * This file is an extension of Twig.
 *
 * (c) 2010 lfyzjck
 */
 
/**
 * parser widget tag in Yii framework
 *
 * {% beginwidget 'CActiveForm' as form %}
 *    content of form
 * {% endwidget %}
 *
 */
class Yii_WidgetBlock_TokenParser extends Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param Twig_Token $token A Twig_Token instance
     *
     * @return Twig_NodeInterface A Twig_NodeInterface instance
     */
    public function parse(Twig_Token $token)
    {
		$lineno = $token->getLine();
        $stream = $this->parser->getStream();
 
        $name = $stream->expect(Twig_Token::STRING_TYPE);
		if($stream->test(Twig_Token::PUNCTUATION_TYPE)){
			$args = $this->parser->getExpressionParser()->parseHashExpression();
		}
		else{
			$args = new Twig_Node_Expression_Array(array(), $lineno);
		}
 
		$stream->expect(Twig_Token::NAME_TYPE);
		$assign = $stream->expect(Twig_Token::NAME_TYPE);
		$stream->expect(Twig_Token::BLOCK_END_TYPE);
 
		$body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
        $stream->expect(Twig_Token::BLOCK_END_TYPE);
 
		return new Yii_Node_WidgetBlock(array(
			'alias' => $name->getValue(),
			'assign' => $assign,
		), $body, $args, $lineno, $this->getTag());
    }
 
    /**
     * Gets the tag name associated with this token parser.
     *
     * @param string The tag name
     */
    public function getTag()
    {
        return 'beginwidget';
    }
 
    public function decideBlockEnd(Twig_Token $token)
    {
        return $token->test('endwidget');
    }
}
 
class Yii_Node_WidgetBlock extends Twig_Node
{
	public function __construct($attrs, Twig_NodeInterface $body, Twig_Node_Expression_Array $args = NULL, $lineno, $tag)
	{
		$attrs = array_merge(array('value' => false),$attrs);
		$nodes = array('args' => $args, 'body' => $body); 
		parent::__construct($nodes, $attrs, $lineno,$tag);
	}
 
    public function compile(Twig_Compiler $compiler)
	{
		$compiler->addDebugInfo($this);
		$compiler->write('$context["'.$this->getAttribute('assign')->getValue().'"] = $context["this"]->beginWidget("'.$this->getAttribute('alias').'",');
		$argNode = $this->getNode('args');
		$compiler->subcompile($argNode)
				 ->raw(');')
				 ->raw("\n");
 
		$compiler->indent()->subcompile($this->getNode('body'));
 
		$compiler->raw('$context["this"]->endWidget();');
	}
}
?>

然后在Twig初始化的地方增加我们的语法解析类:

1
$twig->addTokenParser(new Yii_WidgetBlock_TokenParser);

然后我们就可以在twig的模板里这么写了:

1
2
3
4
5
6
7
8
9
10
11
12
{% beginwidget 'CActiveForm' as form %}
	<ul>
		<li>
			{{ form.label(model, 'username') }}
			{{ form.textField(model, 'username') }}
		</li>
		<li>
			{{ form.label(model, 'password') }}
			{{ form.passwordField(model, 'password') }}
		</li>
	</ul>
{% endwidget %}

你可能还对以下日志感兴趣

分类:PHPer  |  阅读(121次)

发表评论

*

评论仅支持“a、abbr、strong、em、blockquote、code”几个简单的标签