diff --git a/src/Latte/Compiler/Nodes/Php/Expression/InRangeNode.php b/src/Latte/Compiler/Nodes/Php/Expression/InNode.php similarity index 90% rename from src/Latte/Compiler/Nodes/Php/Expression/InRangeNode.php rename to src/Latte/Compiler/Nodes/Php/Expression/InNode.php index 8bf51ef6bd..c865cf9b2f 100644 --- a/src/Latte/Compiler/Nodes/Php/Expression/InRangeNode.php +++ b/src/Latte/Compiler/Nodes/Php/Expression/InNode.php @@ -14,7 +14,7 @@ use Latte\Compiler\PrintContext; -class InRangeNode extends ExpressionNode +class InNode extends ExpressionNode { public function __construct( public ExpressionNode $needle, @@ -26,11 +26,11 @@ public function __construct( public function print(PrintContext $context): string { - return 'in_array(' + return 'LR\Filters::contains(' . $this->needle->print($context) . ', ' . $this->haystack->print($context) - . ', true)'; + . ')'; } diff --git a/src/Latte/Compiler/TagParserData.php b/src/Latte/Compiler/TagParserData.php index 1850a4f24d..af658697fe 100644 --- a/src/Latte/Compiler/TagParserData.php +++ b/src/Latte/Compiler/TagParserData.php @@ -482,7 +482,7 @@ protected function reduce(int $rule, int $pos): void 124 => fn() => $this->semValue = new Expression\BinaryOpNode($this->semStack[$pos - 2], '<<', $this->semStack[$pos], $this->startTokenStack[$pos - 2]->position), 125 => fn() => $this->semValue = new Expression\BinaryOpNode($this->semStack[$pos - 2], '>>', $this->semStack[$pos], $this->startTokenStack[$pos - 2]->position), 126 => fn() => $this->semValue = new Expression\BinaryOpNode($this->semStack[$pos - 2], '**', $this->semStack[$pos], $this->startTokenStack[$pos - 2]->position), - 127 => fn() => $this->semValue = new Expression\InRangeNode($this->semStack[$pos - 2], $this->semStack[$pos], $this->startTokenStack[$pos - 2]->position), + 127 => fn() => $this->semValue = new Expression\InNode($this->semStack[$pos - 2], $this->semStack[$pos], $this->startTokenStack[$pos - 2]->position), 128 => fn() => $this->semValue = new Expression\UnaryOpNode($this->semStack[$pos], '+', $this->startTokenStack[$pos - 1]->position), 129 => fn() => $this->semValue = new Expression\UnaryOpNode($this->semStack[$pos], '-', $this->startTokenStack[$pos - 1]->position), 130, 131 => fn() => $this->semValue = new Expression\NotNode($this->semStack[$pos], $this->startTokenStack[$pos - 1]->position), diff --git a/src/Latte/Runtime/Filters.php b/src/Latte/Runtime/Filters.php index 6509ce1e05..1e128e914c 100644 --- a/src/Latte/Runtime/Filters.php +++ b/src/Latte/Runtime/Filters.php @@ -174,6 +174,17 @@ public static function escapeHtmlRawText($s): string } + /** + * Determine if a string or array contains a given needle. + */ + public static function contains(mixed $needle, array|string $haystack): bool + { + return is_array($haystack) + ? in_array($needle, $haystack, true) + : str_contains($haystack, (string) $needle); + } + + /** * Converts ... to ... */ diff --git a/tests/common/TagParser.parseArguments().phpt b/tests/common/TagParser.parseArguments().phpt index 28420d5e8b..f1c6a226ea 100644 --- a/tests/common/TagParser.parseArguments().phpt +++ b/tests/common/TagParser.parseArguments().phpt @@ -94,10 +94,10 @@ test('inline modifiers', function () { test('in operator', function () { - Assert::same("in_array(\$a, ['a', 'b'], true), 1", formatArgs('$a in [a, b], 1')); - Assert::same('$a, in_array($b->func(), [1, 2], true)', formatArgs('$a, $b->func() in [1, 2]')); - Assert::same('$a, in_array($b[1], [1, 2], true)', formatArgs('$a, $b[1] in [1, 2]')); - Assert::same('in_array($b, [1, [2], 3], true)', formatArgs('$b in [1, [2], 3]')); + Assert::same("LR\\Filters::contains(\$a, ['a', 'b']), 1", formatArgs('$a in [a, b], 1')); + Assert::same('$a, LR\Filters::contains($b->func(), [1, 2])', formatArgs('$a, $b->func() in [1, 2]')); + Assert::same('$a, LR\Filters::contains($b[1], [1, 2])', formatArgs('$a, $b[1] in [1, 2]')); + Assert::same('LR\Filters::contains($b, [1, [2], 3])', formatArgs('$b in [1, [2], 3]')); }); diff --git a/tests/filters/contains.phpt b/tests/filters/contains.phpt new file mode 100644 index 0000000000..17f7002931 --- /dev/null +++ b/tests/filters/contains.phpt @@ -0,0 +1,26 @@ + Latte\Compiler\Nodes\Php\Expression\ArrayItemNode - | | value: Latte\Compiler\Nodes\Php\Expression\InRangeNode + | | value: Latte\Compiler\Nodes\Php\Expression\InNode | | | needle: Latte\Compiler\Nodes\Php\Expression\VariableNode | | | | name: 'a' | | | | position: 1:1 (offset 0) @@ -41,7 +41,7 @@ Latte\Compiler\Nodes\Php\Expression\ArrayNode | | position: 1:1 (offset 0) | 1 => Latte\Compiler\Nodes\Php\Expression\ArrayItemNode | | value: Latte\Compiler\Nodes\Php\Expression\BinaryOpNode - | | | left: Latte\Compiler\Nodes\Php\Expression\InRangeNode + | | | left: Latte\Compiler\Nodes\Php\Expression\InNode | | | | needle: Latte\Compiler\Nodes\Php\Expression\VariableNode | | | | | name: 'a' | | | | | position: 4:1 (offset 28) @@ -50,7 +50,7 @@ Latte\Compiler\Nodes\Php\Expression\ArrayNode | | | | | position: 4:7 (offset 34) | | | | position: 4:1 (offset 28) | | | operator: '||' - | | | right: Latte\Compiler\Nodes\Php\Expression\InRangeNode + | | | right: Latte\Compiler\Nodes\Php\Expression\InNode | | | | needle: Latte\Compiler\Nodes\Php\Expression\VariableNode | | | | | name: 'c' | | | | | position: 4:13 (offset 40) @@ -69,7 +69,7 @@ Latte\Compiler\Nodes\Php\Expression\ArrayNode | | | | name: 'a' | | | | position: 5:1 (offset 50) | | | expr: Latte\Compiler\Nodes\Php\Expression\NotNode - | | | | expr: Latte\Compiler\Nodes\Php\Expression\InRangeNode + | | | | expr: Latte\Compiler\Nodes\Php\Expression\InNode | | | | | needle: Latte\Compiler\Nodes\Php\Expression\BinaryOpNode | | | | | | left: Latte\Compiler\Nodes\Php\Scalar\IntegerNode | | | | | | | value: 10 diff --git a/tests/phpPrint/operators.phpt b/tests/phpPrint/operators.phpt index 4e646846c9..40c912dfc2 100644 --- a/tests/phpPrint/operators.phpt +++ b/tests/phpPrint/operators.phpt @@ -154,7 +154,7 @@ $a xor $b, $a or $b, $a instanceof Foo, $a instanceof $b, -in_array($a, $b, true), -in_array(!$a, $b, true) && !in_array($a + 2, $b, true), +LR\Filters::contains($a, $b), +LR\Filters::contains(!$a, $b) && !LR\Filters::contains($a + 2, $b), !$a, !($a > $b) && !($c == !$d)