mirror of
https://github.com/processwire/processwire.git
synced 2025-08-09 16:26:59 +02:00
Move implementation for wirePopulateStringTags() function into WireTextTools class. Add new WireData() shortcut function for consistency with existing WireArray() and PageArray() functions.
This commit is contained in:
@@ -444,78 +444,7 @@ function wireMail($to = '', $from = '', $subject = '', $body = '', $options = ar
|
||||
*
|
||||
*/
|
||||
function wirePopulateStringTags($str, $vars, array $options = array()) {
|
||||
|
||||
$defaults = array(
|
||||
// opening tag (required)
|
||||
'tagOpen' => '{',
|
||||
// closing tag (optional)
|
||||
'tagClose' => '}',
|
||||
// if replacement value contains tags, populate those too?
|
||||
'recursive' => false,
|
||||
// if a tag value resolves to a NULL, remove it? If false, tag will be left in tact.
|
||||
'removeNullTags' => true,
|
||||
// entity encode values pulled from $vars?
|
||||
'entityEncode' => false,
|
||||
// entity decode values pulled from $vars?
|
||||
'entityDecode' => false,
|
||||
);
|
||||
|
||||
$options = array_merge($defaults, $options);
|
||||
|
||||
// check if this string even needs anything populated
|
||||
if(strpos($str, $options['tagOpen']) === false) return $str;
|
||||
if(strlen($options['tagClose']) && strpos($str, $options['tagClose']) === false) return $str;
|
||||
|
||||
// find all tags
|
||||
$tagOpen = preg_quote($options['tagOpen']);
|
||||
$tagClose = preg_quote($options['tagClose']);
|
||||
$numFound = preg_match_all('/' . $tagOpen . '([-_.|a-zA-Z0-9]+)' . $tagClose . '/', $str, $matches);
|
||||
if(!$numFound) return $str;
|
||||
$replacements = array();
|
||||
|
||||
// create a list of replacements by finding replacement values in $vars
|
||||
foreach($matches[1] as $key => $fieldName) {
|
||||
|
||||
$tag = $matches[0][$key];
|
||||
if(isset($replacements[$tag])) continue; // if already found, don't continue
|
||||
$fieldValue = null;
|
||||
|
||||
if(is_object($vars)) {
|
||||
if($vars instanceof Page) {
|
||||
$fieldValue = $vars->getMarkup($fieldName);
|
||||
|
||||
} else if($vars instanceof WireData) {
|
||||
$fieldValue = $vars->get($fieldName);
|
||||
|
||||
} else {
|
||||
$fieldValue = $vars->$fieldName;
|
||||
}
|
||||
} else if(is_array($vars)) {
|
||||
$fieldValue = isset($vars[$fieldName]) ? $vars[$fieldName] : null;
|
||||
}
|
||||
|
||||
if($options['entityEncode']) $fieldValue = htmlentities($fieldValue, ENT_QUOTES, 'UTF-8', false);
|
||||
if($options['entityDecode']) $fieldValue = html_entity_decode($fieldValue, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
$replacements[$tag] = $fieldValue;
|
||||
}
|
||||
|
||||
// replace the tags
|
||||
foreach($replacements as $tag => $value) {
|
||||
|
||||
// populate tags recursively, if asked to do so
|
||||
if($options['recursive'] && strpos($value, $options['tagOpen'])) {
|
||||
$opt = array_merge($options, array('recursive' => false)); // don't go recursive beyond 1 level
|
||||
$value = wirePopulateStringTags($value, $vars, $opt);
|
||||
}
|
||||
|
||||
// replace tags with replacement values
|
||||
if($value !== null || $options['removeNullTags']) {
|
||||
$str = str_replace($tag, (string) $value, $str);
|
||||
}
|
||||
}
|
||||
|
||||
return $str;
|
||||
return wire('sanitizer')->getTextTools()->populatePlaceholders($str, $vars, $options);
|
||||
}
|
||||
|
||||
|
||||
@@ -1223,6 +1152,29 @@ function WireArray($items = array()) {
|
||||
return WireArray::newInstance($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WireData instance and optionally add given associative array of data to it
|
||||
*
|
||||
* ~~~~~
|
||||
* $data = WireData([ 'hello' => 'world', 'foo' => 'bar' ]);
|
||||
* ~~~~~
|
||||
*
|
||||
* @param array|\Traversable $data Can be an associative array or Traversable object of data to set, or omit if not needed
|
||||
* @return WireData
|
||||
* @since 3.0.126
|
||||
*
|
||||
*/
|
||||
function WireData($data = array()) {
|
||||
$wireData = new WireData();
|
||||
if(is_array($data)) {
|
||||
if(!empty($data)) $wireData->setArray($data);
|
||||
} else if($data instanceof \Traversable) {
|
||||
foreach($data as $k => $v) $wireData->set($k, $v);
|
||||
}
|
||||
$wireData->resetTrackChanges(true);
|
||||
return $wireData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new PageArray, add given $items (pages) to it, and return it
|
||||
*
|
||||
@@ -1243,7 +1195,9 @@ function WireArray($items = array()) {
|
||||
*
|
||||
*/
|
||||
function PageArray($items = array()) {
|
||||
return PageArray::newInstance($items);
|
||||
/** @var PageArray $pa */
|
||||
$pa = PageArray::newInstance($items);
|
||||
return $pa;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -604,4 +604,156 @@ class WireTextTools extends Wire {
|
||||
return explode(' ', $s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return all {placeholder} tags found in given string
|
||||
*
|
||||
* @param string $str String that might contain field {tags}
|
||||
* @param array $options
|
||||
* - `has` (bool): Specify true to only return true or false if it has tags (default=false).
|
||||
* - `tagOpen` (string): The required opening tag character(s), default is '{'
|
||||
* - `tagClose` (string): The required closing tag character(s), default is '}'
|
||||
* @return array|bool
|
||||
* @since 3.0.126
|
||||
*
|
||||
*/
|
||||
public function findPlaceholders($str, array $options = array()) {
|
||||
|
||||
$defaults = array(
|
||||
'has' => false,
|
||||
'tagOpen' => '{',
|
||||
'tagClose' => '}',
|
||||
);
|
||||
|
||||
$options = array_merge($defaults, $options);
|
||||
$tags = array();
|
||||
$pos1 = strpos($str, $options['tagOpen']);
|
||||
|
||||
if($pos1 === false) return $options['has'] ? false : $tags;
|
||||
|
||||
if(strlen($options['tagClose'])) {
|
||||
$pos2 = strpos($str, $options['tagClose']);
|
||||
if($pos2 === false) return $options['has'] ? false : $tags;
|
||||
}
|
||||
|
||||
$regex = '/' . preg_quote($options['tagOpen']) . '([-_.|a-zA-Z0-9]+)' . preg_quote($options['tagClose']) . '/';
|
||||
if($options['has']) return (bool) preg_match($regex, $str);
|
||||
if(!preg_match_all($regex, $str, $matches)) return $tags;
|
||||
|
||||
foreach($matches[0] as $key => $tag) {
|
||||
$name = $matches[1][$key];
|
||||
$tags[$name] = $tag;
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the string have any {placeholder} tags in it?
|
||||
*
|
||||
* @param string $str
|
||||
* @param array $options
|
||||
* - `tagOpen` (string): The required opening tag character(s), default is '{'
|
||||
* - `tagClose` (string): The required closing tag character(s), default is '}'
|
||||
* @return bool
|
||||
* @since 3.0.126
|
||||
*
|
||||
*/
|
||||
public function hasPlaceholders($str, array $options = array()) {
|
||||
$options['has'] = true;
|
||||
return $this->findPlaceholders($str, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a string ($str) and values ($vars), populate placeholder “{tags}” in the string with the values
|
||||
*
|
||||
* - The `$vars` should be an associative array of `[ 'tag' => 'value' ]`.
|
||||
* - The `$vars` may also be an object, in which case values will be pulled as properties of the object.
|
||||
*
|
||||
* By default, tags are specified in the format: {first_name} where first_name is the name of the
|
||||
* variable to pull from $vars, `{` is the opening tag character, and `}` is the closing tag char.
|
||||
*
|
||||
* The tag parser can also handle subfields and OR tags, if `$vars` is an object that supports that.
|
||||
* For instance `{products.title}` is a subfield, and `{first_name|title|name}` is an OR tag.
|
||||
*
|
||||
* ~~~~~
|
||||
* $vars = [ 'foo' => 'FOO!', 'bar' => 'BAR!' ];
|
||||
* $str = 'This is a test: {foo}, and this is another test: {bar}';
|
||||
* echo $sanitizer->getTextTools()->populatePlaceholders($str, $vars);
|
||||
* // outputs: This is a test: FOO!, and this is another test: BAR!
|
||||
* ~~~~~
|
||||
*
|
||||
* @param string $str The string to operate on (where the {tags} might be found)
|
||||
* @param WireData|object|array $vars Object or associative array to pull replacement values from.
|
||||
* @param array $options Array of optional changes to default behavior, including:
|
||||
* - `tagOpen` (string): The required opening tag character(s), default is '{'
|
||||
* - `tagClose` (string): The optional closing tag character(s), default is '}'
|
||||
* - `recursive` (bool): If replacement value contains tags, populate those too? (default=false)
|
||||
* - `removeNullTags` (bool): If a tag resolves to a NULL, remove it? If false, tag will remain. (default=true)
|
||||
* - `entityEncode` (bool): Entity encode the values pulled from $vars? (default=false)
|
||||
* - `entityDecode` (bool): Entity decode the values pulled from $vars? (default=false)
|
||||
* - `allowMarkup` (bool): Allow markup to appear in populated variables? (default=true)
|
||||
* @return string String with tags populated.
|
||||
* @since 3.0.126 Use wirePopulateStringTags() function for older versions
|
||||
*
|
||||
*/
|
||||
public function populatePlaceholders($str, $vars, array $options = array()) {
|
||||
|
||||
$defaults = array(
|
||||
'tagOpen' => '{', // opening tag (required)
|
||||
'tagClose' => '}', // closing tag (optional)
|
||||
'recursive' => false, // if replacement value contains tags, populate those too?
|
||||
'removeNullTags' => true, // if a tag value resolves to a NULL, remove it? If false, tag will be left in tact.
|
||||
'entityEncode' => false, // entity encode values pulled from $vars?
|
||||
'entityDecode' => false, // entity decode values pulled from $vars?
|
||||
'allowMarkup' => true, // allow markup to appear in populated variables?
|
||||
);
|
||||
|
||||
$options = array_merge($defaults, $options);
|
||||
$optionsNoRecursive = $options['recursive'] ? array_merge($options, array('recursive' => false)) : $options;
|
||||
$replacements = array();
|
||||
$tags = $this->findPlaceholders($str, $options);
|
||||
|
||||
// create a list of replacements by finding replacement values in $vars
|
||||
foreach($tags as $fieldName => $tag) {
|
||||
|
||||
if(isset($replacements[$tag])) continue; // if already found, do not do it again
|
||||
$fieldValue = null;
|
||||
|
||||
if(is_object($vars)) {
|
||||
if($vars instanceof Page) {
|
||||
$fieldValue = $options['allowMarkup'] ? $vars->getMarkup($fieldName) : $vars->getText($fieldName);
|
||||
} else if($vars instanceof WireData) {
|
||||
$fieldValue = $vars->get($fieldName);
|
||||
} else {
|
||||
$fieldValue = $vars->$fieldName;
|
||||
}
|
||||
} else if(is_array($vars)) {
|
||||
$fieldValue = isset($vars[$fieldName]) ? $vars[$fieldName] : null;
|
||||
}
|
||||
|
||||
// if value resolves to null and we are not removing null tags, then do not add to replacements
|
||||
if($fieldValue === null && !$options['removeNullTags']) continue;
|
||||
|
||||
$fieldValue = (string) $fieldValue;
|
||||
|
||||
if(!$options['allowMarkup'] && strpos($fieldValue, '<') !== false) $fieldValue = strip_tags($fieldValue);
|
||||
if($options['entityEncode']) $fieldValue = htmlentities($fieldValue, ENT_QUOTES, 'UTF-8', false);
|
||||
if($options['entityDecode']) $fieldValue = html_entity_decode($fieldValue, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
if($options['recursive'] && strpos($fieldValue, $options['tagOpen']) !== false) {
|
||||
$fieldValue = $this->populatePlaceholders($fieldValue, $vars, $optionsNoRecursive);
|
||||
}
|
||||
|
||||
$replacements[$tag] = $fieldValue;
|
||||
}
|
||||
|
||||
// replace the tags
|
||||
if(count($tags)) {
|
||||
$str = str_replace(array_keys($replacements), array_values($replacements), $str);
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user