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();
+ }
+}