return $pathItem;
}
+
+ /**
+ * Multi-byte safe implementation of substr_replace where $start and $length are character offset and count rather
+ * than byte offset and counts.
+ *
+ * Depends on mbstring, use default encoding.
+ *
+ * @param string $string
+ * @param string $replacement
+ * @param int $start
+ * @param int|null $length
+ * @return string
+ * @see substr_replace()
+ */
+ public static function substringReplace(string $string, string $replacement, int $start, int $length = null)
+ {
+ $string_length = mb_strlen($string);
+
+ $length = $length ?? $string_length;
+
+ if ($start < 0) {
+ $start = max(0, $string_length + $start);
+ } else if ($start > $string_length) {
+ $start = $string_length;
+ }
+
+ if ($length < 0) {
+ $length = max(0, $string_length - $start + $length);
+ } else if ($length > $string_length) {
+ $length = $string_length;
+ }
+
+ if (($start + $length) > $string_length) {
+ $length = $string_length - $start;
+ }
+
+ return mb_substr($string, 0, $start) . $replacement . mb_substr($string, $start + $length, $string_length - $start - $length);
+ }
}
{
$this->assertEquals($valid, Strings::isHex($input));
}
+
+ /**
+ * Tests that Strings::substringReplace behaves the same as substr_replace with ASCII strings in all the possible
+ * numerical parameter configurations (positive, negative, zero, out of bounds either side, null)
+ */
+ public function testSubstringReplaceASCII()
+ {
+ for ($start = -10; $start <= 10; $start += 5) {
+ $this->assertEquals(
+ substr_replace('string', 'replacement', $start),
+ Strings::substringReplace('string', 'replacement', $start)
+ );
+
+ for ($length = -10; $length <= 10; $length += 5) {
+ $this->assertEquals(
+ substr_replace('string', 'replacement', $start, $length),
+ Strings::substringReplace('string', 'replacement', $start, $length)
+ );
+ }
+ }
+ }
+
+
+ public function dataSubstringReplaceMultiByte()
+ {
+ return [
+ 'issue-8470' => [
+ 'expected' => 'Je n’y pense que maintenant (pask ma sonnette ne fonctionne pas) : mettre un gentil mot avec mes coordonnées sur ma porte est le moyen le plus simple de rester en contact si besoin avec mon voisinage direct ! [url=https://www.instagram.com/p/B-UdH2loee1/?igshid=x4aglyju9kva]instagram.com/p/B-UdH2loee1/…[/url] [rest of the post]',
+ 'string' => 'Je n’y pense que maintenant (pask ma sonnette ne fonctionne pas) : mettre un gentil mot avec mes coordonnées sur ma porte est le moyen le plus simple de rester en contact si besoin avec mon voisinage direct ! https://t.co/YoBWTHsAAk [rest of the post]',
+ 'replacement' => '[url=https://www.instagram.com/p/B-UdH2loee1/?igshid=x4aglyju9kva]instagram.com/p/B-UdH2loee1/…[/url]',
+ 'start' => 209,
+ 'length' => 23,
+ ],
+ ];
+ }
+
+ /**
+ * Tests cases where Strings::substringReplace is needed over substr_replace with multi-byte strings and character
+ * offsets
+ *
+ * @param string $expected
+ * @param string $string
+ * @param string $replacement
+ * @param int $start
+ * @param int|null $length
+ *
+ * @dataProvider dataSubstringReplaceMultiByte
+ */
+ public function testSubstringReplaceMultiByte(string $expected, string $string, string $replacement, int $start, int $length = null)
+ {
+ $this->assertEquals(
+ $expected,
+ Strings::substringReplace(
+ $string,
+ $replacement,
+ $start,
+ $length
+ )
+ );
+ }
}