diff --git a/src/Illuminate/View/Compilers/Concerns/CompilesConditionals.php b/src/Illuminate/View/Compilers/Concerns/CompilesConditionals.php index f54d33b5ffc9..eacc865f00d6 100644 --- a/src/Illuminate/View/Compilers/Concerns/CompilesConditionals.php +++ b/src/Illuminate/View/Compilers/Concerns/CompilesConditionals.php @@ -2,6 +2,7 @@ namespace Illuminate\View\Compilers\Concerns; +use Illuminate\Contracts\View\ViewCompilationException; use Illuminate\Support\Str; trait CompilesConditionals @@ -417,4 +418,25 @@ protected function compileEndPushIf() { return 'stopPush(); endif; ?>'; } + + /** + * Compile conditional HTML attributes into valid PHP. + * + * @param string $expression + * @return string + * + * @throws \Illuminate\Contracts\View\ViewCompilationException + */ + protected function compileMaybe($expression) + { + $parts = explode(',', $this->stripParentheses($expression), 2); + + if (2 !== count($parts)) { + throw new ViewCompilationException('The @maybe directive requires exactly 2 parameters.'); + } + + [$attribute, $data] = array_map('trim', $parts); + + return ""; + } } diff --git a/tests/View/Blade/BladeMaybeStatementsTest.php b/tests/View/Blade/BladeMaybeStatementsTest.php new file mode 100644 index 000000000000..0fc0b0be4a7b --- /dev/null +++ b/tests/View/Blade/BladeMaybeStatementsTest.php @@ -0,0 +1,168 @@ +expectException(ViewCompilationException::class); + $this->expectExceptionMessage('The @maybe directive requires exactly 2 parameters.'); + + $string = "@maybe('title')"; + $this->compiler->compileString($string); + } + + public function testMaybeWithSimpleVariable() + { + $string = 'Link'; + $expected = '>Link'; + + $this->assertEquals($expected, $this->compiler->compileString($string)); + } + + public function testMaybeWithObjectProperty() + { + $string = 'title)>Link'; + $expected = 'title !== \'\' && $link->title !== null && trim(is_bool($link->title) ? ($link->title ? \'true\' : \'false\') : $link->title) !== \'\') echo \'title\' . \'="\' . e(is_bool($link->title) ? ($link->title ? \'true\' : \'false\') : $link->title) . \'"\'; ?>>Link'; + + $this->assertEquals($expected, $this->compiler->compileString($string)); + } + + public function testMaybeWithArrayAccess() + { + $string = 'Link'; + $expected = '>Link'; + + $this->assertEquals($expected, $this->compiler->compileString($string)); + } + + public function testMaybeWithMultipleAttributes() + { + $string = ''; + $expected = ' />'; + + $this->assertEquals($expected, $this->compiler->compileString($string)); + } + + public function testMaybeWithSpacesAroundParameters() + { + $string = 'Link'; + $expected = '>Link'; + + $this->assertEquals($expected, $this->compiler->compileString($string)); + } + + public function testMaybeWithNonEmptyString() + { + $this->assertSame( + "", + $this->compiler->compileString("@maybe('title', \$title)") + ); + } + + public function testMaybeWithEmptyString() + { + $string = "@maybe('title', \$title)"; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('', $this->evaluateBlade($compiled, ['title' => ''])); + } + + public function testMaybeWithNull() + { + $string = "@maybe('title', \$title)"; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('', $this->evaluateBlade($compiled, ['title' => null])); + } + + public function testMaybeWithZero() + { + $string = "@maybe('data-count', \$count)"; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('data-count="0"', $this->evaluateBlade($compiled, ['count' => 0])); + } + + public function testMaybeWithFalse() + { + $string = "@maybe('data-active', \$active)"; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('data-active="false"', $this->evaluateBlade($compiled, ['active' => false])); + } + + public function testMaybeWithTrue() + { + $string = "@maybe('data-active', \$active)"; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('data-active="true"', $this->evaluateBlade($compiled, ['active' => true])); + } + + public function testMaybeWithValidString() + { + $string = "@maybe('title', \$title)"; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('title="You can just do things"', $this->evaluateBlade($compiled, ['title' => 'You can just do things'])); + } + + public function testMaybeEscapesHtmlEntities() + { + $string = "@maybe('title', \$title)"; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('title="<script>alert('xss')</script>"', + $this->evaluateBlade($compiled, ['title' => ""])); + } + + public function testMaybeWithWhitespaceOnlyString() + { + $string = "@maybe('title', \$title)"; + $compiled = $this->compiler->compileString($string); + + // Whitespace-only strings are considered empty. + $this->assertSame('', $this->evaluateBlade($compiled, ['title' => ' '])); + } + + public function testMaybeWithNumericString() + { + $string = "@maybe('data-id', \$id)"; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('data-id="123"', $this->evaluateBlade($compiled, ['id' => '123'])); + } + + public function testMaybeWithInt() + { + $string = "@maybe('data-id', \$id)"; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('data-id="123"', $this->evaluateBlade($compiled, ['id' => 123])); + } + + public function testMaybeInHtmlContext() + { + $string = 'Link'; + $compiled = $this->compiler->compileString($string); + + $this->assertSame('Link', + $this->evaluateBlade($compiled, ['title' => 'click'])); + + $this->assertSame('Link', + $this->evaluateBlade($compiled, ['title' => ''])); + } + + protected function evaluateBlade(string $compiled, array $data = []): string + { + extract($data); + ob_start(); + eval('?>'.$compiled); + + return ob_get_clean(); + } +}