PageRenderTime 45ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/View/StringTemplate.php

https://github.com/binondord/cakephp
PHP | 316 lines | 161 code | 21 blank | 134 comment | 14 complexity | 27d7b4f2d7e5d762b5edf81ad799bf64 MD5 | raw file
  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\Core\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. {
  27. use InstanceConfigTrait {
  28. config as get;
  29. }
  30. /**
  31. * List of attributes that can be made compact.
  32. *
  33. * @var array
  34. */
  35. protected $_compactAttributes = [
  36. 'allowfullscreen' => true,
  37. 'async' => true,
  38. 'autofocus' => true,
  39. 'autoplay' => true,
  40. 'checked' => true,
  41. 'compact' => true,
  42. 'controls' => true,
  43. 'declare' => true,
  44. 'default' => true,
  45. 'defaultchecked' => true,
  46. 'defaultmuted' => true,
  47. 'defaultselected' => true,
  48. 'defer' => true,
  49. 'disabled' => true,
  50. 'enabled' => true,
  51. 'formnovalidate' => true,
  52. 'hidden' => true,
  53. 'indeterminate' => true,
  54. 'inert' => true,
  55. 'ismap' => true,
  56. 'itemscope' => true,
  57. 'loop' => true,
  58. 'multiple' => true,
  59. 'muted' => true,
  60. 'nohref' => true,
  61. 'noresize' => true,
  62. 'noshade' => true,
  63. 'novalidate' => true,
  64. 'nowrap' => true,
  65. 'open' => true,
  66. 'pauseonexit' => true,
  67. 'readonly' => true,
  68. 'required' => true,
  69. 'reversed' => true,
  70. 'scoped' => true,
  71. 'seamless' => true,
  72. 'selected' => true,
  73. 'sortable' => true,
  74. 'spellcheck' => true,
  75. 'truespeed' => true,
  76. 'typemustmatch' => true,
  77. 'visible' => true,
  78. ];
  79. /**
  80. * The default templates this instance holds.
  81. *
  82. * @var array
  83. */
  84. protected $_defaultConfig = [
  85. ];
  86. /**
  87. * A stack of template sets that have been stashed temporarily.
  88. *
  89. * @var array
  90. */
  91. protected $_configStack = [];
  92. /**
  93. * Contains the list of compiled templates
  94. *
  95. * @var array
  96. */
  97. protected $_compiled = [];
  98. /**
  99. * Constructor.
  100. *
  101. * @param array $config A set of templates to add.
  102. */
  103. public function __construct(array $config = [])
  104. {
  105. $this->add($config);
  106. }
  107. /**
  108. * Push the current templates into the template stack.
  109. *
  110. * @return void
  111. */
  112. public function push()
  113. {
  114. $this->_configStack[] = [
  115. $this->_config,
  116. $this->_compiled
  117. ];
  118. }
  119. /**
  120. * Restore the most recently pushed set of templates.
  121. *
  122. * @return void
  123. */
  124. public function pop()
  125. {
  126. if (empty($this->_configStack)) {
  127. return;
  128. }
  129. list($this->_config, $this->_compiled) = array_pop($this->_configStack);
  130. }
  131. /**
  132. * Registers a list of templates by name
  133. *
  134. * ### Example:
  135. *
  136. * ```
  137. * $templater->add([
  138. * 'link' => '<a href="{{url}}">{{title}}</a>'
  139. * 'button' => '<button>{{text}}</button>'
  140. * ]);
  141. * ```
  142. *
  143. * @param array $templates An associative list of named templates.
  144. * @return $this
  145. */
  146. public function add(array $templates)
  147. {
  148. $this->config($templates);
  149. $this->_compileTemplates(array_keys($templates));
  150. return $this;
  151. }
  152. /**
  153. * Compile templates into a more efficient printf() compatible format.
  154. *
  155. * @param array $templates The template names to compile. If empty all templates will be compiled.
  156. * @return void
  157. */
  158. protected function _compileTemplates(array $templates = [])
  159. {
  160. if (empty($templates)) {
  161. $templates = array_keys($this->_config);
  162. }
  163. foreach ($templates as $name) {
  164. $template = $this->get($name);
  165. if ($template === null) {
  166. $this->_compiled[$name] = [null, null];
  167. }
  168. preg_match_all('#\{\{([\w\d\._]+)\}\}#', $template, $matches);
  169. $this->_compiled[$name] = [
  170. str_replace($matches[0], '%s', $template),
  171. $matches[1]
  172. ];
  173. }
  174. }
  175. /**
  176. * Load a config file containing templates.
  177. *
  178. * Template files should define a `$config` variable containing
  179. * all the templates to load. Loaded templates will be merged with existing
  180. * templates.
  181. *
  182. * @param string $file The file to load
  183. * @return void
  184. */
  185. public function load($file)
  186. {
  187. $loader = new PhpConfig();
  188. $templates = $loader->read($file);
  189. $this->add($templates);
  190. }
  191. /**
  192. * Remove the named template.
  193. *
  194. * @param string $name The template to remove.
  195. * @return void
  196. */
  197. public function remove($name)
  198. {
  199. $this->config($name, null);
  200. unset($this->_compiled[$name]);
  201. }
  202. /**
  203. * Format a template string with $data
  204. *
  205. * @param string $name The template name.
  206. * @param array $data The data to insert.
  207. * @return string
  208. */
  209. public function format($name, array $data)
  210. {
  211. if (!isset($this->_compiled[$name])) {
  212. return '';
  213. }
  214. list($template, $placeholders) = $this->_compiled[$name];
  215. if ($template === null) {
  216. return '';
  217. }
  218. $replace = [];
  219. foreach ($placeholders as $placeholder) {
  220. $replace[] = isset($data[$placeholder]) ? $data[$placeholder] : null;
  221. }
  222. return vsprintf($template, $replace);
  223. }
  224. /**
  225. * Returns a space-delimited string with items of the $options array. If a key
  226. * of $options array happens to be one of those listed
  227. * in `StringTemplate::$_compactAttributes` and its value is one of:
  228. *
  229. * - '1' (string)
  230. * - 1 (integer)
  231. * - true (boolean)
  232. * - 'true' (string)
  233. *
  234. * Then the value will be reset to be identical with key's name.
  235. * If the value is not one of these 4, the parameter is not output.
  236. *
  237. * 'escape' is a special option in that it controls the conversion of
  238. * attributes to their HTML-entity encoded equivalents. Set to false to disable HTML-encoding.
  239. *
  240. * If value for any option key is set to `null` or `false`, that option will be excluded from output.
  241. *
  242. * This method uses the 'attribute' and 'compactAttribute' templates. Each of
  243. * these templates uses the `name` and `value` variables. You can modify these
  244. * templates to change how attributes are formatted.
  245. *
  246. * @param array|null $options Array of options.
  247. * @param array|null $exclude Array of options to be excluded, the options here will not be part of the return.
  248. * @return string Composed attributes.
  249. */
  250. public function formatAttributes($options, $exclude = null)
  251. {
  252. $insertBefore = ' ';
  253. $options = (array)$options + ['escape' => true];
  254. if (!is_array($exclude)) {
  255. $exclude = [];
  256. }
  257. $exclude = ['escape' => true, 'idPrefix' => true] + array_flip($exclude);
  258. $escape = $options['escape'];
  259. $attributes = [];
  260. foreach ($options as $key => $value) {
  261. if (!isset($exclude[$key]) && $value !== false && $value !== null) {
  262. $attributes[] = $this->_formatAttribute($key, $value, $escape);
  263. }
  264. }
  265. $out = trim(implode(' ', $attributes));
  266. return $out ? $insertBefore . $out : '';
  267. }
  268. /**
  269. * Formats an individual attribute, and returns the string value of the composed attribute.
  270. * Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
  271. *
  272. * @param string $key The name of the attribute to create
  273. * @param string|array $value The value of the attribute to create.
  274. * @param bool $escape Define if the value must be escaped
  275. * @return string The composed attribute.
  276. */
  277. protected function _formatAttribute($key, $value, $escape = true)
  278. {
  279. if (is_array($value)) {
  280. $value = implode(' ', $value);
  281. }
  282. if (is_numeric($key)) {
  283. return "$value=\"$value\"";
  284. }
  285. $truthy = [1, '1', true, 'true', $key];
  286. $isMinimized = isset($this->_compactAttributes[$key]);
  287. if ($isMinimized && in_array($value, $truthy, true)) {
  288. return "$key=\"$key\"";
  289. }
  290. if ($isMinimized) {
  291. return '';
  292. }
  293. return $key . '="' . ($escape ? h($value) : $value) . '"';
  294. }
  295. }