Skip to content

Commit 22f4318

Browse files
author
Hugo Hamon
committed
[Utf8] added Bytes, CodePoints and Graphemes implementations.
1 parent 9220df4 commit 22f4318

File tree

11 files changed

+1871
-3
lines changed

11 files changed

+1871
-3
lines changed

src/Symfony/Component/Utf8/Bytes.php

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
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 Bytes implements GenericStringInterface
17+
{
18+
private $string = '';
19+
20+
/**
21+
* {@inheritdoc}
22+
*/
23+
public static function create(string $string): self
24+
{
25+
$bytes = new self($string);
26+
$bytes->string = $string;
27+
28+
return $bytes;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function __toString()
35+
{
36+
return $this->string;
37+
}
38+
39+
/**
40+
* {@inheritdoc}
41+
*/
42+
public function length(): int
43+
{
44+
return strlen($this->string);
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function isEmpty(): bool
51+
{
52+
return '' === $this->string;
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public function split(string $delimiter, int $limit = null): array
59+
{
60+
if (null !== $limit && $limit < 1) {
61+
throw new InvalidArgumentException('The length of each segment must be greater than zero.');
62+
}
63+
64+
if ('' === $delimiter) {
65+
if ('' === $this->string) {
66+
return array(clone $this);
67+
}
68+
69+
$chunks = str_split($this->string, $limit ?: 1);
70+
} else {
71+
$chunks = explode($delimiter, $this->string, $limit ?: PHP_INT_MAX);
72+
}
73+
74+
foreach ($chunks as $i => $string) {
75+
$chunks[$i] = $chunk = clone $this;
76+
$chunk->string = $string;
77+
}
78+
79+
return $chunks;
80+
}
81+
82+
/**
83+
* {@inheritdoc}
84+
*/
85+
public function toLowerCase(): self
86+
{
87+
$result = clone $this;
88+
$result->string = strtolower($this->string);
89+
90+
return $result;
91+
}
92+
93+
/**
94+
* {@inheritdoc}
95+
*/
96+
public function toUpperCase(): self
97+
{
98+
$result = clone $this;
99+
$result->string = strtoupper($this->string);
100+
101+
return $result;
102+
}
103+
104+
/**
105+
* {@inheritdoc}
106+
*/
107+
public function substring(int $start = 0, int $length = null): self
108+
{
109+
$result = clone $this;
110+
$result->string = substr($this->string, $start, $length);
111+
112+
return $result;
113+
}
114+
115+
/**
116+
* {@inheritdoc}
117+
*/
118+
public function trim(string $charsList = null): self
119+
{
120+
$result = clone $this;
121+
$result->string = trim($this->string, $charsList ?: " \t\n\r\0\x0B");
122+
123+
return $result;
124+
}
125+
126+
/**
127+
* {@inheritdoc}
128+
*/
129+
public function trimLeft(string $charsList = null): self
130+
{
131+
$result = clone $this;
132+
$result->string = ltrim($this->string, $charsList ?: " \t\n\r\0\x0B");
133+
134+
return $result;
135+
}
136+
137+
/**
138+
* {@inheritdoc}
139+
*/
140+
public function trimRight(string $charsList = null): self
141+
{
142+
$result = clone $this;
143+
$result->string = rtrim($this->string, $charsList ?: " \t\n\r\0\x0B");
144+
145+
return $result;
146+
}
147+
148+
/**
149+
* {@inheritdoc}
150+
*/
151+
public function startsWith(string $prefix, int $offset = 0): bool
152+
{
153+
return $offset === strpos($this->string, $prefix, $offset);
154+
}
155+
156+
/**
157+
* {@inheritdoc}
158+
*/
159+
public function endsWith(string $suffix): bool
160+
{
161+
$suffix = (string) $suffix;
162+
163+
return substr($this->string, -strlen($suffix)) === $suffix;
164+
}
165+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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
17+
{
18+
private $string = '';
19+
20+
/**
21+
* {@inheritdoc}
22+
*/
23+
public static function create(string $string): self
24+
{
25+
if (!preg_match('//u', $string)) {
26+
throw new InvalidArgumentException('Given string is not a valid UTF-8 encoded string.');
27+
}
28+
29+
$codePoints = new self($string);
30+
$codePoints->string = $string;
31+
32+
return $codePoints;
33+
}
34+
35+
/**
36+
* {@inheritdoc}
37+
*/
38+
public function __toString()
39+
{
40+
return $this->string;
41+
}
42+
43+
/**
44+
* {@inheritdoc}
45+
*/
46+
public function length(): int
47+
{
48+
return mb_strlen($this->string, 'UTF-8');
49+
}
50+
51+
/**
52+
* {@inheritdoc}
53+
*/
54+
public function isEmpty(): bool
55+
{
56+
return '' === $this->string;
57+
}
58+
59+
/**
60+
* {@inheritdoc}
61+
*/
62+
public function split(string $delimiter, int $limit = null): array
63+
{
64+
if (null !== $limit && $limit < 1) {
65+
throw new InvalidArgumentException('The length of each segment must be greater than zero.');
66+
}
67+
68+
if ('' === $delimiter) {
69+
if ('' === $this->string) {
70+
return array($this);
71+
}
72+
73+
if (null === $limit) {
74+
$limit = 1;
75+
} else if ($limit > 65535) {
76+
$limit = 65535;
77+
}
78+
79+
// When limit is too high it fails...
80+
$chunks = preg_split('/(.{'.$limit.'})/u', $this->string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
81+
82+
} else {
83+
$chunks = preg_split('/'.preg_quote($delimiter).'/u', $this->string, $limit ?: -1);
84+
}
85+
86+
foreach ($chunks as $i => $string) {
87+
$chunks[$i] = $chunk = clone $this;
88+
$chunk->string = $string;
89+
}
90+
91+
return $chunks;
92+
}
93+
94+
/**
95+
* {@inheritdoc}
96+
*/
97+
public function toLowerCase(): self
98+
{
99+
$result = clone $this;
100+
$result->string = mb_strtolower($this->string, 'UTF-8');
101+
102+
return $result;
103+
}
104+
105+
/**
106+
* {@inheritdoc}
107+
*/
108+
public function toUpperCase(): self
109+
{
110+
$result = clone $this;
111+
$result->string = mb_strtoupper($this->string, 'UTF-8');
112+
113+
return $result;
114+
}
115+
116+
/**
117+
* {@inheritdoc}
118+
*/
119+
public function substring(int $start = 0, int $length = null): self
120+
{
121+
$result = clone $this;
122+
$result->string = mb_substr($this->string, $start, $length, 'UTF-8');
123+
124+
return $result;
125+
}
126+
127+
/**
128+
* {@inheritdoc}
129+
*/
130+
public function trim(string $charsList = null): self
131+
{
132+
$charsList = $charsList ? preg_quote($charsList) : '[:space:]';
133+
134+
$result = clone $this;
135+
$result->string = preg_replace("/^[$charsList]+|[$charsList]+\$/u", '', $this->string);
136+
137+
return $result;
138+
}
139+
140+
/**
141+
* {@inheritdoc}
142+
*/
143+
public function trimLeft(string $charsList = null): self
144+
{
145+
$charsList = $charsList ? preg_quote($charsList) : '[:space:]';
146+
147+
$result = clone $this;
148+
$result->string = preg_replace("/^[$charsList]+/u", '', $this->string);
149+
150+
return $result;
151+
}
152+
153+
/**
154+
* {@inheritdoc}
155+
*/
156+
public function trimRight(string $charsList = null): self
157+
{
158+
$charsList = $charsList ? preg_quote($charsList) : '[:space:]';
159+
160+
$result = clone $this;
161+
$result->string = preg_replace("/[$charsList]+\$/u", '', $this->string);
162+
163+
return $result;
164+
}
165+
166+
/**
167+
* {@inheritdoc}
168+
*/
169+
public function startsWith(string $prefix, int $offset = 0): bool
170+
{
171+
return $offset === mb_strpos($this->string, $prefix, $offset, 'UTF-8');
172+
}
173+
174+
/**
175+
* {@inheritdoc}
176+
*/
177+
public function endsWith(string $suffix): bool
178+
{
179+
$suffix = (string) $suffix;
180+
181+
return mb_substr($this->string, -mb_strlen($suffix), null, 'UTF-8') === $suffix;
182+
}
183+
}
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
15+
{
16+
}

0 commit comments

Comments
 (0)