View.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <?php
  2. /**
  3. * Acts as an object wrapper for HTML pages with embedded PHP, called "views".
  4. * Variables can be assigned with the view object and referenced locally within
  5. * the view.
  6. *
  7. * @package Kohana
  8. * @category Base
  9. * @author Kohana Team
  10. * @copyright (c) Kohana Team
  11. * @license https://koseven.ga/LICENSE.md
  12. */
  13. class Kohana_View {
  14. // Array of global variables
  15. protected static $_global_data = [];
  16. /**
  17. * Returns a new View object. If you do not define the "file" parameter,
  18. * you must call [View::set_filename].
  19. *
  20. * $view = View::factory($file);
  21. *
  22. * @param string $file view filename
  23. * @param array $data array of values
  24. * @return View
  25. */
  26. public static function factory($file = NULL, array $data = NULL)
  27. {
  28. return new View($file, $data);
  29. }
  30. /**
  31. * Captures the output that is generated when a view is included.
  32. * The view data will be extracted to make local variables. This method
  33. * is static to prevent object scope resolution.
  34. *
  35. * $output = View::capture($file, $data);
  36. *
  37. * @param string $kohana_view_filename filename
  38. * @param array $kohana_view_data variables
  39. * @return string
  40. * @throws Exception
  41. */
  42. protected static function capture($kohana_view_filename, array $kohana_view_data)
  43. {
  44. // Import the view variables to local namespace
  45. extract($kohana_view_data, EXTR_SKIP);
  46. if (View::$_global_data)
  47. {
  48. // Import the global view variables to local namespace
  49. extract(View::$_global_data, EXTR_SKIP | EXTR_REFS);
  50. }
  51. // Capture the view output
  52. ob_start();
  53. try
  54. {
  55. // Load the view within the current scope
  56. include $kohana_view_filename;
  57. }
  58. catch (Exception $e)
  59. {
  60. // Delete the output buffer
  61. ob_end_clean();
  62. // Re-throw the exception
  63. throw $e;
  64. }
  65. // Get the captured output and close the buffer
  66. return ob_get_clean();
  67. }
  68. /**
  69. * Sets a global variable, similar to [View::set], except that the
  70. * variable will be accessible to all views.
  71. *
  72. * View::set_global($name, $value);
  73. *
  74. * You can also use an array or Traversable object to set several values at once:
  75. *
  76. * // Create the values $food and $beverage in the view
  77. * View::set_global(array('food' => 'bread', 'beverage' => 'water'));
  78. *
  79. * [!!] Note: When setting with using Traversable object we're not attaching the whole object to the view,
  80. * i.e. the object's standard properties will not be available in the view context.
  81. *
  82. * @param string|array|Traversable $key variable name or an array of variables
  83. * @param mixed $value value
  84. * @return void
  85. */
  86. public static function set_global($key, $value = NULL)
  87. {
  88. if (is_array($key) OR $key instanceof Traversable)
  89. {
  90. foreach ($key as $name => $value)
  91. {
  92. View::$_global_data[$name] = $value;
  93. }
  94. }
  95. else
  96. {
  97. View::$_global_data[$key] = $value;
  98. }
  99. }
  100. /**
  101. * Assigns a global variable by reference, similar to [View::bind], except
  102. * that the variable will be accessible to all views.
  103. *
  104. * View::bind_global($key, $value);
  105. *
  106. * @param string $key variable name
  107. * @param mixed $value referenced variable
  108. * @return void
  109. */
  110. public static function bind_global($key, & $value)
  111. {
  112. View::$_global_data[$key] =& $value;
  113. }
  114. // View filename
  115. protected $_file;
  116. // Array of local variables
  117. protected $_data = [];
  118. /**
  119. * Sets the initial view filename and local data. Views should almost
  120. * always only be created using [View::factory].
  121. *
  122. * $view = new View($file);
  123. *
  124. * @param string $file view filename
  125. * @param array $data array of values
  126. * @uses View::set_filename
  127. */
  128. public function __construct($file = NULL, array $data = NULL)
  129. {
  130. if ($file !== NULL)
  131. {
  132. $this->set_filename($file);
  133. }
  134. if ($data !== NULL)
  135. {
  136. // Add the values to the current data
  137. $this->_data = $data + $this->_data;
  138. }
  139. }
  140. /**
  141. * Magic method, searches for the given variable and returns its value.
  142. * Local variables will be returned before global variables.
  143. *
  144. * $value = $view->foo;
  145. *
  146. * [!!] If the variable has not yet been set, an exception will be thrown.
  147. *
  148. * @param string $key variable name
  149. * @return mixed
  150. * @throws Kohana_Exception
  151. */
  152. public function & __get($key)
  153. {
  154. if (array_key_exists($key, $this->_data))
  155. {
  156. return $this->_data[$key];
  157. }
  158. elseif (array_key_exists($key, View::$_global_data))
  159. {
  160. return View::$_global_data[$key];
  161. }
  162. else
  163. {
  164. throw new Kohana_Exception('View variable is not set: :var',
  165. [':var' => $key]);
  166. }
  167. }
  168. /**
  169. * Magic method, calls [View::set] with the same parameters.
  170. *
  171. * $view->foo = 'something';
  172. *
  173. * @param string $key variable name
  174. * @param mixed $value value
  175. * @return void
  176. */
  177. public function __set($key, $value)
  178. {
  179. $this->set($key, $value);
  180. }
  181. /**
  182. * Magic method, determines if a variable is set.
  183. *
  184. * isset($view->foo);
  185. *
  186. * [!!] `NULL` variables are not considered to be set by [isset](http://php.net/isset).
  187. *
  188. * @param string $key variable name
  189. * @return boolean
  190. */
  191. public function __isset($key)
  192. {
  193. return (isset($this->_data[$key]) OR isset(View::$_global_data[$key]));
  194. }
  195. /**
  196. * Magic method, unsets a given variable.
  197. *
  198. * unset($view->foo);
  199. *
  200. * @param string $key variable name
  201. * @return void
  202. */
  203. public function __unset($key)
  204. {
  205. unset($this->_data[$key], View::$_global_data[$key]);
  206. }
  207. /**
  208. * Magic method, returns the output of [View::render].
  209. *
  210. * @return string
  211. * @uses View::render
  212. */
  213. public function __toString()
  214. {
  215. try
  216. {
  217. return $this->render();
  218. }
  219. catch (Exception $e)
  220. {
  221. /**
  222. * Display the exception message.
  223. *
  224. * We use this method here because it's impossible to throw an
  225. * exception from __toString().
  226. */
  227. $error_response = Kohana_Exception::_handler($e);
  228. return $error_response->body();
  229. }
  230. }
  231. /**
  232. * Sets the view filename.
  233. *
  234. * $view->set_filename($file);
  235. *
  236. * @param string $file view filename
  237. * @return View
  238. * @throws View_Exception
  239. */
  240. public function set_filename($file)
  241. {
  242. if (($path = Kohana::find_file('views', $file)) === FALSE)
  243. {
  244. throw new View_Exception('The requested view :file could not be found', [
  245. ':file' => $file,
  246. ]);
  247. }
  248. // Store the file path locally
  249. $this->_file = $path;
  250. return $this;
  251. }
  252. /**
  253. * Assigns a variable by name. Assigned values will be available as a
  254. * variable within the view file:
  255. *
  256. * // This value can be accessed as $foo within the view
  257. * $view->set('foo', 'my value');
  258. *
  259. * You can also use an array or Traversable object to set several values at once:
  260. *
  261. * // Create the values $food and $beverage in the view
  262. * $view->set(array('food' => 'bread', 'beverage' => 'water'));
  263. *
  264. * [!!] Note: When setting with using Traversable object we're not attaching the whole object to the view,
  265. * i.e. the object's standard properties will not be available in the view context.
  266. *
  267. * @param string|array|Traversable $key variable name or an array of variables
  268. * @param mixed $value value
  269. * @return $this
  270. */
  271. public function set($key, $value = NULL)
  272. {
  273. if (is_array($key) OR $key instanceof Traversable)
  274. {
  275. foreach ($key as $name => $value)
  276. {
  277. $this->_data[$name] = $value;
  278. }
  279. }
  280. else
  281. {
  282. $this->_data[$key] = $value;
  283. }
  284. return $this;
  285. }
  286. /**
  287. * Assigns a value by reference. The benefit of binding is that values can
  288. * be altered without re-setting them. It is also possible to bind variables
  289. * before they have values. Assigned values will be available as a
  290. * variable within the view file:
  291. *
  292. * // This reference can be accessed as $ref within the view
  293. * $view->bind('ref', $bar);
  294. *
  295. * @param string $key variable name
  296. * @param mixed $value referenced variable
  297. * @return $this
  298. */
  299. public function bind($key, & $value)
  300. {
  301. $this->_data[$key] =& $value;
  302. return $this;
  303. }
  304. /**
  305. * Renders the view object to a string. Global and local data are merged
  306. * and extracted to create local variables within the view file.
  307. *
  308. * $output = $view->render();
  309. *
  310. * [!!] Global variables with the same key name as local variables will be
  311. * overwritten by the local variable.
  312. *
  313. * @param string $file view filename
  314. * @return string
  315. * @throws View_Exception
  316. * @uses View::capture
  317. */
  318. public function render($file = NULL)
  319. {
  320. if ($file !== NULL)
  321. {
  322. $this->set_filename($file);
  323. }
  324. if (empty($this->_file))
  325. {
  326. throw new View_Exception('You must set the file to use within your view before rendering');
  327. }
  328. // Combine local and global data and capture the output
  329. return View::capture($this->_file, $this->_data);
  330. }
  331. }