PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/laravel/view.php

https://bitbucket.org/anujan/shurima.net
PHP | 589 lines | 267 code | 63 blank | 259 comment | 16 complexity | df99431f4269228252fa4c452272f116 MD5 | raw file
  1. <?php namespace Laravel; use Closure, ArrayAccess;
  2. class View implements ArrayAccess {
  3. /**
  4. * The name of the view.
  5. *
  6. * @var string
  7. */
  8. public $view;
  9. /**
  10. * The view data.
  11. *
  12. * @var array
  13. */
  14. public $data;
  15. /**
  16. * The path to the view on disk.
  17. *
  18. * @var string
  19. */
  20. public $path;
  21. /**
  22. * All of the shared view data.
  23. *
  24. * @var array
  25. */
  26. public static $shared = array();
  27. /**
  28. * All of the registered view names.
  29. *
  30. * @var array
  31. */
  32. public static $names = array();
  33. /**
  34. * The cache content of loaded view files.
  35. *
  36. * @var array
  37. */
  38. public static $cache = array();
  39. /**
  40. * THe last view to be rendered.
  41. *
  42. * @var string
  43. */
  44. public static $last;
  45. /**
  46. * The Laravel view loader event name.
  47. *
  48. * @var string
  49. */
  50. const loader = 'laravel.view.loader';
  51. /**
  52. * The Laravel view engine event name.
  53. *
  54. * @var string
  55. */
  56. const engine = 'laravel.view.engine';
  57. /**
  58. * Create a new view instance.
  59. *
  60. * <code>
  61. * // Create a new view instance
  62. * $view = new View('home.index');
  63. *
  64. * // Create a new view instance of a bundle's view
  65. * $view = new View('admin::home.index');
  66. *
  67. * // Create a new view instance with bound data
  68. * $view = new View('home.index', array('name' => 'Taylor'));
  69. * </code>
  70. *
  71. * @param string $view
  72. * @param array $data
  73. * @return void
  74. */
  75. public function __construct($view, $data = array())
  76. {
  77. $this->view = $view;
  78. $this->data = $data;
  79. // In order to allow developers to load views outside of the normal loading
  80. // conventions, we'll allow for a raw path to be given in place of the
  81. // typical view name, giving total freedom on view loading.
  82. if (starts_with($view, 'path: '))
  83. {
  84. $this->path = substr($view, 6);
  85. }
  86. else
  87. {
  88. $this->path = $this->path($view);
  89. }
  90. // If a session driver has been specified, we will bind an instance of the
  91. // validation error message container to every view. If an error instance
  92. // exists in the session, we will use that instance.
  93. if ( ! isset($this->data['errors']))
  94. {
  95. if (Session::started() and Session::has('errors'))
  96. {
  97. $this->data['errors'] = Session::get('errors');
  98. }
  99. else
  100. {
  101. $this->data['errors'] = new Messages;
  102. }
  103. }
  104. }
  105. /**
  106. * Determine if the given view exists.
  107. *
  108. * @param string $view
  109. * @param boolean $return_path
  110. * @return string|bool
  111. */
  112. public static function exists($view, $return_path = false)
  113. {
  114. if (starts_with($view, 'name: ') and array_key_exists($name = substr($view, 6), static::$names))
  115. {
  116. $view = static::$names[$name];
  117. }
  118. list($bundle, $view) = Bundle::parse($view);
  119. $view = str_replace('.', '/', $view);
  120. // We delegate the determination of view paths to the view loader event
  121. // so that the developer is free to override and manage the loading
  122. // of views in any way they see fit for their application.
  123. $path = Event::until(static::loader, array($bundle, $view));
  124. if ( ! is_null($path))
  125. {
  126. return $return_path ? $path : true;
  127. }
  128. return false;
  129. }
  130. /**
  131. * Get the path to a given view on disk.
  132. *
  133. * @param string $view
  134. * @return string
  135. */
  136. protected function path($view)
  137. {
  138. if ($path = $this->exists($view,true))
  139. {
  140. return $path;
  141. }
  142. throw new \Exception("View [$view] doesn't exist.");
  143. }
  144. /**
  145. * Get the path to a view using the default folder convention.
  146. *
  147. * @param string $bundle
  148. * @param string $view
  149. * @param string $directory
  150. * @return string
  151. */
  152. public static function file($bundle, $view, $directory)
  153. {
  154. $directory = str_finish($directory, DS);
  155. // Views may have either the default PHP file extension or the "Blade"
  156. // extension, so we will need to check for both in the view path
  157. // and return the first one we find for the given view.
  158. if (file_exists($path = $directory.$view.EXT))
  159. {
  160. return $path;
  161. }
  162. elseif (file_exists($path = $directory.$view.BLADE_EXT))
  163. {
  164. return $path;
  165. }
  166. }
  167. /**
  168. * Create a new view instance.
  169. *
  170. * <code>
  171. * // Create a new view instance
  172. * $view = View::make('home.index');
  173. *
  174. * // Create a new view instance of a bundle's view
  175. * $view = View::make('admin::home.index');
  176. *
  177. * // Create a new view instance with bound data
  178. * $view = View::make('home.index', array('name' => 'Taylor'));
  179. * </code>
  180. *
  181. * @param string $view
  182. * @param array $data
  183. * @return View
  184. */
  185. public static function make($view, $data = array())
  186. {
  187. return new static($view, $data);
  188. }
  189. /**
  190. * Create a new view instance of a named view.
  191. *
  192. * <code>
  193. * // Create a new named view instance
  194. * $view = View::of('profile');
  195. *
  196. * // Create a new named view instance with bound data
  197. * $view = View::of('profile', array('name' => 'Taylor'));
  198. * </code>
  199. *
  200. * @param string $name
  201. * @param array $data
  202. * @return View
  203. */
  204. public static function of($name, $data = array())
  205. {
  206. return new static(static::$names[$name], $data);
  207. }
  208. /**
  209. * Assign a name to a view.
  210. *
  211. * <code>
  212. * // Assign a name to a view
  213. * View::name('partials.profile', 'profile');
  214. *
  215. * // Resolve an instance of a named view
  216. * $view = View::of('profile');
  217. * </code>
  218. *
  219. * @param string $view
  220. * @param string $name
  221. * @return void
  222. */
  223. public static function name($view, $name)
  224. {
  225. static::$names[$name] = $view;
  226. }
  227. /**
  228. * Register a view composer with the Event class.
  229. *
  230. * <code>
  231. * // Register a composer for the "home.index" view
  232. * View::composer('home.index', function($view)
  233. * {
  234. * $view['title'] = 'Home';
  235. * });
  236. * </code>
  237. *
  238. * @param string|array $views
  239. * @param Closure $composer
  240. * @return void
  241. */
  242. public static function composer($views, $composer)
  243. {
  244. $views = (array) $views;
  245. foreach ($views as $view)
  246. {
  247. Event::listen("laravel.composing: {$view}", $composer);
  248. }
  249. }
  250. /**
  251. * Get the rendered contents of a partial from a loop.
  252. *
  253. * @param string $view
  254. * @param array $data
  255. * @param string $iterator
  256. * @param string $empty
  257. * @return string
  258. */
  259. public static function render_each($view, array $data, $iterator, $empty = 'raw|')
  260. {
  261. $result = '';
  262. // If is actually data in the array, we will loop through the data and
  263. // append an instance of the partial view to the final result HTML,
  264. // passing in the iterated value of the data array.
  265. if (count($data) > 0)
  266. {
  267. foreach ($data as $key => $value)
  268. {
  269. $with = array('key' => $key, $iterator => $value);
  270. $result .= render($view, $with);
  271. }
  272. }
  273. // If there is no data in the array, we will render the contents of
  274. // the "empty" view. Alternatively, the "empty view" can be a raw
  275. // string that is prefixed with "raw|" for convenience.
  276. else
  277. {
  278. if (starts_with($empty, 'raw|'))
  279. {
  280. $result = substr($empty, 4);
  281. }
  282. else
  283. {
  284. $result = render($empty);
  285. }
  286. }
  287. return $result;
  288. }
  289. /**
  290. * Get the evaluated string content of the view.
  291. *
  292. * @return string
  293. */
  294. public function render()
  295. {
  296. Event::fire("laravel.composing: {$this->view}", array($this));
  297. // If there are listeners to the view engine event, we'll pass them
  298. // the view so they can render it according to their needs, which
  299. // allows easy attachment of other view parsers.
  300. if (Event::listeners(static::engine))
  301. {
  302. $result = Event::until(static::engine, array($this));
  303. if ( ! is_null($result)) return $result;
  304. }
  305. return $this->get();
  306. }
  307. /**
  308. * Get the evaluated contents of the view.
  309. *
  310. * @return string
  311. */
  312. public function get()
  313. {
  314. $__data = $this->data();
  315. // The contents of each view file is cached in an array for the
  316. // request since partial views may be rendered inside of for
  317. // loops which could incur performance penalties.
  318. $__contents = $this->load();
  319. ob_start() and extract($__data, EXTR_SKIP);
  320. // We'll include the view contents for parsing within a catcher
  321. // so we can avoid any WSOD errors. If an exception occurs we
  322. // will throw it out to the exception handler.
  323. try
  324. {
  325. eval('?>'.$__contents);
  326. }
  327. // If we caught an exception, we'll silently flush the output
  328. // buffer so that no partially rendered views get thrown out
  329. // to the client and confuse the user with junk.
  330. catch (\Exception $e)
  331. {
  332. ob_get_clean(); throw $e;
  333. }
  334. $content = ob_get_clean();
  335. // The view filter event gives us a last chance to modify the
  336. // evaluated contents of the view and return them. This lets
  337. // us do something like run the contents through Jade, etc.
  338. if (Event::listeners('view.filter'))
  339. {
  340. return Event::first('view.filter', array($content, $this->path));
  341. }
  342. return $content;
  343. }
  344. /**
  345. * Get the contents of the view file from disk.
  346. *
  347. * @return string
  348. */
  349. protected function load()
  350. {
  351. static::$last = array('name' => $this->view, 'path' => $this->path);
  352. if (isset(static::$cache[$this->path]))
  353. {
  354. return static::$cache[$this->path];
  355. }
  356. else
  357. {
  358. return static::$cache[$this->path] = file_get_contents($this->path);
  359. }
  360. }
  361. /**
  362. * Get the array of view data for the view instance.
  363. *
  364. * The shared view data will be combined with the view data.
  365. *
  366. * @return array
  367. */
  368. public function data()
  369. {
  370. $data = array_merge($this->data, static::$shared);
  371. // All nested views and responses are evaluated before the main view.
  372. // This allows the assets used by nested views to be added to the
  373. // asset container before the main view is evaluated.
  374. foreach ($data as $key => $value)
  375. {
  376. if ($value instanceof View or $value instanceof Response)
  377. {
  378. $data[$key] = $value->render();
  379. }
  380. }
  381. return $data;
  382. }
  383. /**
  384. * Add a view instance to the view data.
  385. *
  386. * <code>
  387. * // Add a view instance to a view's data
  388. * $view = View::make('foo')->nest('footer', 'partials.footer');
  389. *
  390. * // Equivalent functionality using the "with" method
  391. * $view = View::make('foo')->with('footer', View::make('partials.footer'));
  392. * </code>
  393. *
  394. * @param string $key
  395. * @param string $view
  396. * @param array $data
  397. * @return View
  398. */
  399. public function nest($key, $view, $data = array())
  400. {
  401. return $this->with($key, static::make($view, $data));
  402. }
  403. /**
  404. * Add a key / value pair to the view data.
  405. *
  406. * Bound data will be available to the view as variables.
  407. *
  408. * @param string $key
  409. * @param mixed $value
  410. * @return View
  411. */
  412. public function with($key, $value = null)
  413. {
  414. if (is_array($key))
  415. {
  416. $this->data = array_merge($this->data, $key);
  417. }
  418. else
  419. {
  420. $this->data[$key] = $value;
  421. }
  422. return $this;
  423. }
  424. /**
  425. * Add a key / value pair to the shared view data.
  426. *
  427. * Shared view data is accessible to every view created by the application.
  428. *
  429. * @param string $key
  430. * @param mixed $value
  431. * @return View
  432. */
  433. public function shares($key, $value)
  434. {
  435. static::share($key, $value);
  436. return $this;
  437. }
  438. /**
  439. * Add a key / value pair to the shared view data.
  440. *
  441. * Shared view data is accessible to every view created by the application.
  442. *
  443. * @param string $key
  444. * @param mixed $value
  445. * @return void
  446. */
  447. public static function share($key, $value)
  448. {
  449. static::$shared[$key] = $value;
  450. }
  451. /**
  452. * Implementation of the ArrayAccess offsetExists method.
  453. */
  454. public function offsetExists($offset)
  455. {
  456. return array_key_exists($offset, $this->data);
  457. }
  458. /**
  459. * Implementation of the ArrayAccess offsetGet method.
  460. */
  461. public function offsetGet($offset)
  462. {
  463. if (isset($this[$offset])) return $this->data[$offset];
  464. }
  465. /**
  466. * Implementation of the ArrayAccess offsetSet method.
  467. */
  468. public function offsetSet($offset, $value)
  469. {
  470. $this->data[$offset] = $value;
  471. }
  472. /**
  473. * Implementation of the ArrayAccess offsetUnset method.
  474. */
  475. public function offsetUnset($offset)
  476. {
  477. unset($this->data[$offset]);
  478. }
  479. /**
  480. * Magic Method for handling dynamic data access.
  481. */
  482. public function __get($key)
  483. {
  484. return $this->data[$key];
  485. }
  486. /**
  487. * Magic Method for handling the dynamic setting of data.
  488. */
  489. public function __set($key, $value)
  490. {
  491. $this->data[$key] = $value;
  492. }
  493. /**
  494. * Magic Method for checking dynamically-set data.
  495. */
  496. public function __isset($key)
  497. {
  498. return isset($this->data[$key]);
  499. }
  500. /**
  501. * Get the evaluated string content of the view.
  502. *
  503. * @return string
  504. */
  505. public function __toString()
  506. {
  507. return $this->render();
  508. }
  509. /**
  510. * Magic Method for handling dynamic functions.
  511. *
  512. * This method handles calls to dynamic with helpers.
  513. */
  514. public function __call($method, $parameters)
  515. {
  516. if (strpos($method, 'with_') === 0)
  517. {
  518. $key = substr($method, 5);
  519. return $this->with($key, $parameters[0]);
  520. }
  521. throw new \Exception("Method [$method] is not defined on the View class.");
  522. }
  523. }