У меня есть почтовый скрипт, который я использую в одном из моих проектов, и я хотел бы разрешить настройку этого письма. проблема в том, что части электронной почты динамически генерируются из базы данных. У меня есть предопределенные токены, которые я использую, чтобы описать, что должно заменить токен, но я хотел бы упростить это, но написать лучший парсер, который может интерпретировать токен и выяснить, какую переменную использовать для его замены.
Прямо сейчас у меня есть очень большой массив со всеми возможными токенами и их соответствующими значениями, например:
$tokens['[property_name]'] = $this->name;
и затем я просматриваю шаблон и заменяю любой экземпляр ключа его значением.
Я бы предпочел просто запустить шаблон, искать [] или все, что я использую для определения токена, а затем прочитать, что внутри, и преобразовать его в имя переменной.
Мне нужно уметь сопоставлять несколько уровней отношений, поэтому $this->account->owner->name;
в качестве примера, и мне нужно иметь возможность ссылаться на методы. $this->account->calcTotal();
– $this->account->calcTotal();
Я подумал, что могу взять пример [property_name]
и заменить экземпляр _
на ->
а затем вызвать его как переменную, но я не думаю, что он работает с методами.
Вы создаете систему шаблонов. Вы можете либо изобретать колесо (вроде), кодируя это самостоятельно, либо просто используя систему весовых шаблонов, такую как усы .
Для очень легкого подхода вы можете использовать регулярные выражения для формулировки синтаксиса ваших переменных шаблона. Просто определите, как можно записать переменную, затем извлеките используемые имена и метки и замените их по желанию.
Для этого используется функция 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();
Пока это не выглядит эффектно, это просто заменяет теги шаблона на значение. Однако, как уже упоминалось, теперь вы можете ввести преобразователь в шаблон, который может разрешать теги шаблонов для значения вместо использования простого массива.
Вы указали в своем вопросе, что хотите использовать символ _
для отделения от элементов / функций объекта. Ниже приведен класс resolver, который будет разрешать все глобальные переменные для этой нотации. В нем показано, как обрабатывать как элементы, так и методы и методы перемещения переменных. Однако он не разрешает $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();
Посмотрите полное демо в действии .
Это потребует незначительной модификации, чтобы в конечном итоге удовлетворить ваши потребности.
PHP уже является отличной системой шаблонов для своих собственных.
Я использую простой класс Template, который принимает переменные (через __set ()), а затем, когда пришло время рендеринга, просто сделайте extract () в массиве переменных и включите файл шаблона.
Это, очевидно, можно комбинировать с выходной буферизацией, если вам нужно записать результат в строку, а не отправлять результат прямо в браузер / оболочку.
Это дает вам возможность иметь очень простые шаблоны, но также дает вам расширенную функциональность, если вам это нужно (т. Е. Для циклов, используя вспомогательные классы и т. Д.),
Я использовал что-то подобное для шаблонов электронной почты:
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; ?>