fenom-template / fenom

Template Engine for PHP. Maintainers wanted!
Other
446 stars 108 forks source link

Расширение глобальной переменной #149

Closed sleuthhound closed 9 years ago

sleuthhound commented 9 years ago

Подскажите, так и должно быть?

$fenom->addAccessor('array', function (Fenom\Tokenizer $tokens) { 
    return array('key' => 'val');
}); 
{$.array.key}

На выходе получаем:

 Unexpected token '.' ... либо Unexpected token '->' ... если имеем дело с объектом
sleuthhound commented 9 years ago

Дошло, тут получается надо самостоятельно разбирать токены.

bzick commented 9 years ago

Да, глобальная переменная обрабатывается во время компиляции. То есть accessor коллбек возвращает PHP код (строка), который будет выполнен во время отрисовки шаблона. Если вам надо вызвать некую функцию то можно написать следующее:

$fenom->addAccessor('array', function (Fenom\Tokenizer $tokens) { 
    return 'my_accessor_array("'.$tokens->next()->getAndNext().'")';
}); 

// $.array.key вызовет my_accessor_array("key")

Подробней о том что в коде выше произошло:

$.array.key это токены $, ., array, ., key. Причем токены $, ., array разбираются системой, а далее предлагается самим разобрать токены ., key. Так как Вы не использовали оставшиеся токены то Fenom выбросил ошибку после разбора тега, так как не разобранных токенов не должно быть, все должно быть при деле. Так как нас токен . не интересует то мы его пропускаем методом ->next() и берем следующий токен key сдвигая указатель в массиве токенов на следующий элемент методом ->getAndNext(), показывая что мы обработали свои токены, остальное нас не интересует.

bzick commented 9 years ago

В случае массива будет

$fenom->addAccessor('array', function (Fenom\Tokenizer $tokens) { 
    return '$array["'.$tokens->next()->getAndNext().'"]';
}); 
bzick commented 9 years ago

Надеюсь объяснение помогло

sleuthhound commented 9 years ago

Да, пока просто выстраиваю цепочку вызовов методов и свойств без проверок:

$fenom->addAccessor('object', function (Fenom\Tokenizer $tokens) {
    while ($tokens->valid()) {
        if ($tokens->is('.')) {
            $callable[] = "['". $tokens->skip('.')->getAndNext() ."']";
        } else {
            $callable[] = $tokens->getAndNext();
        }
    }
    return 'Class::$object'. join('', $callable);
});

И далее, например, использую:

{$.object->set('array', array('key' => 'value'))->get('array').key}
или
{$.object->array.key}

Просто, у меня есть уже определенные объекты, например сайт, раздел, которые не подлежат изменению, потому решил сделать их доступными через глобальную переменную, и потом просто выводить название текущего сайта, раздела или какие-то другие свойства:

{$.site->title} {$.section->title}

Вот еще бы foreach воспринимал глобальную переменную, чтобы так работало:

{foreach $.site->section()->findMany() as $section}
{$section->title}
{/foreach}

сейчас приходиться заранее определять переменную и потом ее перебирать:

{set $sections = $.site->section()->findMany()}
bzick commented 9 years ago

то что foreach не воспринимает — баг, буду править

bzick commented 9 years ago

В 2.5 теперь foreach воспринимает глобальную переменную

sleuthhound commented 9 years ago

Спасибо!

sleuthhound commented 9 years ago

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

{if $.myAccesor ?}
   ...
{/if}

Моем случае пока формируется примерно такая каша:

if($object->myAccesor?) { ?>

В теории, понимаю, что надо получить на выходе:

if(!empty($object->myAccesor)) { ?>

Следовательно, надо отследить токен ? и каким то образом, произвести модификацию.

bzick commented 9 years ago

Рекомендую глянуть сюда — https://github.com/fenom-template/fenom/blob/master/src/Fenom/Accessor.php#L41 Это обработка глобальных типа $.get, $.post и тд

Токен ? будет переменной сам в глобальной переменной. Но глобальная переменная не воспринимается парсером как переменная, а как некий набор действий, поэтому он не может применить к ним empty и по этой причине нельзя задавать значение глобальной переменной то есть $.get.one = 1 работать не будет.

sleuthhound commented 9 years ago

Пока изменил предыдущий вариант https://github.com/fenom-template/fenom/issues/149#issuecomment-75248814 на такой:

$fenom->addAccessor('object', function ($tokens, $tpl) {
    $body = 'Class::$object';
    while ($tokens->valid()) {
        if ($tokens->is('.')) {
            $body = $tpl->parseVariable($tokens, $body);
        } elseif ($tokens->is('|')) {
            $body = $tpl->parseModifier($tokens, $body);
        } elseif ($tokens->is('?') || $tokens->is('!')) {
            $body = $tpl->parseTernary($tokens, $body, true);
        } elseif ($tokens->is('->')) {
            $body = $tpl->parseChain($tokens, $body);
        } else {
            $body .= $tokens->getAndNext();
        }
    }
    return $body;
});

С первого взгляда вроде получилось, то что нужно. Можно применять модификаторы и использовать в условных операторах.

bzick commented 9 years ago

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

$fenom->addAccessor('object', function ($tokens, $tpl) {
    return 'Class::$object';
});

Остальное распарсит сам шаблонизатор (точку, модификаторы, условные операторы и тд), то есть будут работать:

$.object->a
$.object->a->b->c
$.object.a
$.object.a->b ?: $c

и прочие операции над значением, которые существуют в Fenom

bzick commented 9 years ago

Ах, стоп. Я Вас ввел в заблуждение, данная возможность еще в разработке, когда шаблонизатору можно сказать самому допарсить по правилам.

sleuthhound commented 9 years ago

Хм, у меня ошибку выдает. Я почему этот сыр бор то и затеял, что ругается Unexpected token '->' in ... . Значит можно в скором времени свои костылики убрать, это хорошо). Ждем.

bzick commented 9 years ago

Да, я написал о своей оплошности выше. Вариант, который я описал будет доступен в следующей версии

bzick commented 9 years ago

будущий вариант будет иметь 3ий аргумент в методе addAccessor, который укажет парсеру что надо распаристь самостоятельно и как

bzick commented 9 years ago

Однако, отчасти я прав в том что когда вами будет обработана глобальная переменная, то парсер сам спарсит модификаторы и условные операторы. То есть parseModifier и parseTernary лишние

sleuthhound commented 9 years ago

Ага, вы правы. Переделал так:

$fenom->addAccessor('object', function ($tokens, $tpl) {
    $body = 'Class::$object';
    $body = $tpl->parseChain($tokens, $body);
    $body = $tpl->parseVariable($tokens, $body);
    return $body;
});

вроде работает. Спасибо.