Расширение Fenom
================

# Добавление тегов

В шаблонизаторе принято различать два типа тегов: _компиляторы_ и _функции_.
Компиляторы вызываются во время преобразования кода шаблона в PHP код и возвращяю PHP код который будет вставлен вместо тега.
А функции вызываются непременно в момент выполнения шаблона и возвращают непосредственно данные которые будут отображены.
Среди тегов как и в HTML есть строчные и блоковые теги.

## Линейные функции

Примитивное добавление функции можно осуществить следующим образом:

```php
$fenom->addFunction(string $function_name, callable $callback[, callable $parser]);
```

В данном случае запускается стандартный парсер, который автоматически разберет аргументы тега, которые должны быть в формате HTML атрибутов и отдаст их в функцию ассоциативным массивом:
```php
$fenom->addFunction("some_function", function (array $params) { /* ... */ });
```
При необходимости можно переопределить парсер на произвольный:
```php
$fenom->addFunction("some_function", $some_function, function (Fenom\Tokenizer $tokenizer, Fenom\Template $template) { /* parse tag */});
```
Существует более простой способ добавления произвольной функции:

```php
$fenom->addFunctionSmarty(string $function_name, callable $callback);
```

В данном случае парсер сканирует список аргументов коллбека и попробует сопоставить с аргументами тега.

```php
// ... class XYCalcs ..
public static function calc($x, $y = 5) { /* ... */}
// ...
$fenom->addFunctionSmart('calc', 'XYCalcs::calc');
```
пример выше позволяет объявить тег `{calc}` и спользовать его:
```smarty
{calc x=$top y=50} или {calc y=50 x=$top} вызовет XYCalcs::calc($top, 50)
{calc x=$top} или {calc $top} вызовет XYCalcs::calc($top)
```
Таким образом вы успешно можете добавлять Ваши функции или методы.

## Блоковые функции

Добавление блоковой функции аналогичен добавлению строковой за исключением того что есть возможность указать парсер для закрывающего тега.

```php
$fenom->addBlockFunction(string $function_name, callable $callback[, callable $parser_open[, callable $parser_close]]);
```

Сам коллбек принимает первым аргументом контент между открывающим и закрывающим тегом, а вторым аргументом - ассоциативный массив из аргуметов тега:
```php
$fenom->addBlockFunction('some_block_function', function (array $params, $content) { /* ... */});
```

## Линейный компилятор

Добавление строчного компилятора осуществляеться очень просто:

```php
$fenom->addCompiler(string $compiler, callable $parser);
```

Парсер должен принимать `Fenom\Tokenizer $tokenizer`, `Fenom\Template $template` и возвращать PHP код.
Компилятор так же можно импортировать из класса автоматически

```php
$fenom->addCompilerSmart(string $compiler, $storage);
```

`$storage` может быть как классом так и объектом. В данном случае шаблонизатор будет искать метод `tag{$compiler}`, который будет взят в качестве парсера тега.

## Блоковый компилятор

Добавление блочного компилятора осуществяется двумя способами. Первый

```php
$fenom->addBlockCompiler(string $compiler, array $parsers, array $tags);
```

где `$parser` ассоциативный массив `["open" => parser, "close" => parser]`, сождержащий парсер на открывающий и на закрывающий тег, а `$tags` содержит список внутренних тегов в формате `["tag_name"] => parser`, которые могут быть использованы только с этим компилятором.
Второй способ добавления парсера через импортирование из класса или объекта методов:

```php
$fenom->addBlockCompilerSmart(string $compiler, $storage, array $tags, array $floats);
```

# Добавление модификаторов

```
$fenom->addModifier(string $modifier, callable $callback);
```

* `$modifier` - название модификатора, которое будет использоваться в шаблоне
* `$callback` - коллбек, который будет вызван для изменения данных

Например:

```smarty
{$variable|my_modifier:$param1:$param2}
```

```php
$fenom->addModifier('my_modifier', function ($variable, $param1, $param2) {
    // ...
});
```

# Расширение тестовго оператора

```php
$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)` позволяет удалить любую определенную глобальную переменную или функцию по ее имени.

# Источники шаблонов

Шаблоны можно получать из самых разных источников.
Когда вы отображаете или вызываете шаблон, либо когда вы подключаете один шаблон к другому, вы указываете источник,
вместе с соответствующим путём и названием шаблона. Если источник явно не задан, то используется источник `Fenom\Provider`,
который считывает шаблоны из указанной директории.

Источник шаблонов должен реализовать интерфейс `Fenom\ProviderInterface`.
Используйте метод `$fenom->setProvider(...)`  что бы добавить источник в шаблонизатор, указав название источника и, если есть необходимость,
задать директорию кеша для шаблонов из этого источника. Рассмотрим на примере, реализуем источник шаблонов из базы данных.

Создадим источник:

```php

class DbProvider implements Fenom\ProviderInterface {
    // ...
}

```

Добавляем источник, указав удобное имя.

```php

$provider = new DbProvider();
$fenom->setProvider("db", $provider, "/tmp/cached/db");
```

Теперь источник можно использовать.

```php
$fenom->display("db:index.tpl", $vars);
```

```smarty
{include "db:menu.tpl"}
```

# Расширение кеша (эксперементальное)

Изначально Fenom не рассчитывался на то что кеш скомпиленых шаблонов может располагаться не на файловой системе.
Однако, в теории, есть возможность реализовать свое кеширование для скомпиленых шаблонов без переопределения шаблонизатора.
Речь идет о своем протоколе, отличным от `file://`, который [можно определить](http://php.net/manual/en/class.streamwrapper.php) в PHP.

Ваш протокол должен иметь класс реализации как указано в документации [Stream Wrapper](http://www.php.net/manual/en/class.streamwrapper.php).
Класс протокола может иметь не все указанные в документации методы. Вот список методов, необходимых шаблонизатору:

* [CacheStreamWrapper::stream_open](http://www.php.net/manual/en/streamwrapper.stream-open.php)
* [CacheStreamWrapper::stream_write](http://www.php.net/manual/en/streamwrapper.stream-write.php)
* [CacheStreamWrapper::stream_close](http://www.php.net/manual/en/streamwrapper.stream-close.php)
* [CacheStreamWrapper::rename](http://www.php.net/manual/en/streamwrapper.rename.php)

Для работы через `include`:

* [CacheStreamWrapper::stream_stat](http://www.php.net/manual/en/streamwrapper.stream-stat.php)
* [CacheStreamWrapper::stream_read](http://www.php.net/manual/en/streamwrapper.stream-read.php)
* [CacheStreamWrapper::stream_eof](http://www.php.net/manual/en/streamwrapper.stream-eof.php)

**Note**
(On 2014-05-13) Zend OpCacher кроме `file://` и `phar://` не поддерживает другие протоколы.

Пример работы кеша

```php
$this->setCacheDir("redis://hash/compiled/");
```

* `$cache = fopen("redis://hash/compiled/XnsbfeDnrd.php", "w");`
* `fwrite($cache, "... <template content> ...");`
* `fclose($cache);`
* `rename("redis://hash/compiled/XnsbfeDnrd.php", "redis://hash/compiled/main.php");`