Volt: Template Engine

Volt 是一个用C为PHP编写的超快的并且对设计师友好的模板语言。Volt 提供一组辅助工具有助于你以一种更简单的的方式编写视图(Views)。 同时,Volt与Phalcon的其他组件高度集成在一起,就像你在应用中单独使用Volt一样。

Volt is an ultra-fast and designer friendly templating language written in C for PHP. It provides you a set of helpers to write views in an easy way. Volt is highly integrated with other components of Phalcon, just as you can use it as a stand-alone component in your applications.

../_images/volt.jpg

Volt受 `Armin Ronacher`_开发的Jinja_启发而来开发的。使用过Jinja_会比较熟悉类似的语法。为了方便Phalcon开发者Volt的增加很多的语法特性。

Volt is inspired by Jinja, originally created by Armin Ronacher. Therefore many developers will be in familiar territory using the same syntax they have been using with similar template engines. Volt’s syntax and features have been enhanced with more elements and of course with the performance that developers have been accustomed to while working with Phalcon.

简介Introduction

Volt视图会编译为纯php代码。所以节省编写php代码的时间。

Volt views are compiled to pure PHP code, so basically they save the effort of writing PHP code manually:

  1. {# app/views/products/show.volt #}
  2. {% block last_products %}
  3. {% for product in products %}
  4. * Name: {{ product.name|e }}
  5. {% if product.status == "Active" %}
  6. Price: {{ product.price + product.taxes/100 }}
  7. {% endif %}
  8. {% endfor %}
  9. {% endblock %}

启用 Vlot Activating Volt

像是其他,模板引擎一样。可以在视图组件注册模板引擎,使用新的扩展名或者是默认的.phtml:

As other template engines, you may register Volt in the view component, using a new extension or reusing the standard .phtml:

  1. <?php
  2. use Phalcon\Mvc\View;
  3. //Registering Volt as template engine
  4. $di->set('view', function() {
  5. $view = new View();
  6. $view->setViewsDir('../app/views/');
  7. $view->registerEngines(array(
  8. ".volt" => 'Phalcon\Mvc\View\Engine\Volt'
  9. ));
  10. return $view;
  11. });

使用 ”.phtml” 扩展名:

Use the standard ”.phtml” extension:

  1. <?php
  2. $view->registerEngines(array(
  3. ".phtml" => 'Phalcon\Mvc\View\Engine\Volt'
  4. ));

基本用法Basic Usage

视图文件由volt代码,php和html组成。一些特殊分隔符被用于volt代码。{% … %} 被用于循环或者复制语句。{{ … }}被用于输出计算结果。

A view consists of Volt code, PHP and HTML. A set of special delimiters is available to enter into Volt mode. {% … %} is used to execute statements such as for-loops or assign values and {{ … }}, prints the result of an expression to the template.

下面是一个简单的例子

Below is a minimal template that illustrates a few basics:

  1. {# app/views/posts/show.phtml #}
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <title>{{ title }} - An example blog</title>
  6. </head>
  7. <body>
  8. {% if show_navigation %}
  9. <ul id="navigation">
  10. {% for item in menu %}
  11. <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
  12. {% endfor %}
  13. </ul>
  14. {% endif %}
  15. <h1>{{ post.title }}</h1>
  16. <div class="content">
  17. {{ post.content }}
  18. </div>
  19. </body>
  20. </html>

使用Phalcon\Mvc\View 可以由控制器向视图传递值。在上面的例子中,三个变量 title, menu and post被传递到视图。

Using Phalcon\Mvc\View you can pass variables from the controller to the views. In the above example, three variables were passed to the view: title, menu and post:

  1. <?php
  2. use Phalcon\Mvc\Controller;
  3. class PostsController extends Controller
  4. {
  5. public function showAction()
  6. {
  7. $post = Post::findFirst();
  8. $this->view->title = $post->title;
  9. $this->view->post = $post;
  10. $this->view->menu = Menu::find();
  11. $this->view->show_navigation = true;
  12. }
  13. }

变量 Variables

对象的属性可以用foo.bar来访问。数据可以用 foo[‘bar’]来访问。

Object variables may have attributes which can be accessed using the syntax: foo.bar. If you are passing arrays, you have to use the square bracket syntax: foo[‘bar’]

  1. {{ post.title }} {# for $post->title #}
  2. {{ post['title'] }} {# for $post['title'] #}

过滤器Filters

变量可以用过滤器进行格式化或者是修改。管道操作符 | 被用来调用过滤器:

Variables can be formatted or modified using filters. The pipe operator | is used to apply filters to variables:

  1. {{ post.title|e }}
  2. {{ post.content|striptags }}
  3. {{ name|capitalize|trim }}

下面是Volt内置的过滤类型:

The following is the list of available built-in filters in Volt:

FilterDescription
eApplies Phalcon\Escaper->escapeHtml to the value
escapeApplies Phalcon\Escaper->escapeHtml to the value
escape_cssApplies Phalcon\Escaper->escapeCss to the value
escape_jsApplies Phalcon\Escaper->escapeJs to the value
escape_attrApplies Phalcon\Escaper->escapeHtmlAttr to the value
trimApplies the trim PHP function to the value. Removing extra spaces
left_trimApplies the ltrim PHP function to the value. Removing extra spaces
right_trimApplies the rtrim PHP function to the value. Removing extra spaces
striptagsApplies the striptags PHP function to the value. Removing HTML tags
slashesApplies the slashes PHP function to the value. Escaping values
stripslashesApplies the stripslashes PHP function to the value. Removing escaped quotes
capitalizeCapitalizes a string by applying the ucwords PHP function to the value
lowerChange the case of a string to lowercase
upperChange the case of a string to uppercase
lengthCounts the string length or how many items are in an array or object
nl2brChanges newlines \n by line breaks (<br />). Uses the PHP function nl2br
sortSorts an array using the PHP function asort
keysReturns the array keys using array_keys
joinJoins the array parts using a separator join
formatFormats a string using sprintf.
json_encodeConverts a value into its JSON representation
json_decodeConverts a value from its JSON representation to a PHP representation
absApplies the abs PHP function to a value.
url_encodeApplies the urlencode PHP function to the value
defaultSets a default value in case that the evaluated expression is empty (is not set or evaluates to a falsy value)
convert_encodingConverts a string from one charset to another

例子 Examples:

  1. {# e or escape filter #}
  2. {{ "<h1>Hello<h1>"|e }}
  3. {{ "<h1>Hello<h1>"|escape }}
  4. {# trim filter #}
  5. {{ " hello "|trim }}
  6. {# striptags filter #}
  7. {{ "<h1>Hello<h1>"|striptags }}
  8. {# slashes filter #}
  9. {{ "'this is a string'"|slashes }}
  10. {# stripslashes filter #}
  11. {{ "\'this is a string\'"|stripslashes }}
  12. {# capitalize filter #}
  13. {{ "hello"|capitalize }}
  14. {# lower filter #}
  15. {{ "HELLO"|lower }}
  16. {# upper filter #}
  17. {{ "hello"|upper }}
  18. {# length filter #}
  19. {{ "robots"|length }}
  20. {{ [1, 2, 3]|length }}
  21. {# nl2br filter #}
  22. {{ "some\ntext"|nl2br }}
  23. {# sort filter #}
  24. {% set sorted=[3, 1, 2]|sort %}
  25. {# keys filter #}
  26. {% set keys=['first': 1, 'second': 2, 'third': 3]|keys %}
  27. {# join filter #}
  28. {% "a".."z"|join(",") %}
  29. {# format filter #}
  30. {% "My real name is %s"|format(name) %}
  31. {# json_encode filter #}
  32. {% robots|json_encode %}
  33. {# json_decode filter #}
  34. {% set decoded='{"one":1,"two":2,"three":3}'|json_decode %}
  35. {# url_encode filter #}
  36. {{ post.permanent_link|url_encode }}
  37. {# convert_encoding filter #}
  38. {{ "désolé"|convert_encoding('utf8', 'latin1') }}

注释Comments

模板中可以使用{# … #}注释代码。所有在其中的代码将被忽略:

Comments may also be added to a template using the {# … #} delimiters. All text inside them is just ignored in the final output:

  1. {# note: this is a comment
  2. {% set price = 100; %}
  3. #}

流程控制列表List of Control Structures

Volt在模板中提供了基础但是强大的流程控制结构。

Volt provides a set of basic but powerful control structures for use in templates:

For

按序循环输出序列。下面的例子演示了如何循环输出”robots”:

Loop over each item in a sequence. The following example shows how to traverse a set of “robots” and print his/her name:

  1. <h1>Robots</h1>
  2. <ul>
  3. {% for robot in robots %}
  4. <li>{{ robot.name|e }}</li>
  5. {% endfor %}
  6. </ul>

循环可以嵌套:

for-loops can also be nested:

  1. <h1>Robots</h1>
  2. {% for robot in robots %}
  3. {% for part in robot.parts %}
  4. Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br/>
  5. {% endfor %}
  6. {% endfor %}

可以像是php那样获取循环的key值

You can get the element “keys” as in the PHP counterpart using the following syntax:

  1. {% set numbers = ['one': 1, 'two': 2, 'three': 3] %}
  2. {% for name, value in numbers %}
  3. Name: {{ name }} Value: {{ value }}
  4. {% endfor %}

可以使用if语句:

An “if” evaluation can be optionally set:

  1. {% set numbers = ['one': 1, 'two': 2, 'three': 3] %}
  2. {% for value in numbers if value < 2 %}
  3. Value: {{ value }}
  4. {% endfor %}
  5. {% for name, value in numbers if name != 'two' %}
  6. Name: {{ name }} Value: {{ value }}
  7. {% endfor %}

如果’else’定义在for内部,当条件循环为零的时候就会执行else条件:

If an ‘else’ is defined inside the ‘for’, it will be executed if the expression in the iterator result in zero iterations:

  1. <h1>Robots</h1>
  2. {% for robot in robots %}
  3. Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br/>
  4. {% else %}
  5. There are no robots to show
  6. {% endfor %}

另一种写法:

Alternative syntax:

  1. <h1>Robots</h1>
  2. {% for robot in robots %}
  3. Robot: {{ robot.name|e }} Part: {{ part.name|e }} <br/>
  4. {% elsefor %}
  5. There are no robots to show
  6. {% endfor %}

循环控制Loop Controls

break和continue语句用户退出或者强制进入下一次循环:

The ‘break’ and ‘continue’ statements can be used to exit from a loop or force an iteration in the current block:

  1. {# skip the even robots #}
  2. {% for index, robot in robots %}
  3. {% if index is even %}
  4. {% continue %}
  5. {% endif %}
  6. ...
  7. {% endfor %}
  1. {# exit the foreach on the first even robot #}
  2. {% for index, robot in robots %}
  3. {% if index is even %}
  4. {% break %}
  5. {% endif %}
  6. ...
  7. {% endfor %}

If

就像在PHP中一样,if语句检查表达式的值为true还是false:

As PHP, an “if” statement checks if an expression is evaluated as true or false:

  1. <h1>Cyborg Robots</h1>
  2. <ul>
  3. {% for robot in robots %}
  4. {% if robot.type == "cyborg" %}
  5. <li>{{ robot.name|e }}</li>
  6. {% endif %}
  7. {% endfor %}
  8. </ul>

同样支持else:

The else clause is also supported:

  1. <h1>Robots</h1>
  2. <ul>
  3. {% for robot in robots %}
  4. {% if robot.type == "cyborg" %}
  5. <li>{{ robot.name|e }}</li>
  6. {% else %}
  7. <li>{{ robot.name|e }} (not a cyborg)</li>
  8. {% endif %}
  9. {% endfor %}
  10. </ul>

elseif控制同样可以和if一起使用来模拟switch操作:

The ‘elseif’ control flow structure can be used together with if to emulate a ‘switch’ block:

  1. {% if robot.type == "cyborg" %}
  2. Robot is a cyborg
  3. {% elseif robot.type == "virtual" %}
  4. Robot is virtual
  5. {% elseif robot.type == "mechanical" %}
  6. Robot is mechanical
  7. {% endif %}

循环上下文Loop Context

在for循环中提供了特殊的变量提示当前循环的信息

A special variable is available inside ‘for’ loops providing you information about

VariableDescription
loop.indexThe current iteration of the loop. (1 indexed)
loop.index0The current iteration of the loop. (0 indexed)
loop.revindexThe number of iterations from the end of the loop (1 indexed)
loop.revindex0The number of iterations from the end of the loop (0 indexed)
loop.firstTrue if in the first iteration.
loop.lastTrue if in the last iteration.
loop.lengthThe number of items to iterate
  1. {% for robot in robots %}
  2. {% if loop.first %}
  3. <table>
  4. <tr>
  5. <th>#</th>
  6. <th>Id</th>
  7. <th>Name</th>
  8. </tr>
  9. {% endif %}
  10. <tr>
  11. <td>{{ loop.index }}</td>
  12. <td>{{ robot.id }}</td>
  13. <td>{{ robot.name }}</td>
  14. </tr>
  15. {% if loop.last %}
  16. </table>
  17. {% endif %}
  18. {% endfor %}

赋值Assignments

在模板中变量可以用set重新赋值:

Variables may be changed in a template using the instruction “set”:

  1. {% set fruits = ['Apple', 'Banana', 'Orange'] %}
  2. {% set name = robot.name %}

同样可以使用连续赋值:

Multiple assignments are allowed in the same instruction:

  1. {% set fruits = ['Apple', 'Banana', 'Orange'], name = robot.name, active = true %}

同时也允许使用复合赋值:

Additionally, you can use compound assignment operators:

  1. {% set price += 100.00 %}
  2. {% set age *= 5 %}

下面是操作符列表:

The following operators are available:

OperatorDescription
=Standard Assignment
+=Addition assignment
-=Subtraction assignment
*=Multiplication assignment
/=Division assignment

表达式Expressions

Volt提供基础的表达式支持,包括字面值和常见的操作符。

Volt provides a basic set of expression support, including literals and common operators.

表达式可以被执行和使用 ‘{{‘ 和 ‘}}’输出:

A expression can be evaluated and printed using the ‘{{‘ and ‘}}’ delimiters:

  1. {{ (1 + 1) * 2 }}

如果表达式仅需要被执行而不需要输出可以使用do

If an expression needs to be evaluated without be printed the ‘do’ statement can be used:

  1. {% do (1 + 1) * 2 %}

字面值Literals

提供下面的字面值支持:

The following literals are supported:

FilterDescription
“this is a string”Text between double quotes or single quotes are handled as strings
100.25Numbers with a decimal part are handled as doubles/floats
100Numbers without a decimal part are handled as integers
falseConstant “false” is the boolean false value
trueConstant “true” is the boolean true value
nullConstant “null” is the Null value

数组Arrays

使用PHP 5.3 or >= 5.4可以使用方括号定义一个数组:

Whether you’re using PHP 5.3 or >= 5.4 you can create arrays by enclosing a list of values in square brackets:

  1. {# Simple array #}
  2. {{ ['Apple', 'Banana', 'Orange'] }}
  3. {# Other simple array #}
  4. {{ ['Apple', 1, 2.5, false, null] }}
  5. {# Multi-Dimensional array #}
  6. {{ [[1, 2], [3, 4], [5, 6]] }}
  7. {# Hash-style array #}
  8. {{ ['first': 1, 'second': 4/2, 'third': '3'] }}

大括号同样可以被用于定义数组或者哈希表:

Curly braces also can be used to define arrays or hashes:

  1. {% set myArray = {'Apple', 'Banana', 'Orange'} %}
  2. {% set myHash = {'first': 1, 'second': 4/2, 'third': '3'} %}

算术运算Math

可以在模板中使用下面的操作符:

You may make calculations in templates using the following operators:

OperatorDescription
+Perform an adding operation. {{ 2 + 3 }} returns 5
-Perform a substraction operation {{ 2 - 3 }} returns -1
Perform a multiplication operation {{ 2 3 }} returns 6
/Perform a division operation {{ 10 / 2 }} returns 5
%Calculate the remainder of an integer division {{ 10 % 3 }} returns 1

比较运算Comparisons

可以在模板中使用下面的操作符:

The following comparison operators are available:

OperatorDescription
==Check whether both operands are equal
!=Check whether both operands aren’t equal
<>Check whether both operands aren’t equal
>Check whether left operand is greater than right operand
<Check whether left operand is less than right operand
<=Check whether left operand is less or equal than right operand
>=Check whether left operand is greater or equal than right operand
===Check whether both operands are identical
!==Check whether both operands aren’t identical

逻辑运算Logic

在if中可以混合使用逻辑运算符去做表达式判断:

Logic operators are useful in the “if” expression evaluation to combine multiple tests:

OperatorDescription
orReturn true if the left or right operand is evaluated as true
andReturn true if both left and right operands are evaluated as true
notNegates an expression
( expr )Parenthesis groups expressions

其他操作Other Operators

其他一些操作符如下:

Additional operators seen the following operators are available:

OperatorDescription
~Concatenates both operands {{ “hello ” ~ “world” }}
|Applies a filter in the right operand to the left {{ “hello”|uppercase }}
..Creates a range {{ ‘a’..’z’ }} {{ 1..10 }}
isSame as == (equals), also performs tests
inTo check if an expression is contained into other expressions if “a” in “abc”
is notSame as != (not equals)
‘a’ ? ‘b’ : ‘c’Ternary operator. The same as the PHP ternary operator
++Increments a value
Decrements a value

下面是如何使用的一个例子:

The following example shows how to use operators:

  1. {% set robots = ['Voltron', 'Astro Boy', 'Terminator', 'C3PO'] %}
  2. {% for index in 0..robots|length %}
  3. {% if robots[index] is defined %}
  4. {{ "Name: " ~ robots[index] }}
  5. {% endif %}
  6. {% endfor %}

测试运算Tests

Tests被用于检查变量是否有期望的值。is操作符被用于tests:

Tests can be used to test if a variable has a valid expected value. The operator “is” is used to perform the tests:

  1. {% set robots = ['1': 'Voltron', '2': 'Astro Boy', '3': 'Terminator', '4': 'C3PO'] %}
  2. {% for position, name in robots %}
  3. {% if position is odd %}
  4. {{ name }}
  5. {% endif %}
  6. {% endfor %}

内置的tests检查如下:

The following built-in tests are available in Volt:

TestDescription
definedChecks if a variable is defined (isset)
emptyChecks if a variable is empty
evenChecks if a numeric value is even
oddChecks if a numeric value is odd
numericChecks if value is numeric
scalarChecks if value is scalar (not an array or object)
iterableChecks if a value is iterable. Can be traversed by a “for” statement
divisiblebyChecks if a value is divisible by other value
sameasChecks if a value is identical to other value
typeChecks if a value is of the specified type

更多的例子:

More examples:

  1. {% if robot is defined %}
  2. The robot variable is defined
  3. {% endif %}
  4. {% if robot is empty %}
  5. The robot is null or isn't defined
  6. {% endif %}
  7. {% for key, name in [1: 'Voltron', 2: 'Astroy Boy', 3: 'Bender'] %}
  8. {% if key is even %}
  9. {{ name }}
  10. {% endif %}
  11. {% endfor %}
  12. {% for key, name in [1: 'Voltron', 2: 'Astroy Boy', 3: 'Bender'] %}
  13. {% if key is odd %}
  14. {{ name }}
  15. {% endif %}
  16. {% endfor %}
  17. {% for key, name in [1: 'Voltron', 2: 'Astroy Boy', 'third': 'Bender'] %}
  18. {% if key is numeric %}
  19. {{ name }}
  20. {% endif %}
  21. {% endfor %}
  22. {% set robots = [1: 'Voltron', 2: 'Astroy Boy'] %}
  23. {% if robots is iterable %}
  24. {% for robot in robots %}
  25. ...
  26. {% endfor %}
  27. {% endif %}
  28. {% set world = "hello" %}
  29. {% if world is sameas("hello") %}
  30. {{ "it's hello" }}
  31. {% endif %}
  32. {% set external = false %}
  33. {% if external is type('boolean') %}
  34. {{ "external is false or true" }}
  35. {% endif %}

宏定义Macros

宏定义可以让我们在模板中重用逻辑。就像PHP函数一样,他们可以接受参数返回数值:

Macros can be used to reuse logic in a template, they act as PHP functions, can receive parameters and return values:

  1. {%- macro related_bar(related_links) %}
  2. <ul>
  3. {%- for rellink in related_links %}
  4. <li><a href="{{ url(link.url) }}" title="{{ link.title|striptags }}">{{ link.text }}</a></li>
  5. {%- endfor %}
  6. </ul>
  7. {%- endmacro %}
  8. {# Print related links #}
  9. {{ related_bar(links) }}
  10. <div>This is the content</div>
  11. {# Print related links again #}
  12. {{ related_bar(links) }}

调用宏定义时候可以通过名称传递参数:

When calling macros, parameters can be passed by name:

  1. {%- macro error_messages(message, field, type) %}
  2. <div>
  3. <span class="error-type">{{ type }}</span>
  4. <span class="error-field">{{ field }}</span>
  5. <span class="error-message">{{ message }}</span>
  6. </div>
  7. {%- endmacro %}
  8. {# Call the macro #}
  9. {{ error_messages('type': 'Invalid', 'message': 'The name is invalid', 'field': 'name') }}

宏定义可以返回数值:

Macros can return values:

  1. {%- macro my_input(name, class) %}
  2. {% return text_field(name, 'class': class) %}
  3. {%- endmacro %}
  4. {# Call the macro #}
  5. {{ '<p>' ~ my_input('name', 'input-text') ~ '</p>' }}

可以接受可选参数:

And receive optional parameters:

  1. {%- macro my_input(name, class="input-text") %}
  2. {% return text_field(name, 'class': class) %}
  3. {%- endmacro %}
  4. {# Call the macro #}
  5. {{ '<p>' ~ my_input('name') ~ '</p>' }}
  6. {{ '<p>' ~ my_input('name', 'input-text') ~ '</p>' }}

使用标签助手Using Tag Helpers

Volt和:doc:`Phalcon\Tag <tags>`整合度很高。所以在Volt模板中可以很方便的使用标签:

Volt is highly integrated with Phalcon\Tag, so it’s easy to use the helpers provided by that component in a Volt template:

  1. {{ javascript_include("js/jquery.js") }}
  2. {{ form('products/save', 'method': 'post') }}
  3. <label for="name">Name</label>
  4. {{ text_field("name", "size": 32) }}
  5. <label for="type">Type</label>
  6. {{ select("type", productTypes, 'using': ['id', 'name']) }}
  7. {{ submit_button('Send') }}
  8. {{ end_form() }}

如下PHP代码将会被生成:

The following PHP is generated:

  1. <?php echo Phalcon\Tag::javascriptInclude("js/jquery.js") ?>
  2. <?php echo Phalcon\Tag::form(array('products/save', 'method' => 'post')); ?>
  3. <label for="name">Name</label>
  4. <?php echo Phalcon\Tag::textField(array('name', 'size' => 32)); ?>
  5. <label for="type">Type</label>
  6. <?php echo Phalcon\Tag::select(array('type', $productTypes, 'using' => array('id', 'name'))); ?>
  7. <?php echo Phalcon\Tag::submitButton('Send'); ?>
  8. {{ end_form() }}

在Volt中调用Phalcon\Tag标签,需要调用函数的别名:

To call a Phalcon\Tag helper, you only need to call an uncamelized version of the method:

MethodVolt function
Phalcon\Tag::linkTolink_to
Phalcon\Tag::textFieldtext_field
Phalcon\Tag::passwordFieldpassword_field
Phalcon\Tag::hiddenFieldhidden_field
Phalcon\Tag::fileFieldfile_field
Phalcon\Tag::checkFieldcheck_field
Phalcon\Tag::radioFieldradio_field
Phalcon\Tag::dateFielddate_field
Phalcon\Tag::emailFieldemail_field
Phalcon\Tag::numberFieldnumber_field
Phalcon\Tag::submitButtonsubmit_button
Phalcon\Tag::selectStaticselect_static
Phalcon\Tag::selectselect
Phalcon\Tag::textAreatext_area
Phalcon\Tag::formform
Phalcon\Tag::endFormend_form
Phalcon\Tag::getTitleget_title
Phalcon\Tag::stylesheetLinkstylesheet_link
Phalcon\Tag::javascriptIncludejavascript_include
Phalcon\Tag::imageimage
Phalcon\Tag::friendlyTitlefriendly_title

函数 Functions

在volt中内置了如下的函数:

The following built-in functions are available in Volt:

NameDescription
contentIncludes the content produced in a previous rendering stage
get_contentSame as ‘content’
partialDynamically loads a partial view in the current template
superRender the contents of the parent block
timeCalls the PHP function with the same name
dateCalls the PHP function with the same name
dumpCalls the PHP function ‘var_dump’
versionReturns the current version of the framework
constantReads a PHP constant
urlGenerate a URL using the ‘url’ service

视图集成 View Integration

Volt和:doc:`Phalcon\Mvc\View <views>`也集成在一起,可以使用视图分层和部分视图功能:

Also, Volt is integrated with Phalcon\Mvc\View, you can play with the view hierarchy and include partials as well:

  1. {{ content() }}
  2. <!-- Simple include of a partial -->
  3. <div id="footer">{{ partial("partials/footer") }}</div>
  4. <!-- Passing extra variables -->
  5. <div id="footer">{{ partial("partials/footer", ['links': links]) }}</div>

局部模板在运行时被包含引入,Volt提供include。编译时将包含文件引入当前视图:

A partial is included in runtime, Volt also provides “include”, this compiles the content of a view and returns its contents as part of the view which was included:

  1. {# Simple include of a partial #}
  2. <div id="footer">{% include "partials/footer" %}</div>
  3. {# Passing extra variables #}
  4. <div id="footer">{% include "partials/footer" with ['links': links] %}</div>

包含Include

include将会增加volt性能,如果定义了包含文件的扩展名并且文件存在,则Volt会将被包含文件内联到父文件中。如果include 使用with传递了变量则不会内联:

‘include’ has a special behavior that will help us improve performance a bit when using Volt, if you specify the extension when including the file and it exists when the template is compiled, Volt can inline the contents of the template in the parent template where it’s included. Templates aren’t inlined if the ‘include’ have variables passed with ‘with’:

  1. {# The contents of 'partials/footer.volt' is compiled and inlined #}
  2. <div id="footer">{% include "partials/footer.volt" %}</div>

模版继承Template Inheritance

使用模板继承可以重用模板代码。基类模板中的*blocks*定义可以被子类模板重写。假设有如下模板文件:

With template inheritance you can create base templates that can be extended by others templates allowing to reuse code. A base template define blocks than can be overridden by a child template. Let’s pretend that we have the following base template:

  1. {# templates/base.volt #}
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. {% block head %}
  6. <link rel="stylesheet" href="style.css" />
  7. {% endblock %}
  8. <title>{% block title %}{% endblock %} - My Webpage</title>
  9. </head>
  10. <body>
  11. <div id="content">{% block content %}{% endblock %}</div>
  12. <div id="footer">
  13. {% block footer %}&copy; Copyright 2012, All rights reserved.{% endblock %}
  14. </div>
  15. </body>
  16. </html>

我们可以扩展基类模板进行替换:

From other template we could extend the base template replacing the blocks:

  1. {% extends "templates/base.volt" %}
  2. {% block title %}Index{% endblock %}
  3. {% block head %}<style type="text/css">.important { color: #336699; }</style>{% endblock %}
  4. {% block content %}
  5. <h1>Index</h1>
  6. <p class="important">Welcome on my awesome homepage.</p>
  7. {% endblock %}

在子类模板中并不是所有的块都要被替换的,最终输出如下所示:

Not all blocks must be replaced at a child template, only those that are needed. The final output produced will be the following:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <style type="text/css">.important { color: #336699; }</style>
  5. <title>Index - My Webpage</title>
  6. </head>
  7. <body>
  8. <div id="content">
  9. <h1>Index</h1>
  10. <p class="important">Welcome on my awesome homepage.</p>
  11. </div>
  12. <div id="footer">
  13. &copy; Copyright 2012, All rights reserved.
  14. </div>
  15. </body>
  16. </html>

多重继承Multiple Inheritance

模板可以多重扩展,如下代码所示:

Extended templates can extend other templates. The following example illustrates this:

  1. {# main.volt #}
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. {% block content %}{% endblock %}
  9. </body>
  10. </html>

“layout.volt”扩展了”main.volt”

Template “layout.volt” extends “main.volt”

  1. {# layout.volt #}
  2. {% extends "main.volt" %}
  3. {% block content %}
  4. <h1>Table of contents</h1>
  5. {% endblock %}

最后模板扩展了”layout.volt”

Finally a view that extends “layout.volt”:

  1. {# index.volt #}
  2. {% extends "layout.volt" %}
  3. {% block content %}
  4. {{ super() }}
  5. <ul>
  6. <li>Some option</li>
  7. <li>Some other option</li>
  8. </ul>
  9. {% endblock %}

“index.volt”最后的输出:

Rendering “index.volt” produces:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Title</title>
  5. </head>
  6. <body>
  7. <h1>Table of contents</h1>
  8. <ul>
  9. <li>Some option</li>
  10. <li>Some other option</li>
  11. </ul>
  12. </body>
  13. </html>

注意到”super()”,它渲染输出了父级块。部分模板调用扩展路径使用的是相对于当前视图路径的相对路径(i.e. app/views/)。

Note the call to the function “super()”. With that function it’s possible to render the contents of the parent block.

As partials, the path set to “extends” is a relative path under the current views directory (i.e. app/views/).

默认情况下为了性能原因,Volt只去检查子类模板的变动去判断是否重新编译模板,所以建议使用’compileAlways’ => true 初始化volt,这样父类模板改动的时候也会重新编译。

By default, and for performance reasons, Volt only checks for changes in the children templates to know when to re-compile to plain PHP again, so it is recommended initialize Volt with the option ‘compileAlways’ => true. Thus, the templates are compiled always taking into account changes in the parent templates.

自动转义模式Autoescape mode

在输出变量的时候可以打开自动转义模式:

You can enable auto-escaping of all variables printed in a block using the autoescape mode:

  1. Manually escaped: {{ robot.name|e }}
  2. {% autoescape true %}
  3. Autoescaped: {{ robot.name }}
  4. {% autoescape false %}
  5. No Autoescaped: {{ robot.name }}
  6. {% endautoescape %}
  7. {% endautoescape %}

配置 Volt 引擎Setting up the Volt Engine

可以配置Volt去改变默认行为,下面例子展示如何使用:

Volt can be configured to alter its default behavior, the following example explain how to do that:

  1. <?php
  2. use Phalcon\Mvc\View;
  3. use Phalcon\Mvc\View\Engine\Volt;
  4. //Register Volt as a service
  5. $di->set('voltService', function($view, $di) {
  6. $volt = new Volt($view, $di);
  7. $volt->setOptions(array(
  8. "compiledPath" => "../app/compiled-templates/",
  9. "compiledExtension" => ".compiled"
  10. ));
  11. return $volt;
  12. });
  13. //Register Volt as template engine
  14. $di->set('view', function() {
  15. $view = new View();
  16. $view->setViewsDir('../app/views/');
  17. $view->registerEngines(array(
  18. ".volt" => 'voltService'
  19. ));
  20. return $view;
  21. });

如果不想让volt去作为一个服务被重用,可以传递一个匿名函数去注册volt。

If you do not want to reuse Volt as a service you can pass an anonymous function to register the engine instead of a service name:

  1. <?php
  2. use Phalcon\Mvc\View;
  3. use Phalcon\Mvc\View\Engine\Volt;
  4. //Register Volt as template engine with an anonymous function
  5. $di->set('view', function() {
  6. $view = new View();
  7. $view->setViewsDir('../app/views/');
  8. $view->registerEngines(array(
  9. ".volt" => function($view, $di) {
  10. $volt = new Volt($view, $di);
  11. //set some options here
  12. return $volt;
  13. }
  14. ));
  15. return $view;
  16. });

Volt有以下配置选项:

The following options are available in Volt:

OptionDescriptionDefault
compiledPathA writable path where the compiled PHP templates will be placed./
compiledExtensionAn additional extension appended to the compiled PHP file.php
compiledSeparatorVolt replaces the directory separators / and \ by this separator in order to create a single file in the compiled directory%%
statWhether Phalcon must check if exists differences between the template file and its compiled pathtrue
compileAlwaysTell Volt if the templates must be compiled in each request or only when they changefalse
prefixAllows to prepend a prefix to the templates in the compilation pathnull

compilation path编译路径根据上面的定义生成,如果开发者想要完全控制编译路径可以定义匿名函数去生成,匿名函数接受相对路径作为参数。下面代码展示了如何动态改变编译后文件路径。

The compilation path is generated according to the above options, if the developer wants total freedom defining the compilation path, an anonymous function can be used to generate it, this function receives the relative path to the template in the views directory. The following examples show how to change the compilation path dynamically:

  1. <?php
  2. // Just append the .php extension to the template path
  3. // leaving the compiled templates in the same directory
  4. $volt->setOptions(array(
  5. 'compiledPath' => function($templatePath) {
  6. return $templatePath . '.php';
  7. }
  8. ));
  9. // ​​Recursively create the same structure in another directory
  10. $volt->setOptions(array(
  11. 'compiledPath' => function($templatePath) {
  12. $dirName = dirname($templatePath);
  13. if (!is_dir('cache/' . $dirName)) {
  14. mkdir('cache/' . $dirName);
  15. }
  16. return 'cache/' . $dirName . '/'. $templatePath . '.php';
  17. }
  18. ));

扩展 Volt Extending Volt

不像是其他的模板引擎,当模板编译后就不再需要volt了。考虑性能和独立性,volt仅仅是个PHP模板编译器。

Unlike other template engines, Volt itself is not required to run the compiled templates. Once the templates are compiled there is no dependence on Volt. With performance independence in mind, Volt only acts as a compiler for PHP templates.

Volt可以被扩展添加更多的函数,tests,和 filters 支持。

The Volt compiler allow you to extend it adding more functions, tests or filters to the existing ones.

函数 Functions

模板中的函数就像是PHP中的普通函数一样。函数需要一个规范的函数名。模板中的函数可以用两种方式定义:一种是定义函数名,一种是使用匿名函数定义。无论哪种总是要返回有效的PHP字符串表达式:

Functions act as normal PHP functions, a valid string name is required as function name. Functions can be added using two strategies, returning a simple string or using an anonymous function. Always is required that the chosen strategy returns a valid PHP string expression:

  1. <?php
  2. use Phalcon\Mvc\View\Engine\Volt;
  3. $volt = new Volt($view, $di);
  4. $compiler = $volt->getCompiler();
  5. //This binds the function name 'shuffle' in Volt to the PHP function 'str_shuffle'
  6. $compiler->addFunction('shuffle', 'str_shuffle');

使用匿名函数注册,我们可以使用$resolvedArgs去传递保留参数:

Register the function with an anonymous function. This case we use $resolvedArgs to pass the arguments exactly as were passed in the arguments:

  1. <?php
  2. $compiler->addFunction('widget', function($resolvedArgs, $exprArgs) {
  3. return 'MyLibrary\Widgets::get(' . $resolvedArgs . ')';
  4. });

独立处理参数并且不做保留处理:

Treat the arguments independently and unresolved:

  1. <?php
  2. $compiler->addFunction('repeat', function($resolvedArgs, $exprArgs) use ($compiler) {
  3. //Resolve the first argument
  4. $firstArgument = $compiler->expression($exprArgs[0]['expr']);
  5. //Checks if the second argument was passed
  6. if (isset($exprArgs[1])) {
  7. $secondArgument = $compiler->expression($exprArgs[1]['expr']);
  8. } else {
  9. //Use '10' as default
  10. $secondArgument = '10';
  11. }
  12. return 'str_repeat(' . $firstArgument . ', ' . $secondArgument . ')';
  13. });

根据是否有内置函数创建函数:

Generate the code based on some function availability:

  1. <?php
  2. $compiler->addFunction('contains_text', function($resolvedArgs, $exprArgs) {
  3. if (function_exists('mb_stripos')) {
  4. return 'mb_stripos(' . $resolvedArgs . ')';
  5. } else {
  6. return 'stripos(' . $resolvedArgs . ')';
  7. }
  8. });

内置的函数可以被重载:

Built-in functions can be overridden adding a function with its name:

  1. <?php
  2. //Replace built-in function dump
  3. $compiler->addFunction('dump', 'print_r');

过滤器Filters

过滤器使用方式为 leftExpr|name(optional-args)。添加新的过滤器和上面的函数一样:

A filter has the following form in a template: leftExpr|name(optional-args). Adding new filters is similar as seen with the functions:

  1. <?php
  2. //This creates a filter 'hash' that uses the PHP function 'md5'
  3. $compiler->addFilter('hash', 'md5');
  1. <?php
  2. $compiler->addFilter('int', function($resolvedArgs, $exprArgs) {
  3. return 'intval(' . $resolvedArgs . ')';
  4. });

内置的过滤器一样可以被重载:

Built-in filters can be overridden adding a function with its name:

  1. <?php
  2. //Replace built-in filter 'capitalize'
  3. $compiler->addFilter('capitalize', 'lcfirst');

扩展Extensions

使用扩展可以灵活的扩展模板引擎,重载内置的结构改变行为和操作,添加函数、过滤器等。

With extensions the developer has more flexibility to extend the template engine, and override the compilation of ​a specific instruction, change the behavior of an expression or operator, add functions/filters, and more.

一个扩展就是一个由Volt触发事件作为其方法的类。

An extension is a class that implements the events triggered by Volt as a method of itself.

例如下面的代码允许在Volt中使用任意PHP函数:

For example, the class below allows to use any PHP function in Volt:

  1. <?php
  2. class PhpFunctionExtension
  3. {
  4. /**
  5. * This method is called on any attempt to compile a function call
  6. */
  7. public function compileFunction($name, $arguments)
  8. {
  9. if (function_exists($name)) {
  10. return $name . '('. $arguments . ')';
  11. }
  12. }
  13. }

上面的compileFunction函数在每次编译模板的函数之前时都会被调用。这个类实现了检查要编译的函数是不是PHP函数,允许模板中直接使用PHP函数。在扩展中的事件必须返回有效的PHP代码,替换内置的编译输出,否则将会使用程序内置的输出。

The above class implements the method ‘compileFunction’ which is invoked before any attempt to compile a function call in any template. The purpose of the extension is to verify if a function to be compiled is a PHP function allowing to call it from the template. Events in extensions must return valid PHP code, this will be used as result of the compilation instead of the one generated by Volt. If an event doesn’t return an string the compilation is done using the default behavior provided by the engine.

下面是可以在模板扩展中使用的编译事件:

The following compilation events are available to be implemented in extensions:

Event/MethodDescription
compileFunctionTriggered before trying to compile any function call in a template
compileFilterTriggered before trying to compile any filter call in a template
resolveExpressionTriggered before trying to compile any expression. This allows the developer to override operators
compileStatementTriggered before trying to compile any expression. This allows the developer to override any statement

Volt扩展必须被注册在compiler编译器中

Volt extensions must be in registered in the compiler making them available in compile time:

  1. <?php
  2. //Register the extension in the compiler
  3. $compiler->addExtension(new PhpFunctionExtension());

缓存视图片段Caching view fragments

使用Volt非常方便的去缓存视图片段。缓存增加了性能,防止内容块在每次浏览的时候被PHP解析执行:

With Volt it’s easy cache view fragments. This caching improves performance preventing that the contents of a block from being executed by PHP each time the view is displayed:

  1. {% cache "sidebar" %}
  2. <!-- generate this content is slow so we are going to cache it -->
  3. {% endcache %}

设定缓存时间:

Setting a specific number of seconds:

  1. {# cache the sidebar by 1 hour #}
  2. {% cache "sidebar" 3600 %}
  3. <!-- generate this content is slow so we are going to cache it -->
  4. {% endcache %}

任何有效的表达式值可以被设置为缓存键名:

Any valid expression can be used as cache key:

  1. {% cache ("article-" ~ post.id) 3600 %}
  2. <h1>{{ post.title }}</h1>
  3. <p>{{ post.content }}</p>
  4. {% endcache %}

通过视图组件的:doc:Phalcon\Cache <cache>`实现了缓存效果。参考:doc:“Caching View Fragments” <views>`了解更多。

The caching is done by the Phalcon\Cache component via the view component. Learn more about how this integration works in the section “Caching View Fragments”.

注入服务到模版Inject Services into a Template

在Volt中服务容器也是可用的。在模板中访问服务模板就可以了:

If a service container (DI) is available for Volt, you can use the services by only accessing the name of the service in the template:

  1. {# Inject the 'flash' service #}
  2. <div id="messages">{{ flash.output() }}</div>
  3. {# Inject the 'security' service #}
  4. <input type="hidden" name="token" value="{{ security.getToken() }}">

独立的组件Stand-alone component

下面例子说明独立使用volt:

Using Volt in a stand-alone mode can be demonstrated below:

  1. <?php
  2. Phalcon\Mvc\View\Engine\Volt\Compiler as VoltCompiler;
  3. //Create a compiler
  4. $compiler = new VoltCompiler();
  5. //Optionally add some options
  6. $compiler->setOptions(array(
  7. //...
  8. ));
  9. //Compile a template string returning PHP code
  10. echo $compiler->compileString('{{ "hello" }}');
  11. //Compile a template in a file specifying the destination file
  12. $compiler->compileFile('layouts/main.volt', 'cache/layouts/main.volt.php');
  13. //Compile a template in a file based on the options passed to the compiler
  14. $compiler->compile('layouts/main.volt');
  15. //Require the compiled templated (optional)
  16. require $compiler->getCompiledTemplatePath();

其他资源

External Resources