1212
1313abstract class Component
1414{
15+ /**
16+ * The properties / methods that should not be exposed to the component.
17+ *
18+ * @var array
19+ */
20+ protected $ except = [];
21+
22+ /**
23+ * The component alias name.
24+ *
25+ * @var string
26+ */
27+ public $ componentName ;
28+
29+ /**
30+ * The component attributes.
31+ *
32+ * @var \Illuminate\View\ComponentAttributeBag
33+ */
34+ public $ attributes ;
35+
36+ /**
37+ * The view factory instance, if any.
38+ *
39+ * @var \Illuminate\Contracts\View\Factory|null
40+ */
41+ protected static $ factory ;
42+
43+ /**
44+ * The component resolver callback.
45+ *
46+ * @var (\Closure(string, array): Component)|null
47+ */
48+ protected static $ componentsResolver ;
49+
50+ /**
51+ * The cache of blade view names, keyed by contents.
52+ *
53+ * @var array<string, string>
54+ */
55+ protected static $ bladeViewCache = [];
56+
1557 /**
1658 * The cache of public property names, keyed by class.
1759 *
@@ -27,32 +69,61 @@ abstract class Component
2769 protected static $ methodCache = [];
2870
2971 /**
30- * The properties / methods that should not be exposed to the component .
72+ * The cache of constructor parameters, keyed by class .
3173 *
32- * @var array
74+ * @var array<class-string, array<int, string>>
3375 */
34- protected $ except = [];
76+ protected static $ constructorParametersCache = [];
3577
3678 /**
37- * The component alias name .
79+ * Get the view / view contents that represent the component .
3880 *
39- * @var string
81+ * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\Support\Htmlable|\Closure| string
4082 */
41- public $ componentName ;
83+ abstract public function render () ;
4284
4385 /**
44- * The component attributes .
86+ * Resolve the component instance with the given data .
4587 *
46- * @var \Illuminate\View\ComponentAttributeBag
88+ * @param array $data
89+ * @return static
4790 */
48- public $ attributes ;
91+ public static function resolve ($ data )
92+ {
93+ if (static ::$ componentsResolver ) {
94+ return call_user_func (static ::$ componentsResolver , static ::class, $ data );
95+ }
96+
97+ $ parameters = static ::extractConstructorParameters ();
98+
99+ $ dataKeys = array_keys ($ data );
100+
101+ if (empty (array_diff ($ parameters , $ dataKeys ))) {
102+ return new static (...array_intersect_key ($ data , array_flip ($ parameters )));
103+ }
104+
105+ return Container::getInstance ()->make (static ::class, $ data );
106+ }
49107
50108 /**
51- * Get the view / view contents that represent the component.
109+ * Extract the constructor parameters for the component.
52110 *
53- * @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\Support\Htmlable|\Closure|string
111+ * @return array
54112 */
55- abstract public function render ();
113+ protected static function extractConstructorParameters ()
114+ {
115+ if (! isset (static ::$ constructorParametersCache [static ::class])) {
116+ $ class = new ReflectionClass (static ::class);
117+
118+ $ constructor = $ class ->getConstructor ();
119+
120+ static ::$ constructorParametersCache [static ::class] = $ constructor
121+ ? collect ($ constructor ->getParameters ())->map ->getName ()->all ()
122+ : [];
123+ }
124+
125+ return static ::$ constructorParametersCache [static ::class];
126+ }
56127
57128 /**
58129 * Resolve the Blade view or view file that should be used when rendering the component.
@@ -72,11 +143,7 @@ public function resolveView()
72143 }
73144
74145 $ resolver = function ($ view ) {
75- $ factory = Container::getInstance ()->make ('view ' );
76-
77- return strlen ($ view ) <= PHP_MAXPATHLEN && $ factory ->exists ($ view )
78- ? $ view
79- : $ this ->createBladeViewFromString ($ factory , $ view );
146+ return $ this ->extractBladeViewFromString ($ view );
80147 };
81148
82149 return $ view instanceof Closure ? function (array $ data = []) use ($ view , $ resolver ) {
@@ -88,13 +155,22 @@ public function resolveView()
88155 /**
89156 * Create a Blade view with the raw component string content.
90157 *
91- * @param \Illuminate\Contracts\View\Factory $factory
92158 * @param string $contents
93159 * @return string
94160 */
95- protected function createBladeViewFromString ( $ factory , $ contents )
161+ protected function extractBladeViewFromString ( $ contents )
96162 {
97- $ factory ->addNamespace (
163+ $ key = sprintf ('%s::%s ' , static ::class, $ contents );
164+
165+ if (isset (static ::$ bladeViewCache [$ key ])) {
166+ return static ::$ bladeViewCache [$ key ];
167+ }
168+
169+ if (strlen ($ contents ) <= PHP_MAXPATHLEN && $ this ->factory ()->exists ($ contents )) {
170+ return static ::$ bladeViewCache [$ key ] = $ contents ;
171+ }
172+
173+ $ this ->factory ()->addNamespace (
98174 '__components ' ,
99175 $ directory = Container::getInstance ()['config ' ]->get ('view.compiled ' )
100176 );
@@ -107,7 +183,7 @@ protected function createBladeViewFromString($factory, $contents)
107183 file_put_contents ($ viewFile , $ contents );
108184 }
109185
110- return '__components:: ' .basename ($ viewFile , '.blade.php ' );
186+ return static :: $ bladeViewCache [ $ key ] = '__components:: ' .basename ($ viewFile , '.blade.php ' );
111187 }
112188
113189 /**
@@ -292,4 +368,79 @@ public function shouldRender()
292368 {
293369 return true ;
294370 }
371+
372+ /**
373+ * Get the evaluated view contents for the given view.
374+ *
375+ * @param string|null $view
376+ * @param \Illuminate\Contracts\Support\Arrayable|array $data
377+ * @param array $mergeData
378+ * @return \Illuminate\Contracts\View\View
379+ */
380+ public function view ($ view , $ data = [], $ mergeData = [])
381+ {
382+ return $ this ->factory ()->make ($ view , $ data , $ mergeData );
383+ }
384+
385+ /**
386+ * Get the view factory instance.
387+ *
388+ * @return \Illuminate\Contracts\View\Factory
389+ */
390+ protected function factory ()
391+ {
392+ if (is_null (static ::$ factory )) {
393+ static ::$ factory = Container::getInstance ()->make ('view ' );
394+ }
395+
396+ return static ::$ factory ;
397+ }
398+
399+ /**
400+ * Flush the component's cached state.
401+ *
402+ * @return void
403+ */
404+ public static function flushCache ()
405+ {
406+ static ::$ bladeViewCache = [];
407+ static ::$ constructorParametersCache = [];
408+ static ::$ methodCache = [];
409+ static ::$ propertyCache = [];
410+ }
411+
412+ /**
413+ * Forget the component's factory instance.
414+ *
415+ * @return void
416+ */
417+ public static function forgetFactory ()
418+ {
419+ static ::$ factory = null ;
420+ }
421+
422+ /**
423+ * Forget the component's resolver callback.
424+ *
425+ * @return void
426+ *
427+ * @internal
428+ */
429+ public static function forgetComponentsResolver ()
430+ {
431+ static ::$ componentsResolver = null ;
432+ }
433+
434+ /**
435+ * Set the callback that should be used to resolve components within views.
436+ *
437+ * @param \Closure(string $component, array $data): Component $resolver
438+ * @return void
439+ *
440+ * @internal
441+ */
442+ public static function resolveComponentsUsing ($ resolver )
443+ {
444+ static ::$ componentsResolver = $ resolver ;
445+ }
295446}
0 commit comments