Skip to content

Commit

Permalink
[11.x] Supercharge Blade (#51143)
Browse files Browse the repository at this point in the history
* Cache identical renders in memory

* Only dispatch if there are listeners

* Inline component props steps

* Cache and reuse ignore anonymous component constructor parameter names

* Revert fingerprinting/in-memory cache

* Fix tests

* formatting

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
assertchris and taylorotwell authored Apr 22, 2024
1 parent 077eba3 commit ca54cb6
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 127 deletions.
4 changes: 2 additions & 2 deletions src/Illuminate/View/Compilers/ComponentTagCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,8 @@ protected function componentString(string $component, array $attributes)
}

return "##BEGIN-COMPONENT-CLASS##@component('{$class}', '{$component}', [".$this->attributesToString($parameters, $escapeBound = false).'])
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag && $constructor = (new ReflectionClass('.$class.'::class))->getConstructor()): ?>
<?php $attributes = $attributes->except(collect($constructor->getParameters())->map->getName()->all()); ?>
<?php if (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag): ?>
<?php $attributes = $attributes->except(\\'.$class.'::ignoredParameterNames()); ?>
<?php endif; ?>
<?php $component->withAttributes(['.$this->attributesToString($attributes->all(), $escapeAttributes = $class !== DynamicComponent::class).']); ?>';
}
Expand Down
40 changes: 28 additions & 12 deletions src/Illuminate/View/Compilers/Concerns/CompilesComponents.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public static function compileClassComponentOpening(string $component, string $a
return implode("\n", [
'<?php if (isset($component)) { $__componentOriginal'.$hash.' = $component; } ?>',
'<?php if (isset($attributes)) { $__attributesOriginal'.$hash.' = $attributes; } ?>',
'<?php $component = '.$component.'::resolve('.($data ?: '[]').' + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? (array) $attributes->getIterator() : [])); ?>',
'<?php $component = '.$component.'::resolve('.($data ?: '[]').' + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? $attributes->all() : [])); ?>',
'<?php $component->withName('.$alias.'); ?>',
'<?php if ($component->shouldRender()): ?>',
'<?php $__env->startComponent($component->resolveView(), $component->data()); ?>',
Expand Down Expand Up @@ -157,19 +157,35 @@ protected function compileEndComponentFirst()
*/
protected function compileProps($expression)
{
return "<?php \$attributes ??= new \\Illuminate\\View\\ComponentAttributeBag; ?>
<?php foreach(\$attributes->onlyProps{$expression} as \$__key => \$__value) {
\$\$__key = \$\$__key ?? \$__value;
} ?>
<?php \$attributes = \$attributes->exceptProps{$expression}; ?>
<?php foreach (array_filter({$expression}, 'is_string', ARRAY_FILTER_USE_KEY) as \$__key => \$__value) {
return "<?php \$attributes ??= new \\Illuminate\\View\\ComponentAttributeBag;
\$__newAttributes = [];
\$__propNames = \Illuminate\View\ComponentAttributeBag::extractPropNames({$expression});
foreach (\$attributes->all() as \$__key => \$__value) {
if (in_array(\$__key, \$__propNames)) {
\$\$__key = \$\$__key ?? \$__value;
} else {
\$__newAttributes[\$__key] = \$__value;
}
}
\$attributes = new \Illuminate\View\ComponentAttributeBag(\$__newAttributes);
unset(\$__propNames);
unset(\$__newAttributes);
foreach (array_filter({$expression}, 'is_string', ARRAY_FILTER_USE_KEY) as \$__key => \$__value) {
\$\$__key = \$\$__key ?? \$__value;
} ?>
<?php \$__defined_vars = get_defined_vars(); ?>
<?php foreach (\$attributes as \$__key => \$__value) {
}
\$__defined_vars = get_defined_vars();
foreach (\$attributes->all() as \$__key => \$__value) {
if (array_key_exists(\$__key, \$__defined_vars)) unset(\$\$__key);
} ?>
<?php unset(\$__defined_vars); ?>";
}
unset(\$__defined_vars); ?>";
}

/**
Expand Down
31 changes: 31 additions & 0 deletions src/Illuminate/View/Component.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ abstract class Component
*/
protected static $constructorParametersCache = [];

/**
* The cache of ignored parameter names.
*
* @var array
*/
protected static $ignoredParameterNames = [];

/**
* Get the view / view contents that represent the component.
*
Expand Down Expand Up @@ -417,6 +424,30 @@ protected function factory()
return static::$factory;
}

/**
* Get the cached set of anonymous component constructor parameter names to exclude.
*
* @return array
*/
public static function ignoredParameterNames()
{
if (! isset(static::$ignoredParameterNames[static::class])) {
$constructor = (new ReflectionClass(
static::class
))->getConstructor();

if (! $constructor) {
return static::$ignoredParameterNames[static::class] = [];
}

static::$ignoredParameterNames[static::class] = collect($constructor->getParameters())
->map->getName()
->all();
}

return static::$ignoredParameterNames[static::class];
}

/**
* Flush the component's cached state.
*
Expand Down
54 changes: 32 additions & 22 deletions src/Illuminate/View/ComponentAttributeBag.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ public function __construct(array $attributes = [])
$this->attributes = $attributes;
}

/**
* Get all of the attribute values.
*
* @return array
*/
public function all()
{
return $this->attributes;
}

/**
* Get the first attribute's value.
*
Expand Down Expand Up @@ -207,7 +217,7 @@ public function thatStartWith($needles)
*/
public function onlyProps($keys)
{
return $this->only($this->extractPropNames($keys));
return $this->only(static::extractPropNames($keys));
}

/**
Expand All @@ -218,27 +228,7 @@ public function onlyProps($keys)
*/
public function exceptProps($keys)
{
return $this->except($this->extractPropNames($keys));
}

/**
* Extract prop names from given keys.
*
* @param mixed|array $keys
* @return array
*/
protected function extractPropNames($keys)
{
$props = [];

foreach ($keys as $key => $defaultValue) {
$key = is_numeric($key) ? $defaultValue : $key;

$props[] = $key;
$props[] = Str::kebab($key);
}

return $props;
return $this->except(static::extractPropNames($keys));
}

/**
Expand Down Expand Up @@ -401,6 +391,26 @@ public function setAttributes(array $attributes)
$this->attributes = $attributes;
}

/**
* Extract "prop" names from given keys.
*
* @param array $keys
* @return array
*/
public static function extractPropNames(array $keys)
{
$props = [];

foreach ($keys as $key => $default) {
$key = is_numeric($key) ? $default : $key;

$props[] = $key;
$props[] = Str::kebab($key);
}

return $props;
}

/**
* Get content as a string of HTML.
*
Expand Down
8 changes: 6 additions & 2 deletions src/Illuminate/View/Concerns/ManagesEvents.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,9 @@ protected function addEventListener($name, $callback)
*/
public function callComposer(ViewContract $view)
{
$this->events->dispatch('composing: '.$view->name(), [$view]);
if ($this->events->hasListeners($event = 'composing: '.$view->name())) {
$this->events->dispatch($event, [$view]);
}
}

/**
Expand All @@ -185,6 +187,8 @@ public function callComposer(ViewContract $view)
*/
public function callCreator(ViewContract $view)
{
$this->events->dispatch('creating: '.$view->name(), [$view]);
if ($this->events->hasListeners($event = 'creating: '.$view->name())) {
$this->events->dispatch($event, [$view]);
}
}
}
Loading

0 comments on commit ca54cb6

Please sign in to comment.