PageRenderTime 72ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/fuel/core/classes/view.php

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