Helper.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 0.2.9
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\View;
  16. use Cake\Core\InstanceConfigTrait;
  17. use Cake\Event\EventListenerInterface;
  18. /**
  19. * Abstract base class for all other Helpers in CakePHP.
  20. * Provides common methods and features.
  21. *
  22. * ### Callback methods
  23. *
  24. * Helpers support a number of callback methods. These callbacks allow you to hook into
  25. * the various view lifecycle events and either modify existing view content or perform
  26. * other application specific logic. The events are not implemented by this base class, as
  27. * implementing a callback method subscribes a helper to the related event. The callback methods
  28. * are as follows:
  29. *
  30. * - `beforeRender(Event $event, $viewFile)` - beforeRender is called before the view file is rendered.
  31. * - `afterRender(Event $event, $viewFile)` - afterRender is called after the view file is rendered
  32. * but before the layout has been rendered.
  33. * - beforeLayout(Event $event, $layoutFile)` - beforeLayout is called before the layout is rendered.
  34. * - `afterLayout(Event $event, $layoutFile)` - afterLayout is called after the layout has rendered.
  35. * - `beforeRenderFile(Event $event, $viewFile)` - Called before any view fragment is rendered.
  36. * - `afterRenderFile(Event $event, $viewFile, $content)` - Called after any view fragment is rendered.
  37. * If a listener returns a non-null value, the output of the rendered file will be set to that.
  38. */
  39. class Helper implements EventListenerInterface
  40. {
  41. use InstanceConfigTrait;
  42. /**
  43. * List of helpers used by this helper
  44. *
  45. * @var array
  46. */
  47. protected $helpers = [];
  48. /**
  49. * Default config for this helper.
  50. *
  51. * @var array
  52. */
  53. protected $_defaultConfig = [];
  54. /**
  55. * A helper lookup table used to lazy load helper objects.
  56. *
  57. * @var array
  58. */
  59. protected $_helperMap = [];
  60. /**
  61. * Unused.
  62. *
  63. * @var array
  64. * @deprecated 3.7.0 This property is unused and will be removed in 4.0.0.
  65. */
  66. public $fieldset = [];
  67. /**
  68. * Unused.
  69. *
  70. * @var array
  71. * @deprecated 3.7.0 This property is unused and will be removed in 4.0.0.
  72. */
  73. public $tags = [];
  74. /**
  75. * The View instance this helper is attached to
  76. *
  77. * @var \Cake\View\View
  78. */
  79. protected $_View;
  80. /**
  81. * Default Constructor
  82. *
  83. * @param \Cake\View\View $View The View this helper is being attached to.
  84. * @param array $config Configuration settings for the helper.
  85. */
  86. public function __construct(View $View, array $config = [])
  87. {
  88. $this->_View = $View;
  89. $this->setConfig($config);
  90. if (!empty($this->helpers)) {
  91. $this->_helperMap = $View->helpers()->normalizeArray($this->helpers);
  92. }
  93. $this->initialize($config);
  94. }
  95. /**
  96. * Provide non fatal errors on missing method calls.
  97. *
  98. * @param string $method Method to invoke
  99. * @param array $params Array of params for the method.
  100. * @return void
  101. */
  102. public function __call($method, $params)
  103. {
  104. trigger_error(sprintf('Method %1$s::%2$s does not exist', get_class($this), $method), E_USER_WARNING);
  105. }
  106. /**
  107. * Lazy loads helpers.
  108. *
  109. * @param string $name Name of the property being accessed.
  110. * @return \Cake\View\Helper|null Helper instance if helper with provided name exists
  111. */
  112. public function __get($name)
  113. {
  114. if (isset($this->_helperMap[$name]) && !isset($this->{$name})) {
  115. $config = ['enabled' => false] + (array)$this->_helperMap[$name]['config'];
  116. $this->{$name} = $this->_View->loadHelper($this->_helperMap[$name]['class'], $config);
  117. return $this->{$name};
  118. }
  119. $removed = [
  120. 'theme' => 'getTheme',
  121. 'plugin' => 'getPlugin',
  122. ];
  123. if (isset($removed[$name])) {
  124. $method = $removed[$name];
  125. deprecationWarning(sprintf(
  126. 'Helper::$%s is removed. Use $view->%s() instead.',
  127. $name,
  128. $method
  129. ));
  130. return $this->_View->{$method}();
  131. }
  132. if ($name === 'request') {
  133. deprecationWarning(
  134. 'Helper::$%s is removed. Use $view->%s() instead. Use $helper->getView()->getRequest() instead.'
  135. );
  136. return $this->_View->getRequest();
  137. }
  138. if ($name === 'helpers') {
  139. deprecationWarning(
  140. 'Helper::$helpers is now protected and should not be accessed from outside a helper class.'
  141. );
  142. return $this->helpers;
  143. }
  144. }
  145. /**
  146. * Magic setter for removed properties.
  147. *
  148. * @param string $name Property name.
  149. * @param mixed $value Value to set.
  150. * @return void
  151. */
  152. public function __set($name, $value)
  153. {
  154. $removed = [
  155. 'theme' => 'setTheme',
  156. 'plugin' => 'setPlugin',
  157. ];
  158. if (isset($removed[$name])) {
  159. $method = $removed[$name];
  160. deprecationWarning(sprintf(
  161. 'Helper::$%s is removed. Use $view->%s() instead.',
  162. $name,
  163. $method
  164. ));
  165. $this->_View->{$method}($value);
  166. return;
  167. }
  168. if ($name === 'request') {
  169. deprecationWarning(
  170. 'Helper::$%s is removed. Use $view->%s() instead. Use $helper->getView()->setRequest() instead.'
  171. );
  172. $this->_View->setRequest($value);
  173. return;
  174. }
  175. if ($name === 'helpers') {
  176. deprecationWarning(
  177. 'Helper::$helpers is now protected and should not be accessed from outside a helper class.'
  178. );
  179. }
  180. $this->{$name} = $value;
  181. }
  182. /**
  183. * Get the view instance this helper is bound to.
  184. *
  185. * @return \Cake\View\View The bound view instance.
  186. */
  187. public function getView()
  188. {
  189. return $this->_View;
  190. }
  191. /**
  192. * Returns a string to be used as onclick handler for confirm dialogs.
  193. *
  194. * @param string $message Message to be displayed
  195. * @param string $okCode Code to be executed after user chose 'OK'
  196. * @param string $cancelCode Code to be executed after user chose 'Cancel'
  197. * @param array $options Array of options
  198. * @return string onclick JS code
  199. */
  200. protected function _confirm($message, $okCode, $cancelCode = '', $options = [])
  201. {
  202. $message = $this->_cleanConfirmMessage($message);
  203. $confirm = "if (confirm({$message})) { {$okCode} } {$cancelCode}";
  204. // We cannot change the key here in 3.x, but the behavior is inverted in this case
  205. $escape = isset($options['escape']) && $options['escape'] === false;
  206. if ($escape) {
  207. /** @var string $confirm */
  208. $confirm = h($confirm);
  209. }
  210. return $confirm;
  211. }
  212. /**
  213. * Returns a string read to be used in confirm()
  214. *
  215. * @param string $message The message to clean
  216. * @return mixed
  217. */
  218. protected function _cleanConfirmMessage($message)
  219. {
  220. return str_replace('\\\n', '\n', json_encode($message));
  221. }
  222. /**
  223. * Adds the given class to the element options
  224. *
  225. * @param array $options Array options/attributes to add a class to
  226. * @param string|null $class The class name being added.
  227. * @param string $key the key to use for class.
  228. * @return array Array of options with $key set.
  229. */
  230. public function addClass(array $options = [], $class = null, $key = 'class')
  231. {
  232. if (isset($options[$key]) && is_array($options[$key])) {
  233. $options[$key][] = $class;
  234. } elseif (isset($options[$key]) && trim($options[$key])) {
  235. $options[$key] .= ' ' . $class;
  236. } else {
  237. $options[$key] = $class;
  238. }
  239. return $options;
  240. }
  241. /**
  242. * Get the View callbacks this helper is interested in.
  243. *
  244. * By defining one of the callback methods a helper is assumed
  245. * to be interested in the related event.
  246. *
  247. * Override this method if you need to add non-conventional event listeners.
  248. * Or if you want helpers to listen to non-standard events.
  249. *
  250. * @return array
  251. */
  252. public function implementedEvents()
  253. {
  254. $eventMap = [
  255. 'View.beforeRenderFile' => 'beforeRenderFile',
  256. 'View.afterRenderFile' => 'afterRenderFile',
  257. 'View.beforeRender' => 'beforeRender',
  258. 'View.afterRender' => 'afterRender',
  259. 'View.beforeLayout' => 'beforeLayout',
  260. 'View.afterLayout' => 'afterLayout'
  261. ];
  262. $events = [];
  263. foreach ($eventMap as $event => $method) {
  264. if (method_exists($this, $method)) {
  265. $events[$event] = $method;
  266. }
  267. }
  268. return $events;
  269. }
  270. /**
  271. * Constructor hook method.
  272. *
  273. * Implement this method to avoid having to overwrite the constructor and call parent.
  274. *
  275. * @param array $config The configuration settings provided to this helper.
  276. * @return void
  277. */
  278. public function initialize(array $config)
  279. {
  280. }
  281. /**
  282. * Returns an array that can be used to describe the internal state of this
  283. * object.
  284. *
  285. * @return array
  286. */
  287. public function __debugInfo()
  288. {
  289. return [
  290. 'helpers' => $this->helpers,
  291. 'implementedEvents' => $this->implementedEvents(),
  292. '_config' => $this->getConfig(),
  293. ];
  294. }
  295. }