From 856d1bf7ad4381afff23f84d36177f9c3b035998 Mon Sep 17 00:00:00 2001 From: Jeremias Wolff Date: Fri, 9 Feb 2024 17:25:46 +0100 Subject: [PATCH] [10.x] Improved Handling of Empty Component Slots with HTML Comments or Line Breaks (#49966) * implement alternative solution for PR#49935 * style ci * style ci * formatting --------- Co-authored-by: Taylor Otwell --- src/Illuminate/View/ComponentSlot.php | 20 +++++++++ tests/View/ComponentTest.php | 58 +++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/src/Illuminate/View/ComponentSlot.php b/src/Illuminate/View/ComponentSlot.php index 85665ad64575..dc48dbc88798 100644 --- a/src/Illuminate/View/ComponentSlot.php +++ b/src/Illuminate/View/ComponentSlot.php @@ -3,6 +3,7 @@ namespace Illuminate\View; use Illuminate\Contracts\Support\Htmlable; +use InvalidArgumentException; class ComponentSlot implements Htmlable { @@ -77,6 +78,25 @@ public function isNotEmpty() return ! $this->isEmpty(); } + /** + * Determine if the slot has non-comment content. + * + * @param callable|string|null $callable + * @return bool + */ + public function hasActualContent(callable|string|null $callable = null) + { + if (is_string($callable) && ! function_exists($callable)) { + throw new InvalidArgumentException('Callable does not exist.'); + } + + return filter_var( + $this->contents, + FILTER_CALLBACK, + ['options' => $callable ?? fn ($input) => trim(preg_replace("//", '', $input))] + ) !== ''; + } + /** * Get the slot's HTML string. * diff --git a/tests/View/ComponentTest.php b/tests/View/ComponentTest.php index a5ff36d82531..87e716edcee7 100644 --- a/tests/View/ComponentTest.php +++ b/tests/View/ComponentTest.php @@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Facade; use Illuminate\Support\HtmlString; use Illuminate\View\Component; +use Illuminate\View\ComponentSlot; use Illuminate\View\Factory; use Illuminate\View\View; use Mockery as m; @@ -305,6 +306,63 @@ public function testFactoryGetsSharedBetweenComponents() Component::forgetFactory(); $this->assertNotSame($this->viewFactory, $getFactory($inline)); } + + public function testComponentSlotIsEmpty() + { + $slot = new ComponentSlot(); + + $this->assertTrue((bool) $slot->isEmpty()); + } + + public function testComponentSlotSanitizedEmpty() + { + // default sanitizer should remove all html tags + $slot = new ComponentSlot(''); + + $linebreakingSlot = new ComponentSlot("\n \t"); + + $moreComplexSlot = new ComponentSlot(''); + + $this->assertFalse((bool) $slot->hasActualContent()); + $this->assertFalse((bool) $linebreakingSlot->hasActualContent('trim')); + $this->assertFalse((bool) $moreComplexSlot->hasActualContent()); + } + + public function testComponentSlotSanitizedNotEmpty() + { + // default sanitizer should remove all html tags + $slot = new ComponentSlot('not empty'); + + $linebreakingSlot = new ComponentSlot("\ntest \t"); + + $moreComplexSlot = new ComponentSlot('beforeafter'); + + $this->assertTrue((bool) $slot->hasActualContent()); + $this->assertTrue((bool) $linebreakingSlot->hasActualContent('trim')); + $this->assertTrue((bool) $moreComplexSlot->hasActualContent()); + } + + public function testComponentSlotIsNotEmpty() + { + $slot = new ComponentSlot('test'); + + $anotherSlot = new ComponentSlot('test'); + + $moreComplexSlot = new ComponentSlot('test'); + + $this->assertTrue((bool) $slot->hasActualContent()); + $this->assertTrue((bool) $anotherSlot->hasActualContent()); + $this->assertTrue((bool) $moreComplexSlot->hasActualContent()); + } } class TestInlineViewComponent extends Component