diff --git a/src/Illuminate/Events/Dispatcher.php b/src/Illuminate/Events/Dispatcher.php index c418fc4d06b8..a43a76aec0a8 100755 --- a/src/Illuminate/Events/Dispatcher.php +++ b/src/Illuminate/Events/Dispatcher.php @@ -66,6 +66,13 @@ class Dispatcher implements DispatcherContract */ protected $transactionManagerResolver; + /** + * The cache of events that have listeners. + * + * @var array + */ + protected $listenerCache = []; + /** * Create a new event dispatcher instance. * @@ -107,6 +114,8 @@ public function listen($events, $listener = null) $this->listeners[$event][] = $listener; } } + + $this->listenerCache = []; } /** @@ -131,9 +140,15 @@ protected function setupWildcardListen($event, $listener) */ public function hasListeners($eventName) { - return isset($this->listeners[$eventName]) || + if (isset($this->listenerCache[$eventName])) { + return $this->listenerCache[$eventName]; + } + + $this->listenerCache[$eventName] = isset($this->listeners[$eventName]) || isset($this->wildcards[$eventName]) || $this->hasWildcardListeners($eventName); + + return $this->listenerCache[$eventName]; } /** @@ -703,6 +718,8 @@ public function forget($event) unset($this->wildcardsCache[$key]); } } + + $this->listenerCache = []; } /** diff --git a/src/Illuminate/View/AnonymousComponent.php b/src/Illuminate/View/AnonymousComponent.php index eba64365626b..f9601e04250d 100644 --- a/src/Illuminate/View/AnonymousComponent.php +++ b/src/Illuminate/View/AnonymousComponent.php @@ -25,10 +25,11 @@ class AnonymousComponent extends Component * @param array $data * @return void */ - public function __construct($view, $data) + public function __construct($view, $data, Factory $factory) { $this->view = $view; $this->data = $data; + self::$factory = $factory; } /** @@ -41,6 +42,29 @@ public function render() return $this->view; } + /** + * Resolve the component instance with the given data. + * + * @param array $data + * @return static + */ + public static function resolve($data) + { + return new static($data['view'], $data['data'], $data['factory']); + } + + /** + * Resolve the Blade view or view file that should be used when rendering the component. + * + * @return string + */ + public function resolveView() + { + $view = $this->render(); + + return $this->extractBladeViewFromString($view); + } + /** * Get the data that should be supplied to the view. * diff --git a/src/Illuminate/View/Compilers/Compiler.php b/src/Illuminate/View/Compilers/Compiler.php index 7ec15ac96f74..f1febb6c9727 100755 --- a/src/Illuminate/View/Compilers/Compiler.php +++ b/src/Illuminate/View/Compilers/Compiler.php @@ -44,6 +44,13 @@ abstract class Compiler */ protected $compiledExtension = 'php'; + /** + * The cache of compiled paths. + * + * @var array + */ + protected $compiledPathCache = []; + /** * Create a new compiler instance. * @@ -82,7 +89,7 @@ public function __construct( */ public function getCompiledPath($path) { - return $this->cachePath.'/'.hash('xxh128', 'v2'.Str::after($path, $this->basePath)).'.'.$this->compiledExtension; + return $this->compiledPathCache[$path] ??= $this->cachePath.'/'.hash('xxh128', 'v2'.Str::after($path, $this->basePath)).'.'.$this->compiledExtension; } /** diff --git a/src/Illuminate/View/Compilers/ComponentTagCompiler.php b/src/Illuminate/View/Compilers/ComponentTagCompiler.php index 9c3d6adbec99..c726f3fb8e98 100644 --- a/src/Illuminate/View/Compilers/ComponentTagCompiler.php +++ b/src/Illuminate/View/Compilers/ComponentTagCompiler.php @@ -251,6 +251,7 @@ protected function componentString(string $component, array $attributes) $parameters = [ 'view' => $view, 'data' => '['.$this->attributesToString($data->all(), $escapeBound = false).']', + 'factory' => '$__env', ]; $class = AnonymousComponent::class; @@ -776,9 +777,17 @@ protected function attributesToString(array $attributes, $escapeBound = true) { return collect($attributes) ->map(function (string $value, string $attribute) use ($escapeBound) { - return $escapeBound && isset($this->boundAttributes[$attribute]) && $value !== 'true' && ! is_numeric($value) - ? "'{$attribute}' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute({$value})" - : "'{$attribute}' => {$value}"; + $shouldSanitize = $escapeBound && isset($this->boundAttributes[$attribute]) && $value !== 'true' && ! is_numeric($value); + + if ($shouldSanitize) { + $value = \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute($value); + + if (is_string($value)) { + return "'{$attribute}' => '{$value}'"; + } + } + + return "'{$attribute}' => {$value}"; }) ->implode(','); } diff --git a/src/Illuminate/View/Concerns/ManagesComponents.php b/src/Illuminate/View/Concerns/ManagesComponents.php index 456e6a044e5d..ab6fc54fb474 100644 --- a/src/Illuminate/View/Concerns/ManagesComponents.php +++ b/src/Illuminate/View/Concerns/ManagesComponents.php @@ -56,9 +56,11 @@ public function startComponent($view, array $data = []) if (ob_start()) { $this->componentStack[] = $view; - $this->componentData[$this->currentComponent()] = $data; + $currentComponent = $this->currentComponent(); - $this->slots[$this->currentComponent()] = []; + $this->componentData[$currentComponent] = $data; + + $this->slots[$currentComponent] = []; } } diff --git a/src/Illuminate/View/Factory.php b/src/Illuminate/View/Factory.php index bc6e59d55afd..2db93d8cdd01 100755 --- a/src/Illuminate/View/Factory.php +++ b/src/Illuminate/View/Factory.php @@ -2,14 +2,15 @@ namespace Illuminate\View; -use Illuminate\Contracts\Container\Container; -use Illuminate\Contracts\Events\Dispatcher; -use Illuminate\Contracts\Support\Arrayable; -use Illuminate\Contracts\View\Factory as FactoryContract; use Illuminate\Support\Arr; +use InvalidArgumentException; use Illuminate\Support\Traits\Macroable; +use Illuminate\Contracts\Events\Dispatcher; +use Illuminate\Contracts\Support\Arrayable; use Illuminate\View\Engines\EngineResolver; -use InvalidArgumentException; +use Illuminate\Contracts\Support\Renderable; +use Illuminate\Contracts\Container\Container; +use Illuminate\Contracts\View\Factory as FactoryContract; class Factory implements FactoryContract { @@ -104,6 +105,20 @@ class Factory implements FactoryContract */ protected $normalizedNameCache = []; + /** + * Flag to determine if renderable shared data cache is set. + * + * @var boolean + */ + protected $renderableSharedCacheIsSet = false; + + /** + * The cache of shared data with objects cast to strings. + * + * @var array + */ + protected $renderableSharedCache = []; + /** * Create a new view factory instance. * @@ -358,6 +373,8 @@ public function share($key, $value = null) $this->shared[$key] = $value; } + $this->renderableSharedCacheIsSet = false; + return $value; } @@ -631,4 +648,22 @@ public function getShared() { return $this->shared; } + + public function getRenderableShared() + { + if ($this->renderableSharedCacheIsSet) { + return $this->renderableSharedCache; + } + + $this->renderableSharedCache = $this->getShared(); + foreach ($this->renderableSharedCache as $key => $value) { + if ($value instanceof Renderable) { + $this->renderableSharedCache[$key] = $value->render(); + } + } + + $this->renderableSharedCacheIsSet = true; + + return $this->renderableSharedCache; + } } diff --git a/src/Illuminate/View/View.php b/src/Illuminate/View/View.php index 0751a8dff5ba..8cd0037b3fa3 100755 --- a/src/Illuminate/View/View.php +++ b/src/Illuminate/View/View.php @@ -215,7 +215,7 @@ protected function getContents() */ public function gatherData() { - $data = array_merge($this->factory->getShared(), $this->data); + $data = $this->data; foreach ($data as $key => $value) { if ($value instanceof Renderable) { @@ -223,7 +223,7 @@ public function gatherData() } } - return $data; + return array_merge($this->factory->getRenderableShared(), $data); } /** @@ -305,7 +305,7 @@ protected function formatErrors($provider) */ public function name() { - return $this->getName(); + return $this->view; } /** diff --git a/tests/View/Blade/BladeComponentTagCompilerTest.php b/tests/View/Blade/BladeComponentTagCompilerTest.php index 38a4a037b21a..2fa1f4d28a59 100644 --- a/tests/View/Blade/BladeComponentTagCompilerTest.php +++ b/tests/View/Blade/BladeComponentTagCompilerTest.php @@ -474,7 +474,7 @@ public function testClasslessComponents() $result = $this->compiler()->compileTags(''); - $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'anonymous-component', ['view' => 'components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']]) + $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'anonymous-component', ['view' => 'components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo'],'factory' => \$__env]) except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> @@ -493,7 +493,7 @@ public function testClasslessComponentsWithIndexView() $result = $this->compiler()->compileTags(''); - $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'anonymous-component', ['view' => 'components.anonymous-component.index','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']]) + $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'anonymous-component', ['view' => 'components.anonymous-component.index','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo'],'factory' => \$__env]) except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> @@ -512,7 +512,7 @@ public function testPackagesClasslessComponents() $result = $this->compiler()->compileTags(''); - $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'package::anonymous-component', ['view' => 'package::components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']]) + $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'package::anonymous-component', ['view' => 'package::components.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo'],'factory' => \$__env]) except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> @@ -546,7 +546,7 @@ public function testClasslessComponentsWithAnonymousComponentNamespace() $result = $compiler->compileTags(''); - $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'frontend::anonymous-component', ['view' => 'public.frontend.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']]) + $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'frontend::anonymous-component', ['view' => 'public.frontend.anonymous-component','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo'],'factory' => \$__env]) except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> @@ -580,7 +580,7 @@ public function testClasslessComponentsWithAnonymousComponentNamespaceWithIndexV $result = $compiler->compileTags(''); - $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'admin.auth::anonymous-component', ['view' => 'admin.auth.components.anonymous-component.index','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo']]) + $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'admin.auth::anonymous-component', ['view' => 'admin.auth.components.anonymous-component.index','data' => ['name' => 'Taylor','age' => 31,'wire:model' => 'foo'],'factory' => \$__env]) except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> @@ -613,7 +613,7 @@ public function testClasslessComponentsWithAnonymousComponentPath() $result = $compiler->compileTags(''); - $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'panel', ['view' => '".md5('test-directory')."::panel.index','data' => []]) + $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'panel', ['view' => '".md5('test-directory')."::panel.index','data' => [],'factory' => \$__env]) except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?> @@ -646,7 +646,7 @@ public function testClasslessIndexComponentsWithAnonymousComponentPath() $result = $compiler->compileTags(''); - $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'panel', ['view' => '".md5('test-directory')."::panel','data' => []]) + $this->assertSame("##BEGIN-COMPONENT-CLASS##@component('Illuminate\View\AnonymousComponent', 'panel', ['view' => '".md5('test-directory')."::panel','data' => [],'factory' => \$__env]) except(\Illuminate\View\AnonymousComponent::ignoredParameterNames()); ?>