diff --git a/src/Symfony/Component/String/AbstractString.php b/src/Symfony/Component/String/AbstractString.php index d3d95d40a4670..cf21fef1f4e6e 100644 --- a/src/Symfony/Component/String/AbstractString.php +++ b/src/Symfony/Component/String/AbstractString.php @@ -616,11 +616,79 @@ abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}") */ abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; + /** + * @param string|string[] $prefix + * + * @return static + */ + public function trimPrefix($prefix): self + { + if (\is_array($prefix) || $prefix instanceof \Traversable) { + foreach ($prefix as $s) { + $t = $this->trimPrefix($s); + + if ($t->string !== $this->string) { + return $t; + } + } + + return clone $this; + } + + $str = clone $this; + + if ($prefix instanceof self) { + $prefix = $prefix->string; + } else { + $prefix = (string) $prefix; + } + + if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) { + $str->string = substr($this->string, \strlen($prefix)); + } + + return $str; + } + /** * @return static */ abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): self; + /** + * @param string|string[] $suffix + * + * @return static + */ + public function trimSuffix($suffix): self + { + if (\is_array($suffix) || $suffix instanceof \Traversable) { + foreach ($suffix as $s) { + $t = $this->trimSuffix($s); + + if ($t->string !== $this->string) { + return $t; + } + } + + return clone $this; + } + + $str = clone $this; + + if ($suffix instanceof self) { + $suffix = $suffix->string; + } else { + $suffix = (string) $suffix; + } + + if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) { + $str->string = substr($this->string, 0, -\strlen($suffix)); + } + + return $str; + } + /** * @return static */ diff --git a/src/Symfony/Component/String/AbstractUnicodeString.php b/src/Symfony/Component/String/AbstractUnicodeString.php index 8af75a1069133..699701b58f21d 100644 --- a/src/Symfony/Component/String/AbstractUnicodeString.php +++ b/src/Symfony/Component/String/AbstractUnicodeString.php @@ -409,6 +409,26 @@ public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): pare return $str; } + public function trimPrefix($prefix): parent + { + if (!$this->ignoreCase) { + return parent::trimPrefix($prefix); + } + + $str = clone $this; + + if ($prefix instanceof \Traversable) { + $prefix = iterator_to_array($prefix, false); + } elseif ($prefix instanceof parent) { + $prefix = $prefix->string; + } + + $prefix = implode('|', array_map('preg_quote', (array) $prefix)); + $str->string = preg_replace("{^(?:$prefix)}iuD", '', $this->string); + + return $str; + } + public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): parent { if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { @@ -422,6 +442,26 @@ public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): pa return $str; } + public function trimSuffix($suffix): parent + { + if (!$this->ignoreCase) { + return parent::trimSuffix($suffix); + } + + $str = clone $this; + + if ($suffix instanceof \Traversable) { + $suffix = iterator_to_array($suffix, false); + } elseif ($suffix instanceof parent) { + $suffix = $suffix->string; + } + + $suffix = implode('|', array_map('preg_quote', (array) $suffix)); + $str->string = preg_replace("{(?:$suffix)$}iuD", '', $this->string); + + return $str; + } + public function upper(): parent { $str = clone $this; diff --git a/src/Symfony/Component/String/CHANGELOG.md b/src/Symfony/Component/String/CHANGELOG.md index 700d214832d32..53af364005e66 100644 --- a/src/Symfony/Component/String/CHANGELOG.md +++ b/src/Symfony/Component/String/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +5.4 +--- + + * Add `trimSuffix()` and `trimPrefix()` methods + 5.3 --- diff --git a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php index d382f82391a6f..70174615d4604 100644 --- a/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php +++ b/src/Symfony/Component/String/Tests/AbstractAsciiTestCase.php @@ -733,6 +733,15 @@ public static function provideTrim() ]; } + public function testTrimPrefix() + { + $str = static::createFromString('abc.def'); + + $this->assertEquals(static::createFromString('def'), $str->trimPrefix('abc.')); + $this->assertEquals(static::createFromString('def'), $str->trimPrefix(['abc.', 'def'])); + $this->assertEquals(static::createFromString('def'), $str->ignoreCase()->trimPrefix('ABC.')); + } + /** * @dataProvider provideTrimStart */ @@ -744,6 +753,15 @@ public function testTrimStart(string $expected, string $origin, ?string $chars) $this->assertEquals(static::createFromString($expected), $result); } + public function testTrimSuffix() + { + $str = static::createFromString('abc.def'); + + $this->assertEquals(static::createFromString('abc'), $str->trimSuffix('.def')); + $this->assertEquals(static::createFromString('abc'), $str->trimSuffix(['.def', 'abc'])); + $this->assertEquals(static::createFromString('abc'), $str->ignoreCase()->trimSuffix('.DEF')); + } + public static function provideTrimStart() { return [