StringTemplate.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\View;
  16. use Cake\Configure\Engine\PhpConfig;
  17. use Cake\Core\InstanceConfigTrait;
  18. /**
  19. * Provides an interface for registering and inserting
  20. * content into simple logic-less string templates.
  21. *
  22. * Used by several helpers to provide simple flexible templates
  23. * for generating HTML and other content.
  24. */
  25. class StringTemplate {
  26. use InstanceConfigTrait {
  27. config as get;
  28. }
  29. /**
  30. * List of attributes that can be made compact.
  31. *
  32. * @var array
  33. */
  34. protected $_compactAttributes = array(
  35. 'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected',
  36. 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize',
  37. 'autoplay', 'controls', 'loop', 'muted', 'required', 'novalidate', 'formnovalidate'
  38. );
  39. /**
  40. * The default templates this instance holds.
  41. *
  42. * @var array
  43. */
  44. protected $_defaultConfig = [
  45. 'attribute' => '{{name}}="{{value}}"',
  46. 'compactAttribute' => '{{name}}="{{value}}"',
  47. ];
  48. /**
  49. * Contains the list of compiled templates
  50. *
  51. * @var array
  52. */
  53. protected $_compiled = [];
  54. /**
  55. * Constructor.
  56. *
  57. * @param array $config A set of templates to add.
  58. */
  59. public function __construct(array $config = []) {
  60. $this->config($config);
  61. }
  62. /**
  63. * Registers a list of templates by name
  64. *
  65. * ### Example:
  66. *
  67. * {{{
  68. * $templater->add([
  69. * 'link' => '<a href="{{url}}">{{title}}</a>'
  70. * 'button' => '<button>{{text}}</button>'
  71. * ]);
  72. * }}}
  73. *
  74. * @param array $templates An associative list of named templates.
  75. * @return $this
  76. */
  77. public function add(array $templates) {
  78. $this->config($templates);
  79. $this->_compiled = array_diff_key($this->_compiled, $templates);
  80. return $this;
  81. }
  82. /**
  83. * Load a config file containing templates.
  84. *
  85. * Template files should define a `$config` variable containing
  86. * all the templates to load. Loaded templates will be merged with existing
  87. * templates.
  88. *
  89. * @param string $file The file to load
  90. * @return void
  91. */
  92. public function load($file) {
  93. $loader = new PhpConfig(APP . 'Config/');
  94. $templates = $loader->read($file);
  95. $this->add($templates);
  96. }
  97. /**
  98. * Remove the named template.
  99. *
  100. * @param string $name The template to remove.
  101. * @return void
  102. */
  103. public function remove($name) {
  104. $this->config($name, null);
  105. unset($this->_compiled[$name]);
  106. }
  107. /**
  108. * Returns an array containing the compiled template to be used with
  109. * the sprintf function and a list of placeholder names that were found
  110. * in the template in the order that they should be replaced.
  111. *
  112. * @param string $name The compiled template info
  113. * @return array
  114. */
  115. public function compile($name) {
  116. if (isset($this->_compiled[$name])) {
  117. return $this->_compiled[$name];
  118. }
  119. $template = $this->get($name);
  120. if ($template === null) {
  121. return $this->_compiled[$name] = [null, null];
  122. }
  123. preg_match_all('#\{\{(\w+)\}\}#', $template, $matches);
  124. return $this->_compiled[$name] = [
  125. str_replace($matches[0], '%s', $template),
  126. $matches[1]
  127. ];
  128. }
  129. /**
  130. * Format a template string with $data
  131. *
  132. * @param string $name The template name.
  133. * @param array $data The data to insert.
  134. * @return string
  135. */
  136. public function format($name, array $data) {
  137. list($template, $placeholders) = $this->compile($name);
  138. if ($template === null) {
  139. return '';
  140. }
  141. $replace = [];
  142. foreach ($placeholders as $placeholder) {
  143. $replace[] = isset($data[$placeholder]) ? $data[$placeholder] : null;
  144. }
  145. return vsprintf($template, $replace);
  146. }
  147. /**
  148. * Returns a space-delimited string with items of the $options array. If a key
  149. * of $options array happens to be one of those listed
  150. * in `StringTemplate::$_compactAttributes` and its value is one of:
  151. *
  152. * - '1' (string)
  153. * - 1 (integer)
  154. * - true (boolean)
  155. * - 'true' (string)
  156. *
  157. * Then the value will be reset to be identical with key's name.
  158. * If the value is not one of these 4, the parameter is not output.
  159. *
  160. * 'escape' is a special option in that it controls the conversion of
  161. * attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
  162. *
  163. * If value for any option key is set to `null` or `false`, that option will be excluded from output.
  164. *
  165. * This method uses the 'attribute' and 'compactAttribute' templates. Each of
  166. * these templates uses the `name` and `value` variables. You can modify these
  167. * templates to change how attributes are formatted.
  168. *
  169. * @param array $options Array of options.
  170. * @param null|array $exclude Array of options to be excluded, the options here will not be part of the return.
  171. * @return string Composed attributes.
  172. */
  173. public function formatAttributes($options, $exclude = null) {
  174. $insertBefore = ' ';
  175. $options = (array)$options + ['escape' => true];
  176. if (!is_array($exclude)) {
  177. $exclude = [];
  178. }
  179. $exclude = ['escape' => true, 'idPrefix' => true] + array_flip($exclude);
  180. $escape = $options['escape'];
  181. $attributes = [];
  182. foreach ($options as $key => $value) {
  183. if (!isset($exclude[$key]) && $value !== false && $value !== null) {
  184. $attributes[] = $this->_formatAttribute($key, $value, $escape);
  185. }
  186. }
  187. $out = trim(implode(' ', $attributes));
  188. return $out ? $insertBefore . $out : '';
  189. }
  190. /**
  191. * Formats an individual attribute, and returns the string value of the composed attribute.
  192. * Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
  193. *
  194. * @param string $key The name of the attribute to create
  195. * @param string|array $value The value of the attribute to create.
  196. * @param bool $escape Define if the value must be escaped
  197. * @return string The composed attribute.
  198. */
  199. protected function _formatAttribute($key, $value, $escape = true) {
  200. if (is_array($value)) {
  201. $value = implode(' ', $value);
  202. }
  203. if (is_numeric($key)) {
  204. return $this->format('compactAttribute', [
  205. 'name' => $value,
  206. 'value' => $value
  207. ]);
  208. }
  209. $truthy = [1, '1', true, 'true', $key];
  210. $isMinimized = in_array($key, $this->_compactAttributes);
  211. if ($isMinimized && in_array($value, $truthy, true)) {
  212. return $this->format('compactAttribute', [
  213. 'name' => $key,
  214. 'value' => $key
  215. ]);
  216. }
  217. if ($isMinimized) {
  218. return '';
  219. }
  220. return $this->format('attribute', [
  221. 'name' => $key,
  222. 'value' => $escape ? h($value) : $value
  223. ]);
  224. }
  225. }