PageRenderTime 60ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/core/classes/view.php

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