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

/vendor/laravel/framework/src/Illuminate/View/Factory.php

https://gitlab.com/ealexis.t/trends
PHP | 974 lines | 402 code | 127 blank | 445 comment | 35 complexity | 375bb0243e60f322c1e56eaf60f60ddd MD5 | raw file
  1. <?php
  2. namespace Illuminate\View;
  3. use Closure;
  4. use Illuminate\Support\Arr;
  5. use Illuminate\Support\Str;
  6. use InvalidArgumentException;
  7. use Illuminate\Contracts\Support\Arrayable;
  8. use Illuminate\View\Engines\EngineResolver;
  9. use Illuminate\Contracts\Events\Dispatcher;
  10. use Illuminate\Contracts\Container\Container;
  11. use Illuminate\Contracts\View\Factory as FactoryContract;
  12. class Factory implements FactoryContract
  13. {
  14. /**
  15. * The engine implementation.
  16. *
  17. * @var \Illuminate\View\Engines\EngineResolver
  18. */
  19. protected $engines;
  20. /**
  21. * The view finder implementation.
  22. *
  23. * @var \Illuminate\View\ViewFinderInterface
  24. */
  25. protected $finder;
  26. /**
  27. * The event dispatcher instance.
  28. *
  29. * @var \Illuminate\Contracts\Events\Dispatcher
  30. */
  31. protected $events;
  32. /**
  33. * The IoC container instance.
  34. *
  35. * @var \Illuminate\Contracts\Container\Container
  36. */
  37. protected $container;
  38. /**
  39. * Data that should be available to all templates.
  40. *
  41. * @var array
  42. */
  43. protected $shared = [];
  44. /**
  45. * Array of registered view name aliases.
  46. *
  47. * @var array
  48. */
  49. protected $aliases = [];
  50. /**
  51. * All of the registered view names.
  52. *
  53. * @var array
  54. */
  55. protected $names = [];
  56. /**
  57. * The extension to engine bindings.
  58. *
  59. * @var array
  60. */
  61. protected $extensions = ['blade.php' => 'blade', 'php' => 'php'];
  62. /**
  63. * The view composer events.
  64. *
  65. * @var array
  66. */
  67. protected $composers = [];
  68. /**
  69. * All of the finished, captured sections.
  70. *
  71. * @var array
  72. */
  73. protected $sections = [];
  74. /**
  75. * The stack of in-progress sections.
  76. *
  77. * @var array
  78. */
  79. protected $sectionStack = [];
  80. /**
  81. * All of the finished, captured push sections.
  82. *
  83. * @var array
  84. */
  85. protected $pushes = [];
  86. /**
  87. * The stack of in-progress push sections.
  88. *
  89. * @var array
  90. */
  91. protected $pushStack = [];
  92. /**
  93. * The number of active rendering operations.
  94. *
  95. * @var int
  96. */
  97. protected $renderCount = 0;
  98. /**
  99. * Create a new view factory instance.
  100. *
  101. * @param \Illuminate\View\Engines\EngineResolver $engines
  102. * @param \Illuminate\View\ViewFinderInterface $finder
  103. * @param \Illuminate\Contracts\Events\Dispatcher $events
  104. * @return void
  105. */
  106. public function __construct(EngineResolver $engines, ViewFinderInterface $finder, Dispatcher $events)
  107. {
  108. $this->finder = $finder;
  109. $this->events = $events;
  110. $this->engines = $engines;
  111. $this->share('__env', $this);
  112. }
  113. /**
  114. * Get the evaluated view contents for the given view.
  115. *
  116. * @param string $path
  117. * @param array $data
  118. * @param array $mergeData
  119. * @return \Illuminate\Contracts\View\View
  120. */
  121. public function file($path, $data = [], $mergeData = [])
  122. {
  123. $data = array_merge($mergeData, $this->parseData($data));
  124. $this->callCreator($view = new View($this, $this->getEngineFromPath($path), $path, $path, $data));
  125. return $view;
  126. }
  127. /**
  128. * Get the evaluated view contents for the given view.
  129. *
  130. * @param string $view
  131. * @param array $data
  132. * @param array $mergeData
  133. * @return \Illuminate\Contracts\View\View
  134. */
  135. public function make($view, $data = [], $mergeData = [])
  136. {
  137. if (isset($this->aliases[$view])) {
  138. $view = $this->aliases[$view];
  139. }
  140. $view = $this->normalizeName($view);
  141. $path = $this->finder->find($view);
  142. $data = array_merge($mergeData, $this->parseData($data));
  143. $this->callCreator($view = new View($this, $this->getEngineFromPath($path), $view, $path, $data));
  144. return $view;
  145. }
  146. /**
  147. * Normalize a view name.
  148. *
  149. * @param string $name
  150. * @return string
  151. */
  152. protected function normalizeName($name)
  153. {
  154. $delimiter = ViewFinderInterface::HINT_PATH_DELIMITER;
  155. if (strpos($name, $delimiter) === false) {
  156. return str_replace('/', '.', $name);
  157. }
  158. list($namespace, $name) = explode($delimiter, $name);
  159. return $namespace.$delimiter.str_replace('/', '.', $name);
  160. }
  161. /**
  162. * Parse the given data into a raw array.
  163. *
  164. * @param mixed $data
  165. * @return array
  166. */
  167. protected function parseData($data)
  168. {
  169. return $data instanceof Arrayable ? $data->toArray() : $data;
  170. }
  171. /**
  172. * Get the evaluated view contents for a named view.
  173. *
  174. * @param string $view
  175. * @param mixed $data
  176. * @return \Illuminate\Contracts\View\View
  177. */
  178. public function of($view, $data = [])
  179. {
  180. return $this->make($this->names[$view], $data);
  181. }
  182. /**
  183. * Register a named view.
  184. *
  185. * @param string $view
  186. * @param string $name
  187. * @return void
  188. */
  189. public function name($view, $name)
  190. {
  191. $this->names[$name] = $view;
  192. }
  193. /**
  194. * Add an alias for a view.
  195. *
  196. * @param string $view
  197. * @param string $alias
  198. * @return void
  199. */
  200. public function alias($view, $alias)
  201. {
  202. $this->aliases[$alias] = $view;
  203. }
  204. /**
  205. * Determine if a given view exists.
  206. *
  207. * @param string $view
  208. * @return bool
  209. */
  210. public function exists($view)
  211. {
  212. try {
  213. $this->finder->find($view);
  214. } catch (InvalidArgumentException $e) {
  215. return false;
  216. }
  217. return true;
  218. }
  219. /**
  220. * Get the rendered contents of a partial from a loop.
  221. *
  222. * @param string $view
  223. * @param array $data
  224. * @param string $iterator
  225. * @param string $empty
  226. * @return string
  227. */
  228. public function renderEach($view, $data, $iterator, $empty = 'raw|')
  229. {
  230. $result = '';
  231. // If is actually data in the array, we will loop through the data and append
  232. // an instance of the partial view to the final result HTML passing in the
  233. // iterated value of this data array, allowing the views to access them.
  234. if (count($data) > 0) {
  235. foreach ($data as $key => $value) {
  236. $data = ['key' => $key, $iterator => $value];
  237. $result .= $this->make($view, $data)->render();
  238. }
  239. }
  240. // If there is no data in the array, we will render the contents of the empty
  241. // view. Alternatively, the "empty view" could be a raw string that begins
  242. // with "raw|" for convenience and to let this know that it is a string.
  243. else {
  244. if (Str::startsWith($empty, 'raw|')) {
  245. $result = substr($empty, 4);
  246. } else {
  247. $result = $this->make($empty)->render();
  248. }
  249. }
  250. return $result;
  251. }
  252. /**
  253. * Get the appropriate view engine for the given path.
  254. *
  255. * @param string $path
  256. * @return \Illuminate\View\Engines\EngineInterface
  257. *
  258. * @throws \InvalidArgumentException
  259. */
  260. public function getEngineFromPath($path)
  261. {
  262. if (! $extension = $this->getExtension($path)) {
  263. throw new InvalidArgumentException("Unrecognized extension in file: $path");
  264. }
  265. $engine = $this->extensions[$extension];
  266. return $this->engines->resolve($engine);
  267. }
  268. /**
  269. * Get the extension used by the view file.
  270. *
  271. * @param string $path
  272. * @return string
  273. */
  274. protected function getExtension($path)
  275. {
  276. $extensions = array_keys($this->extensions);
  277. return Arr::first($extensions, function ($key, $value) use ($path) {
  278. return Str::endsWith($path, '.'.$value);
  279. });
  280. }
  281. /**
  282. * Add a piece of shared data to the environment.
  283. *
  284. * @param array|string $key
  285. * @param mixed $value
  286. * @return mixed
  287. */
  288. public function share($key, $value = null)
  289. {
  290. if (! is_array($key)) {
  291. return $this->shared[$key] = $value;
  292. }
  293. foreach ($key as $innerKey => $innerValue) {
  294. $this->share($innerKey, $innerValue);
  295. }
  296. }
  297. /**
  298. * Register a view creator event.
  299. *
  300. * @param array|string $views
  301. * @param \Closure|string $callback
  302. * @return array
  303. */
  304. public function creator($views, $callback)
  305. {
  306. $creators = [];
  307. foreach ((array) $views as $view) {
  308. $creators[] = $this->addViewEvent($view, $callback, 'creating: ');
  309. }
  310. return $creators;
  311. }
  312. /**
  313. * Register multiple view composers via an array.
  314. *
  315. * @param array $composers
  316. * @return array
  317. */
  318. public function composers(array $composers)
  319. {
  320. $registered = [];
  321. foreach ($composers as $callback => $views) {
  322. $registered = array_merge($registered, $this->composer($views, $callback));
  323. }
  324. return $registered;
  325. }
  326. /**
  327. * Register a view composer event.
  328. *
  329. * @param array|string $views
  330. * @param \Closure|string $callback
  331. * @param int|null $priority
  332. * @return array
  333. */
  334. public function composer($views, $callback, $priority = null)
  335. {
  336. $composers = [];
  337. foreach ((array) $views as $view) {
  338. $composers[] = $this->addViewEvent($view, $callback, 'composing: ', $priority);
  339. }
  340. return $composers;
  341. }
  342. /**
  343. * Add an event for a given view.
  344. *
  345. * @param string $view
  346. * @param \Closure|string $callback
  347. * @param string $prefix
  348. * @param int|null $priority
  349. * @return \Closure|null
  350. */
  351. protected function addViewEvent($view, $callback, $prefix = 'composing: ', $priority = null)
  352. {
  353. $view = $this->normalizeName($view);
  354. if ($callback instanceof Closure) {
  355. $this->addEventListener($prefix.$view, $callback, $priority);
  356. return $callback;
  357. } elseif (is_string($callback)) {
  358. return $this->addClassEvent($view, $callback, $prefix, $priority);
  359. }
  360. }
  361. /**
  362. * Register a class based view composer.
  363. *
  364. * @param string $view
  365. * @param string $class
  366. * @param string $prefix
  367. * @param int|null $priority
  368. * @return \Closure
  369. */
  370. protected function addClassEvent($view, $class, $prefix, $priority = null)
  371. {
  372. $name = $prefix.$view;
  373. // When registering a class based view "composer", we will simply resolve the
  374. // classes from the application IoC container then call the compose method
  375. // on the instance. This allows for convenient, testable view composers.
  376. $callback = $this->buildClassEventCallback($class, $prefix);
  377. $this->addEventListener($name, $callback, $priority);
  378. return $callback;
  379. }
  380. /**
  381. * Add a listener to the event dispatcher.
  382. *
  383. * @param string $name
  384. * @param \Closure $callback
  385. * @param int|null $priority
  386. * @return void
  387. */
  388. protected function addEventListener($name, $callback, $priority = null)
  389. {
  390. if (is_null($priority)) {
  391. $this->events->listen($name, $callback);
  392. } else {
  393. $this->events->listen($name, $callback, $priority);
  394. }
  395. }
  396. /**
  397. * Build a class based container callback Closure.
  398. *
  399. * @param string $class
  400. * @param string $prefix
  401. * @return \Closure
  402. */
  403. protected function buildClassEventCallback($class, $prefix)
  404. {
  405. list($class, $method) = $this->parseClassEvent($class, $prefix);
  406. // Once we have the class and method name, we can build the Closure to resolve
  407. // the instance out of the IoC container and call the method on it with the
  408. // given arguments that are passed to the Closure as the composer's data.
  409. return function () use ($class, $method) {
  410. $callable = [$this->container->make($class), $method];
  411. return call_user_func_array($callable, func_get_args());
  412. };
  413. }
  414. /**
  415. * Parse a class based composer name.
  416. *
  417. * @param string $class
  418. * @param string $prefix
  419. * @return array
  420. */
  421. protected function parseClassEvent($class, $prefix)
  422. {
  423. if (Str::contains($class, '@')) {
  424. return explode('@', $class);
  425. }
  426. $method = Str::contains($prefix, 'composing') ? 'compose' : 'create';
  427. return [$class, $method];
  428. }
  429. /**
  430. * Call the composer for a given view.
  431. *
  432. * @param \Illuminate\Contracts\View\View $view
  433. * @return void
  434. */
  435. public function callComposer(View $view)
  436. {
  437. $this->events->fire('composing: '.$view->getName(), [$view]);
  438. }
  439. /**
  440. * Call the creator for a given view.
  441. *
  442. * @param \Illuminate\Contracts\View\View $view
  443. * @return void
  444. */
  445. public function callCreator(View $view)
  446. {
  447. $this->events->fire('creating: '.$view->getName(), [$view]);
  448. }
  449. /**
  450. * Start injecting content into a section.
  451. *
  452. * @param string $section
  453. * @param string $content
  454. * @return void
  455. */
  456. public function startSection($section, $content = '')
  457. {
  458. if ($content === '') {
  459. if (ob_start()) {
  460. $this->sectionStack[] = $section;
  461. }
  462. } else {
  463. $this->extendSection($section, $content);
  464. }
  465. }
  466. /**
  467. * Inject inline content into a section.
  468. *
  469. * @param string $section
  470. * @param string $content
  471. * @return void
  472. */
  473. public function inject($section, $content)
  474. {
  475. return $this->startSection($section, $content);
  476. }
  477. /**
  478. * Stop injecting content into a section and return its contents.
  479. *
  480. * @return string
  481. */
  482. public function yieldSection()
  483. {
  484. if (empty($this->sectionStack)) {
  485. return '';
  486. }
  487. return $this->yieldContent($this->stopSection());
  488. }
  489. /**
  490. * Stop injecting content into a section.
  491. *
  492. * @param bool $overwrite
  493. * @return string
  494. * @throws \InvalidArgumentException
  495. */
  496. public function stopSection($overwrite = false)
  497. {
  498. if (empty($this->sectionStack)) {
  499. throw new InvalidArgumentException('Cannot end a section without first starting one.');
  500. }
  501. $last = array_pop($this->sectionStack);
  502. if ($overwrite) {
  503. $this->sections[$last] = ob_get_clean();
  504. } else {
  505. $this->extendSection($last, ob_get_clean());
  506. }
  507. return $last;
  508. }
  509. /**
  510. * Stop injecting content into a section and append it.
  511. *
  512. * @return string
  513. * @throws \InvalidArgumentException
  514. */
  515. public function appendSection()
  516. {
  517. if (empty($this->sectionStack)) {
  518. throw new InvalidArgumentException('Cannot end a section without first starting one.');
  519. }
  520. $last = array_pop($this->sectionStack);
  521. if (isset($this->sections[$last])) {
  522. $this->sections[$last] .= ob_get_clean();
  523. } else {
  524. $this->sections[$last] = ob_get_clean();
  525. }
  526. return $last;
  527. }
  528. /**
  529. * Append content to a given section.
  530. *
  531. * @param string $section
  532. * @param string $content
  533. * @return void
  534. */
  535. protected function extendSection($section, $content)
  536. {
  537. if (isset($this->sections[$section])) {
  538. $content = str_replace('@parent', $content, $this->sections[$section]);
  539. }
  540. $this->sections[$section] = $content;
  541. }
  542. /**
  543. * Get the string contents of a section.
  544. *
  545. * @param string $section
  546. * @param string $default
  547. * @return string
  548. */
  549. public function yieldContent($section, $default = '')
  550. {
  551. $sectionContent = $default;
  552. if (isset($this->sections[$section])) {
  553. $sectionContent = $this->sections[$section];
  554. }
  555. $sectionContent = str_replace('@@parent', '--parent--holder--', $sectionContent);
  556. return str_replace(
  557. '--parent--holder--', '@parent', str_replace('@parent', '', $sectionContent)
  558. );
  559. }
  560. /**
  561. * Start injecting content into a push section.
  562. *
  563. * @param string $section
  564. * @param string $content
  565. * @return void
  566. */
  567. public function startPush($section, $content = '')
  568. {
  569. if ($content === '') {
  570. if (ob_start()) {
  571. $this->pushStack[] = $section;
  572. }
  573. } else {
  574. $this->extendPush($section, $content);
  575. }
  576. }
  577. /**
  578. * Stop injecting content into a push section.
  579. *
  580. * @return string
  581. * @throws \InvalidArgumentException
  582. */
  583. public function stopPush()
  584. {
  585. if (empty($this->pushStack)) {
  586. throw new InvalidArgumentException('Cannot end a section without first starting one.');
  587. }
  588. $last = array_pop($this->pushStack);
  589. $this->extendPush($last, ob_get_clean());
  590. return $last;
  591. }
  592. /**
  593. * Append content to a given push section.
  594. *
  595. * @param string $section
  596. * @param string $content
  597. * @return void
  598. */
  599. protected function extendPush($section, $content)
  600. {
  601. if (! isset($this->pushes[$section])) {
  602. $this->pushes[$section] = [];
  603. }
  604. if (! isset($this->pushes[$section][$this->renderCount])) {
  605. $this->pushes[$section][$this->renderCount] = $content;
  606. } else {
  607. $this->pushes[$section][$this->renderCount] .= $content;
  608. }
  609. }
  610. /**
  611. * Get the string contents of a push section.
  612. *
  613. * @param string $section
  614. * @param string $default
  615. * @return string
  616. */
  617. public function yieldPushContent($section, $default = '')
  618. {
  619. if (! isset($this->pushes[$section])) {
  620. return $default;
  621. }
  622. return implode(array_reverse($this->pushes[$section]));
  623. }
  624. /**
  625. * Flush all of the section contents.
  626. *
  627. * @return void
  628. */
  629. public function flushSections()
  630. {
  631. $this->renderCount = 0;
  632. $this->sections = [];
  633. $this->sectionStack = [];
  634. $this->pushes = [];
  635. $this->pushStack = [];
  636. }
  637. /**
  638. * Flush all of the section contents if done rendering.
  639. *
  640. * @return void
  641. */
  642. public function flushSectionsIfDoneRendering()
  643. {
  644. if ($this->doneRendering()) {
  645. $this->flushSections();
  646. }
  647. }
  648. /**
  649. * Increment the rendering counter.
  650. *
  651. * @return void
  652. */
  653. public function incrementRender()
  654. {
  655. $this->renderCount++;
  656. }
  657. /**
  658. * Decrement the rendering counter.
  659. *
  660. * @return void
  661. */
  662. public function decrementRender()
  663. {
  664. $this->renderCount--;
  665. }
  666. /**
  667. * Check if there are no active render operations.
  668. *
  669. * @return bool
  670. */
  671. public function doneRendering()
  672. {
  673. return $this->renderCount == 0;
  674. }
  675. /**
  676. * Add a location to the array of view locations.
  677. *
  678. * @param string $location
  679. * @return void
  680. */
  681. public function addLocation($location)
  682. {
  683. $this->finder->addLocation($location);
  684. }
  685. /**
  686. * Add a new namespace to the loader.
  687. *
  688. * @param string $namespace
  689. * @param string|array $hints
  690. * @return void
  691. */
  692. public function addNamespace($namespace, $hints)
  693. {
  694. $this->finder->addNamespace($namespace, $hints);
  695. }
  696. /**
  697. * Prepend a new namespace to the loader.
  698. *
  699. * @param string $namespace
  700. * @param string|array $hints
  701. * @return void
  702. */
  703. public function prependNamespace($namespace, $hints)
  704. {
  705. $this->finder->prependNamespace($namespace, $hints);
  706. }
  707. /**
  708. * Register a valid view extension and its engine.
  709. *
  710. * @param string $extension
  711. * @param string $engine
  712. * @param \Closure $resolver
  713. * @return void
  714. */
  715. public function addExtension($extension, $engine, $resolver = null)
  716. {
  717. $this->finder->addExtension($extension);
  718. if (isset($resolver)) {
  719. $this->engines->register($engine, $resolver);
  720. }
  721. unset($this->extensions[$extension]);
  722. $this->extensions = array_merge([$extension => $engine], $this->extensions);
  723. }
  724. /**
  725. * Get the extension to engine bindings.
  726. *
  727. * @return array
  728. */
  729. public function getExtensions()
  730. {
  731. return $this->extensions;
  732. }
  733. /**
  734. * Get the engine resolver instance.
  735. *
  736. * @return \Illuminate\View\Engines\EngineResolver
  737. */
  738. public function getEngineResolver()
  739. {
  740. return $this->engines;
  741. }
  742. /**
  743. * Get the view finder instance.
  744. *
  745. * @return \Illuminate\View\ViewFinderInterface
  746. */
  747. public function getFinder()
  748. {
  749. return $this->finder;
  750. }
  751. /**
  752. * Set the view finder instance.
  753. *
  754. * @param \Illuminate\View\ViewFinderInterface $finder
  755. * @return void
  756. */
  757. public function setFinder(ViewFinderInterface $finder)
  758. {
  759. $this->finder = $finder;
  760. }
  761. /**
  762. * Get the event dispatcher instance.
  763. *
  764. * @return \Illuminate\Contracts\Events\Dispatcher
  765. */
  766. public function getDispatcher()
  767. {
  768. return $this->events;
  769. }
  770. /**
  771. * Set the event dispatcher instance.
  772. *
  773. * @param \Illuminate\Contracts\Events\Dispatcher $events
  774. * @return void
  775. */
  776. public function setDispatcher(Dispatcher $events)
  777. {
  778. $this->events = $events;
  779. }
  780. /**
  781. * Get the IoC container instance.
  782. *
  783. * @return \Illuminate\Contracts\Container\Container
  784. */
  785. public function getContainer()
  786. {
  787. return $this->container;
  788. }
  789. /**
  790. * Set the IoC container instance.
  791. *
  792. * @param \Illuminate\Contracts\Container\Container $container
  793. * @return void
  794. */
  795. public function setContainer(Container $container)
  796. {
  797. $this->container = $container;
  798. }
  799. /**
  800. * Get an item from the shared data.
  801. *
  802. * @param string $key
  803. * @param mixed $default
  804. * @return mixed
  805. */
  806. public function shared($key, $default = null)
  807. {
  808. return Arr::get($this->shared, $key, $default);
  809. }
  810. /**
  811. * Get all of the shared data for the environment.
  812. *
  813. * @return array
  814. */
  815. public function getShared()
  816. {
  817. return $this->shared;
  818. }
  819. /**
  820. * Check if section exists.
  821. *
  822. * @param string $name
  823. * @return bool
  824. */
  825. public function hasSection($name)
  826. {
  827. return array_key_exists($name, $this->sections);
  828. }
  829. /**
  830. * Get the entire array of sections.
  831. *
  832. * @return array
  833. */
  834. public function getSections()
  835. {
  836. return $this->sections;
  837. }
  838. /**
  839. * Get all of the registered named views in environment.
  840. *
  841. * @return array
  842. */
  843. public function getNames()
  844. {
  845. return $this->names;
  846. }
  847. }