Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
minor #33971 [String] optimize equalsTo/startsWith/endsWith (gharlan)
This PR was merged into the 5.0-dev branch.

Discussion
----------

[String] optimize equalsTo/startsWith/endsWith

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | no
| Deprecations? | no
| Tickets       | n/a
| License       | MIT
| Doc PR        | n/a

`ByteString::startsWith`: https://3v4l.org/7TkbQ
`UnicodeString::startsWith`: https://3v4l.org/gd91p
`UnicodeString::endsWith`: https://3v4l.org/Nhhv4

Commits
-------

59c215c [String] optimize equalsTo/startsWith/endsWith
  • Loading branch information
nicolas-grekas committed Oct 14, 2019
2 parents 006c09a + 59c215c commit b6d4690
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/Symfony/Component/String/ByteString.php
Expand Up @@ -97,7 +97,7 @@ public function endsWith($suffix): bool
$suffix = (string) $suffix;
}

return \strlen($this->string) - \strlen($suffix) === ($this->ignoreCase ? strripos($this->string, $suffix) : strrpos($this->string, $suffix));
return '' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase);
}

public function equalsTo($string): bool
Expand Down Expand Up @@ -362,7 +362,7 @@ public function startsWith($prefix): bool
return parent::startsWith($prefix);
}

return '' !== $prefix && 0 === ($this->ignoreCase ? stripos($this->string, $prefix) : strpos($this->string, $prefix));
return '' !== $prefix && 0 === ($this->ignoreCase ? strncasecmp($this->string, $prefix, \strlen($prefix)) : strncmp($this->string, $prefix, \strlen($prefix)));
}

public function title(bool $allWords = false): parent
Expand Down
10 changes: 7 additions & 3 deletions src/Symfony/Component/String/CodePointString.php
Expand Up @@ -93,7 +93,7 @@ public function endsWith($suffix): bool
return preg_match('{'.preg_quote($suffix).'$}iuD', $this->string);
}

return \strlen($this->string) - \strlen($suffix) === strrpos($this->string, $suffix);
return \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix));
}

public function equalsTo($string): bool
Expand All @@ -107,7 +107,7 @@ public function equalsTo($string): bool
}

if ('' !== $string && $this->ignoreCase) {
return mb_strlen($string, 'UTF-8') === mb_strlen($this->string, 'UTF-8') && 0 === mb_stripos($this->string, $string, 0, 'UTF-8');
return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8');
}

return $string === $this->string;
Expand Down Expand Up @@ -256,6 +256,10 @@ public function startsWith($prefix): bool
return false;
}

return 0 === ($this->ignoreCase ? mb_stripos($this->string, $prefix, 0, 'UTF-8') : strpos($this->string, $prefix));
if ($this->ignoreCase) {
return 0 === mb_stripos($this->string, $prefix, 0, 'UTF-8');
}

return 0 === strncmp($this->string, $prefix, \strlen($prefix));
}
}
14 changes: 10 additions & 4 deletions src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php
Expand Up @@ -859,9 +859,12 @@ public static function provideSnake()
/**
* @dataProvider provideStartsWith
*/
public function testStartsWith(bool $expected, string $origin, $prefix)
public function testStartsWith(bool $expected, string $origin, $prefix, int $form = null)
{
$this->assertSame($expected, static::createFromString($origin)->startsWith($prefix));
$instance = static::createFromString($origin);
$instance = $form ? $instance->normalize($form) : $instance;

$this->assertSame($expected, $instance->startsWith($prefix));
}

public static function provideStartsWith()
Expand Down Expand Up @@ -910,9 +913,12 @@ public static function provideStartsWithIgnoreCase()
/**
* @dataProvider provideEndsWith
*/
public function testEndsWith(bool $expected, string $origin, $suffix)
public function testEndsWith(bool $expected, string $origin, $suffix, int $form = null)
{
$this->assertSame($expected, static::createFromString($origin)->endsWith($suffix));
$instance = static::createFromString($origin);
$instance = $form ? $instance->normalize($form) : $instance;

$this->assertSame($expected, $instance->endsWith($suffix));
}

public static function provideEndsWith()
Expand Down
22 changes: 22 additions & 0 deletions src/Symfony/Component/String/Tests/UnicodeStringTest.php
Expand Up @@ -200,4 +200,26 @@ public static function provideReplaceIgnoreCase(): array
]
);
}

public static function provideStartsWith()
{
return array_merge(
parent::provideStartsWith(),
[
[false, "cle\u{0301} prive\u{0301}e", 'cle', UnicodeString::NFD],
[true, "cle\u{0301} prive\u{0301}e", 'clé', UnicodeString::NFD],
]
);
}

public static function provideEndsWith()
{
return array_merge(
parent::provideEndsWith(),
[
[false, "cle\u{0301} prive\u{0301}e", 'ee', UnicodeString::NFD],
[true, "cle\u{0301} prive\u{0301}e", 'ée', UnicodeString::NFD],
]
);
}
}
20 changes: 16 additions & 4 deletions src/Symfony/Component/String/UnicodeString.php
Expand Up @@ -97,11 +97,15 @@ public function endsWith($suffix): bool
$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($suffix, $form) ?: $suffix = normalizer_normalize($suffix, $form);

if ('' === $suffix || false === $suffix || false === $i = $this->ignoreCase ? grapheme_strripos($this->string, $suffix) : grapheme_strrpos($this->string, $suffix)) {
if ('' === $suffix || false === $suffix) {
return false;
}

return grapheme_strlen($this->string) - grapheme_strlen($suffix) === $i;
if ($this->ignoreCase) {
return 0 === mb_stripos(grapheme_extract($this->string, \strlen($suffix), GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)), $suffix, 0, 'UTF-8');
}

return $suffix === grapheme_extract($this->string, \strlen($suffix), GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix));
}

public function equalsTo($string): bool
Expand All @@ -118,7 +122,7 @@ public function equalsTo($string): bool
normalizer_is_normalized($string, $form) ?: $string = normalizer_normalize($string, $form);

if ('' !== $string && false !== $string && $this->ignoreCase) {
return grapheme_strlen($string) === grapheme_strlen($this->string) && 0 === grapheme_stripos($this->string, $string);
return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8');
}

return $string === $this->string;
Expand Down Expand Up @@ -332,7 +336,15 @@ public function startsWith($prefix): bool
$form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC;
normalizer_is_normalized($prefix, $form) ?: $prefix = normalizer_normalize($prefix, $form);

return '' !== $prefix && false !== $prefix && 0 === ($this->ignoreCase ? grapheme_stripos($this->string, $prefix) : grapheme_strpos($this->string, $prefix));
if ('' === $prefix || false === $prefix) {
return false;
}

if ($this->ignoreCase) {
return 0 === mb_stripos(grapheme_extract($this->string, \strlen($prefix), GRAPHEME_EXTR_MAXBYTES), $prefix, 0, 'UTF-8');
}

return $prefix === grapheme_extract($this->string, \strlen($prefix), GRAPHEME_EXTR_MAXBYTES);
}

public function __clone()
Expand Down

0 comments on commit b6d4690

Please sign in to comment.