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

https://gitlab.com/jjpa2018/dashboard · PHP · 296 lines · 147 code · 43 blank · 106 comment · 6 complexity · 03981aa44f0940ca64f620dc2912cf8d MD5 · raw file

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