PageRenderTime 33ms CodeModel.GetById 37ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/view.php

https://github.com/mjphaynes/core
PHP | 557 lines | 241 code | 61 blank | 255 comment | 24 complexity | bc06875ea6b8157acd65ea0e9469e9fb MD5 | raw file
  1. <?php
  2. /**
  3. * Fuel is a fast, lightweight, community driven PHP5 framework.
  4. *
  5. * @package Fuel
  6. * @version 1.0
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2011 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. /**
  14. * View class
  15. *
  16. * Acts as an object wrapper for HTML pages with embedded PHP, called "views".
  17. * Variables can be assigned with the view object and referenced locally within
  18. * the view.
  19. *
  20. * @package Fuel
  21. * @category Core
  22. * @link http://fuelphp.com/docs/classes/view.html
  23. */
  24. class View {
  25. /**
  26. * @var array Global view data
  27. */
  28. protected static $global_data = array();
  29. /**
  30. * @var array Holds a list of specific filter rules for global variables
  31. */
  32. protected static $global_filter = array();
  33. /**
  34. * @var array Current active search paths
  35. */
  36. protected $request_paths = array();
  37. /**
  38. * @var bool Whether to auto-filter the view's data
  39. */
  40. protected $auto_filter = true;
  41. /**
  42. * @var array Holds a list of specific filter rules for local variables
  43. */
  44. protected $local_filter = array();
  45. /**
  46. * @var string The view's filename
  47. */
  48. protected $file_name;
  49. /**
  50. * @var array The view's data
  51. */
  52. protected $data = array();
  53. /**
  54. * @var string The view file extension
  55. */
  56. protected $extension = 'php';
  57. /**
  58. * @var Request active request when the View was created
  59. */
  60. protected $active_request;
  61. /**
  62. * This method is deprecated...use forge() instead.
  63. *
  64. * @deprecated until 1.2
  65. */
  66. public static function factory($file = null, $data = null, $auto_filter = null)
  67. {
  68. logger(\Fuel::L_WARNING, 'This method is deprecated. Please use a forge() instead.', __METHOD__);
  69. return static::forge($file, $data, $auto_filter);
  70. }
  71. /**
  72. * Returns a new View object. If you do not define the "file" parameter,
  73. * you must call [static::set_filename].
  74. *
  75. * $view = View::forge($file);
  76. *
  77. * @param string view filename
  78. * @param array array of values
  79. * @return View
  80. */
  81. public static function forge($file = null, $data = null, $auto_filter = null)
  82. {
  83. return new static($file, $data, $auto_filter);
  84. }
  85. /**
  86. * Sets the initial view filename and local data.
  87. *
  88. * $view = new View($file);
  89. *
  90. * @param string view filename
  91. * @param array array of values
  92. * @return void
  93. * @uses View::set_filename
  94. */
  95. public function __construct($file = null, $data = null, $filter = null)
  96. {
  97. if (is_object($data) === true)
  98. {
  99. $data = get_object_vars($data);
  100. }
  101. elseif ($data and ! is_array($data))
  102. {
  103. throw new \InvalidArgumentException('The data parameter only accepts objects and arrays.');
  104. }
  105. // @TODO in v1.2 remove the auto_encode_view_data reference.
  106. $this->auto_filter = is_null($filter) ? \Config::get('security.auto_filter_output', \Config::get('security.auto_encode_view_data', true)) : $filter;
  107. if ($file !== null)
  108. {
  109. $this->set_filename($file);
  110. }
  111. if ($data !== null)
  112. {
  113. // Add the values to the current data
  114. $this->data = $data;
  115. }
  116. // store the current request search paths to deal with out-of-context rendering
  117. if (class_exists('Request', false) and $active = \Request::active() and \Request::main() != $active)
  118. {
  119. $this->request_paths = $active->get_paths();
  120. }
  121. isset($active) and $this->active_request = $active;
  122. }
  123. /**
  124. * Magic method, searches for the given variable and returns its value.
  125. * Local variables will be returned before global variables.
  126. *
  127. * $value = $view->foo;
  128. *
  129. * @param string variable name
  130. * @return mixed
  131. * @throws OutOfBoundsException
  132. */
  133. public function & __get($key)
  134. {
  135. return $this->get($key);
  136. }
  137. /**
  138. * Magic method, calls [static::set] with the same parameters.
  139. *
  140. * $view->foo = 'something';
  141. *
  142. * @param string variable name
  143. * @param mixed value
  144. * @return void
  145. */
  146. public function __set($key, $value)
  147. {
  148. $this->set($key, $value);
  149. }
  150. /**
  151. * Magic method, determines if a variable is set.
  152. *
  153. * isset($view->foo);
  154. *
  155. * [!!] `null` variables are not considered to be set by [isset](http://php.net/isset).
  156. *
  157. * @param string variable name
  158. * @return boolean
  159. */
  160. public function __isset($key)
  161. {
  162. return (isset($this->data[$key]) or isset(static::$global_data[$key]));
  163. }
  164. /**
  165. * Magic method, unsets a given variable.
  166. *
  167. * unset($view->foo);
  168. *
  169. * @param string variable name
  170. * @return void
  171. */
  172. public function __unset($key)
  173. {
  174. unset($this->data[$key], static::$global_data[$key]);
  175. }
  176. /**
  177. * Magic method, returns the output of [static::render].
  178. *
  179. * @return string
  180. * @uses View::render
  181. */
  182. public function __toString()
  183. {
  184. try
  185. {
  186. return $this->render();
  187. }
  188. catch (\Exception $e)
  189. {
  190. \Error::exception_handler($e);
  191. return '';
  192. }
  193. }
  194. /**
  195. * Captures the output that is generated when a view is included.
  196. * The view data will be extracted to make local variables. This method
  197. * is static to prevent object scope resolution.
  198. *
  199. * $output = $this->process_file();
  200. *
  201. * @param string File override
  202. * @param array variables
  203. * @return string
  204. */
  205. protected function process_file($file_override = false)
  206. {
  207. $clean_room = function ($__file_name, array $__data) {
  208. extract($__data, EXTR_REFS);
  209. // Capture the view output
  210. ob_start();
  211. try
  212. {
  213. // Load the view within the current scope
  214. include $__file_name;
  215. }
  216. catch (\Exception $e)
  217. {
  218. // Delete the output buffer
  219. ob_end_clean();
  220. // Re-throw the exception
  221. throw $e;
  222. }
  223. // Get the captured output and close the buffer
  224. return ob_get_clean();
  225. };
  226. return $clean_room($file_override ?: $this->file_name, $this->get_data());
  227. }
  228. /**
  229. * Retrieves all the data, both local and global. It filters the data if
  230. * necessary.
  231. *
  232. * $data = $this->get_data();
  233. *
  234. * @return array
  235. */
  236. protected function get_data()
  237. {
  238. $clean_it = function ($data, $rules, $auto_filter) {
  239. foreach ($data as $key => $value)
  240. {
  241. $filter = array_key_exists($key, $rules) ? $rules[$key] : null;
  242. $filter = is_null($filter) ? $auto_filter : $filter;
  243. $data[$key] = $filter ? \Security::clean($value, null, 'security.output_filter') : $value;
  244. }
  245. return $data;
  246. };
  247. $data = array();
  248. if ( ! empty($this->data))
  249. {
  250. $data += $clean_it($this->data, $this->local_filter, $this->auto_filter);
  251. }
  252. if ( ! empty(static::$global_data))
  253. {
  254. $data += $clean_it(static::$global_data, static::$global_filter, $this->auto_filter);
  255. }
  256. return $data;
  257. }
  258. /**
  259. * Sets a global variable, similar to [static::set], except that the
  260. * variable will be accessible to all views.
  261. *
  262. * View::set_global($name, $value);
  263. *
  264. * @param string variable name or an array of variables
  265. * @param mixed value
  266. * @param bool whether to filter the data or not
  267. * @return void
  268. */
  269. public static function set_global($key, $value = null, $filter = null)
  270. {
  271. if (is_array($key))
  272. {
  273. foreach ($key as $name => $value)
  274. {
  275. if ($filter !== null)
  276. {
  277. static::$global_filter[$name] = $filter;
  278. }
  279. static::$global_data[$name] = $value;
  280. }
  281. }
  282. else
  283. {
  284. if ($filter !== null)
  285. {
  286. static::$global_filter[$key] = $filter;
  287. }
  288. static::$global_data[$key] = $value;
  289. }
  290. }
  291. /**
  292. * Assigns a global variable by reference, similar to [static::bind], except
  293. * that the variable will be accessible to all views.
  294. *
  295. * View::bind_global($key, $value);
  296. *
  297. * @param string variable name
  298. * @param mixed referenced variable
  299. * @param bool whether to filter the data or not
  300. * @return void
  301. */
  302. public static function bind_global($key, &$value, $filter = null)
  303. {
  304. if ($filter !== null)
  305. {
  306. static::$global_filter[$key] = $filter;
  307. }
  308. static::$global_data[$key] =& $value;
  309. }
  310. /**
  311. * Sets whether to filter the data or not.
  312. *
  313. * $view->auto_filter(false);
  314. *
  315. * @param bool whether to auto filter or not
  316. * @return View
  317. */
  318. public function auto_filter($filter = true)
  319. {
  320. if (func_num_args() == 0)
  321. {
  322. return $this->auto_filter;
  323. }
  324. $this->auto_filter = $filter;
  325. return $this;
  326. }
  327. /**
  328. * Sets the view filename.
  329. *
  330. * $view->set_filename($file);
  331. *
  332. * @param string view filename
  333. * @return View
  334. * @throws Fuel_Exception
  335. */
  336. public function set_filename($file)
  337. {
  338. // set find_file's one-time-only search paths
  339. \Fuel::$volatile_paths = $this->request_paths;
  340. // locate the view file
  341. if (($path = \Fuel::find_file('views', $file, '.'.$this->extension, false, false)) === false)
  342. {
  343. throw new \Fuel_Exception('The requested view could not be found: '.\Fuel::clean_path($file));
  344. }
  345. // Store the file path locally
  346. $this->file_name = $path;
  347. return $this;
  348. }
  349. /**
  350. * Searches for the given variable and returns its value.
  351. * Local variables will be returned before global variables.
  352. *
  353. * $value = $view->get('foo', 'bar');
  354. *
  355. * If a default parameter is not given and the variable does not
  356. * exist, it will throw an OutOfBoundsException.
  357. *
  358. * @param string The variable name
  359. * @param mixed The default value to return
  360. * @return mixed
  361. * @throws OutOfBoundsException
  362. */
  363. public function &get($key, $default = null)
  364. {
  365. if (array_key_exists($key, $this->data))
  366. {
  367. return $this->data[$key];
  368. }
  369. elseif (array_key_exists($key, static::$global_data))
  370. {
  371. return static::$global_data[$key];
  372. }
  373. if (is_null($default) and func_num_args() === 1)
  374. {
  375. throw new \OutOfBoundsException('View variable is not set: '.$key);
  376. }
  377. else
  378. {
  379. return \Fuel::value($default);
  380. }
  381. }
  382. /**
  383. * Assigns a variable by name. Assigned values will be available as a
  384. * variable within the view file:
  385. *
  386. * // This value can be accessed as $foo within the view
  387. * $view->set('foo', 'my value');
  388. *
  389. * You can also use an array to set several values at once:
  390. *
  391. * // Create the values $food and $beverage in the view
  392. * $view->set(array('food' => 'bread', 'beverage' => 'water'));
  393. *
  394. * @param string variable name or an array of variables
  395. * @param mixed value
  396. * @param bool whether to filter the data or not
  397. * @return $this
  398. */
  399. public function set($key, $value = null, $filter = null)
  400. {
  401. if (is_array($key))
  402. {
  403. foreach ($key as $name => $value)
  404. {
  405. if ($filter !== null)
  406. {
  407. $this->local_filter[$name] = $filter;
  408. }
  409. $this->data[$name] = $value;
  410. }
  411. }
  412. else
  413. {
  414. if ($filter !== null)
  415. {
  416. $this->local_filter[$key] = $filter;
  417. }
  418. $this->data[$key] = $value;
  419. }
  420. return $this;
  421. }
  422. /**
  423. * The same as set(), except this defaults to not-encoding the variable
  424. * on output.
  425. *
  426. * $view->set_safe('foo', 'bar');
  427. *
  428. * @param string variable name or an array of variables
  429. * @param mixed value
  430. * @return $this
  431. */
  432. public function set_safe($key, $value = null)
  433. {
  434. return $this->set($key, $value, false);
  435. }
  436. /**
  437. * Assigns a value by reference. The benefit of binding is that values can
  438. * be altered without re-setting them. It is also possible to bind variables
  439. * before they have values. Assigned values will be available as a
  440. * variable within the view file:
  441. *
  442. * // This reference can be accessed as $ref within the view
  443. * $view->bind('ref', $bar);
  444. *
  445. * @param string variable name
  446. * @param mixed referenced variable
  447. * @param bool Whether to filter the var on output
  448. * @return $this
  449. */
  450. public function bind($key, &$value, $filter = null)
  451. {
  452. if ($filter !== null)
  453. {
  454. $this->local_filter[$key] = $filter;
  455. }
  456. $this->data[$key] =& $value;
  457. return $this;
  458. }
  459. /**
  460. * Renders the view object to a string. Global and local data are merged
  461. * and extracted to create local variables within the view file.
  462. *
  463. * $output = $view->render();
  464. *
  465. * [!!] Global variables with the same key name as local variables will be
  466. * overwritten by the local variable.
  467. *
  468. * @param string view filename
  469. * @return string
  470. * @throws Fuel_Exception
  471. * @uses static::capture
  472. */
  473. public function render($file = null)
  474. {
  475. if (class_exists('Request', false))
  476. {
  477. $current_request = Request::active();
  478. Request::active($this->active_request);
  479. }
  480. if ($file !== null)
  481. {
  482. $this->set_filename($file);
  483. }
  484. if (empty($this->file_name))
  485. {
  486. throw new \Fuel_Exception('You must set the file to use within your view before rendering');
  487. }
  488. // Combine local and global data and capture the output
  489. $return = $this->process_file();
  490. if (class_exists('Request', false))
  491. {
  492. Request::active($current_request);
  493. }
  494. return $return;
  495. }
  496. }