Dynamic Token Analysis

I have a mail script that I use in one of my projects, and I would like to allow customization of this letter. the problem is that parts of the email are dynamically generated from the database. I have predefined tokens that I use to describe what should replace the token, but I would like to simplify this, but write a better parser that can interpret the token and figure out which variable to use to replace it.

Right now I have a very large array with all possible tokens and their corresponding values, for example:

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

and then I run the template and replace any key instance with its value.

I would rather just run the template, search for [] or everything that I use to determine the token, and then read what's inside and convert it to a variable name.

I need to be able to match multiple levels of relationships, so $this->account->owner->name; as an example, and I need to be able to reference methods. $this->account->calcTotal();

I thought I could take an example [property_name] and replace the _ instance with -> , and then call it as a variable, but I don't believe that it works with methods.

+1
source share
3 answers

You create a template system. You can either invent a wheel (sort of) by encoding it yourself, or simply using a weighting system such as mustache .

For a very easy approach, you can use regular expressions to formulate the syntax of your template variables. Just determine how you can write the variable, then extract the names and labels used and replace them as you wish.

The function used for this is preg_replace_callback . The following is a small example code ( Demo ) that reflects only simple substitution, however you can change the replacement procedure to access the required values ​​(in this example I use a variable that is either Array or implements 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(); 

Until it looks spectacular, it simply replaces the template tags with a value. However, as already mentioned, you can now introduce a converter into a template that can resolve template tags for a value instead of using a simple array.

You indicated in your question that you want to use the _ symbol to separate from members / functions of an object. The following is a resolver class that will resolve all global variables for this notation. It shows how to handle both elements and methods and methods for moving variables. However, it does not allow $this , but the global namespace:

 /** * 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.'); } } 

This recognizer class can then be used to use some approximate values:

 /** * 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 own resolver class */ $templ = new Template($template, new GlobalResolver); echo $templ->substituteVars(); 

Watch the full demo in action .

This will require minor modifications to ultimately satisfy your needs.

+1
source

PHP is already an excellent template system.

I use a simple Template class that accepts variables (via __set ()), and then when it is time for rendering, just do extract () in the variable array and include the template file.

This, obviously, can be combined with output buffering if you need to write the result to a string rather than send the result directly to the browser / shell.

This gives you the opportunity to have very simple templates, but also gives you enhanced functionality if you need it (i.e. for loops, using helper classes, etc.)

+2
source

I used something similar for email templates:

 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; ?> 
0
source

Source: https://habr.com/ru/post/914997/


All Articles