PageRenderTime 57ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Core/View.php

http://github.com/AlephTav/Aleph
PHP | 340 lines | 148 code | 25 blank | 167 comment | 19 complexity | 8a25bbcb6189dd43dfda8e161d83c11e MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * Copyright (c) 2013 - 2016 Aleph Tav
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  6. * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
  8. * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  11. *
  12. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  13. * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  15. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  16. *
  17. * @author Aleph Tav <4lephtav@gmail.com>
  18. * @link http://www.4leph.com
  19. * @copyright Copyright &copy; 2013 - 2016 Aleph Tav
  20. * @license http://www.opensource.org/licenses/MIT
  21. */
  22. namespace Aleph\Core;
  23. /**
  24. * The simple view class using PHP as template language.
  25. *
  26. * @author Aleph Tav <4lephtav@gmail.com>
  27. * @version 1.0.3
  28. * @package aleph.core
  29. */
  30. class View extends Template
  31. {
  32. /**
  33. * Error message templates.
  34. */
  35. const ERR_VIEW_1 = 'View "%s" is not found.';
  36. const ERR_VIEW_2 = 'No blocks have been started yet.';
  37. /**
  38. * Blocks of views.
  39. *
  40. * @var array
  41. */
  42. private $blocks = [];
  43. /**
  44. * Contains in-progress blocks.
  45. *
  46. * @var array
  47. */
  48. private $stack = [];
  49. /**
  50. * The number of rendering views.
  51. *
  52. * @var int
  53. */
  54. private $level = 0;
  55. /**
  56. * Name of the parent view.
  57. *
  58. * @var string
  59. */
  60. private $parentView = '';
  61. /**
  62. * Name of the parent block.
  63. * This block is used as container that contains all outputs of the current view.
  64. *
  65. * @var string
  66. */
  67. private $parentBlock = '';
  68. /**
  69. * Extension of all view files.
  70. *
  71. * @var string
  72. */
  73. private $extension = '';
  74. /**
  75. * View directories.
  76. *
  77. * @var array
  78. */
  79. private $directories = [];
  80. /**
  81. * Constructor.
  82. *
  83. * @param string $view A view string or path to a view file.
  84. * @param array $vars The view variables.
  85. * @param string $extension Extension of all view files.
  86. * @param array $directories Directories of view files.
  87. */
  88. public function __construct(string $view = '', array $vars = [], string $extension = '', array $directories = [])
  89. {
  90. parent::__construct($view);
  91. $this->setVars($vars);
  92. $this->extension = $extension;
  93. $this->directories = $directories;
  94. }
  95. /**
  96. * Returns extension of view files.
  97. *
  98. * @return string
  99. */
  100. public function getExtension() : string
  101. {
  102. return $this->extension;
  103. }
  104. /**
  105. * Sets extension of view files.
  106. *
  107. * @param string $extension
  108. * @return void
  109. */
  110. public function setExtension(string $extension)
  111. {
  112. $this->extension = $extension;
  113. }
  114. /**
  115. * Returns directories of view files.
  116. *
  117. * @return array
  118. */
  119. public function getDirectories() : array
  120. {
  121. return $this->directories;
  122. }
  123. /**
  124. * Sets directories of view files.
  125. *
  126. * @param array $directories
  127. * @return void
  128. */
  129. public function setDirectories(array $directories = [])
  130. {
  131. $this->directories = $directories;
  132. }
  133. /**
  134. * Returns rendered view content.
  135. *
  136. * @return string
  137. */
  138. public function render() : string
  139. {
  140. ${'(_._)'} = $this->findViewFile();
  141. $this->level++;
  142. ob_start();
  143. ob_implicit_flush(false);
  144. extract($this->getVars());
  145. require(${'(_._)'});
  146. while ($this->stack) {
  147. $this->endBlock();
  148. }
  149. $content = ob_get_clean();
  150. if ($this->parentView) {
  151. if (strlen($this->parentBlock)) {
  152. $this->blocks[$this->parentBlock] = $content;
  153. }
  154. $currentView = $this->getTemplate();
  155. $this->setTemplate($this->parentView);
  156. $this->parentView = null;
  157. $this->parentBlock = null;
  158. $content = $this->render();
  159. $this->setTemplate($currentView);
  160. }
  161. $this->level--;
  162. if ($this->level == 0) {
  163. $this->blocks = [];
  164. }
  165. return $content;
  166. }
  167. /**
  168. * Returns the block content.
  169. *
  170. * @param string $block The block name.
  171. * @param mixed $default The default block content.
  172. * @return string
  173. */
  174. protected function getBlock(string $block, $default = null) : string
  175. {
  176. return str_replace(md5($block), '', $this->blocks[$block] ?? $default);
  177. }
  178. /**
  179. * Sets block content.
  180. *
  181. * @param string $block The block name.
  182. * @param mixed $content The block content.
  183. * @return void
  184. */
  185. protected function setBlock(string $block, $content)
  186. {
  187. $this->blocks[$block] = $content;
  188. }
  189. /**
  190. * Starts the new block of a view.
  191. *
  192. * @param string $block The block name.
  193. * @param mixed $content The block content.
  194. * @return void
  195. */
  196. protected function startBlock(string $block, $content = null)
  197. {
  198. if ((string)$content === '') {
  199. $this->extendBlock($block, $content);
  200. } else {
  201. ob_start();
  202. ob_implicit_flush(false);
  203. $this->stack[] = $block;
  204. }
  205. }
  206. /**
  207. * Ends the block of a view.
  208. *
  209. * @param bool $overwrite Determines if the old block content should be overwritten.
  210. * @return string The block name.
  211. * @throws \BadMethodCallException If no blocks have been started yet.
  212. */
  213. protected function endBlock(bool $overwrite = false) : string
  214. {
  215. if (!$this->stack) {
  216. throw new \BadMethodCallException(static::ERR_VIEW_2);
  217. }
  218. $block = array_pop($this->stack);
  219. if ($overwrite) {
  220. $this->blocks[$block] = ob_get_clean();
  221. } else {
  222. $this->extendBlock($block, ob_get_clean());
  223. }
  224. return $block;
  225. }
  226. /**
  227. * Extends the given block.
  228. *
  229. * @param string $block The block name.
  230. * @param mixed $content The inherited block content.
  231. * @return void
  232. */
  233. protected function extendBlock(string $block, $content)
  234. {
  235. if (isset($this->blocks[$block])) {
  236. $content = str_replace(md5($block), $content, $this->blocks[$block]);
  237. }
  238. $this->blocks[$block] = $content;
  239. }
  240. /**
  241. * Ends block and appends its content.
  242. *
  243. * @return string The block name.
  244. * @throws \BadMethodCallException If no blocks have been started yet.
  245. */
  246. protected function appendBlock() : string
  247. {
  248. if (!$this->stack) {
  249. throw new \BadMethodCallException(static::ERR_VIEW_2);
  250. }
  251. $block = array_pop($this->stack);
  252. if (isset($this->blocks[$block])) {
  253. $this->blocks[$block] .= ob_get_clean();
  254. } else {
  255. $this->blocks[$block] = ob_get_clean();
  256. }
  257. return $block;
  258. }
  259. /**
  260. * Outputs the block content.
  261. *
  262. * @param string $block The block name. If it is not defined, the previously started block will be shown.
  263. * @return void
  264. */
  265. protected function showBlock(string $block = '')
  266. {
  267. echo $this->getBlock($block !== '' ? $block : $this->endBlock(false));
  268. }
  269. /**
  270. * Outputs the parent block content.
  271. *
  272. * @return void
  273. * @throws \BadMethodCallException If no blocks have been started yet.
  274. */
  275. protected function parentContent()
  276. {
  277. if (!$this->stack) {
  278. throw new \BadMethodCallException(static::ERR_VIEW_2);
  279. }
  280. echo md5(end($this->stack));
  281. }
  282. /**
  283. * Extends the current view from the parent one.
  284. *
  285. * @param string $view The parent view name or path to the view file.
  286. * @param string $block The name of the parent view's block which the current view's content will be inserted to.
  287. * @return void
  288. */
  289. protected function inherit(string $view, string $block = '')
  290. {
  291. $this->parentView = $view;
  292. $this->parentBlock = $block;
  293. }
  294. /**
  295. * Searches view file by view name.
  296. *
  297. * @return string
  298. * @throws \LogicException If the given view is not found.
  299. */
  300. private function findViewFile() : string
  301. {
  302. $view = $this->getTemplate();
  303. if (strlen($view) <= PHP_MAXPATHLEN && is_file($view)) {
  304. return $view;
  305. }
  306. $ext = $this->extension !== '' ? '.' . ltrim($this->extension, '.') : '';
  307. foreach ($this->directories as $dir) {
  308. $file = $dir . DIRECTORY_SEPARATOR . $view . $ext;
  309. if (strlen($file) <= PHP_MAXPATHLEN && is_file($file)) {
  310. return $file;
  311. }
  312. }
  313. throw new \LogicException(sprintf(static::ERR_VIEW_1, $view));
  314. }
  315. }