'{{name}}="{{value}}"',
'compactAttribute' => '{{name}}="{{value}}"',
];
protected $_config;
/**
* Contains the list of compiled templates
*
* @var array
*/
protected $_compiled = [];
/**
* Constructor.
*
* @param array $config A set of templates to add.
*/
public function __construct(array $config = []) {
$this->config($config);
}
/**
* StringTemplate::config()
*
* @param string|array|null $key The key to get/set, or a complete array of configs.
* @param mixed|null $value The value to set.
* @param bool $merge Whether to merge or overwrite existing config defaults to true.
* @return mixed Config value being read, or the whole array itself on write operations.
*/
public function config($key = null, $value = null, $merge = true) {
if ($key === null) {
return $this->_config;
}
if (is_array($key)) {
if ($merge) {
$this->_config = $key + $this->_defaultConfig;
} else {
$this->_config = $key;
}
return;
}
if (func_num_args() >= 2) {
if ($value === null) {
unset($this->_config[$key]);
} else {
$this->_config[$key] = $value;
}
return $this->_config;
}
if (!isset($this->_config[$key])) {
return null;
}
return $this->_config[$key];
}
/**
* Registers a list of templates by name
*
* ### Example:
*
* {{{
* $templater->add([
* 'link' => '{{title}}'
* 'button' => ''
* ]);
* }}}
*
* @param array an associative list of named templates
* @return \Cake\View\StringTemplate same instance
*/
public function add(array $templates) {
$this->config($templates);
$this->_compiled = array_diff_key($this->_compiled, $templates);
return $this;
}
/**
* Load a config file containing templates.
*
* Template files should define a `$config` variable containing
* all the templates to load. Loaded templates will be merged with existing
* templates.
*
* @param string $file The file to load
* @return void
*/
public function load($file) {
$loader = new PhpReader();
$templates = $loader->read($file);
$this->add($templates);
}
/**
* Remove the named template.
*
* @param string $name The template to remove.
* @return void
*/
public function remove($name) {
$this->config($name, null);
unset($this->_compiled[$name]);
}
/**
* Returns an array containing the compiled template to be used with
* the sprintf function and a list of placeholder names that were found
* in the template in the order that they should be replaced.
*
* @param string $name The compiled template info
* @return array
*/
public function compile($name) {
if (isset($this->_compiled[$name])) {
return $this->_compiled[$name];
}
$template = $this->config($name);
if ($template === null) {
return $this->_compiled[$name] = [null, null];
}
preg_match_all('#\{\{(\w+)\}\}#', $template, $matches);
return $this->_compiled[$name] = [
str_replace($matches[0], '%s', $template),
$matches[1]
];
}
/**
* Format a template string with $data
*
* @param string $name The template name.
* @param array $data The data to insert.
* @return string
*/
public function format($name, array $data) {
list($template, $placeholders) = $this->compile($name);
if ($template === null) {
return '';
}
$replace = [];
foreach ($placeholders as $placeholder) {
$replace[] = isset($data[$placeholder]) ? $data[$placeholder] : null;
}
return vsprintf($template, $replace);
}
/**
* Returns a space-delimited string with items of the $options array. If a key
* of $options array happens to be one of those listed
* in `StringTemplate::$_compactAttributes` and its value is one of:
*
* - '1' (string)
* - 1 (integer)
* - true (boolean)
* - 'true' (string)
*
* Then the value will be reset to be identical with key's name.
* If the value is not one of these 4, the parameter is not output.
*
* 'escape' is a special option in that it controls the conversion of
* attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
*
* If value for any option key is set to `null` or `false`, that option will be excluded from output.
*
* This method uses the 'attribute' and 'compactAttribute' templates. Each of
* these templates uses the `name` and `value` variables. You can modify these
* templates to change how attributes are formatted.
*
* @param array $options Array of options.
* @param null|array $exclude Array of options to be excluded, the options here will not be part of the return.
* @return string Composed attributes.
*/
public function formatAttributes($options, $exclude = null) {
$insertBefore = ' ';
$options = (array)$options + ['escape' => true];
if (!is_array($exclude)) {
$exclude = [];
}
$exclude = ['escape' => true, 'idPrefix' => true] + array_flip($exclude);
$escape = $options['escape'];
$attributes = [];
foreach ($options as $key => $value) {
if (!isset($exclude[$key]) && $value !== false && $value !== null) {
$attributes[] = $this->_formatAttribute($key, $value, $escape);
}
}
$out = trim(implode(' ', $attributes));
return $out ? $insertBefore . $out : '';
}
/**
* Formats an individual attribute, and returns the string value of the composed attribute.
* Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
*
* @param string $key The name of the attribute to create
* @param string|array $value The value of the attribute to create.
* @param bool $escape Define if the value must be escaped
* @return string The composed attribute.
*/
protected function _formatAttribute($key, $value, $escape = true) {
if (is_array($value)) {
$value = implode(' ', $value);
}
if (is_numeric($key)) {
return $this->format('compactAttribute', [
'name' => $value,
'value' => $value
]);
}
$truthy = [1, '1', true, 'true', $key];
$isMinimized = in_array($key, $this->_compactAttributes);
if ($isMinimized && in_array($value, $truthy, true)) {
return $this->format('compactAttribute', [
'name' => $key,
'value' => $key
]);
}
if ($isMinimized) {
return '';
}
return $this->format('attribute', [
'name' => $key,
'value' => $escape ? h($value) : $value
]);
}
}