mirror of
https://github.com/fenom-template/fenom.git
synced 2023-08-10 21:13:07 +03:00
commit
e2a3087dfa
@ -120,22 +120,93 @@ $fenom->addTest(string $name, string $code);
|
|||||||
Например, тест на целое число `is int` можно добавить как `$fenom->addTest('int', 'is_int(%s)')`.
|
Например, тест на целое число `is int` можно добавить как `$fenom->addTest('int', 'is_int(%s)')`.
|
||||||
В шаблоне тесты выглядит как `{$a is int}`, а после компиляции выглядит приблизительно так - `is_int($a)`.
|
В шаблоне тесты выглядит как `{$a is int}`, а после компиляции выглядит приблизительно так - `is_int($a)`.
|
||||||
|
|
||||||
# Расширение глобальной переменной или функции
|
# Расширение глобальной переменной
|
||||||
|
|
||||||
Fenom обладает определенным набором глобальных переменных и функций. Однако их может не хватать для удобной работы с шаблонами.
|
Fenom обладает определенным [набором глобальных переменных](../syntax.md#Системная-переменная).
|
||||||
В этом случае потребуется добавить, переопределить или удалить существующие глобальные переменные или функции.
|
Однако их может не хватать для удобной работы и в этом случае потребуется добавить свои или переопределить/удалить существующие.
|
||||||
Метод `Fenom::addAccessor(string $name, callable $parser)` позволяет добавить свой обработчик на не известную глобальную переменную или функцию.
|
Метод `Fenom::addAccessor(string $name, callable $parser)` позволяет добавить свой обработчик-парсер `$parser`,
|
||||||
|
который будет вызван при встрече с глобальной переменной `$name` **во время компиляции шаблона**.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$fenom->addAccessor('project', function (Fenom\Tokenizer $tokens) { /* code */ });
|
$fenom->addAccessor('project', function (Fenom\Tokenizer $tokens) { /* code */ });
|
||||||
```
|
```
|
||||||
|
|
||||||
Указанный вторым аргументом, парсер будет вызван при встречи компилятором конструкции `{$.project}`.
|
Указанный вторым аргументом, парсер будет вызван при встречи компилятором конструкции `$.project`.
|
||||||
Парсеры вызываются только на момент сборки шаблона, а не во время его выполенения.
|
Парсер сам должен разобрать все токены из набора токенов `$tokens` до того момента пока не посчитает что ему их хватит для
|
||||||
|
интерпретации. Возвращает парсер PHP код, который должен представлять значение восле выполенения, то есть его можно втавить в `if()`.
|
||||||
|
|
||||||
Через метод `Fenom::addAccessor($name, $parser)` можно переопределить уже любую другую существующую глобальную переменную или функцию.
|
Через метод `Fenom::addAccessor($name, $parser)` можно переопределить уже любую другую существующую глобальную переменную.
|
||||||
Метод `Fenom::removeAccessor($name)` позволяет удалить любую определенную глобальную переменную или функцию по ее имени.
|
Метод `Fenom::removeAccessor($name)` позволяет удалить любую определенную глобальную переменную или функцию по ее имени.
|
||||||
|
|
||||||
|
## Готовые решения
|
||||||
|
|
||||||
|
Орпеделить парсер для глобальной переменной весьма трудозатратно и требует полного понимания как работают парсеры в Fenom.
|
||||||
|
Это не удобно. Поэтому есть несколько предзаготовленных (умных) парсеоров, которые берут рутину на себя, а пользоватею остается указать ключевые параметры.
|
||||||
|
|
||||||
|
Умные парсеты добавляются через метод `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'
|
||||||
|
];
|
||||||
|
```
|
||||||
|
В шаблоне появится глобальная переменная `$.site`:
|
||||||
|
```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>
|
||||||
|
```
|
||||||
|
Свойством может быть любое значение — масиив, объект и т.д.
|
||||||
|
|
||||||
|
### Доступ к методу
|
||||||
|
|
||||||
|
Парсер `Fenom::ACCESSOR_METHOD` позволит обратится к указанному методу шаблонизатора из шаблона.
|
||||||
|
Параметр `$accessor` выступает как **имя метода**:
|
||||||
|
```php
|
||||||
|
$fenom->addAccessorSmart("fetch", "fetch", Fenom::ACCESSOR_METHOD);
|
||||||
|
```
|
||||||
|
В шаблоне появится глобальная функция `$.fetch`:
|
||||||
|
```smarty
|
||||||
|
{set $menu = $.fetch("site/menu.tpl")} {* $menu = $fenom->fetch("site/menu.tpl") *}
|
||||||
|
```
|
||||||
|
Шаблонизатор не проверят количество и тип параметров которые передает в метод.
|
||||||
|
|
||||||
|
### Доступ к значению
|
||||||
|
|
||||||
|
Парсер `Fenom::ACCESSOR_VAR` позволит обратится к указанному значению из шаблона.
|
||||||
|
Параметр `$accessor` выступает как **PHP выражение**, описывающее значение:
|
||||||
|
```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 выражение**, описывающее название функции или метод:
|
||||||
|
```php
|
||||||
|
$fenom->addAccessorSmart("di", "App::getInstance()->di->get", Fenom::ACCESSOR_CALL);
|
||||||
|
```
|
||||||
|
`App::getInstance()->di->get` доллжно быть callable, то есть
|
||||||
|
```php
|
||||||
|
is_callable([App::getInstance()->di, "get"]) === true;
|
||||||
|
```
|
||||||
|
В шаблоне появится глобальная переменная `$.di`:
|
||||||
|
```smarty
|
||||||
|
{set $st = $.di("stamp")} {* $st = App::getInstance()->di->get("stamp") *}
|
||||||
|
```
|
||||||
|
Шаблонизатор не проверят количество и тип параметров которые передает в метод или функцию.
|
||||||
|
|
||||||
# Источники шаблонов
|
# Источники шаблонов
|
||||||
|
|
||||||
Шаблоны можно получать из самых разных источников.
|
Шаблоны можно получать из самых разных источников.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Документация
|
Документация
|
||||||
=============
|
=============
|
||||||
|
|
||||||
<img style="float:right" src="http://aco.oml.ru/thumb/2Tdrgd9_ttbaBvqKcsSKIA/100r100/188321/gif-%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-grammar-nazi-412594.jpg" alt="grammar nazi required">
|
<img style="float:right" src="https://ficbook.net/images/user_avatars/avatar_%D0%93%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%D1%80-%D0%9D%D0%B0%D1%86%D0%B8_1382414316.jpg" alt="grammar nazi required">
|
||||||
|
|
||||||
**Внимание! Документация в режиме беты, тексты могут содержать опечатки**
|
**Внимание! Документация в режиме беты, тексты могут содержать опечатки**
|
||||||
|
|
||||||
|
@ -102,10 +102,7 @@
|
|||||||
если такой констатнты нет будет взята константа `Storage\FS\DIR_SEPARATOR`.
|
если такой констатнты нет будет взята константа `Storage\FS\DIR_SEPARATOR`.
|
||||||
* `$.php` обращение к статическомому методу. `$.php.Storage.FS::put($filename, $data)` обращение к методу `Storage\FS::put($filename, $data)`.
|
* `$.php` обращение к статическомому методу. `$.php.Storage.FS::put($filename, $data)` обращение к методу `Storage\FS::put($filename, $data)`.
|
||||||
`$.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` — имя шаблона,
|
* так же вы можете [добавить](./ext/extend.md#Расширение-глобальной-переменной) свои системные переменные и функции
|
||||||
`$values` — дополнительные переменные, которые будут добавлены к существующим.
|
|
||||||
Функция позволяет получить резуьтат работы шаблона в переменную.
|
|
||||||
* так же вы можете [добавить](./ext/extend.md#Расширение-глобальной-переменной-или-функции) свои системные переменные и функции
|
|
||||||
|
|
||||||
|
|
||||||
## Скалярные значения
|
## Скалярные значения
|
||||||
|
@ -5,10 +5,9 @@ require_once __DIR__.'/../tests/tools.php';
|
|||||||
|
|
||||||
\Fenom::registerAutoload();
|
\Fenom::registerAutoload();
|
||||||
|
|
||||||
$fenom = Fenom::factory(__DIR__.'/templates', __DIR__.'/compiled');
|
$fenom = Fenom::factory(__DIR__.'/../tests/resources/provider', __DIR__.'/../tests/resources/compile');
|
||||||
$fenom->setOptions(Fenom::AUTO_RELOAD | Fenom::FORCE_COMPILE);
|
$fenom->setOptions(Fenom::AUTO_RELOAD);
|
||||||
$fenom->addAccessorSmart('g', 'App::$q->get', Fenom::ACCESSOR_CALL);
|
var_dump($fenom->fetch('extends/auto/parent.tpl'));
|
||||||
var_dump($fenom->compileCode('{$.g("env")}')->getBody());
|
|
||||||
//var_dump($fenom->compile("bug158/main.tpl", [])->getTemplateCode());
|
//var_dump($fenom->compile("bug158/main.tpl", [])->getTemplateCode());
|
||||||
//var_dump($fenom->display("bug158/main.tpl", []));
|
//var_dump($fenom->display("bug158/main.tpl", []));
|
||||||
// $fenom->getTemplate("problem.tpl");
|
// $fenom->getTemplate("problem.tpl");
|
@ -18,7 +18,7 @@ use Fenom\Template;
|
|||||||
*/
|
*/
|
||||||
class Fenom
|
class Fenom
|
||||||
{
|
{
|
||||||
const VERSION = '2.7';
|
const VERSION = '2.8';
|
||||||
const REV = 1;
|
const REV = 1;
|
||||||
/* Actions */
|
/* Actions */
|
||||||
const INLINE_COMPILER = 1;
|
const INLINE_COMPILER = 1;
|
||||||
@ -54,9 +54,11 @@ class Fenom
|
|||||||
|
|
||||||
const MAX_MACRO_RECURSIVE = 32;
|
const MAX_MACRO_RECURSIVE = 32;
|
||||||
|
|
||||||
const ACCESSOR_CUSTOM = null;
|
const ACCESSOR_CUSTOM = null;
|
||||||
const ACCESSOR_VAR = 'Fenom\Accessor::parserVar';
|
const ACCESSOR_VAR = 'Fenom\Accessor::parserVar';
|
||||||
const ACCESSOR_CALL = 'Fenom\Accessor::parserCall';
|
const ACCESSOR_CALL = 'Fenom\Accessor::parserCall';
|
||||||
|
const ACCESSOR_PROPERTY = 'Fenom\Accessor::parserProperty';
|
||||||
|
const ACCESSOR_METHOD = 'Fenom\Accessor::parserMethod';
|
||||||
|
|
||||||
public static $charset = "UTF-8";
|
public static $charset = "UTF-8";
|
||||||
|
|
||||||
@ -832,7 +834,7 @@ class Fenom
|
|||||||
/**
|
/**
|
||||||
* Add global accessor ($.)
|
* Add global accessor ($.)
|
||||||
* @param string $name
|
* @param string $name
|
||||||
* @param callable|string $accessor
|
* @param mixed $accessor
|
||||||
* @param string $parser
|
* @param string $parser
|
||||||
* @return Fenom
|
* @return Fenom
|
||||||
*/
|
*/
|
||||||
@ -961,7 +963,12 @@ class Fenom
|
|||||||
{
|
{
|
||||||
$options |= $this->_options;
|
$options |= $this->_options;
|
||||||
if (is_array($template)) {
|
if (is_array($template)) {
|
||||||
$key = $options . "@" . implode(",", $template);
|
if(count($template) === 1) {
|
||||||
|
$template = current($template);
|
||||||
|
$key = $options . "@" . $template;
|
||||||
|
} else {
|
||||||
|
$key = $options . "@" . implode(",", $template);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$key = $options . "@" . $template;
|
$key = $options . "@" . $template;
|
||||||
}
|
}
|
||||||
@ -973,8 +980,8 @@ class Fenom
|
|||||||
} else {
|
} else {
|
||||||
return $tpl;
|
return $tpl;
|
||||||
}
|
}
|
||||||
} elseif ($this->_options & self::FORCE_COMPILE) {
|
} elseif ($this->_options & (self::FORCE_COMPILE | self::DISABLE_CACHE)) {
|
||||||
return $this->compile($template, $this->_options & self::DISABLE_CACHE & ~self::FORCE_COMPILE, $options);
|
return $this->compile($template, !($this->_options & self::DISABLE_CACHE), $options);
|
||||||
} else {
|
} else {
|
||||||
return $this->_storage[$key] = $this->_load($template, $options);
|
return $this->_storage[$key] = $this->_load($template, $options);
|
||||||
}
|
}
|
||||||
|
@ -28,16 +28,53 @@ class Accessor {
|
|||||||
'env' => '$_ENV'
|
'env' => '$_ENV'
|
||||||
);
|
);
|
||||||
|
|
||||||
public static function parserVar($var, Tokenizer $tokens, Template $tpl, &$is_var) {
|
/**
|
||||||
|
* @param string $var variable expression on PHP ('App::get("storage")->user')
|
||||||
|
* @param Tokenizer $tokens
|
||||||
|
* @param Template $tpl
|
||||||
|
* @param $is_var
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function parserVar($var, Tokenizer $tokens, Template $tpl, &$is_var)
|
||||||
|
{
|
||||||
$is_var = true;
|
$is_var = true;
|
||||||
return $tpl->parseVariable($tokens, $var);
|
return $tpl->parseVariable($tokens, $var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
public static function parserCall($call, Tokenizer $tokens, Template $tpl) {
|
* @param string $call method name expression on PHP ('App::get("storage")->getUser')
|
||||||
|
* @param Tokenizer $tokens
|
||||||
|
* @param Template $tpl
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function parserCall($call, Tokenizer $tokens, Template $tpl)
|
||||||
|
{
|
||||||
return $call.$tpl->parseArgs($tokens);
|
return $call.$tpl->parseArgs($tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $prop fenom's property name
|
||||||
|
* @param Tokenizer $tokens
|
||||||
|
* @param Template $tpl
|
||||||
|
* @param $is_var
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function parserProperty($prop, Tokenizer $tokens, Template $tpl, &$is_var)
|
||||||
|
{
|
||||||
|
return self::parserVar('$tpl->getStorage()->'.$prop, $tokens, $tpl, $is_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $method fenom's method name
|
||||||
|
* @param Tokenizer $tokens
|
||||||
|
* @param Template $tpl
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function parserMethod($method, Tokenizer $tokens, Template $tpl)
|
||||||
|
{
|
||||||
|
return self::parserCall('$tpl->getStorage()->'.$method, $tokens, $tpl);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accessor for global variables
|
* Accessor for global variables
|
||||||
* @param Tokenizer $tokens
|
* @param Tokenizer $tokens
|
||||||
@ -70,6 +107,9 @@ class Accessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public static function version()
|
public static function version()
|
||||||
{
|
{
|
||||||
return 'Fenom::VERSION';
|
return 'Fenom::VERSION';
|
||||||
|
@ -227,4 +227,33 @@ class AccessorTest extends TestCase
|
|||||||
public function testFetchInvalidTpl($tpl, $exception, $message) {
|
public function testFetchInvalidTpl($tpl, $exception, $message) {
|
||||||
$this->execError($tpl, $exception, $message);
|
$this->execError($tpl, $exception, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getThree() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function providerSmartAccessor() {
|
||||||
|
return array(
|
||||||
|
array('acc', '$tpl->getStorage()->test->values', \Fenom::ACCESSOR_VAR, '{$.acc.three}', '3'),
|
||||||
|
array('acc', '$tpl->getStorage()->test->getThree', \Fenom::ACCESSOR_CALL, '{$.acc()}', '3'),
|
||||||
|
array('acc', 'three', \Fenom::ACCESSOR_PROPERTY, '{$.acc}', '3'),
|
||||||
|
array('acc', 'templateExists', \Fenom::ACCESSOR_METHOD, '{$.acc("persist:pipe.tpl")}', '1')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group testSmartAccessor
|
||||||
|
* @dataProvider providerSmartAccessor
|
||||||
|
* @param $name
|
||||||
|
* @param $accessor
|
||||||
|
* @param $type
|
||||||
|
* @param $code
|
||||||
|
* @param $result
|
||||||
|
*/
|
||||||
|
public function testSmartAccessor($name, $accessor, $type, $code, $result) {
|
||||||
|
$this->fenom->test = $this;
|
||||||
|
$this->fenom->three = 3;
|
||||||
|
$this->fenom->addAccessorSmart($name, $accessor, $type);
|
||||||
|
$this->assertRender($code, $result, $this->getVars());
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user