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

https://github.com/ladybirdweb/agorainvoicing · PHP · 276 lines · 135 code · 41 blank · 100 comment · 5 complexity · cad9414d7c32e84c6750e6452bf50167 MD5 · raw file

  1. <?php
  2. namespace Illuminate\View;
  3. use Closure;
  4. use Illuminate\Container\Container;
  5. use Illuminate\Support\Str;
  6. use ReflectionClass;
  7. use ReflectionMethod;
  8. use ReflectionProperty;
  9. abstract class Component
  10. {
  11. /**
  12. * The cache of public property names, keyed by class.
  13. *
  14. * @var array
  15. */
  16. protected static $propertyCache = [];
  17. /**
  18. * The cache of public method names, keyed by class.
  19. *
  20. * @var array
  21. */
  22. protected static $methodCache = [];
  23. /**
  24. * The properties / methods that should not be exposed to the component.
  25. *
  26. * @var array
  27. */
  28. protected $except = [];
  29. /**
  30. * The component alias name.
  31. *
  32. * @var string
  33. */
  34. public $componentName;
  35. /**
  36. * The component attributes.
  37. *
  38. * @var \Illuminate\View\ComponentAttributeBag
  39. */
  40. public $attributes;
  41. /**
  42. * Get the view / view contents that represent the component.
  43. *
  44. * @return \Illuminate\View\View|string
  45. */
  46. abstract public function render();
  47. /**
  48. * Resolve the Blade view or view file that should be used when rendering the component.
  49. *
  50. * @return \Illuminate\View\View|string
  51. */
  52. public function resolveView()
  53. {
  54. $view = $this->render();
  55. if ($view instanceof View) {
  56. return $view;
  57. }
  58. $resolver = function ($view) {
  59. $factory = Container::getInstance()->make('view');
  60. return $factory->exists($view)
  61. ? $view
  62. : $this->createBladeViewFromString($factory, $view);
  63. };
  64. return $view instanceof Closure ? function (array $data = []) use ($view, $resolver) {
  65. return $resolver($view($data));
  66. }
  67. : $resolver($view);
  68. }
  69. /**
  70. * Create a Blade view with the raw component string content.
  71. *
  72. * @param \Illuminate\Contracts\View\Factory $factory
  73. * @param string $contents
  74. * @return string
  75. */
  76. protected function createBladeViewFromString($factory, $contents)
  77. {
  78. $factory->addNamespace(
  79. '__components',
  80. $directory = Container::getInstance()['config']->get('view.compiled')
  81. );
  82. if (! file_exists($viewFile = $directory.'/'.sha1($contents).'.blade.php')) {
  83. if (! is_dir($directory)) {
  84. mkdir($directory, 0755, true);
  85. }
  86. file_put_contents($viewFile, $contents);
  87. }
  88. return '__components::'.basename($viewFile, '.blade.php');
  89. }
  90. /**
  91. * Get the data that should be supplied to the view.
  92. *
  93. * @author Freek Van der Herten
  94. * @author Brent Roose
  95. *
  96. * @return array
  97. */
  98. public function data()
  99. {
  100. $this->attributes = $this->attributes ?: new ComponentAttributeBag;
  101. return array_merge($this->extractPublicProperties(), $this->extractPublicMethods());
  102. }
  103. /**
  104. * Extract the public properties for the component.
  105. *
  106. * @return array
  107. */
  108. protected function extractPublicProperties()
  109. {
  110. $class = get_class($this);
  111. if (! isset(static::$propertyCache[$class])) {
  112. $reflection = new ReflectionClass($this);
  113. static::$propertyCache[$class] = collect($reflection->getProperties(ReflectionProperty::IS_PUBLIC))
  114. ->reject(function (ReflectionProperty $property) {
  115. return $this->shouldIgnore($property->getName());
  116. })
  117. ->map(function (ReflectionProperty $property) {
  118. return $property->getName();
  119. })->all();
  120. }
  121. $values = [];
  122. foreach (static::$propertyCache[$class] as $property) {
  123. $values[$property] = $this->{$property};
  124. }
  125. return $values;
  126. }
  127. /**
  128. * Extract the public methods for the component.
  129. *
  130. * @return array
  131. */
  132. protected function extractPublicMethods()
  133. {
  134. $class = get_class($this);
  135. if (! isset(static::$methodCache[$class])) {
  136. $reflection = new ReflectionClass($this);
  137. static::$methodCache[$class] = collect($reflection->getMethods(ReflectionMethod::IS_PUBLIC))
  138. ->reject(function (ReflectionMethod $method) {
  139. return $this->shouldIgnore($method->getName());
  140. })
  141. ->map(function (ReflectionMethod $method) {
  142. return $method->getName();
  143. });
  144. }
  145. $values = [];
  146. foreach (static::$methodCache[$class] as $method) {
  147. $values[$method] = $this->createVariableFromMethod(new ReflectionMethod($this, $method));
  148. }
  149. return $values;
  150. }
  151. /**
  152. * Create a callable variable from the given method.
  153. *
  154. * @param \ReflectionMethod $method
  155. * @return mixed
  156. */
  157. protected function createVariableFromMethod(ReflectionMethod $method)
  158. {
  159. return $method->getNumberOfParameters() === 0
  160. ? $this->createInvokableVariable($method->getName())
  161. : Closure::fromCallable([$this, $method->getName()]);
  162. }
  163. /**
  164. * Create an invokable, toStringable variable for the given component method.
  165. *
  166. * @param string $method
  167. * @return \Illuminate\View\InvokableComponentVariable
  168. */
  169. protected function createInvokableVariable(string $method)
  170. {
  171. return new InvokableComponentVariable(function () use ($method) {
  172. return $this->{$method}();
  173. });
  174. }
  175. /**
  176. * Determine if the given property / method should be ignored.
  177. *
  178. * @param string $name
  179. * @return bool
  180. */
  181. protected function shouldIgnore($name)
  182. {
  183. return Str::startsWith($name, '__') ||
  184. in_array($name, $this->ignoredMethods());
  185. }
  186. /**
  187. * Get the methods that should be ignored.
  188. *
  189. * @return array
  190. */
  191. protected function ignoredMethods()
  192. {
  193. return array_merge([
  194. 'data',
  195. 'render',
  196. 'resolveView',
  197. 'shouldRender',
  198. 'view',
  199. 'withName',
  200. 'withAttributes',
  201. ], $this->except);
  202. }
  203. /**
  204. * Set the component alias name.
  205. *
  206. * @param string $name
  207. * @return $this
  208. */
  209. public function withName($name)
  210. {
  211. $this->componentName = $name;
  212. return $this;
  213. }
  214. /**
  215. * Set the extra attributes that the component should make available.
  216. *
  217. * @param array $attributes
  218. * @return $this
  219. */
  220. public function withAttributes(array $attributes)
  221. {
  222. $this->attributes = $this->attributes ?: new ComponentAttributeBag;
  223. $this->attributes->setAttributes($attributes);
  224. return $this;
  225. }
  226. /**
  227. * Determine if the component should be rendered.
  228. *
  229. * @return bool
  230. */
  231. public function shouldRender()
  232. {
  233. return true;
  234. }
  235. }