PageRenderTime 127ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/core/classes/view.php

https://bitbucket.org/sriedel/iccrm-wip
PHP | 557 lines | 244 code | 60 blank | 253 comment | 24 complexity | a0d7ec0e9743184523581bfdf430ed2e MD5 | raw file
Possible License(s): MIT
  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 - 2012 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. * Returns a new View object. If you do not define the "file" parameter,
  64. * you must call [static::set_filename].
  65. *
  66. * $view = View::forge($file);
  67. *
  68. * @param string view filename
  69. * @param array array of values
  70. * @return View
  71. */
  72. public static function forge($file = null, $data = null, $auto_filter = null)
  73. {
  74. return new static($file, $data, $auto_filter);
  75. }
  76. /**
  77. * Sets the initial view filename and local data.
  78. *
  79. * $view = new View($file);
  80. *
  81. * @param string view filename
  82. * @param array array of values
  83. * @return void
  84. * @uses View::set_filename
  85. */
  86. public function __construct($file = null, $data = null, $filter = null)
  87. {
  88. if (is_object($data) === true)
  89. {
  90. $data = get_object_vars($data);
  91. }
  92. elseif ($data and ! is_array($data))
  93. {
  94. throw new \InvalidArgumentException('The data parameter only accepts objects and arrays.');
  95. }
  96. $this->auto_filter = is_null($filter) ? \Config::get('security.auto_filter_output', true) : $filter;
  97. if ($file !== null)
  98. {
  99. $this->set_filename($file);
  100. }
  101. if ($data !== null)
  102. {
  103. // Add the values to the current data
  104. $this->data = $data;
  105. }
  106. // store the current request search paths to deal with out-of-context rendering
  107. if (class_exists('Request', false) and $active = \Request::active() and \Request::main() != $active)
  108. {
  109. $this->request_paths = $active->get_paths();
  110. }
  111. isset($active) and $this->active_request = $active;
  112. }
  113. /**
  114. * Magic method, searches for the given variable and returns its value.
  115. * Local variables will be returned before global variables.
  116. *
  117. * $value = $view->foo;
  118. *
  119. * @param string variable name
  120. * @return mixed
  121. * @throws OutOfBoundsException
  122. */
  123. public function & __get($key)
  124. {
  125. return $this->get($key);
  126. }
  127. /**
  128. * Magic method, calls [static::set] with the same parameters.
  129. *
  130. * $view->foo = 'something';
  131. *
  132. * @param string variable name
  133. * @param mixed value
  134. * @return void
  135. */
  136. public function __set($key, $value)
  137. {
  138. $this->set($key, $value);
  139. }
  140. /**
  141. * Magic method, determines if a variable is set.
  142. *
  143. * isset($view->foo);
  144. *
  145. * [!!] `null` variables are not considered to be set by [isset](http://php.net/isset).
  146. *
  147. * @param string variable name
  148. * @return boolean
  149. */
  150. public function __isset($key)
  151. {
  152. return (isset($this->data[$key]) or isset(static::$global_data[$key]));
  153. }
  154. /**
  155. * Magic method, unsets a given variable.
  156. *
  157. * unset($view->foo);
  158. *
  159. * @param string variable name
  160. * @return void
  161. */
  162. public function __unset($key)
  163. {
  164. unset($this->data[$key], static::$global_data[$key]);
  165. }
  166. /**
  167. * Magic method, returns the output of [static::render].
  168. *
  169. * @return string
  170. * @uses View::render
  171. */
  172. public function __toString()
  173. {
  174. try
  175. {
  176. return $this->render();
  177. }
  178. catch (\Exception $e)
  179. {
  180. \Error::exception_handler($e);
  181. return '';
  182. }
  183. }
  184. /**
  185. * Captures the output that is generated when a view is included.
  186. * The view data will be extracted to make local variables. This method
  187. * is static to prevent object scope resolution.
  188. *
  189. * $output = $this->process_file();
  190. *
  191. * @param string File override
  192. * @param array variables
  193. * @return string
  194. */
  195. protected function process_file($file_override = false)
  196. {
  197. $clean_room = function($__file_name, array $__data)
  198. {
  199. extract($__data, EXTR_REFS);
  200. // Capture the view output
  201. ob_start();
  202. try
  203. {
  204. // Load the view within the current scope
  205. include $__file_name;
  206. }
  207. catch (\Exception $e)
  208. {
  209. // Delete the output buffer
  210. ob_end_clean();
  211. // Re-throw the exception
  212. throw $e;
  213. }
  214. // Get the captured output and close the buffer
  215. return ob_get_clean();
  216. };
  217. return $clean_room($file_override ?: $this->file_name, $this->get_data());
  218. }
  219. /**
  220. * Retrieves all the data, both local and global. It filters the data if
  221. * necessary.
  222. *
  223. * $data = $this->get_data();
  224. *
  225. * @param string $scope local/glocal/all
  226. * @return array view data
  227. */
  228. protected function get_data($scope = 'all')
  229. {
  230. $clean_it = function ($data, $rules, $auto_filter)
  231. {
  232. foreach ($data as $key => &$value)
  233. {
  234. $filter = array_key_exists($key, $rules) ? $rules[$key] : null;
  235. $filter = is_null($filter) ? $auto_filter : $filter;
  236. $value = $filter ? \Security::clean($value, null, 'security.output_filter') : $value;
  237. }
  238. return $data;
  239. };
  240. $data = array();
  241. if ( ! empty($this->data) and ($scope === 'all' or $scope === 'local'))
  242. {
  243. $data += $clean_it($this->data, $this->local_filter, $this->auto_filter);
  244. }
  245. if ( ! empty(static::$global_data) and ($scope === 'all' or $scope === 'global'))
  246. {
  247. $data += $clean_it(static::$global_data, static::$global_filter, $this->auto_filter);
  248. }
  249. return $data;
  250. }
  251. /**
  252. * Sets a global variable, similar to [static::set], except that the
  253. * variable will be accessible to all views.
  254. *
  255. * View::set_global($name, $value);
  256. *
  257. * @param string variable name or an array of variables
  258. * @param mixed value
  259. * @param bool whether to filter the data or not
  260. * @return void
  261. */
  262. public static function set_global($key, $value = null, $filter = null)
  263. {
  264. if (is_array($key))
  265. {
  266. foreach ($key as $name => $value)
  267. {
  268. if ($filter !== null)
  269. {
  270. static::$global_filter[$name] = $filter;
  271. }
  272. static::$global_data[$name] = $value;
  273. }
  274. }
  275. else
  276. {
  277. if ($filter !== null)
  278. {
  279. static::$global_filter[$key] = $filter;
  280. }
  281. static::$global_data[$key] = $value;
  282. }
  283. }
  284. /**
  285. * Assigns a global variable by reference, similar to [static::bind], except
  286. * that the variable will be accessible to all views.
  287. *
  288. * View::bind_global($key, $value);
  289. *
  290. * @param string variable name
  291. * @param mixed referenced variable
  292. * @param bool whether to filter the data or not
  293. * @return void
  294. */
  295. public static function bind_global($key, &$value, $filter = null)
  296. {
  297. if ($filter !== null)
  298. {
  299. static::$global_filter[$key] = $filter;
  300. }
  301. static::$global_data[$key] =& $value;
  302. }
  303. /**
  304. * Sets whether to filter the data or not.
  305. *
  306. * $view->auto_filter(false);
  307. *
  308. * @param bool whether to auto filter or not
  309. * @return View
  310. */
  311. public function auto_filter($filter = true)
  312. {
  313. if (func_num_args() == 0)
  314. {
  315. return $this->auto_filter;
  316. }
  317. $this->auto_filter = $filter;
  318. return $this;
  319. }
  320. /**
  321. * Sets the view filename.
  322. *
  323. * $view->set_filename($file);
  324. *
  325. * @param string view filename
  326. * @return View
  327. * @throws FuelException
  328. */
  329. public function set_filename($file)
  330. {
  331. // set find_file's one-time-only search paths
  332. \Finder::instance()->flash($this->request_paths);
  333. // locate the view file
  334. if (($path = \Finder::search('views', $file, '.'.$this->extension, false, false)) === false)
  335. {
  336. throw new \FuelException('The requested view could not be found: '.\Fuel::clean_path($file));
  337. }
  338. // Store the file path locally
  339. $this->file_name = $path;
  340. return $this;
  341. }
  342. /**
  343. * Searches for the given variable and returns its value.
  344. * Local variables will be returned before global variables.
  345. *
  346. * $value = $view->get('foo', 'bar');
  347. *
  348. * If the key is not given or null, the entire data array is returned.
  349. *
  350. * If a default parameter is not given and the variable does not
  351. * exist, it will throw an OutOfBoundsException.
  352. *
  353. * @param string The variable name
  354. * @param mixed The default value to return
  355. * @return mixed
  356. * @throws OutOfBoundsException
  357. */
  358. public function &get($key = null, $default = null)
  359. {
  360. if (func_num_args() === 0 or $key === null)
  361. {
  362. return $this->data;
  363. }
  364. elseif (array_key_exists($key, $this->data))
  365. {
  366. return $this->data[$key];
  367. }
  368. elseif (array_key_exists($key, static::$global_data))
  369. {
  370. return static::$global_data[$key];
  371. }
  372. if (is_null($default) and func_num_args() === 1)
  373. {
  374. throw new \OutOfBoundsException('View variable is not set: '.$key);
  375. }
  376. else
  377. {
  378. // assign it first, you can't return a return value by reference directly!
  379. $default = \Fuel::value($default);
  380. return $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. }