fenom/docs/ru/ext/extend.md

309 lines
16 KiB
Markdown
Raw Permalink Normal View History

2014-08-08 15:58:50 +04:00
Расширение Fenom
================
2014-08-01 12:12:19 +04:00
2014-08-08 15:58:50 +04:00
# Добавление тегов
2014-08-01 12:12:19 +04:00
В шаблонизаторе принято различать два типа тегов: омпиляторы_ и ункции_.
2015-03-08 09:17:14 +03:00
Компиляторы вызываются во время преобразования кода шаблона в PHP код и возвращяют PHP код который будет вставлен вместо тега.
2014-08-01 12:12:19 +04:00
А функции вызываются непременно в момент выполнения шаблона и возвращают непосредственно данные которые будут отображены.
2020-04-12 04:11:04 +03:00
Среди тегов, как и в HTML, есть строчные и блоковые теги.
2014-08-01 12:12:19 +04:00
2014-08-08 15:58:50 +04:00
## Линейные функции
2014-08-01 12:12:19 +04:00
2020-04-12 04:11:04 +03:00
Добавление функции можно осуществить следующим образом:
2014-08-01 12:12:19 +04:00
```php
$fenom->addFunction(string $function_name, callable $callback[, callable $parser]);
```
2020-04-12 04:11:04 +03:00
В данном случае запускается стандартный парсер, который автоматически разберет аргументы тега (которые должны быть в формате HTML атрибутов) и отдаст их в функцию ассоциативным массивом:
2014-08-01 12:12:19 +04:00
```php
$fenom->addFunction("some_function", function (array $params) { /* ... */ });
```
2020-04-12 04:11:04 +03:00
При необходимости можно переопределить парсер:
2014-08-01 12:12:19 +04:00
```php
$fenom->addFunction("some_function", $some_function, function (Fenom\Tokenizer $tokenizer, Fenom\Template $template) { /* parse tag */});
```
2020-04-12 04:11:04 +03:00
2014-08-01 12:12:19 +04:00
Существует более простой способ добавления произвольной функции:
```php
2020-04-12 04:11:04 +03:00
$fenom->addFunctionSmart(string $function_name, callable $callback);
2014-08-01 12:12:19 +04:00
```
В данном случае парсер сканирует список аргументов коллбека и попробует сопоставить с аргументами тега.
```php
2020-04-12 04:11:04 +03:00
class XYCalcs {
public static function calc($x, $y = 5) {
return $x + $y;
}
}
2014-08-01 12:12:19 +04:00
// ...
$fenom->addFunctionSmart('calc', 'XYCalcs::calc');
```
2020-04-12 04:11:04 +03:00
пример выше позволяет объявить тег `{calc}` и использовать его:
2014-08-01 12:12:19 +04:00
```smarty
2014-08-23 12:29:25 +04:00
{calc x=$top y=50} или {calc y=50 x=$top} вызовет XYCalcs::calc($top, 50)
{calc x=$top} или {calc $top} вызовет XYCalcs::calc($top)
2014-08-01 12:12:19 +04:00
```
2020-04-12 04:11:04 +03:00
Таким образом Вы легко можете добавлять свои функции или методы.
2014-08-01 12:12:19 +04:00
2014-08-08 15:58:50 +04:00
## Блоковые функции
2014-08-01 12:12:19 +04:00
2020-04-12 04:11:04 +03:00
Добавление блоковой функции аналогично добавлению строковой, за исключением того, что есть возможность указать парсер для закрывающего тега.
2014-08-01 12:12:19 +04:00
```php
$fenom->addBlockFunction(string $function_name, callable $callback[, callable $parser_open[, callable $parser_close]]);
```
Сам коллбек принимает первым аргументом контент между открывающим и закрывающим тегом, а вторым аргументом - ассоциативный массив из аргуметов тега:
2020-04-12 04:11:04 +03:00
2014-08-01 12:12:19 +04:00
```php
2020-04-12 04:11:04 +03:00
$fenom->addBlockFunction('some_block_function', function ($content, array $params) { /* ... */ });
2014-08-01 12:12:19 +04:00
```
2014-08-08 15:58:50 +04:00
## Линейный компилятор
2014-08-01 12:12:19 +04:00
2020-04-12 04:11:04 +03:00
Добавление строчного компилятора осуществляется очень просто:
2014-08-01 12:12:19 +04:00
```php
$fenom->addCompiler(string $compiler, callable $parser);
```
Парсер должен принимать `Fenom\Tokenizer $tokenizer`, `Fenom\Template $template` и возвращать PHP код.
Компилятор так же можно импортировать из класса автоматически
```php
$fenom->addCompilerSmart(string $compiler, $storage);
```
2020-04-12 04:11:04 +03:00
`$storage` может быть как именем класса, так и объектом. В данном случае шаблонизатор будет искать метод `tag{$compiler}` и взят в качестве парсера тега.
2014-08-01 12:12:19 +04:00
2014-08-08 15:58:50 +04:00
## Блоковый компилятор
2014-08-01 12:12:19 +04:00
2020-04-12 04:11:04 +03:00
Добавление блочного компилятора осуществяется двумя способами. Первый:
2014-08-01 12:12:19 +04:00
```php
$fenom->addBlockCompiler(string $compiler, array $parsers, array $tags);
```
где `$parser` ассоциативный массив `["open" => parser, "close" => parser]`, сождержащий парсер на открывающий и на закрывающий тег, а `$tags` содержит список внутренних тегов в формате `["tag_name"] => parser`, которые могут быть использованы только с этим компилятором.
2020-04-12 04:11:04 +03:00
2014-08-01 12:12:19 +04:00
Второй способ добавления парсера через импортирование из класса или объекта методов:
```php
$fenom->addBlockCompilerSmart(string $compiler, $storage, array $tags, array $floats);
```
2014-08-08 15:58:50 +04:00
# Добавление модификаторов
2014-08-01 12:12:19 +04:00
```
$fenom->addModifier(string $modifier, callable $callback);
```
* `$modifier` - название модификатора, которое будет использоваться в шаблоне
* `$callback` - коллбек, который будет вызван для изменения данных
Например:
2014-08-01 12:12:19 +04:00
```smarty
{$variable|my_modifier:$param1:$param2}
```
```php
$fenom->addModifier('my_modifier', function ($variable, $param1, $param2) {
2020-04-12 04:11:04 +03:00
// изменение $variable
return $variable;
2014-08-01 12:12:19 +04:00
});
```
2015-03-08 09:17:14 +03:00
# Расширение тестового оператора
2014-08-01 12:12:19 +04:00
```php
2014-11-05 16:07:50 +03:00
$fenom->addTest(string $name, string $code);
2014-08-01 12:12:19 +04:00
```
2020-04-12 04:11:04 +03:00
`$code` - PHP код для условия с маркером для замены на значение или переменную.
2014-11-05 16:07:50 +03:00
Например, тест на целое число `is int` можно добавить как `$fenom->addTest('int', 'is_int(%s)')`.
2020-04-12 04:11:04 +03:00
В шаблоне тест выглядит как `{$a is int}`, а после компиляции - `is_int($a)`.
2014-08-01 12:12:19 +04:00
2015-08-13 14:55:00 +03:00
# Расширение глобальной переменной
2014-10-31 00:24:56 +03:00
2015-08-13 14:55:00 +03:00
Fenom обладает определенным [набором глобальных переменных](../syntax.md#Системная-переменная).
2020-04-12 04:11:04 +03:00
Однако, их может не хватать для удобной работы и в этом случае потребуется добавить свои или переопределить/удалить существующие.
2015-08-13 14:55:00 +03:00
Метод `Fenom::addAccessor(string $name, callable $parser)` позволяет добавить свой обработчик-парсер `$parser`,
который будет вызван при встрече с глобальной переменной `$name` **во время компиляции шаблона**.
2014-10-31 00:24:56 +03:00
```php
2020-04-12 04:11:04 +03:00
$fenom->addAccessor('project', function (Fenom\Tokenizer $tokens) { /* ... */ });
2014-10-31 00:24:56 +03:00
```
2015-08-13 14:55:00 +03:00
Указанный вторым аргументом, парсер будет вызван при встречи компилятором конструкции `$.project`.
Парсер сам должен разобрать все токены из набора токенов `$tokens` до того момента пока не посчитает что ему их хватит для
интерпретации. Возвращает парсер PHP код, который должен представлять значение восле выполенения, то есть его можно втавить в `if()`.
2014-10-31 00:24:56 +03:00
2015-08-13 14:55:00 +03:00
Через метод `Fenom::addAccessor($name, $parser)` можно переопределить уже любую другую существующую глобальную переменную.
2014-10-31 00:24:56 +03:00
Метод `Fenom::removeAccessor($name)` позволяет удалить любую определенную глобальную переменную или функцию по ее имени.
2014-08-01 12:12:19 +04:00
2015-08-13 14:55:00 +03:00
## Готовые решения
Орпеделить парсер для глобальной переменной весьма трудозатратно и требует полного понимания как работают парсеры в Fenom.
2016-09-16 09:16:30 +03:00
Это не удобно. Поэтому есть несколько предзаготовленных (умных) парсеров, которые берут рутину на себя, а пользователю остается указать ключевые параметры.
2015-08-13 14:55:00 +03:00
Умные парсеты добавляются через метод `Fenom::addAccessorSmart(string $name, string $accessor, string $parser)`,
где `$name` имя глобальной переменной, `$accessor` — параметр к парсеру, `$parser` — предопределенный парсер.
### Доступ к свойству
Парсер `Fenom::ACCESSOR_PROPERTY` позволит обратится к указанному свойству шаблонизатора из шаблона.
Параметр `$accessor` выступает как **имя свойства**:
```php
$fenom->addAccessorSmart("site", "data", Fenom::ACCESSOR_PROPERTY);
$fenom->data = [
"domain" => 'example.ru',
"support" => 'support@example.ru'
];
```
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
В шаблоне появится глобальная переменная `$.site`:
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
```smarty
<div class="copyright">© <a href="//{$.site.domain}">{$.site.domain}</a></div>
<div class="support">Support <a href="mailto:{$.site.support}">{$.site.support}</a></div>
```
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
Свойством может быть любое значение — масиив, объект и т.д.
### Доступ к методу
Парсер `Fenom::ACCESSOR_METHOD` позволит обратится к указанному методу шаблонизатора из шаблона.
Параметр `$accessor` выступает как **имя метода**:
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
```php
$fenom->addAccessorSmart("fetch", "fetch", Fenom::ACCESSOR_METHOD);
```
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
В шаблоне появится глобальная функция `$.fetch`:
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
```smarty
{set $menu = $.fetch("site/menu.tpl")} {* $menu = $fenom->fetch("site/menu.tpl") *}
```
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
Шаблонизатор не проверят количество и тип параметров которые передает в метод.
### Доступ к значению
Парсер `Fenom::ACCESSOR_VAR` позволит обратится к указанному значению из шаблона.
Параметр `$accessor` выступает как **PHP выражение**, описывающее значение:
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
```php
$fenom->addAccessorSmart("storage", "App::getInstance()->storage", Fenom::ACCESSOR_VAR);
```
В шаблоне появится глобальная переменная `$.storage`:
```smarty
{set $st = $.storage.di.stamp} {* $st = App::getInstance()->storage['di']['stamp'] *}
```
### Доступ к callable
Парсер `Fenom::ACCESSOR_CALL` позволит вызвать указанную финкцию или метод из шаблона.
Параметр `$accessor` выступает как **PHP выражение**, описывающее название функции или метод:
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
```php
$fenom->addAccessorSmart("di", "App::getInstance()->di->get", Fenom::ACCESSOR_CALL);
```
2020-04-12 04:11:04 +03:00
`App::getInstance()->di->get` должно быть callable, то есть:
2015-08-13 14:55:00 +03:00
```php
is_callable([App::getInstance()->di, "get"]) === true;
```
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
В шаблоне появится глобальная переменная `$.di`:
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
```smarty
{set $st = $.di("stamp")} {* $st = App::getInstance()->di->get("stamp") *}
```
2020-04-12 04:11:04 +03:00
2015-08-13 14:55:00 +03:00
Шаблонизатор не проверят количество и тип параметров которые передает в метод или функцию.
2014-08-08 15:58:50 +04:00
# Источники шаблонов
2014-08-01 12:12:19 +04:00
2014-08-08 15:58:50 +04:00
Шаблоны можно получать из самых разных источников.
Когда вы отображаете или вызываете шаблон, либо когда вы подключаете один шаблон к другому, вы указываете источник,
вместе с соответствующим путём и названием шаблона. Если источник явно не задан, то используется источник `Fenom\Provider`,
который считывает шаблоны из указанной директории.
2014-08-01 12:12:19 +04:00
2014-08-08 15:58:50 +04:00
Источник шаблонов должен реализовать интерфейс `Fenom\ProviderInterface`.
2014-08-23 12:29:25 +04:00
Используйте метод `$fenom->setProvider(...)` что бы добавить источник в шаблонизатор, указав название источника и, если есть необходимость,
задать директорию кеша для шаблонов из этого источника. Рассмотрим на примере, реализуем источник шаблонов из базы данных.
2014-08-08 15:58:50 +04:00
Создадим источник:
```php
class DbProvider implements Fenom\ProviderInterface {
// ...
}
```
Добавляем источник, указав удобное имя.
```php
$provider = new DbProvider();
$fenom->setProvider("db", $provider, "/tmp/cached/db");
```
2020-04-12 04:11:04 +03:00
Теперь источник можно использовать:
2014-08-08 15:58:50 +04:00
```php
$fenom->display("db:index.tpl", $vars);
```
```smarty
{include "db:menu.tpl"}
```
# Расширение кеша (эксперементальное)
2014-08-01 12:12:19 +04:00
2014-08-23 12:29:25 +04:00
Изначально Fenom не рассчитывался на то что кеш скомпиленых шаблонов может располагаться не на файловой системе.
2014-08-01 12:12:19 +04:00
Однако, в теории, есть возможность реализовать свое кеширование для скомпиленых шаблонов без переопределения шаблонизатора.
Речь идет о своем протоколе, отличным от `file://`, который [можно определить](http://php.net/manual/en/class.streamwrapper.php) в PHP.
2014-08-23 12:29:25 +04:00
Ваш протокол должен иметь класс реализации как указано в документации [Stream Wrapper](http://www.php.net/manual/en/class.streamwrapper.php).
2014-08-01 12:12:19 +04:00
Класс протокола может иметь не все указанные в документации методы. Вот список методов, необходимых шаблонизатору:
* [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)
2014-08-23 12:29:25 +04:00
Для работы через `include`:
2014-08-01 12:12:19 +04:00
* [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**
2014-08-23 12:29:25 +04:00
(On 2014-05-13) Zend OpCacher кроме `file://` и `phar://` не поддерживает другие протоколы.
2014-08-01 12:12:19 +04:00
2020-04-12 04:11:04 +03:00
Пример работы кеша:
2014-08-01 12:12:19 +04:00
```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");`