minor #3897 Compile starts/ends with using str_starts_with and str_ends_with (GromNaN)

This PR was merged into the 3.x branch.

Discussion
----------

Compile `starts/ends with` using `str_starts_with` and `str_ends_with`

Since we now require PHP 8.0 polyfill #3884, we can use `str_starts_with` and `str_ends_with` to compile `starts with` and `ends with` expressions.

Example with `bootstrap_4_layout.html.twig` [line 6-7](https://github.com/symfony/symfony/blob/e6d1ed4edb5ae197ec7d25ddaf64cfa456229504/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig#L6-L7) (see deep [diff](https://www.diffchecker.com/18CtPeja/)):

```twig
    {%- set prepend = not (money_pattern starts with '{{') -%}
    {%- set append = not (money_pattern ends with '}}') -%}
```

Previous compilation:
```php
// line 6
$context["prepend"] =  !(is_string($__internal_compile_0 = (isset($context["money_pattern"]) || array_key_exists("money_pattern", $context) ? $context["money_pattern"] : (function () { throw new RuntimeError('Variable "money_pattern" does not exist.', 6, $this->source); })())) && is_string($__internal_compile_1 = "{{") && ('' === $__internal_compile_1 || 0 === strpos($__internal_compile_0, $__internal_compile_1)));
// line 7
$context["append"] =  !(is_string($__internal_compile_2 = (isset($context["money_pattern"]) || array_key_exists("money_pattern", $context) ? $context["money_pattern"] : (function () { throw new RuntimeError('Variable "money_pattern" does not exist.', 7, $this->source); })())) && is_string($__internal_compile_3 = "}}") && ('' === $__internal_compile_3 || $__internal_compile_3 === substr($__internal_compile_2, -strlen($__internal_compile_3))));
```

With this change:
```php
// line 6
$context["prepend"] =  !(is_string($__internal_compile_0 = (isset($context["money_pattern"]) || array_key_exists("money_pattern", $context) ? $context["money_pattern"] : (function () { throw new RuntimeError('Variable "money_pattern" does not exist.', 6, $this->source); })())) && is_string($__internal_compile_1 = "{{") && str_starts_with($__internal_compile_0, $__internal_compile_1));
// line 7
$context["append"] =  !(is_string($__internal_compile_2 = (isset($context["money_pattern"]) || array_key_exists("money_pattern", $context) ? $context["money_pattern"] : (function () { throw new RuntimeError('Variable "money_pattern" does not exist.', 7, $this->source); })())) && is_string($__internal_compile_3 = "}}") && str_ends_with($__internal_compile_2, $__internal_compile_3));
```

Commits
-------

30b5a566 Compile starts/ends with using PHP8 functions str_starts/ends_with
This commit is contained in:
Fabien Potencier
2023-10-22 14:56:22 +02:00
2 changed files with 2 additions and 2 deletions
@@ -24,7 +24,7 @@ class EndsWithBinary extends AbstractBinary
->subcompile($this->getNode('left'))
->raw(sprintf(') && is_string($%s = ', $right))
->subcompile($this->getNode('right'))
->raw(sprintf(') && (\'\' === $%2$s || $%2$s === substr($%1$s, -strlen($%2$s))))', $left, $right))
->raw(sprintf(') && str_ends_with($%1$s, $%2$s))', $left, $right))
;
}
@@ -24,7 +24,7 @@ class StartsWithBinary extends AbstractBinary
->subcompile($this->getNode('left'))
->raw(sprintf(') && is_string($%s = ', $right))
->subcompile($this->getNode('right'))
->raw(sprintf(') && (\'\' === $%2$s || 0 === strpos($%1$s, $%2$s)))', $left, $right))
->raw(sprintf(') && str_starts_with($%1$s, $%2$s))', $left, $right))
;
}