Merge pull request #127 from bzick/develop

Release 2.4.0
This commit is contained in:
Ivan Shalganov 2015-01-02 23:11:47 +03:00
commit 964f20a914
23 changed files with 1046 additions and 422 deletions

8
.gitignore vendored
View File

@ -1,17 +1,9 @@
.idea
vendor
coverage
build
composer.lock
composer.phar
tests/resources/compile/*
!.gitkeep
tests/resources/template/*
!.gitkeep
sandbox/compiled/*
!.gitkeep
benchmark/compile/*
benchmark/templates/inheritance/smarty
benchmark/templates/inheritance/twig
benchmark/sandbox/compiled/*
!.gitkeep

View File

@ -1,6 +1,24 @@
Changelog
=========
## 2.4.0 (2015-01-02)
- Fix bugs #120, #104, #119
- Add `~~` operator. Concatenation with space.
- Improve #126. Disable clearcachestats() by default in Fenom\Provider. clearcachestats() may be enabled.
- Improve accessors (unnamed system variable). Now possible add, redefine yours accessors.
- ++Docs
- ++Tests
### 2.3.1 (2014-11-06)
- Fix #122
### 2.3.1 (2014-08-27)
- Fix #105
- ++Tests
## 2.3.0 (2014-08-08)
- Add tags {set} and {add}

View File

@ -1,8 +1,6 @@
Documentation
=============
**Please, help translate documentation to english or fix typos. [Read more](./helpme.md).**
### Fenom
* [Quick start](./start.md)
@ -11,7 +9,13 @@ Documentation
* [For developers](./dev/readme.md)
* [Configuration](./configuration.md)
* [Syntax](./syntax.md)
* [Operators](./operators.md)
* [Variables](./syntax.md#Variables)
* [Values](./syntax.md#Values)
* [Arrays](./syntax.md#Arrays)
* [Operators](./operators.md)
* [Modificators](./syntax.md#Modificators)
* [Tags](./syntax.md#Tags)
* [Tag configuration](./syntax.md#Tag-configuration)
***
@ -19,7 +23,7 @@ Documentation
[Usage](./syntax.md#tags)
* [var](./tags/var.md) — define variable
* [set](./tags/var.md), `add` and `var` — define variables
* [if](./tags/if.md), `elseif` and `else` — conditional statement
* [foreach](./tags/foreach.md), `foreaelse`, `break` and `continue` — traversing items in an array or object
* [for](./tags/for.md), `forelse`, `break` and `continue` — loop statement
@ -72,7 +76,7 @@ Documentation
* [Comparison operators](./operators.md#comparison-operators) — `>`, `>=`, `<`, `<=`, `==`, `!=`, `!==`, `<>`
* [Bitwise operators](./operators.md#bitwise-operators) — `|`, `&`, `^`, `~$var`, `>>`, `<<`
* [Assignment operators](./operators.md#assignment-operators) — `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `>>=`, `<<=`
* [String concatenation operator](./operators.md#string-operator) — `$str1 ~ $str2`
* [String concatenation operators](./operators.md#string-operators) — `$str1 ~ $str2`, `$str1 ~~ $str2`, `$str1 ~= $str2`
* [Ternary operators](./operators.md#ternary-operators) — `$a ? $b : $c`, `$a ! $b : $c`, `$a ?: $c`, `$a !: $c`
* [Check operators](./operators.md#check-operators) — `$var?`, `$var!`
* [Test operator](./operators.md#test-operator) — `is`, `is not`

View File

@ -76,16 +76,16 @@ Below is complex example:
### System variable
Unnamed system variable starts with `$.` and allows access to global variables and template information:
Unnamed system variable starts with `$.` and allows access to global system variables and template information:
* `$.get` is `$_GET`.
* `$.post` is `$_POST`.
* `$.cookie` is `$_COOKIE`.
* `$.session` is `$_SESSION`.
* `$.globals` is `$GLOBALS`.
* `$.request` is `$_REQUEST`.
* `$.files` is `$_FILES`.
* `$.server` is `$_SERVER`.
* `$.get` — array `$_GET`.
* `$.post` — array `$_POST`.
* `$.cookie` — array `$_COOKIE`.
* `$.session` — array `$_SESSION`.
* `$.globals` — array `$GLOBALS`.
* `$.request` — array `$_REQUEST`.
* `$.files` — array `$_FILES`.
* `$.server` — array `$_SERVER`.
* `$.env` is `$_ENV`.
* `$.tpl.name` returns current template name.
* `$.tpl.schema` returns current schema of the template.
@ -98,6 +98,165 @@ Unnamed system variable starts with `$.` and allows access to global variables a
{/if}
```
Безименная системная переменная начинается с `$.` и предоставляет доступ к глобальным системным переменным и системной информации:
* `$.env` — array `$_ENV`.
* `$.get` — array `$_GET`.
* `$.post` — array `$_POST`.
* `$.files` — array `$_FILES`.
* `$.cookie` — array `$_COOKIE`.
* `$.server` — array `$_SERVER`.
* `$.session` — array `$_SESSION`.
* `$.globals` — array `$GLOBALS`.
* `$.request` — array `$_REQUEST`.
* `$.tpl.name` returns name for current template.
* `$.tpl.basename` returns name without schema for current template.
* `$.tpl.scm` returns schema for current template.
* `$.tpl.options` returns options as integer for current template.
* `$.tpl.depends` <!-- возвращает массив шаблонов на которые ссылается текущий шаблон.-->
* `$.tpl.time` returns last modified timestamp for current template
* `$.version` returns Fenom version.
* `$.const` returns the value of a PHP constant: `$.const.PHP_EOL` get value of constant `PHP_EOL`.
Supported namespace for constants, use dot instead of back-slash for namespace separators: `$.const.Storage.FS::DIR_SEPARATOR` get value of constant `Storage\FS::DIR_SEPARATOR`.
But if constant `Storage\FS::DIR_SEPARATOR` does not exists then constant `Storage\FS\DIR_SEPARATOR` will be taken.
* `$.php`call PHP static method. `$.php.Storage.FS::put($filename, $data)` calls method `Storage\FS::put($filename, $data)`.
`$.php.Storage.FS.put($filename, $data)` `Storage\FS\put($filename, $data)`
* System function `$.fetch($name, $values)` calls Fenom::fetch() in template. `$name` — template name,
`$values` — additional variables.
* also you may [add](./ext/extend.md#Extends-system-variable) yours system variables and functions.
## Scalar values
### Strings
A string literal can be specified in two different ways: double quotes (`"string"`) and single quotes (`'string'`).
#### Double quotes
If the string is enclosed in double-quotes `"`, Fenom will interpret more escape sequences for special characters:
| Последовательность | Значение |
|---------------------|----------|
| `\n` | linefeed (LF or 0x0A (10) in ASCII)
| `\r` | carriage return (CR or 0x0D (13) in ASCII)
| `\t` | horizontal tab (HT or 0x09 (9) in ASCII)
| `\v` | vertical tab (VT or 0x0B (11) in ASCII)
| `\f` | form feed (FF or 0x0C (12) in ASCII)
| `\\` | backslash
| `\$` | dollar sign
| `\"` | double-quote
| `\[0-7]{1,3}` | the sequence of characters matching the regular expression is a character in octal notation
| `\x[0-9A-Fa-f]{1,2}`| the sequence of characters matching the regular expression is a character in hexadecimal notation
The most important feature of double-quoted strings is the fact that variable names will be expanded.
There are two types of syntax: a simple one and a complex one. The simple syntax is the most common and convenient.
It provides a way to embed a variable, an array value, or an object property in a string with a minimum of effort.
The complex syntax can be recognised by the curly braces surrounding the expression.
##### Simple syntax
If a dollar sign `$` is encountered, the parser will greedily take as many tokens as possible to form a valid variable name.
Enclose the variable name in curly braces to explicitly specify the end of the name.
```smarty
{"Hi, $username!"} outputs "Hi, Username!"
```
For anything more complex, you should use the complex syntax.
##### Complex syntax
This isn't called complex because the syntax is complex, but because it allows for the use of complex expressions.
Any scalar variable, array element or object property with a string representation can be included via this syntax.
Simply write the expression the same way as it would appear outside the string, and then wrap it in `{` and `}`.
Since `{` can not be escaped, this syntax will only be recognised when the `$` immediately follows the `{`.
Use `{\$` to get a literal `{$`. Some examples to make it clear:
```smarty
{"Hi, {$user.name}!"} outputs: Hi, Username!
{"Hi, {$user->name}!"} outputs: Hi, Username!
{"Hi, {$user->getName()}!"} outputs: Hi, Username!
{"Hi, {\$user->name}!"} outputs: Hi, {\$user->name}!
```
Allows modifiers and operators:
```smarty
{"Hi, {$user.name|up}!"} outputs: Hi, USERNAME!
{"Hi, {$user.name|up ~ " (admin)"}!"} outputs: Hi, USERNAME (admin)!
```
#### Single quotes
The simplest way to specify a string is to enclose it in single quotes (the character `'`).
To specify a literal single quote, escape it with a backslash (`\`).
To specify a literal backslash, double it (`\\`).
All other instances of backslash will be treated as a literal backslash: this means that the other escape sequences you might be used to, such as `\r` or `\n`, will be output literally as specified rather than having any special meaning.
```smarty
{'Hi, $foo'} outputs: 'Hi, $foo'
{'Hi, {$foo}'} outputs: 'Hi, {$foo}'
{'Hi, {$user.name}'} outputs: 'Hi, {$user.name}'
{'Hi, {$user.name|up}'} outputs: "Hi, {$user.name|up}"
```
### Integers
Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8) or binary (base 2) notation, optionally preceded by a sign (- or +).
To use octal notation, precede the number with a 0 (zero).
To use hexadecimal notation precede the number with 0x.
To use binary notation precede the number with 0b.
``smarty
{var $a = 1234} decimal number
{var $a = -123} a negative number
{var $a = 0123} octal number (equivalent to 83 decimal)
{var $a = 0x1A} hexadecimal number (equivalent to 26 decimal)
{var $a = 0b11111111} binary number (equivalent to 255 decimal)
```
**Notice**
Binary notation (`0b1011011`) unavailable on PHP older than 5.3.
**Notice**
The size of an integer is platform-dependent, although a maximum value of about two billion is the usual value (that's 32 bits signed).
64-bit platforms usually have a maximum value of about 9E18
**Warning**
If an invalid digit is given in an octal integer (i.e. 8 or 9), the rest of the number is ignored.
### Floating point numbers
Floating point numbers (also known as "floats", "doubles", or "real numbers") can be specified using any of the following syntaxes:
```smarty
{var $a = 1.234}
{var $b = 1.2e3}
{var $c = 7E-10}
```
### Booleans
This is the simplest type. A boolean expresses a truth value. It can be either TRUE or FALSE.
To specify a boolean literal, use the constants TRUE or FALSE. Both are case-insensitive.
```smarty
{set $a = true}
```
### NULL
The special NULL value represents a variable with no value. NULL is the only possible value of type null.
------
### Variable operations
Fenom supports math, logic, comparison, containment, test, concatenation operators...
@ -206,6 +365,97 @@ Floating point numbers (also known as "floats", "doubles", or "real numbers") ca
{var $c = 7E-10}
```
### Operators
Fenom supports operators on values:
* Arithmetic operators — `+`, `-`, `*`, `/`, `%`
* Logical operators — `||`, `&&`, `!$var`, `and`, `or`, `xor`
* Comparison operators — `>`, `>=`, `<`, `<=`, `==`, `!=`, `!==`, `<>`
* Bitwise operators — `|`, `&`, `^`, `~$var`, `>>`, `<<`
* Assignment operators — `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `>>=`, `<<=`
* String concatenation operators — `$str1 ~ $str2`, `$str1 ~~ $str2`, `$str1 ~= $str2`
* Ternary operators — `$a ? $b : $c`, `$a ! $b : $c`, `$a ?: $c`, `$a !: $c`
* Check operators — `$var?`, `$var!`
* Test operator — `is`, `is not`
* Containment operator — `in`, `not in`
About [operators](./operators.md).
## Arrays
An array can be created using the `[]` language construct. It takes any number of comma-separated `key => value` pairs as arguments.
```
[
key => value,
key2 => value2,
key3 => value3,
...
]
```
The comma after the last array element is optional and can be omitted.
This is usually done for single-line arrays, i.e. `[1, 2]` is preferred over `[1, 2, ]`.
For multi-line arrays on the other hand the trailing comma is commonly used, as it allows easier addition of new elements at the end.
```smarty
{set $array = [
"foo" => "bar",
"bar" => "foo",
]}
```
The key can either be an integer or a string. The value can be of any type.
Additionally the following key casts will occur:
* Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8.
On the other hand "08" will not be cast, as it isn't a valid decimal integer.
* Floats are also cast to integers, which means that the fractional part will be truncated.
E.g. the key 8.7 will actually be stored under 8.
* Bools are cast to integers, too, i.e. the key true will actually be stored under 1 and the key false under 0.
* Null will be cast to the empty string, i.e. the key null will actually be stored under "".
* Arrays and objects can not be used as keys. Doing so will result in a warning: Illegal offset type.
If multiple elements in the array declaration use the same key, only the last one will be used as all others are overwritten.
An existing array can be modified by explicitly setting values in it.
This is done by assigning values to the array, specifying the key after dot or in brackets.
The key can also be omitted, resulting in an empty pair of brackets (`[]`).
```smarty
{set $arr.key = value}
{set $arr[] = value} {* append value to end of array *}
```
If `$arr` doesn't exist yet, it will be created, so this is also an alternative way to create an array.
This practice is however discouraged because if `$arr` already contains some value (e.g. string from request variable)
then this value will stay in the place and `[]` may actually stand for string access operator.
It is always better to initialize variable by a direct assignment.
## Constants
A constant is an identifier (name) for a simple value in PHP.
As the name suggests, that value cannot change during the execution of the script.
A constant is case-sensitive by default. By convention, constant identifiers are always uppercase.
## PHP functions and methods
**TODO**
```smarty
{$.php.some_function($a, $b, $c)}
```
```smarty
{$.php.MyClass::method($a, $b, $c)}
```
```smarty
{$.php.My.NS.some_function($a, $b, $c)}
{$.php.My.NS.MyClass::method($a, $b, $c)}
```
## Modifiers
@ -229,63 +479,7 @@ These parameters follow the modifier name and are separated by a : (colon).
## Tags
Basically, tag seems like
```smarty
{FUNCNAME attr1 = "val1" attr2 = $val2}
```
Tags starts with name and may have attributes
Это общий формат функций, но могут быть исключения, например функция [{var}](./tags/var.md), использованная выше.
```smarty
{include file="my.tpl"}
{var $foo=5}
{if $user.loggined}
Welcome, <span style="color: red">{$user.name}!</span>
{else}
Who are you?
{/if}
```
В общем случае аргументы принимают любой формат переменных, в том числе результаты арифметических операций и модификаторов.
```smarty
{funct arg=true}
{funct arg=5}
{funct arg=1.2}
{funct arg='string'}
{funct arg="string this {$var}"}
{funct arg=[1,2,34]}
{funct arg=$x}
{funct arg=$x.c}
```
```smarty
{funct arg="ivan"|upper}
{funct arg=$a.d.c|lower}
```
```smarty
{funct arg=1+2}
{funct arg=$a.d.c+4}
{funct arg=($a.d.c|count+4)/3}
```
## Static method support
By default static methods are allowed in templates
```smarty
{Lib\Math::multiple x=3 y=4} static method as tag
{Lib\Math::multiple(3,4)} inline static method
{12 + Lib\Math::multiple(3,4)}
{12 + 3|Lib\Math::multiple:4} static method as modifier
```
You may disable call static methods in template, see in [security options](./settings.md) option `deny_static`
**TODO**
## Ignoring template code
@ -360,7 +554,7 @@ Tags to allow any number of spaces
### Tag options
TODO
**TODO**
| name | code | type | description |
| ------- | ---- | ----- | ------------ |

View File

@ -36,7 +36,8 @@ $fenom->setOptions($options);
| *force_include* | `Fenom::FORCE_INCLUDE` | стараться по возможности вставить код дочернего шаблона в родительский при подключении шаблона | повышает производительность, увеличивает размер файлов в кеше, уменьшает количество файлов в кеше |
| *auto_escape* | `Fenom::AUTO_ESCAPE` | автоматически экранировать HTML сущности при выводе переменных в шаблон | понижает производительность |
| *force_verify* | `Fenom::FORCE_VERIFY` | автоматически проверять существование переменной перед использованием в шаблоне | понижает производительность |
| *disable_statics* | `Fenom::DENY_STATICS` | отключает воззможность вызова статических методов в шаблоне | |
| *disable_php_calls* | `Fenom::DENY_PHP_CALLS` | отключает возможность вызова статических методов и функций в шаблоне | |
| *disable_statics* | `Fenom::DENY_STATICS` | устаревшее название disable_php_calls | |
| *strip* | `Fenom::AUTO_STRIP` | удаляет лишиние пробелы в шаблоне | уменьшает размер кеша |
```php
@ -48,5 +49,9 @@ $fenom->setOptions(array(
$fenom->setOptions(Fenom::AUTO_RELOAD | Fenom::FORCE_INCLUDE);
```
```php
$fenom->addCallFilter('View\Widget\*::get*')
```
**Замечание**
По умолчанию все параметры деактивированы.

View File

@ -1,8 +1,6 @@
Расширение Fenom
================
*TODO*
# Добавление тегов
В шаблонизаторе принято различать два типа тегов: омпиляторы_ и ункции_.
@ -116,11 +114,27 @@ $fenom->addModifier('my_modifier', function ($variable, $param1, $param2) {
# Расширение тестовго оператора
```php
$fenom->addTest($name, $code);
?>
$fenom->addTest(string $name, string $code);
```
`$code` - PHP код для условия, с маркером для замены на значение или переменную.
Например, тест на целое число `is int` можно добавить как `$fenom->addTest('int', 'is_int(%s)')`.
В шаблоне тесты выглядит как `{$a is int}`, а после компиляции выглядит приблизительно так - `is_int($a)`.
# Расширение глобальной переменной или функции
Fenom обладает определенным набором глобальных переменных и функций. Однако их может не хватать для удобной работы с шаблонами.
В этом случае потребуется добавить, переопределить или удалить существующие глобальные переменные или функции.
Метод `Fenom::addAccessor(string $name, callable $parser)` позволяет добавить свой обработчик на не известную глобальную переменную или функцию.
```php
$fenom->addAccessor('project', function (Fenom\Tokenizer $tokens) { /* code */ });
```
# Расширение глобальной переменной
Указанный вторым аргументом, парсер будет вызван при встречи компилятором конструкции `{$.project}`.
Парсеры вызываются только на момент сборки шаблона, а не во время его выполенения.
Через метод `Fenom::addAccessor($name, $parser)` можно переопределить уже любую другую существующую глобальную переменную или функцию.
Метод `Fenom::removeAccessor($name)` позволяет удалить любую определенную глобальную переменную или функцию по ее имени.
# Источники шаблонов

View File

@ -127,11 +127,13 @@ Fenom поддерживает префиксные и постфиксные о
* `--$a` - префиксный декремент, уменьшает $a на единицу, затем возвращает значение $a.
* `$a--` - постфиксный декремент, возвращает значение $a, затем уменьшает $a на единицу.
### Строковый оператор
### Строковые операторы
Оператор объединения `~` возвращает строку, представляющую собой соединение левого и правого аргумента.
`$a ~ $b` - возвращает результат объединения сток `$a` и `$b`
* `$a ~ $b` - возвращает результат объединения сток `$a` и `$b`
* `$a ~~ $b` - возвращает результат объединения сток `$a` и `$b` через пробел
* `$a ~= $b` - присвоение с объединением
### Тернарные операторы

View File

@ -24,7 +24,7 @@
### Теги
[Использование](./syntax.md#tags) тегов.
[Использование](./syntax.md#Теги) тегов.
* [set](./tags/set.md), `add` и `var` — определение значения переменной
* [if](./tags/if.md), `elseif` и `else` — условный оператор
@ -40,7 +40,7 @@
* [autoescape](./tags/autoescape.md) — экранирует фрагмент шаблона
* [raw](./tags/raw.md) — отключает экранирование фрагмента шаблона
* [unset](./tags/unset.md) — удаляет переменные
* или [добавте](./ext/extend.md#add-tags) свои
* или [добавте](./ext/extend.md#Добавление-тегов) свои
***
@ -68,7 +68,7 @@
* [join](./mods/join.md) — объединяет массив в строку
* так же разрешены функции: `json_encode`, `json_decode`, `count`, `is_string`, `is_array`, `is_numeric`, `is_int`, `is_object`,
`strtotime`, `gettype`, `is_double`, `ip2long`, `long2ip`, `strip_tags`, `nl2br`
* или [добавте](./ext/extend.md#add-modifiers) свои
* или [добавте](./ext/extend.md#Добавление-модификаторов) свои
***
@ -79,7 +79,7 @@
* [Операторы сравнения](./operators.md#Операторы-сравнения) — `>`, `>=`, `<`, `<=`, `==`, `!=`, `!==`, `<>`
* [Битовые операторы](./operators.md#Битовые-операторы) — `|`, `&`, `^`, `~$var`, `>>`, `<<`
* [Операторы присвоения](./operators.md#Операторы-присвоения) — `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `>>=`, `<<=`
* [Строковый оператор](./operators.md#Строковый-оператор) — `$str1 ~ $str2`
* [Строковые операторы](./operators.md#Строковые-операторы) — `$str1 ~ $str2`, `$str1 ~~ $str2`, `$str1 ~= $str2`
* [Тернарные операторы](./operators.md#Тернарные-операторы) — `$a ? $b : $c`, `$a ! $b : $c`, `$a ?: $c`, `$a !: $c`
* [Проверяющие операторы](./operators.md#Проверяющие-операторы) — `$var?`, `$var!`
* [Оператор тестирования](./operators.md#Оператор-тестирования) — `is`, `is not`

View File

@ -2,7 +2,7 @@
=========
По синтаксису шаблона Fenom похож на [Smarty](http://www.smarty.net/), но обладает рядом улучшений.
Все теги шаблонизатора заключаются в фигурные скобки: `{` — открытие тега и `}` — закрытие тега.
Все теги шаблонизатора заключаются в фигрные скобки: `{` — открытие тега и `}` — закрытие тега.
**Замечание**
Хоть Fenom и позаимствовал синтаксис Smarty, но он не заимствовал теги Smarty как есть.
@ -29,7 +29,7 @@
<div class="user">Hello, <a href="/users/17">Bzick</a>.</div>
```
Переменные могут быть массивом. В этом случае обращение по ключу происходит через оператор `.` или, как в PHP, через операторы `[` и `]`
Переменные могут быть массивом. В этом случае обращение по ключу происходит через опертор `.` или, как в PHP, через операторы `[` и `]`
```smarty
<div class="user">Hello, <a href="/users/{$user.id}">{$user.name}</a>.</div>
```
@ -38,12 +38,12 @@
<div class="user">Hello, <a href="/users/{$user['id']}">{$user['name']}</a>.</div>
```
В случае объекта, доступ к его свойствам осуществляется так как и в PHP — через оператор `->`:
В случае объекта, доступ к его свойствам осущесвляется так как и в PHP — через оператор `->`:
```smarty
<div class="user">Hello, <a href="/users/{$user->id}">{$user->name}</a>.</div>
```
Методы, как и свойства можно вызвать через оператор `->`, передав в метод любые аргументы:
Методы, как и свойства можно вызвать через оператор `->`, передав в метод любые рагументы:
```smarty
<div class="user">Hello, <a href="/users/{$user->getId()}">{$user->getName()}</a>.</div>
```
@ -53,7 +53,7 @@
Что бы избежать фатальной ошибки определите метод `__call` у класса объекта.
Вызов методов в шаблоне можно вообще выключить в [настройках](./docs/configuration.md).
Ниже приведены комбинированные примеры работы с переменными:
Ниже приведены комбинированые примеры работы с переменными:
```smarty
{$foo.bar.baz}
@ -79,27 +79,34 @@
### Системная переменная
Безымянная системная переменная начинается с `$.` и предоставляет доступ к глобальным системным переменным и системной информации:
Безименная системная переменная начинается с `$.` и предоставляет доступ к глобальным системным переменным и системной информации:
* `$.get` — `$_GET`.
* `$.post` — `$_POST`.
* `$.cookie` — `$_COOKIE`.
* `$.session` — `$_SESSION`.
* `$.globals` — `$GLOBALS`.
* `$.request` — `$_REQUEST`.
* `$.files` — `$_FILES`.
* `$.server` — `$_SERVER`.
* `$.env` — `$_ENV`.
* `$.env` — массив `$_ENV`.
* `$.get` — массив `$_GET`.
* `$.post` — массив `$_POST`.
* `$.files` — массив `$_FILES`.
* `$.cookie` — массив `$_COOKIE`.
* `$.server` — массив `$_SERVER`.
* `$.session` — массив `$_SESSION`.
* `$.globals` — массив `$GLOBALS`.
* `$.request` — массив `$_REQUEST`.
* `$.tpl.name` возвращает текущее название шаблона.
* `$.tpl.schema` возвращает код провайдера шаблона.
* `$.tpl.basename` возвращает текущее название шаблона без схемы.
* `$.tpl.scm` возвращает схему шаблона.
* `$.tpl.options` возвращает параметры шбалона в виде целого числа.
* `$.tpl.depends` возвращает массив шаблонов на которые ссылается текущий шаблон.
* `$.tpl.time` возвращает штамп времени когда шаблон последний раз менялся
* `$.version` возвращает версию Fenom.
* `$.const` обращение к PHP константе: `$.const.PHP_EOL` .
* `$.const` обращение к PHP константе: `$.const.PHP_EOL` обращение к константе `PHP_EOL`. Поддерживается пространство имен
которое разделяется через точку: `$.const.Storage.FS::DIR_SEPARATOR` обращение к PHP константе `Storage\FS::DIR_SEPARATOR`
если такой констатнты нет будет взята константа `Storage\FS\DIR_SEPARATOR`.
* `$.php` обращение к статическомому методу. `$.php.Storage.FS::put($filename, $data)` обращение к методу `Storage\FS::put($filename, $data)`.
`$.php.Storage.FS.put($filename, $data)` `Storage\FS\put($filename, $data)`
* Системная функция `$.fetch($name, $values)` реализует метод Fenom::fetch() в шаблоне. `$name` — имя шаблона,
`$values` — дополнительные переменные, которые будут добавлены к существующим.
Функция позволяет получить резуьтат работы шаблона в переменную.
* так же вы можете [добавить](./ext/extend.md#Расширение-глобальной-переменной-или-функции) свои системные переменные и функции
```smarty
{if $.get.debug? && $.const.DEBUG}
...
{/if}
```
## Скалярные значения
@ -110,7 +117,20 @@
#### Двойные кавычки
Если строка заключена в двойные кавычки `"`, Fenom распознает большее количество управляющих последовательностей для специальных символов:
`\n`, `\r`, `\t`, `\v`, `\e`, `\f`, `\\`, `\$`, `\"`, `\[0-7]{1,3}`, `\x[0-9A-Fa-f]{1,2}`.
| Последовательность | Значение |
|---------------------|----------|
| `\n` | новая строка (LF или 0x0A (10) в ASCII)
| `\r` | возврат каретки (CR или 0x0D (13) в ASCII)
| `\t` | горизонтальная табуляция (HT или 0x09 (9) в ASCII)
| `\v` | вертикальная табуляция (VT или 0x0B (11) в ASCII)
| `\f` | подача страницы (FF или 0x0C (12) в ASCII)
| `\\` | обратная косая черта
| `\$` | знак доллара
| `\"` | двойная кавычка
| `\[0-7]{1,3}` | последовательность символов, соответствующая регулярному выражению символа в восьмеричной системе счисления
| `\x[0-9A-Fa-f]{1,2}`| последовательность символов, соответствующая регулярному выражению символа в шестнадцатеричной системе счисления
Но самым важным свойством строк в двойных кавычках является обработка переменных.
Существует два типа синтаксиса: простой и сложный. Простой синтаксис более легок и удобен.
Он дает возможность обработки переменной, значения массива или свойства объекта с минимумом усилий.
@ -168,22 +188,23 @@
### Целые числа
Целые числа могут быть указаны в десятичной (основание 10), шестнадцатеричной (основание 16),
восьмеричной (основание 8) или двоичной (основание 2) системе счисления, с необязательным предшествующим знаком (- или +).
восьмеричной (основание 8) или двоичной (основание 2) системе счисления, с необязательным предшествующим знаком (`-` или `+`).
Для записи в восьмеричной системе счисления, необходимо поставить пред числом 0 (ноль). Для записи в шестнадцатеричной системе счисления, необходимо поставить перед числом 0x.
Для записи в восьмеричной системе счисления, необходимо поставить пред числом 0 (ноль).
Для записи в шестнадцатеричной системе счисления, необходимо поставить перед числом 0x.
Для записи в двоичной системе счисления, необходимо поставить перед числом 0b
```smarty
{var $a = 1234} // десятичное число
{var $a = -123} // отрицательное число
{var $a = 0123} // восьмеричное число (эквивалентно 83 в десятичной системе)
{var $a = 0x1A} // шестнадцатеричное число (эквивалентно 26 в десятичной системе)
{var $a = 0b11111111} // двоичное число (эквивалентно 255 в десятичной системе)
{var $a = 1234} десятичное число
{var $a = -123} отрицательное число
{var $a = 0123} восьмеричное число (эквивалентно 83 в десятичной системе)
{var $a = 0x1A} шестнадцатеричное число (эквивалентно 26 в десятичной системе)
{var $a = 0b11111111} двоичное число (эквивалентно 255 в десятичной системе)
```
**Замечение**
Двоичная запись числа (`0b1011011`) не доступна на старых версиях PHP — 5.3 или ниже.
Попытка использовать на старых версия PHP приведет к исключению при компиляциях.
Попытка исользовать на старых версия PHP приведет к исключению при компиляциях.
**Замечение**
Размер целого числа зависит от платформы, хотя, как правило, максимальное значение примерно равно 2 миллиардам (это 32-битное знаковое).
@ -207,14 +228,16 @@
Это простейший тип. Булевое выражает истинность значения. Он может быть либо TRUE либо FALSE.
Для указания булевого значения, используйте ключевое слово TRUE или FALSE. Оба регистро-независимы.
```smarty
{set $a = true}
```
### NULL
Специальное значение NULL представляет собой переменную без значения. NULL - это единственно возможное значение типа null.
Обычно возникают путаницы между NULL и FALSE, так как по роли они похожи, но различаются по принципу:
NULL - это отсутствие присутствия, а FALSE - присутствие отсутствия.
Обычно возникают путаницы между NULL и FALSE, так как по роли они похожи, но разлицаются по принципу:
NULL - это отсутствие присутствия, а FALSE - присутвие отсутствия.
### Операции
@ -253,7 +276,6 @@ NULL - это отсутствие присутствия, а FALSE - прису
"foo" => "bar",
"bar" => "foo",
]}
```
`key` может быть либо целым числом, либо строкой. `value` может быть любого типа.
@ -264,7 +286,7 @@ NULL - это отсутствие присутствия, а FALSE - прису
* Числа с плавающей точкой также будут преобразованы к числу, т.е. дробная часть будет отброшена. Например, ключ со значением `8.7` будет в действительности сохранен со значением `8`.
* Булев также преобразовываются к целому числу. Например, ключ со значением `true` будет сохранен со значением `1` и ключ со значением `false` будет сохранен со значением `0`.
* NULL будет преобразован к пустой строке. Например, ключ со значением `null` будет в действительности сохранен со значением `""`.
* Массивы (тип array) и объекты (тип object) не могут использоваться в качестве ключей. При подобном использовании компилятор будет генерировать предупреждение: Недопустимый тип смещения (Illegal offset type).
* Массивы (тип array) и объекты (тип object) не могут использоваться в качестве ключей. При подобном использовании будет генерироваться предупреждение: Недопустимый тип смещения (Illegal offset type).
Если несколько элементов в объявлении массива используют одинаковый ключ, то только последний будет использоваться, а все другие будут перезаписаны.
@ -283,9 +305,40 @@ NULL - это отсутствие присутствия, а FALSE - прису
Однако такой способ применять не рекомендуется, так как если переменная `$arr` уже содержит некоторое значение (например, строку),
то это значение останется на месте и `[]` может на самом деле означать доступ к символу в строке. Лучше инициализировать переменную путем явного присваивания значения.
## Константы
Константы - это идентификаторы (имена) простых значений, определенные в PHP.
Исходя из их названия, нетрудно понять, что их значение не может изменяться в ходе выполнения шаблона.
Имена констант чувствительны к регистру. По принятому соглашению, имена констант всегда пишутся в верхнем регистре.
Константы доступны из любого шаблона через глобальную переменную {$.const.*}: `{$.const.PHP_EOL}`.
В шаблоне определить константу нельзя.
## PHP функции и методы
Fenom предоставляет возможноть обращаться к функиям и методам самого PHP. Использование их в шаблонах не рекомендуется.
Используя системную переменную `$.php` можно вызвать любую функцию или метод в шаблоне:
```smarty
{$.php.some_function($a, $b, $c)}
```
метод вызывается иначе
```smarty
{$.php.MyClass::method($a, $b, $c)}
```
пространство имен указывается перед функций или классом, разделяя точкой вместо обратного слеша:
```smarty
{$.php.My.NS.some_function($a, $b, $c)}
{$.php.My.NS.MyClass::method($a, $b, $c)}
```
Вызов функции и методов можно выключить настройкой `???` или ограничить.
## Модификаторы
Модификаторы переменных могут быть применены к переменным, пользовательским функциям или строкам.
Модификаторы переменных могут быть прмменены к переменным, пользовательским функциям или строкам.
Для их применения надо после модифицируемого значения указать символ `|` (вертикальная черта) и название модификатора.
Так же модификаторы могут принимать параметры, которые влияют на их поведение.
Эти параметры следуют за названием модификатора и разделяются `:` (двоеточием).
@ -304,14 +357,14 @@ NULL - это отсутствие присутствия, а FALSE - прису
## Теги
Все сущности шаблона можно разбить на две группы:
Все сущности шаблона можно разжелить на две группы:
* заполнитель (placeholder) — вывод переменной в шаблоне, например `{$name}`
* тег — конструкция выполняющая некоторые действия, выглядит как именованный заполнитель (placeholder), например `{include $name}`
* тег — конструкция, выполняющаяя некоторые действия, которая выглядит как именованный заполнитель (placeholder), например `{include $name}`
Теги так же можно разделить на две группы:
* Функии. Тег функции вызывает пользовательскую функцию во время выполнения шаблона, результат функции будет выведен вместо тега.
* Функии. Тег функции вызывает пользовательскую во время выполнения шаблона, результат функции будет выведен вместо тега.
Пользовательские функции являются дополнительными и могут быть индивидуальными. Они могут быть изменены по вашему желанию, также вы можете создать новые.
* Компиляторы. В отличии от функций компиляторы вызываются во время компиляции шаблона и возвращают PHP код, который описывает некоторое действие.
Компиляторы и формируют основные конструкции типа `if`, `foreach` и т.д.

View File

@ -18,6 +18,12 @@
Все значения присвоенных переменных восстанавливаются после того, как подключаемый шаблон отработал.
Это значит, что вы можете использовать все переменные из подключающего шаблона в подключаемом, но изменения переменных внутри подключаемого шаблона не будут видны внутри подключающего шаблона после команды {include}.
Если требуется сохранить результат отрисовки шаблона в переменную то используйте `$.fetch($templates, $values)`:
```smarty
{set $data = $.fetch('user.tpl', ["name" => $data.name, "email" => $data.email])}
```
### {insert}
В отличии от `{include}` тег `{insert}` не вызывает дочерний шаблон во время отрисовки, в ставляет код дочернего шаблона в родительский на момент компиляции.

View File

@ -1,8 +1,10 @@
<?php
require_once __DIR__.'/../src/Fenom.php';
\Fenom::registerAutoload();
$fenom = Fenom::factory(__DIR__.'/templates', __DIR__.'/compiled', Fenom::AUTO_RELOAD | Fenom::AUTO_ESCAPE);
$fenom->display('greeting.tpl');
$fenom = Fenom::factory(__DIR__.'/templates', __DIR__.'/compiled');
$fenom->setOptions(Fenom::AUTO_RELOAD | Fenom::AUTO_STRIP);
echo($fenom->compile("problem.tpl", false)->getBody());
// $fenom->getTemplate("problem.tpl");

View File

@ -13,11 +13,12 @@ use Fenom\Template;
/**
* Fenom Template Engine
*
*
* @author Ivan Shalganov <a.cobest@gmail.com>
*/
class Fenom
{
const VERSION = '2.0';
const VERSION = '2.4';
/* Actions */
const INLINE_COMPILER = 1;
const BLOCK_COMPILER = 5;
@ -35,8 +36,13 @@ class Fenom
const DISABLE_CACHE = 0x400;
const FORCE_VERIFY = 0x800;
const AUTO_TRIM = 0x1000; // reserved
const DENY_STATICS = 0x2000;
const DENY_PHP_CALLS = 0x2000;
const AUTO_STRIP = 0x4000;
/**
* Use DENY_PHP_CALLS
* @deprecated
*/
const DENY_STATICS = 0x2000;
/* Default parsers */
const DEFAULT_CLOSE_COMPILER = 'Fenom\Compiler::stdClose';
@ -62,6 +68,7 @@ class Fenom
"auto_escape" => self::AUTO_ESCAPE,
"force_verify" => self::FORCE_VERIFY,
"auto_trim" => self::AUTO_TRIM,
"disable_php_calls" => self::DENY_PHP_CALLS,
"disable_statics" => self::DENY_STATICS,
"strip" => self::AUTO_STRIP,
);
@ -81,6 +88,11 @@ class Fenom
*/
public $tag_filters = array();
/**
* @var string[]
*/
public $call_filters = array();
/**
* @var callable[]
*/
@ -340,6 +352,24 @@ class Fenom
'third' => '!(%s %% 3)'
);
protected $_accessors = array(
'get' => 'Fenom\Accessor::getVar',
'env' => 'Fenom\Accessor::getVar',
'post' => 'Fenom\Accessor::getVar',
'request' => 'Fenom\Accessor::getVar',
'cookie' => 'Fenom\Accessor::getVar',
'globals' => 'Fenom\Accessor::getVar',
'server' => 'Fenom\Accessor::getVar',
'session' => 'Fenom\Accessor::getVar',
'files' => 'Fenom\Accessor::getVar',
'tpl' => 'Fenom\Accessor::tpl',
'version' => 'Fenom\Accessor::version',
'const' => 'Fenom\Accessor::constant',
'php' => 'Fenom\Accessor::php',
'tag' => 'Fenom\Accessor::Tag',
'fetch' => 'Fenom\Accessor::Fetch',
);
/**
* Just factory
*
@ -375,6 +405,9 @@ class Fenom
$this->_provider = $provider;
}
public function setCachePerms() {
}
/**
* Set compile directory
*
@ -510,12 +543,7 @@ class Fenom
* @param array $tags
* @return Fenom
*/
public function addBlockCompiler(
$compiler,
$open_parser,
$close_parser = self::DEFAULT_CLOSE_COMPILER,
array $tags = array()
) {
public function addBlockCompiler($compiler, $open_parser, $close_parser = self::DEFAULT_CLOSE_COMPILER, array $tags = array()) {
$this->_actions[$compiler] = array(
'type' => self::BLOCK_COMPILER,
'open' => $open_parser,
@ -602,12 +630,8 @@ class Fenom
* @param callable|string $parser_close
* @return Fenom
*/
public function addBlockFunction(
$function,
$callback,
$parser_open = self::DEFAULT_FUNC_OPEN,
$parser_close = self::DEFAULT_FUNC_CLOSE
) {
public function addBlockFunction($function, $callback, $parser_open = self::DEFAULT_FUNC_OPEN, $parser_close = self::DEFAULT_FUNC_CLOSE)
{
$this->_actions[$function] = array(
'type' => self::BLOCK_FUNCTION,
'open' => $parser_open,
@ -772,6 +796,53 @@ class Fenom
return $this->_options;
}
/**
* Add global accessor ($.)
* @param string $name
* @param callable $parser
* @return Fenom
*/
public function addAccessor($name, $parser)
{
$this->_accessors[$name] = $parser;
return $this;
}
/**
* Remove accessor
* @param string $name
* @return Fenom
*/
public function removeAccessor($name)
{
unset($this->_accessors[$name]);
return $this;
}
/**
* Get an accessor
* @param string $name
* @return callable
*/
public function getAccessor($name) {
if(isset($this->_accessors[$name])) {
return $this->_accessors[$name];
} else {
return false;
}
}
/**
* Add filter for $.php accessor.
* Uses glob syntax.
* @param string $pattern
* @return $this
*/
public function addCallFilter($pattern) {
$this->call_filters[] = $pattern;
return $this;
}
/**
* @param bool|string $scm
* @return Fenom\ProviderInterface
@ -803,7 +874,8 @@ class Fenom
/**
* Execute template and write result into stdout
*
* @param string $template name of template
* @param string|array $template name of template.
* If it is array of names of templates they will be extended from left to right.
* @param array $vars array of data for template
* @return Fenom\Render
*/
@ -814,7 +886,8 @@ class Fenom
/**
*
* @param string $template name of template
* @param string|array $template name of template.
* If it is array of names of templates they will be extended from left to right.
* @param array $vars array of data for template
* @return mixed
*/
@ -826,7 +899,8 @@ class Fenom
/**
* Creates pipe-line of template's data to callback
* @note Method not works correctly in old PHP 5.3.*
* @param string $template name of the template
* @param string|array $template name of the template.
* If it is array of names of templates they will be extended from left to right.
* @param callable $callback template's data handler
* @param array $vars
* @param float $chunk amount of bytes of chunk
@ -969,7 +1043,7 @@ class Fenom
}
/**
* Flush internal memory template cache
* Flush internal template in-memory-cache
*/
public function flush()
{
@ -982,6 +1056,7 @@ class Fenom
public function clearAllCompiles()
{
\Fenom\Provider::clean($this->_compile_dir);
$this->flush();
}
/**

143
src/Fenom/Accessor.php Normal file
View File

@ -0,0 +1,143 @@
<?php
/*
* This file is part of Fenom.
*
* (c) 2013 Ivan Shalganov
*
* For the full copyright and license information, please view the license.md
* file that was distributed with this source code.
*/
namespace Fenom;
use Fenom\Error\UnexpectedTokenException;
/**
* Class Accessor
* @package Fenom
*/
class Accessor {
public static $vars = array(
'get' => '$_GET',
'post' => '$_POST',
'session' => '$_SESSION',
'cookie' => '$_COOKIE',
'request' => '$_REQUEST',
'files' => '$_FILES',
'globals' => '$GLOBALS',
'server' => '$_SERVER',
'env' => '$_ENV'
);
/**
* Accessor for global variables
* @param Tokenizer $tokens
* @param Template $tpl
*/
public static function getVar(Tokenizer $tokens, Template $tpl)
{
$name = $tokens->prev[Tokenizer::TEXT];
if(isset(self::$vars[$name])) {
$var = $tpl->parseVariable($tokens, self::$vars[$name]);
return "(isset($var) ? $var : null)";
} else {
throw new UnexpectedTokenException($tokens->back());
}
}
/**
* Accessor for template information
* @param Tokenizer $tokens
*/
public static function tpl(Tokenizer $tokens)
{
$method = $tokens->skip('.')->need(T_STRING)->getAndNext();
if(method_exists('Fenom\Render', 'get'.$method)) {
return '$tpl->get'.ucfirst($method).'()';
} else {
throw new UnexpectedTokenException($tokens->back());
}
}
public static function version()
{
return 'Fenom::VERSION';
}
/**
* @param Tokenizer $tokens
* @return string
*/
public static function constant(Tokenizer $tokens)
{
$const = array($tokens->skip('.')->need(Tokenizer::MACRO_STRING)->getAndNext());
while($tokens->is('.')) {
$const[] = $tokens->next()->need(Tokenizer::MACRO_STRING)->getAndNext();
}
$const = implode('\\', $const);
if($tokens->is(T_DOUBLE_COLON)) {
$const .= '::'.$tokens->next()->need(Tokenizer::MACRO_STRING)->getAndNext();
}
return '@constant('.var_export($const, true).')';
}
/**
* @param Tokenizer $tokens
* @param Template $tpl
* @return string
*/
public static function php(Tokenizer $tokens, Template $tpl)
{
$callable = array($tokens->skip('.')->need(Tokenizer::MACRO_STRING)->getAndNext());
while($tokens->is('.')) {
$callable[] = $tokens->next()->need(Tokenizer::MACRO_STRING)->getAndNext();
}
$callable = implode('\\', $callable);
if($tokens->is(T_DOUBLE_COLON)) {
$callable .= '::'.$tokens->next()->need(Tokenizer::MACRO_STRING)->getAndNext();
}
$call_filter = $tpl->getStorage()->call_filters;
if($call_filter) {
foreach($call_filter as $filter) {
if(!fnmatch(addslashes($filter), $callable)) {
throw new \LogicException("Callback ".str_replace('\\', '.', $callable)." is not available by settings");
}
}
}
if(!is_callable($callable)) {
throw new \RuntimeException("PHP method ".str_replace('\\', '.', $callable).' does not exists.');
}
if($tokens->is('(')) {
$arguments = 'array'.$tpl->parseArgs($tokens).'';
} else {
$arguments = 'array()';
}
return 'call_user_func_array('.var_export($callable, true).', '.$arguments.')';
}
/**
* Accessor {$.fetch(...)}
* @param Tokenizer $tokens
* @param Template $tpl
* @return string
*/
public static function fetch(Tokenizer $tokens, Template $tpl)
{
$tokens->skip('(');
$name = $tpl->parsePlainArg($tokens, $static);
if($static) {
if(!$tpl->getStorage()->templateExists($static)) {
throw new \RuntimeException("Template $static not found");
}
}
if($tokens->is(',')) {
$tokens->skip()->need('[');
$vars = $tpl->parseArray($tokens) . ' + $var';
} else {
$vars = '$var';
}
$tokens->skip(')');
return '$tpl->getStorage()->fetch('.$name.', '.$vars.')';
}
}

View File

@ -241,7 +241,7 @@ class Compiler
*/
public static function forOpen(Tokenizer $tokens, Tag $scope)
{
$p = array(
$p = array(
"index" => false,
"first" => false,
"last" => false,

View File

@ -9,8 +9,6 @@
*/
namespace Fenom;
use Fenom\ProviderInterface;
/**
* Base template provider
* @author Ivan Shalganov
@ -19,6 +17,8 @@ class Provider implements ProviderInterface
{
private $_path;
protected $_clear_cache = false;
/**
* Clean directory from files
*
@ -75,6 +75,15 @@ class Provider implements ProviderInterface
}
}
/**
* Disable PHP cache for files. PHP cache some operations with files then script works.
* @see http://php.net/manual/en/function.clearstatcache.php
* @param bool $status
*/
public function setClearCachedStats($status = true) {
$this->_clear_cache = $status;
}
/**
* Get source and mtime of template by name
* @param string $tpl
@ -84,7 +93,9 @@ class Provider implements ProviderInterface
public function getSource($tpl, &$time)
{
$tpl = $this->_getTemplatePath($tpl);
clearstatcache(true, $tpl);
if($this->_clear_cache) {
clearstatcache(true, $tpl);
}
$time = filemtime($tpl);
return file_get_contents($tpl);
}
@ -96,7 +107,10 @@ class Provider implements ProviderInterface
*/
public function getLastModified($tpl)
{
clearstatcache(true, $tpl = $this->_getTemplatePath($tpl));
$tpl = $this->_getTemplatePath($tpl);
if($this->_clear_cache) {
clearstatcache(true, $tpl);
}
return filemtime($tpl);
}
@ -158,7 +172,10 @@ class Provider implements ProviderInterface
public function verify(array $templates)
{
foreach ($templates as $template => $mtime) {
clearstatcache(true, $template = $this->_path . '/' . $template);
$template = $this->_path . '/' . $template;
if($this->_clear_cache) {
clearstatcache(true, $template);
}
if (@filemtime($template) !== $mtime) {
return false;
}

View File

@ -90,8 +90,7 @@ class Render extends \ArrayObject
$this->_time = $props["time"];
$this->_depends = $props["depends"];
$this->_macros = $props["macros"];
// $this->_blocks = $props["blocks"];
$this->_code = $code;
$this->_code = $code;
}
/**
@ -249,19 +248,6 @@ class Render extends \ArrayObject
public function __get($name)
{
if ($name == 'info') {
return array(
'name' => $this->_name,
'schema' => $this->_scm,
'time' => $this->_time
);
} else {
return null;
}
}
public function __isset($name)
{
return $name == 'info';
return $this->$name = null;
}
}

View File

@ -268,7 +268,7 @@ class Template extends Render
throw new CompileException("Unclosed tag" . (count($_names) > 1 ? "s" : "") . ": " . implode(
", ",
$_names
), 0, 1, $this->_name, $scope->line); // $scope already defined there!
), 0, 1, $this->_name, $scope->line); // for PHPStorm: $scope already defined there!
}
$this->_src = ""; // cleanup
if ($this->_post) {
@ -343,8 +343,9 @@ class Template extends Render
$text = str_replace("<?", '<?php echo "<?"; ?>' . PHP_EOL, $text);
}
if($this->_options & Fenom::AUTO_STRIP) {
$text = preg_replace('/\s+/uS', ' ', $text);
$text = preg_replace('/\s*([\pP\pS]+)\s*/uS', '$1', $text);
$text = preg_replace('/\s+/uS', ' ', str_replace(array("\r", "\n"), " ", $text));
// $text = preg_replace('/\s*([\pP\pS]+)\s*/uS', '$1', $text);
}
$this->_body .= $text;
}
@ -662,10 +663,6 @@ class Template extends Render
// parse term
$term = $this->parseTerm($tokens, $var); // term of the expression
if ($term !== false) {
if ($this->_options & Fenom::FORCE_VERIFY) {
$term = '(isset(' . $term . ') ? ' . $term . ' : null)';
$var = false;
}
if ($tokens->is('|')) {
$term = $this->parseModifier($tokens, $term);
$var = false;
@ -728,6 +725,10 @@ class Template extends Render
if ($tokens->is(T_LNUMBER, T_DNUMBER)) {
$concat[] = "strval(" . $this->parseTerm($tokens) . ")";
} else {
if($tokens->is('~')) {
$tokens->next();
$concat[] = " ";
}
if(!$concat[] = $this->parseTerm($tokens)) {
throw new UnexpectedTokenException($tokens);
}
@ -775,23 +776,37 @@ class Template extends Render
}
return $this->parseScalar($tokens, true);
} elseif ($tokens->is(T_VARIABLE)) {
$code = $unary . $this->parseVariable($tokens);
$code = $this->parseVariable($tokens);
if ($tokens->is("(") && $tokens->hasBackList(T_STRING, T_OBJECT_OPERATOR)) {
if ($this->_options & Fenom::DENY_METHODS) {
throw new \LogicException("Forbidden to call methods");
}
$code = $this->parseChain($tokens, $code);
return $this->parseChain($tokens, $code);
} elseif ($tokens->is(Tokenizer::MACRO_INCDEC)) {
$code .= $tokens->getAndNext();
if($this->_options & Fenom::FORCE_VERIFY) {
return $unary . '(isset(' . $code . ') ? ' . $code . $tokens->getAndNext() . ' : null)';
} else {
return $unary . $code . $tokens->getAndNext();
}
} else {
$is_var = true;
if($this->_options & Fenom::FORCE_VERIFY) {
return $unary . '(isset(' . $code . ') ? ' . $code . ' : null)';
} else {
$is_var = true;
return $unary . $code;
}
}
return $code;
} elseif ($tokens->is('$')) {
$var = $this->parseAccessor($tokens, $is_var);
$is_var = false;
$var = $this->parseAccessor($tokens);
return $unary . $var;
} elseif ($tokens->is(Tokenizer::MACRO_INCDEC)) {
return $unary . $tokens->getAndNext() . $this->parseVariable($tokens);
if($this->_options & Fenom::FORCE_VERIFY) {
$var = $this->parseVariable($tokens);
return $unary . '(isset(' . $var . ') ? ' . $tokens->getAndNext() . $this->parseVariable($tokens).' : null)';
} else {
return $unary . $tokens->getAndNext() . $this->parseVariable($tokens);
}
} elseif ($tokens->is("(")) {
$tokens->next();
$code = $unary . "(" . $this->parseExpr($tokens) . ")";
@ -913,44 +928,18 @@ class Template extends Render
/**
* Parse accessor
* @param Tokenizer $tokens
* @return string
*/
public function parseAccessor(Tokenizer $tokens, &$is_var)
public function parseAccessor(Tokenizer $tokens)
{
$is_var = false;
$vars = array(
'get' => '$_GET',
'post' => '$_POST',
'session' => '$_SESSION',
'cookie' => '$_COOKIE',
'request' => '$_REQUEST',
'files' => '$_FILES',
'globals' => '$GLOBALS',
'server' => '$_SERVER',
'env' => '$_ENV',
'tpl' => '$tpl->info'
);
if ($this->_options & Fenom::DENY_ACCESSOR) {
throw new \LogicException("Accessor are disabled");
$accessor = $tokens->need('$')->next()->need('.')->next()->current();
$callback = $this->getStorage()->getAccessor($accessor);
if($callback) {
return call_user_func($callback, $tokens->next(), $this);
} else {
throw new \RuntimeException("Unknown accessor '$accessor'");
}
$key = $tokens->need('$')->next()->need('.')->next()->current();
$tokens->next();
if (isset($vars[$key])) {
$is_var = true;
return $this->parseVariable($tokens, $vars[$key]);
}
switch ($key) {
case 'const':
$tokens->need('.')->next();
$var = '@constant(' . var_export($this->parseName($tokens), true) . ')';
break;
case 'version':
$var = '\Fenom::VERSION';
break;
default:
throw new UnexpectedTokenException($tokens->back());
}
return $var;
}
/**

View File

@ -11,17 +11,6 @@ namespace Fenom;
use Fenom\Error\UnexpectedTokenException;
/**
* for PHP <5.4 compatible
*/
defined('T_INSTEADOF') || define('T_INSTEADOF', 341);
defined('T_TRAIT') || define('T_TRAIT', 355);
defined('T_TRAIT_C') || define('T_TRAIT_C', 365);
/**
* for PHP <5.5 compatible
*/
defined('T_YIELD') || define('T_YIELD', 267);
/**
* Each token have structure
* - Token (constant T_* or text)
@ -93,154 +82,62 @@ class Tokenizer
*/
public static $macros = array(
self::MACRO_STRING => array(
\T_ABSTRACT => 1,
\T_ARRAY => 1,
\T_AS => 1,
\T_BREAK => 1,
\T_BREAK => 1,
\T_CASE => 1,
\T_CATCH => 1,
\T_CLASS => 1,
\T_CLASS_C => 1,
\T_CLONE => 1,
\T_CONST => 1,
\T_CONTINUE => 1,
\T_DECLARE => 1,
\T_DEFAULT => 1,
\T_DIR => 1,
\T_DO => 1,
\T_ECHO => 1,
\T_ELSE => 1,
\T_ELSEIF => 1,
\T_EMPTY => 1,
\T_ENDDECLARE => 1,
\T_ENDFOR => 1,
\T_ENDFOREACH => 1,
\T_ENDIF => 1,
\T_ENDSWITCH => 1,
\T_ENDWHILE => 1,
\T_EVAL => 1,
\T_EXIT => 1,
\T_EXTENDS => 1,
\T_FILE => 1,
\T_FINAL => 1,
\T_FOR => 1,
\T_FOREACH => 1,
\T_FUNCTION => 1,
\T_FUNC_C => 1,
\T_GLOBAL => 1,
\T_GOTO => 1,
\T_HALT_COMPILER => 1,
\T_IF => 1,
\T_IMPLEMENTS => 1,
\T_INCLUDE => 1,
\T_INCLUDE_ONCE => 1,
\T_INSTANCEOF => 1,
\T_INSTEADOF => 1,
\T_INTERFACE => 1,
\T_ISSET => 1,
\T_LINE => 1,
\T_LIST => 1,
\T_LOGICAL_AND => 1,
\T_LOGICAL_OR => 1,
\T_LOGICAL_XOR => 1,
\T_METHOD_C => 1,
\T_NAMESPACE => 1,
\T_NS_C => 1,
\T_NEW => 1,
\T_PRINT => 1,
\T_PRIVATE => 1,
\T_PUBLIC => 1,
\T_PROTECTED => 1,
\T_REQUIRE => 1,
\T_REQUIRE_ONCE => 1,
\T_RETURN => 1,
\T_RETURN => 1,
\T_STRING => 1,
\T_SWITCH => 1,
\T_THROW => 1,
\T_TRAIT => 1,
\T_TRAIT_C => 1,
\T_TRY => 1,
\T_UNSET => 1,
\T_USE => 1,
\T_VAR => 1,
\T_WHILE => 1,
\T_YIELD => 1
\T_ABSTRACT => 1, \T_ARRAY => 1, \T_AS => 1, \T_BREAK => 1,
\T_BREAK => 1, \T_CASE => 1, \T_CATCH => 1, \T_CLASS => 1,
\T_CLASS_C => 1, \T_CLONE => 1, \T_CONST => 1, \T_CONTINUE => 1,
\T_DECLARE => 1, \T_DEFAULT => 1, \T_DIR => 1, \T_DO => 1,
\T_ECHO => 1, \T_ELSE => 1, \T_ELSEIF => 1, \T_EMPTY => 1,
\T_ENDDECLARE => 1, \T_ENDFOR => 1, \T_ENDFOREACH => 1, \T_ENDIF => 1,
\T_ENDSWITCH => 1, \T_ENDWHILE => 1, \T_EVAL => 1, \T_EXIT => 1,
\T_EXTENDS => 1, \T_FILE => 1, \T_FINAL => 1, \T_FOR => 1,
\T_FOREACH => 1, \T_FUNCTION => 1, \T_FUNC_C => 1, \T_GLOBAL => 1,
\T_GOTO => 1, \T_HALT_COMPILER => 1, \T_IF => 1, \T_IMPLEMENTS => 1,
\T_INCLUDE => 1, \T_INCLUDE_ONCE => 1, \T_INSTANCEOF => 1, 341 /* T_INSTEADOF */ => 1,
\T_INTERFACE => 1, \T_ISSET => 1, \T_LINE => 1, \T_LIST => 1,
\T_LOGICAL_AND => 1, \T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_METHOD_C => 1,
\T_NAMESPACE => 1, \T_NS_C => 1, \T_NEW => 1, \T_PRINT => 1,
\T_PRIVATE => 1, \T_PUBLIC => 1, \T_PROTECTED => 1, \T_REQUIRE => 1,
\T_REQUIRE_ONCE => 1, \T_RETURN => 1, \T_RETURN => 1, \T_STRING => 1,
\T_SWITCH => 1, \T_THROW => 1, 355 /* T_TRAIT */ => 1, 365 /* T_TRAIT_C */ => 1,
\T_TRY => 1, \T_UNSET => 1, \T_USE => 1, \T_VAR => 1,
\T_WHILE => 1, 267 /* T_YIELD */ => 1
),
self::MACRO_INCDEC => array(
\T_INC => 1,
\T_DEC => 1
\T_INC => 1, \T_DEC => 1
),
self::MACRO_UNARY => array(
"!" => 1,
"~" => 1,
"-" => 1
"!" => 1, "~" => 1, "-" => 1
),
self::MACRO_BINARY => array(
\T_BOOLEAN_AND => 1,
\T_BOOLEAN_OR => 1,
\T_IS_GREATER_OR_EQUAL => 1,
\T_IS_EQUAL => 1,
\T_IS_IDENTICAL => 1,
\T_IS_NOT_EQUAL => 1,
\T_IS_NOT_IDENTICAL => 1,
\T_IS_SMALLER_OR_EQUAL => 1,
\T_LOGICAL_AND => 1,
\T_LOGICAL_OR => 1,
\T_LOGICAL_XOR => 1,
\T_SL => 1,
\T_SR => 1,
"+" => 1,
"-" => 1,
"*" => 1,
"/" => 1,
">" => 1,
"<" => 1,
"^" => 1,
"%" => 1,
\T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1, \T_IS_GREATER_OR_EQUAL => 1,
\T_IS_EQUAL => 1, \T_IS_IDENTICAL => 1, \T_IS_NOT_EQUAL => 1,
\T_IS_NOT_IDENTICAL => 1, \T_IS_SMALLER_OR_EQUAL => 1, \T_LOGICAL_AND => 1,
\T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1, \T_SL => 1,
\T_SR => 1, "+" => 1, "-" => 1,
"*" => 1, "/" => 1, ">" => 1,
"<" => 1, "^" => 1, "%" => 1,
"&" => 1
),
self::MACRO_BOOLEAN => array(
\T_LOGICAL_OR => 1,
\T_LOGICAL_XOR => 1,
\T_BOOLEAN_AND => 1,
\T_BOOLEAN_OR => 1,
\T_LOGICAL_OR => 1, \T_LOGICAL_XOR => 1,
\T_BOOLEAN_AND => 1, \T_BOOLEAN_OR => 1,
\T_LOGICAL_AND => 1
),
self::MACRO_MATH => array(
"+" => 1,
"-" => 1,
"*" => 1,
"/" => 1,
"^" => 1,
"%" => 1,
"&" => 1,
"|" => 1
"+" => 1, "-" => 1, "*" => 1,
"/" => 1, "^" => 1, "%" => 1,
"&" => 1, "|" => 1
),
self::MACRO_COND => array(
\T_IS_EQUAL => 1,
\T_IS_IDENTICAL => 1,
">" => 1,
"<" => 1,
\T_SL => 1,
\T_SR => 1,
\T_IS_NOT_EQUAL => 1,
\T_IS_NOT_IDENTICAL => 1,
\T_IS_SMALLER_OR_EQUAL => 1,
\T_IS_EQUAL => 1, \T_IS_IDENTICAL => 1, ">" => 1,
"<" => 1, \T_SL => 1, \T_SR => 1,
\T_IS_NOT_EQUAL => 1, \T_IS_NOT_IDENTICAL => 1, \T_IS_SMALLER_OR_EQUAL => 1,
),
self::MACRO_EQUALS => array(
\T_AND_EQUAL => 1,
\T_DIV_EQUAL => 1,
\T_MINUS_EQUAL => 1,
\T_MOD_EQUAL => 1,
\T_MUL_EQUAL => 1,
\T_OR_EQUAL => 1,
\T_PLUS_EQUAL => 1,
\T_SL_EQUAL => 1,
\T_SR_EQUAL => 1,
\T_XOR_EQUAL => 1,
'=' => 1,
\T_AND_EQUAL => 1, \T_DIV_EQUAL => 1, \T_MINUS_EQUAL => 1,
\T_MOD_EQUAL => 1, \T_MUL_EQUAL => 1, \T_OR_EQUAL => 1,
\T_PLUS_EQUAL => 1, \T_SL_EQUAL => 1, \T_SR_EQUAL => 1,
\T_XOR_EQUAL => 1, '=' => 1,
),
self::MACRO_SCALAR => array(
\T_LNUMBER => 1,

View File

@ -161,13 +161,13 @@ class TestCase extends \PHPUnit_Framework_TestCase
$this->fail("Code $code must be invalid");
}
public function assertRender($tpl, $result, $debug = false)
public function assertRender($tpl, $result, array $vars = array(), $debug = false)
{
$template = $this->fenom->compileCode($tpl);
if ($debug) {
print_r("\nDEBUG $tpl:\n" . $template->getBody());
}
$this->assertSame($result, $template->fetch($this->values));
$this->assertSame($result, $template->fetch($vars + $this->values));
return $template;
}
@ -276,9 +276,13 @@ class TestCase extends \PHPUnit_Framework_TestCase
}
}
const HELPER_CONSTANT = 'helper.const';
class Helper
{
const CONSTANT = "helper.class.const";
public $word = 'helper';
public function __construct($word)
@ -306,3 +310,7 @@ class Helper
}
}
function helper_func($string, $pad = 10) {
return str_pad($string, $pad, ".");
}

View File

@ -0,0 +1,230 @@
<?php
namespace Fenom;
class AccessorTest extends TestCase
{
public static function providerGetVar()
{
return array(
array("get"),
array("post"),
array("cookie"),
array("request"),
array("files"),
array("globals"),
array("server"),
array("session"),
array("env"),
);
}
/**
* @dataProvider providerGetVar
* @backupGlobals
* @param string $var
*/
public function testGetVar($var)
{
$_GET['one'] = 'get1';
$_POST['one'] = 'post1';
$_COOKIE['one'] = 'cookie1';
$_REQUEST['one'] = 'request1';
$_FILES['one'] = 'files1';
$GLOBALS['one'] = 'globals1';
$_SERVER['one'] = 'server1';
$_SESSION['one'] = 'session1';
$_ENV['one'] = 'env1';
$this->exec('{$.'.$var.'.one}', self::getVars(), "{$var}1");
$this->exec('{$.'.$var.'.undefined}', self::getVars(), "");
}
public static function providerTpl()
{
return array(
array("name"),
array("scm"),
array("basename"),
array("options"),
array("time"),
);
}
/**
* @dataProvider providerTpl
* @param string $name
*/
public function testTpl($name)
{
$this->tpl("accessor.tpl", '{$.tpl.'.$name.'}');
$tpl = $this->fenom->setOptions(\Fenom::FORCE_VERIFY)->getTemplate('accessor.tpl');
$this->assertSame(strval($tpl->{"get$name"}()), $tpl->fetch(self::getVars()));
}
public function testVersion()
{
$this->assertRender('{$.version}', \Fenom::VERSION);
}
public static function providerConst()
{
return array(
array("$.const.PHP_VERSION_ID", PHP_VERSION_ID),
array('$.const.UNDEFINED', ''),
array("$.const.FENOM_RESOURCES", FENOM_RESOURCES),
array("$.const.Fenom.HELPER_CONSTANT", HELPER_CONSTANT),
array("$.const.Fenom.UNDEFINED", ''),
array("$.const.Fenom::VERSION", \Fenom::VERSION),
array("$.const.Fenom::UNDEFINED", ''),
array("$.const.Fenom.Helper::CONSTANT", Helper::CONSTANT),
array("$.const.Fenom.Helper::UNDEFINED", ''),
);
}
/**
* @dataProvider providerConst
* @param $tpl
* @param $value
* @group const
*/
public function testConst($tpl, $value)
{
$this->assertRender('{'.$tpl.'}', strval($value));
}
public static function providerPHP() {
return array(
array('$.php.strrev("string")', strrev("string")),
array('$.php.strrev("string")', strrev("string"), 'str*'),
array('$.php.strrev("string")', strrev("string"), 'strrev'),
array('$.php.get_current_user', get_current_user()),
array('$.php.Fenom.helper_func("string", 12)', helper_func("string", 12)),
array('$.php.Fenom.helper_func("string", 12)', helper_func("string", 12), 'Fenom\\*'),
array('$.php.Fenom.helper_func("string", 12)', helper_func("string", 12), 'Fenom\helper_func'),
array('$.php.Fenom.helper_func("string", 12)', helper_func("string", 12), '*helper_func'),
array('$.php.Fenom.helper_func("string", 12)', helper_func("string", 12), '*'),
array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string")),
array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string"), 'Fenom\*'),
array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string"), 'Fenom\TestCase*'),
array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string"), 'Fenom\TestCase::*'),
array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string"), 'Fenom\*::dots'),
array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string"), 'Fenom\*::*'),
array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string"), 'Fenom\TestCase::dots'),
array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string"), '*::dots'),
array('$.php.Fenom.TestCase::dots("string")', TestCase::dots("string"), '*'),
);
}
/**
* @dataProvider providerPHP
* @group php
*/
public function testPHP($tpl, $result, $mask = null) {
if($mask) {
$this->fenom->addCallFilter($mask);
}
$this->assertRender('{'.$tpl.'}', $result);
}
public static function providerPHPInvalid() {
return array(
array('$.php.aaa("string")', 'Fenom\Error\CompileException', 'PHP method aaa does not exists'),
array('$.php.strrev("string")', 'Fenom\Error\SecurityException', 'Callback strrev is not available by settings', 'strrevZ'),
array('$.php.strrev("string")', 'Fenom\Error\SecurityException', 'Callback strrev is not available by settings', 'str*Z'),
array('$.php.strrev("string")', 'Fenom\Error\SecurityException', 'Callback strrev is not available by settings', '*Z'),
array('$.php.Fenom.aaa("string")', 'Fenom\Error\CompileException', 'PHP method Fenom.aaa does not exists'),
array('$.php.Fenom.helper_func("string")', 'Fenom\Error\SecurityException', 'Callback Fenom.helper_func is not available by settings', 'Reflection\*'),
array('$.php.Fenom.helper_func("string")', 'Fenom\Error\SecurityException', 'Callback Fenom.helper_func is not available by settings', 'Fenom\*Z'),
array('$.php.Fenom.helper_func("string")', 'Fenom\Error\SecurityException', 'Callback Fenom.helper_func is not available by settings', 'Fenom\*::*'),
array('$.php.TestCase::aaa("string")', 'Fenom\Error\CompileException', 'PHP method TestCase::aaa does not exists'),
array('$.php.Fenom.TestCase::aaa("string")', 'Fenom\Error\CompileException', 'PHP method Fenom.TestCase::aaa does not exists'),
array('$.php.Fenom.TestCase::dots("string")', 'Fenom\Error\SecurityException', 'Callback Fenom.TestCase::dots is not available by settings', 'Reflection\*'),
array('$.php.Fenom.TestCase::dots("string")', 'Fenom\Error\SecurityException', 'Callback Fenom.TestCase::dots is not available by settings', 'Fenom\*Z'),
array('$.php.Fenom.TestCase::dots("string")', 'Fenom\Error\SecurityException', 'Callback Fenom.TestCase::dots is not available by settings', 'Fenom\*::get*'),
array('$.php.Fenom.TestCase::dots("string")', 'Fenom\Error\SecurityException', 'Callback Fenom.TestCase::dots is not available by settings', 'Fenom\TestCase::get*'),
array('$.php.Fenom.TestCase::dots("string")', 'Fenom\Error\SecurityException', 'Callback Fenom.TestCase::dots is not available by settings', 'Fenom\TestCase::*Z'),
array('$.php.Fenom.TestCase::dots("string")', 'Fenom\Error\SecurityException', 'Callback Fenom.TestCase::dots is not available by settings', '*::*Z'),
);
}
/**
* @dataProvider providerPHPInvalid
* @group php
*/
public function testPHPInvalid($tpl, $exception, $message, $methods = null) {
if($methods) {
$this->fenom->addCallFilter($methods);
}
$this->execError('{'.$tpl.'}', $exception, $message);
}
public static function providerAccessor()
{
return array(
array('{$.get.one}', 'get1'),
array('{$.post.one}', 'post1'),
array('{$.request.one}', 'request1'),
array('{$.session.one}', 'session1'),
array('{$.files.one}', 'files1'),
array('{$.globals.one}', 'globals1'),
array('{$.cookie.one}', 'cookie1'),
array('{$.server.one}', 'server1'),
array('{"string"|append:"_":$.get.one}', 'string_get1'),
array('{$.get.one?}', '1'),
array('{$.get.one is set}', '1'),
array('{$.get.two is empty}', '1'),
array('{$.version}', \Fenom::VERSION),
array('{$.tpl.name}', 'runtime.tpl'),
array('{$.tpl.time}', '0'),
array('{$.tpl.schema}', ''),
);
}
public static function providerAccessorInvalid()
{
return array(
array('{$.nope.one}', 'Fenom\Error\CompileException', "Unexpected token 'nope'"),
array('{$.get.one}', 'Fenom\Error\SecurityException', 'Accessor are disabled', \Fenom::DENY_ACCESSOR),
);
}
public static function providerFetch()
{
return array(
array('{$.fetch("welcome.tpl")}'),
array('{set $tpl = "welcome.tpl"}{$.fetch($tpl)}'),
array('{$.fetch("welcome.tpl", ["username" => "Bzick", "email" => "bzick@dev.null"])}'),
array('{set $tpl = "welcome.tpl"}{$.fetch($tpl, ["username" => "Bzick", "email" => "bzick@dev.null"])}'),
);
}
/**
* @group fetch
* @dataProvider providerFetch
*/
public function testFetch($code)
{
$this->tpl('welcome.tpl', '<b>Welcome, {$username} ({$email})</b>');
$values = array('username' => 'Bzick', 'email' => 'bzick@dev.null');
$this->assertRender($code, $this->fenom->fetch('welcome.tpl', $values), $values);
}
public static function providerFetchInvalid()
{
return array(
array('{$.fetch("welcome_.tpl")}', 'Fenom\Error\CompileException', "Template welcome_.tpl not found"),
array('{$.fetch("welcome_.tpl", [])}', 'Fenom\Error\CompileException', "Template welcome_.tpl not found"),
);
}
/**
* @group fetchInvalid
* @dataProvider providerFetchInvalid
*/
public function testFetchInvalidTpl($tpl, $exception, $message) {
$this->execError($tpl, $exception, $message);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Fenom;
class SandboxTest extends TestCase {
public function test()
{
// $this->assertEquals([1, 2, 4, "as" => 767, "df" => ["qert"]], [1, 2, 4, "as" => 767, "df" => ["qet"]]);
// $this->fenom->addBlockCompiler('php', 'Fenom\Compiler::nope', function ($tokens, Tag $tag) {
// return '<?php ' . $tag->cutContent();
// });
// $this->tpl('welcome.tpl', '{$a}');
// try {
// var_dump($this->fenom->compileCode('{$.fetch("welcome.tpl", ["a" => 1])}')->getBody());
// } catch (\Exception $e) {
// print_r($e->getMessage() . "\n" . $e->getTraceAsString());
// while ($e->getPrevious()) {
// $e = $e->getPrevious();
// print_r("\n\n" . $e->getMessage() . " in {$e->getFile()}:{$e->getLine()}\n" . $e->getTraceAsString());
// }
// }
// exit;
}
}

View File

@ -17,15 +17,7 @@ class TemplateTest extends TestCase
{
parent::setUp();
$this->tpl('welcome.tpl', '<b>Welcome, {$username} ({$email})</b>');
$_GET['one'] = 'get1';
$_POST['one'] = 'post1';
$_REQUEST['one'] = 'request1';
$_FILES['one'] = 'files1';
$_SERVER['one'] = 'server1';
$_SESSION['one'] = 'session1';
$GLOBALS['one'] = 'globals1';
$_ENV['one'] = 'env1';
$_COOKIE['one'] = 'cookie1';
}
public static function providerVars()
@ -441,6 +433,7 @@ class TemplateTest extends TestCase
'if: block1 end'
),
array('if: {if $unexist} block1 {else} block2 {/if} end', $a, 'if: block2 end', Fenom::FORCE_VERIFY),
array('if: {if !$unexist} block1 {else} block2 {/if} end', $a, 'if: block1 end', Fenom::FORCE_VERIFY),
);
}
@ -1217,40 +1210,6 @@ class TemplateTest extends TestCase
);
}
public static function providerAccessor()
{
return array(
array('{$.get.one}', 'get1'),
array('{$.post.one}', 'post1'),
array('{$.request.one}', 'request1'),
array('{$.session.one}', 'session1'),
array('{$.files.one}', 'files1'),
array('{$.globals.one}', 'globals1'),
array('{$.cookie.one}', 'cookie1'),
array('{$.server.one}', 'server1'),
array('{$.const.PHP_EOL}', PHP_EOL),
array('{$.const.MY}', ''),
array('{$.version}', Fenom::VERSION),
array('{"string"|append:"_":$.get.one}', 'string_get1'),
array('{$.get.one?}', '1'),
array('{$.get.one is set}', '1'),
array('{$.get.two is empty}', '1'),
array('{$.version}', Fenom::VERSION),
array('{$.tpl?}', '1'),
array('{$.tpl.name}', 'runtime.tpl'),
array('{$.tpl.time}', '0'),
array('{$.tpl.schema}', ''),
);
}
public static function providerAccessorInvalid()
{
return array(
array('{$.nope.one}', 'Fenom\Error\CompileException', "Unexpected token 'nope'"),
array('{$.get.one}', 'Fenom\Error\SecurityException', 'Accessor are disabled', Fenom::DENY_ACCESSOR),
);
}
public function providerStatic()
{
return array(
@ -1622,19 +1581,19 @@ class TemplateTest extends TestCase
* @group accessor
* @dataProvider providerAccessor
*/
public function testAccessor($code, $result)
{
$this->exec($code, self::getVars(), $result);
}
// public function testAccessor($code, $result)
// {
// $this->exec($code, self::getVars(), $result);
// }
/**
* @group accessor
* @dataProvider providerAccessorInvalid
*/
public function testAccessorInvalid($code, $exception, $message, $options = 0)
{
$this->execError($code, $exception, $message, $options);
}
// public function testAccessorInvalid($code, $exception, $message, $options = 0)
// {
// $this->execError($code, $exception, $message, $options);
// }
/**
* @group static

View File

@ -96,6 +96,7 @@ class FenomTest extends \Fenom\TestCase
public function testCheckMTime()
{
$this->fenom->setOptions(Fenom::FORCE_COMPILE);
$this->fenom->getProvider()->setClearCachedStats();
$this->tpl('custom.tpl', 'Custom template');
$this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array()));
$tpl = $this->fenom->getTemplate('custom.tpl');
@ -324,7 +325,7 @@ class FenomTest extends \Fenom\TestCase
<a href="/item/{\$one}">number {\$num.1}</a>
</div>
TPL;
$this->assertRender($tpl, '<div class="item item-one"><a href="/item/1">number one</a></div>');
$this->assertRender($tpl, '<div class="item item-one"> <a href="/item/1">number one</a> </div>');
}
}