Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8f52667

Browse files
author
Hugo Hamon
committed
[Utf8] added Bytes, CodePoints and Graphemes implementations
1 parent e1af0cd commit 8f52667

14 files changed

+3529
-1
lines changed

src/Symfony/Component/Utf8/Bytes.php

Lines changed: 526 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Utf8;
13+
14+
use Symfony\Component\Utf8\Exception\InvalidArgumentException;
15+
16+
final class CodePoints implements GenericStringInterface, \Countable
17+
{
18+
use Utf8Trait;
19+
20+
/**
21+
* {@inheritdoc}
22+
*/
23+
public function count(): int
24+
{
25+
return mb_strlen($this->string, 'UTF-8');
26+
}
27+
28+
/**
29+
* {@inheritdoc}
30+
*/
31+
public function length(): int
32+
{
33+
return mb_strlen($this->string, 'UTF-8');
34+
}
35+
36+
/**
37+
* {@inheritdoc}
38+
*/
39+
public function substr(int $start = 0, int $length = null): self
40+
{
41+
$result = clone $this;
42+
$result->string = mb_substr($this->string, $start, $length, 'UTF-8');
43+
44+
return $result;
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function getIterator(int $maxChunkLength = 1): \Generator
51+
{
52+
if (1 > $maxChunkLength) {
53+
throw new InvalidArgumentException('The maximum length of each segment must be greater than zero.');
54+
}
55+
56+
if ('' === $this->string) {
57+
return;
58+
}
59+
60+
if (65535 < $maxChunkLength) {
61+
throw new InvalidArgumentException('Maximum chunk length must not exceed 65535.');
62+
}
63+
64+
foreach (preg_split('/(.{'.$maxChunkLength.'})/u', $this->string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $char) {
65+
$clone = clone $this;
66+
$clone->string = $char;
67+
yield $clone;
68+
}
69+
}
70+
71+
/**
72+
* {@inheritdoc}
73+
*/
74+
public function indexOf(string $needle, int $offset = 0)
75+
{
76+
if ('' === $needle) {
77+
return;
78+
}
79+
80+
if (0 <= $offset || PHP_VERSION_ID >= 70100) {
81+
$result = mb_strpos($this->string, $needle, $offset);
82+
83+
return false === $result ? null : $result;
84+
}
85+
86+
// Workaround to support negative offsets with mb_strpos() in PHP < 7.1
87+
$start = $offset + mb_strlen($this->string);
88+
$offset = mb_strpos(mb_substr($this->string, $start), $needle);
89+
90+
return false === $offset ? null : $start + $offset;
91+
}
92+
93+
/**
94+
* {@inheritdoc}
95+
*/
96+
public function indexOfIgnoreCase(string $needle, int $offset = 0)
97+
{
98+
if ('' === $needle) {
99+
return;
100+
}
101+
102+
if (0 <= $offset || PHP_VERSION_ID >= 70100) {
103+
$result = mb_stripos($this->string, $needle, $offset);
104+
105+
return false === $result ? null : $result;
106+
}
107+
108+
// Workaround to support negative offsets with mb_stripos() in PHP < 7.1
109+
$start = $offset + mb_strlen($this->string);
110+
$offset = mb_stripos(mb_substr($this->string, $start), $needle);
111+
112+
return false === $offset ? null : $start + $offset;
113+
}
114+
115+
/**
116+
* {@inheritdoc}
117+
*/
118+
public function lastIndexOf(string $needle, int $offset = 0)
119+
{
120+
if ('' === $needle) {
121+
return;
122+
}
123+
124+
$result = mb_strrpos($this->string, $needle, $offset);
125+
126+
return false === $result ? null : $result;
127+
}
128+
129+
/**
130+
* {@inheritdoc}
131+
*/
132+
public function lastIndexOfIgnoreCase(string $needle, int $offset = 0)
133+
{
134+
if ('' === $needle) {
135+
return;
136+
}
137+
138+
$result = mb_strripos($this->string, $needle, $offset);
139+
140+
return false === $result ? null : $result;
141+
}
142+
143+
/**
144+
* {@inheritdoc}
145+
*/
146+
public function substringOf(string $needle, bool $beforeNeedle = false)
147+
{
148+
if ('' === $needle) {
149+
return;
150+
}
151+
152+
if (false === $part = mb_strstr($this->string, $needle, $beforeNeedle, 'UTF-8')) {
153+
return;
154+
}
155+
156+
$result = clone $this;
157+
$result->string = $part;
158+
159+
return $result;
160+
}
161+
162+
/**
163+
* {@inheritdoc}
164+
*/
165+
public function substringOfIgnoreCase(string $needle, bool $beforeNeedle = false)
166+
{
167+
if ('' === $needle) {
168+
return;
169+
}
170+
171+
if (false === $part = mb_stristr($this->string, $needle, $beforeNeedle, 'UTF-8')) {
172+
return;
173+
}
174+
175+
$result = clone $this;
176+
$result->string = $part;
177+
178+
return $result;
179+
}
180+
181+
/**
182+
* {@inheritdoc}
183+
*/
184+
public function width(bool $ignoreAnsiDecoration = true): int
185+
{
186+
$s = $this->string;
187+
188+
if (false !== strpos($s, "\r")) {
189+
$s = str_replace("\r\n", "\n", $s);
190+
$s = strtr($s, "\r", "\n");
191+
}
192+
193+
$width = 0;
194+
foreach (explode("\n", $s) as $s) {
195+
if ($ignoreAnsiDecoration) {
196+
$s = preg_replace('/\x1B\[[\d;]*m/', '', $s);
197+
}
198+
199+
$c = substr_count($s, "\xAD") - substr_count($s, "\x08");
200+
$s = preg_replace('/[\x00\x05\x07\p{Mn}\p{Me}\p{Cf}\x{1160}-\x{11FF}\x{200B}]+/u', '', $s);
201+
preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
202+
203+
if ($width < $c = mb_strlen($s, 'UTF-8') + $wide + $c) {
204+
$width = $c;
205+
}
206+
}
207+
208+
return $width;
209+
}
210+
211+
/**
212+
* {@inheritdoc}
213+
*/
214+
public function toBytes(): Bytes
215+
{
216+
return Bytes::fromString($this->string);
217+
}
218+
219+
/**
220+
* {@inheritdoc}
221+
*/
222+
public function toCodePoints(): self
223+
{
224+
return $this;
225+
}
226+
227+
/**
228+
* {@inheritdoc}
229+
*/
230+
public function toGraphemes(): Graphemes
231+
{
232+
return Graphemes::fromString($this->string);
233+
}
234+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Utf8\Exception;
13+
14+
interface ExceptionInterface
15+
{
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Utf8\Exception;
13+
14+
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
15+
{
16+
}

0 commit comments

Comments
 (0)