динамический анализ токена - PullRequest
1 голос
/ 06 августа 2011

У меня есть почтовый скрипт, который я использую в одном из своих проектов, и я хотел бы разрешить настройку этого письма. проблема в том, что части электронной почты динамически генерируются из базы данных. У меня есть предопределенные токены, которые я использую для описания того, что должно заменить токен, но я хотел бы упростить это, но написать лучший анализатор, который может интерпретировать токен и выяснить, какую переменную использовать для его замены.

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

$tokens['[property_name]'] = $this->name;

и затем я запускаю шаблон и заменяю любой экземпляр ключа его значением.

Я бы предпочел просто просмотреть шаблон, найти [] или что-то еще, что я использую для определения токена, а затем прочитать, что внутри, и преобразовать его в имя переменной.

Мне нужно соответствовать нескольким уровням отношений, поэтому $this->account->owner->name; в качестве примера, и я должен иметь возможность ссылаться на методы. $this->account->calcTotal();

Я думал, что смогу взять пример [property_name] и заменить экземпляр _ на ->, а затем вызвать его как переменную, но я не верю, что он работает с методами.

Ответы [ 3 ]

2 голосов
/ 06 августа 2011

PHP уже сам по себе является отличной системой шаблонов.

Я использую простой класс Template, который принимает переменные (через __set ()), а затем, когда приходит время рендеринга, просто извлекаю () из массива переменных и включаю файл шаблона.

Очевидно, что это можно комбинировать с буферизацией вывода, если вам нужно записать результат в строку, а не отправлять результат прямо обратно в браузер / оболочку.

Это дает вам возможность иметь очень простые шаблоны, но также дает вам расширенные функциональные возможности, если вам это нужно (например, для циклов, использования вспомогательных классов и т. Д.)

1 голос
/ 06 августа 2011

Вы создаете своего рода систему шаблонов.Вы можете заново изобрести колесо (вроде), кодируя его самостоятельно или просто используя облегченную систему шаблонов, такую ​​как усы .

. Для очень легкого подхода вы можете использоватьрегулярные выражения для формулировки синтаксиса переменных вашего шаблона.Просто определите, как можно записать переменную, затем извлеките используемые имена / метки и замените их по желанию.

Функция, используемая для этого, - preg_replace_callback.Вот небольшой пример кода ( Demo ), который отражает только простую замену, однако вы можете изменить подпрограмму замены для доступа к нужным значениям (в этом примере я использую переменную, которая либоArray или реализует ArrayAccess):

<?php
$template = <<<EOD
This is my template,

I can use [vars] at free [will].
EOD;

class Template
{
    private $template;
    private $vars;
    public function __construct($template, $vars)
    {
        $this->template = $template;
        $this->vars = $vars;
    }
    public function replace(array $matches)
    {
        list(, $var) = $matches;
        if (isset($this->vars[$var]))
        {
             return $this->vars[$var];
        }
        return sprintf('<<undefined:%s>>', $var);

    }
    public function substituteVars()
    {
        $pattern = '~\[([a-z_]{3,})\]~';
        $callback = array($this, 'replace');
        return preg_replace_callback($pattern, $callback, $this->template );
    }

}

$templ = new Template($template, array('vars' => 'variables'));

echo $templ->substituteVars();

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

Вы указали в своем вопросе, что хотели бы использовать _ символ для отделения от элементов / функций объекта.Ниже приведен класс распознавателя, который разрешает все глобальные переменные в этой записи.Он показывает, как обрабатывать как члены, объекты и методы, так и обход переменных.Однако он разрешается не в $this, а в глобальное пространство имен:

/**
 * Resolve template variables from the global namespace
 */
class GlobalResolver implements ArrayAccess 
{
    private function resolve($offset)
    {
        $stack = explode('_', $offset);

        return $this->resolveOn($stack, $GLOBALS);
    }

    private function resolveOn($stack, $base)
    {
        $c = count($stack);
        if (!$c)
            return array(false, NULL);

        $var = array_shift($stack);
        $varIsset = isset($base[$var]);

        # non-set variables don't count
        if (!$varIsset)
        {
            return array($varIsset, NULL);
        }

        # simple variable
        if (1 === $c)
        {
            return array($varIsset, $base[$var]);
        }

        # descendant    
        $operator = $stack[0];
        $subject = $base[$var];
        $desc = $this->resolvePair($subject, $operator);

        if (2 === $c || !$desc[0])
            return $desc;

        $base = array($operator => $desc[1]);
        return $this->resolveOn($stack, $base);
    }

    private function resolvePair($subject, $operator)
    {
        if (is_object($subject))
        {
            if (property_exists($subject, $operator))
            {
                return array(true, $subject->$operator);
            }
            if (method_exists($subject, $operator))
            {
                return array(true, $subject->$operator());
            }
        }
        if (is_array($subject))
        {
            if (array_key_exists($operator, $subject))
            {
                return array(true, $subject[$operator]);
            }
        }
        return array(false, NULL);
    }

    public function offsetExists($offset)
    {
        list($isset) = $this->resolve($offset);
        return $isset;
    }
    public function offsetGet($offset)
    {
        list($isset, $value) = $this->resolve($offset);
        return $value;
    }
    public function offsetSet ($offset, $value)
    {
        throw new BadMethodCallException('Read only.');
    }
    public function offsetUnset($offset)
    {
        throw new BadMethodCallException('Read only.');
    }
}

Этот класс преобразователя можно использовать затем для использования некоторых примеров значений:

/**
 * fill the global namespace with some classes and variables
 */
class Foo
{
   public $member = 'object member';
   public function func()
   {
       return 'function result';
   }
   public function child()
   {
       $child->member = 'child member';
       return $child;
   }
}

$vars = 'variables';
$foo = new Foo;

$template = <<<EOD
This is my template,

I can use [vars] at free [foo_func] or [foo_member] and even [foo_child_member].
EOD;

/**
 * this time use the template with it's own resolver class
 */
$templ = new Template($template, new GlobalResolver);

echo $templ->substituteVars();

См. полная демонстрация в действии .

Для этого потребуется лишь небольшое изменение, чтобы соответствовать вашим потребностям, и, наконец,

0 голосов
/ 06 августа 2011

Я использовал нечто подобное для шаблонов писем:

function call_php_with_vars( $_t_filename, $_t_variables ){
  extract( $_t_variables );

  ob_start();
    include $_t_filename;
    $_t_result = ob_get_contents();
  ob_end_clean();

  return $_t_result;
}

echo call_php_with_vars('email_template.php',array(
  'name'=>'Little Friend'
 ,'object'=>(object)array(
    'field'=>'value'
  )
));

email_template.php:

Hello, <?php echo $name; ?>
<?php echo $object->field; ?>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...