1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace StringObject; |
4
|
|
|
|
5
|
|
|
class AString extends AnyString implements \ArrayAccess, \Countable, \Iterator |
6
|
|
|
{ |
7
|
|
|
// CONSTANTS |
8
|
|
|
|
9
|
|
|
const START = 0; |
10
|
|
|
const END = 1; |
11
|
|
|
const BOTH_ENDS = 2; |
12
|
|
|
const NORMAL = 0; |
13
|
|
|
const CASE_INSENSITIVE = 1; |
14
|
|
|
const REVERSE = 2; |
15
|
|
|
const EXACT_POSITION = 4; |
16
|
|
|
const CURRENT_LOCALE = 2; |
17
|
|
|
const NATURAL_ORDER = 4; |
18
|
|
|
const FIRST_N = 8; |
19
|
|
|
const C_STYLE = 1; |
20
|
|
|
const META = 2; |
21
|
|
|
const GREEDY = 0; |
22
|
|
|
const LAZY = 1; |
23
|
|
|
|
24
|
|
|
// PROPERTIES |
25
|
|
|
|
26
|
|
|
protected $raw; |
27
|
|
|
protected $token = false; |
28
|
|
|
protected $caret = 0; |
29
|
|
|
|
30
|
|
|
// MAGIC METHODS |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @return mixed |
34
|
|
|
*/ |
35
|
|
|
public function __get($name) |
36
|
|
|
{ |
37
|
|
|
return $this->$name; |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @return string |
42
|
|
|
*/ |
43
|
|
|
public function __toString() |
44
|
|
|
{ |
45
|
|
|
return $this->raw; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
View Code Duplication |
public function toArray($delim = '', $limit = null) |
|
|
|
|
49
|
|
|
{ |
50
|
|
|
if (empty($delim)) { |
51
|
|
|
return \str_split($this->raw); |
52
|
|
|
} |
53
|
|
|
if (is_int($delim)) { |
54
|
|
|
return \str_split($this->raw, $delim); |
55
|
|
|
} |
56
|
|
|
if ($limit === null) { |
57
|
|
|
return \explode($delim, $this->raw); |
58
|
|
|
} |
59
|
|
|
return \explode($delim, $this->raw, $limit); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
// INFORMATIONAL METHODS |
63
|
|
|
|
64
|
|
|
public function charAt($offset) |
65
|
|
|
{ |
66
|
|
|
return new self($this->raw{$offset}); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @param integer $offset |
71
|
|
|
*/ |
72
|
|
|
public function charCodeAt($offset) |
73
|
|
|
{ |
74
|
|
|
return \ord($this->raw{$offset}); |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
public function compareTo($str, $flags = self::NORMAL, $length = 1) |
78
|
|
|
{ |
79
|
|
|
// strip out bits we don't understand |
80
|
|
|
$flags &= (self::CASE_INSENSITIVE | self::CURRENT_LOCALE | self::NATURAL_ORDER | self::FIRST_N); |
81
|
|
|
|
82
|
|
|
$flagsmap = [ |
83
|
|
|
self::NORMAL => 'strcmp', |
84
|
|
|
self::CASE_INSENSITIVE => 'strcasecmp', |
85
|
|
|
self::CURRENT_LOCALE => 'strcoll', |
86
|
|
|
self::NATURAL_ORDER => 'strnatcmp', |
87
|
|
|
(self::NATURAL_ORDER | self::CASE_INSENSITIVE) => 'strnatcasecmp', |
88
|
|
|
self::FIRST_N => 'strncmp', |
89
|
|
|
(self::FIRST_N | self::CASE_INSENSITIVE) => 'strncasecmp', |
90
|
|
|
]; |
91
|
|
|
|
92
|
|
|
if ($flags & self::FIRST_N) { |
93
|
|
|
return \call_user_func($flagsmap[$flags], $this->raw, $str, $length); |
94
|
|
|
} |
95
|
|
|
return \call_user_func($flagsmap[$flags], $this->raw, $str); |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
public function indexOf($needle, $offset = 0, $flags = self::NORMAL) |
99
|
|
|
{ |
100
|
|
|
// strip out bits we don't understand |
101
|
|
|
$flags &= (self::REVERSE | self::CASE_INSENSITIVE); |
102
|
|
|
|
103
|
|
|
$flagsmap = [ |
104
|
|
|
self::NORMAL => 'strpos', |
105
|
|
|
self::CASE_INSENSITIVE => 'stripos', |
106
|
|
|
self::REVERSE => 'strrpos', |
107
|
|
|
(self::REVERSE | self::CASE_INSENSITIVE) => 'strripos', |
108
|
|
|
]; |
109
|
|
|
return \call_user_func($flagsmap[$flags], $this->raw, $needle, $offset); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
public function length() |
113
|
|
|
{ |
114
|
|
|
return \strlen($this->raw); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
// MODIFYING METHODS |
118
|
|
|
|
119
|
|
|
public function append($str) |
120
|
|
|
{ |
121
|
|
|
return new self($this->raw . $str); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
public function chunk($length = 76, $ending = "\r\n") |
125
|
|
|
{ |
126
|
|
|
return new self(\chunk_split($this->raw, $length, $ending)); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
public function concat($str) |
130
|
|
|
{ |
131
|
|
|
return $this->append($str); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
public function escape($flags = self::NORMAL, $charlist = '') |
135
|
|
|
{ |
136
|
|
|
// strip out bits we don't understand |
137
|
|
|
$flags &= (self::C_STYLE | self::META); |
138
|
|
|
|
139
|
|
|
$flagsmap = [ |
140
|
|
|
self::NORMAL => 'addslashes', |
141
|
|
|
self::C_STYLE => 'addcslashes', |
142
|
|
|
self::META => 'quotemeta', |
143
|
|
|
]; |
144
|
|
View Code Duplication |
if ($flags === self::C_STYLE) { |
|
|
|
|
145
|
|
|
return new self(\call_user_func($flagsmap[$flags], $this->raw, $charlist)); |
146
|
|
|
} |
147
|
|
|
return new self(\call_user_func($flagsmap[$flags], $this->raw)); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
public function insertAt($str, $offset) |
151
|
|
|
{ |
152
|
|
|
return $this->replaceSubstr($str, $offset, 0); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
public function nextToken($delim) |
156
|
|
|
{ |
157
|
|
|
if ($this->token) { |
158
|
|
|
return new self(\strtok($delim)); |
159
|
|
|
} |
160
|
|
|
$this->token = true; |
161
|
|
|
return new self(\strtok($this->raw, $delim)); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
public function pad($newlength, $padding = ' ', $flags = self::END) |
165
|
|
|
{ |
166
|
|
|
return new self(\str_pad($this->raw, $newlength, $padding, $flags)); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
public function prepend($str) |
170
|
|
|
{ |
171
|
|
|
return new self($str . $this->raw); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
public function remove($str, $flags = self::NORMAL) |
175
|
|
|
{ |
176
|
|
|
return $this->replace($str, '', $flags); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
public function removeSubstr($start, $length = null) |
180
|
|
|
{ |
181
|
|
|
return $this->replaceSubstr('', $start, $length); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
public function repeat($times) |
185
|
|
|
{ |
186
|
|
|
return new self(\str_repeat($this->raw, $times)); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* @param string $replace |
191
|
|
|
*/ |
192
|
|
|
public function replace($search, $replace, $flags = self::NORMAL) |
193
|
|
|
{ |
194
|
|
|
if ($flags & self::CASE_INSENSITIVE) { |
195
|
|
|
return new self(\str_ireplace($search, $replace, $this->raw)); |
196
|
|
|
} |
197
|
|
|
return new self(\str_replace($search, $replace, $this->raw)); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
View Code Duplication |
public function replaceSubstr($replacement, $start, $length = null) |
|
|
|
|
201
|
|
|
{ |
202
|
|
|
if ($length === null) { |
203
|
|
|
$length = $this->length(); |
204
|
|
|
} |
205
|
|
|
return new self(\substr_replace($this->raw, $replacement, $start, $length)); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
public function replaceWhole($replacement = '') |
209
|
|
|
{ |
210
|
|
|
return new self($replacement); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
public function resetToken() |
214
|
|
|
{ |
215
|
|
|
$this->token = false; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
public function reverse() |
219
|
|
|
{ |
220
|
|
|
return new self(\strrev($this->raw)); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
public function shuffle() |
224
|
|
|
{ |
225
|
|
|
return new self(\str_shuffle($this->raw)); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
public function substr($start, $length = 'omitted') |
229
|
|
|
{ |
230
|
|
|
if ($length === 'omitted') { |
231
|
|
|
return new self(\substr($this->raw, $start)); |
232
|
|
|
} |
233
|
|
|
return new self(\substr($this->raw, $start, $length)); |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
public function times($times) |
237
|
|
|
{ |
238
|
|
|
return $this->repeat($times); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
public function translate($search, $replace = '') |
242
|
|
|
{ |
243
|
|
|
if (is_array($search)) { |
244
|
|
|
return new self(\strtr($this->raw, $search)); |
245
|
|
|
} |
246
|
|
|
return new self(\strtr($this->raw, $search, $replace)); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
View Code Duplication |
public function trim($mask = " \t\n\r\0\x0B", $flags = self::BOTH_ENDS) |
250
|
|
|
{ |
251
|
|
|
// strip out bits we don't understand |
252
|
|
|
$flags &= (self::END | self::BOTH_ENDS); |
253
|
|
|
|
254
|
|
|
$flagsmap = [ |
255
|
|
|
self::START => 'ltrim', |
256
|
|
|
self::END => 'rtrim', |
257
|
|
|
self::BOTH_ENDS => 'trim', |
258
|
|
|
]; |
259
|
|
|
return new self(\call_user_func($flagsmap[$flags], $this->raw, $mask)); |
260
|
|
|
} |
261
|
|
|
|
262
|
|
View Code Duplication |
public function unescape($flags = self::NORMAL) |
263
|
|
|
{ |
264
|
|
|
// strip out bits we don't understand |
265
|
|
|
$flags &= (self::C_STYLE | self::META); |
266
|
|
|
|
267
|
|
|
$flagsmap = [ |
268
|
|
|
self::NORMAL => 'stripslashes', |
269
|
|
|
self::C_STYLE => 'stripcslashes', |
270
|
|
|
self::META => 'stripslashes', |
271
|
|
|
]; |
272
|
|
|
return new self(\call_user_func($flagsmap[$flags], $this->raw)); |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
public function uuDecode() |
276
|
|
|
{ |
277
|
|
|
return new self(\convert_uudecode($this->raw)); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
public function uuEncode() |
281
|
|
|
{ |
282
|
|
|
return new self(\convert_uuencode($this->raw)); |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
public function wordwrap($width = 75, $break = "\n") |
286
|
|
|
{ |
287
|
|
|
return new self(\wordwrap($this->raw, $width, $break, false)); |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
public function wordwrapBreaking($width = 75, $break = "\n") |
291
|
|
|
{ |
292
|
|
|
return new self(\wordwrap($this->raw, $width, $break, true)); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
// TESTING METHODS |
296
|
|
|
|
297
|
|
|
public function contains($needle, $offset = 0, $flags = self::NORMAL) |
298
|
|
|
{ |
299
|
|
|
if ($flags & self::EXACT_POSITION) { |
300
|
|
|
return ($this->indexOf($needle, $offset, $flags) === $offset); |
301
|
|
|
} |
302
|
|
|
return ($this->indexOf($needle, $offset, $flags) !== false); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
public function countSubstr($needle, $offset = 0, $length = null) |
306
|
|
|
{ |
307
|
|
|
if ($length === null) { |
308
|
|
|
return \substr_count($this->raw, $needle, $offset); |
309
|
|
|
} |
310
|
|
|
return \substr_count($this->raw, $needle, $offset, $length); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
public function endsWith($str, $flags = self::NORMAL) |
314
|
|
|
{ |
315
|
|
|
$flags &= self::CASE_INSENSITIVE; |
316
|
|
|
$offset = $this->length() - \strlen($str); |
317
|
|
|
return $this->contains($str, $offset, $flags | self::EXACT_POSITION | self::REVERSE); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
public function equals($str) |
321
|
|
|
{ |
322
|
|
|
self::testStringableObject($str); |
323
|
|
|
|
324
|
|
|
$str = (string) $str; |
325
|
|
|
return ($str == $this->raw); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
public function isAscii() |
329
|
|
|
{ |
330
|
|
|
$len = $this->length(); |
331
|
|
|
|
332
|
|
|
for ($i = 0; $i < $len; $i++) { |
333
|
|
|
if ($this->charCodeAt($i) >= 128) { |
334
|
|
|
return false; |
335
|
|
|
} |
336
|
|
|
} |
337
|
|
|
return true; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
public function isEmpty() |
341
|
|
|
{ |
342
|
|
|
return empty($this->raw); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
public function startsWith($str, $flags = self::NORMAL) |
346
|
|
|
{ |
347
|
|
|
$flags &= self::CASE_INSENSITIVE; |
348
|
|
|
return $this->contains($str, 0, $flags | self::EXACT_POSITION); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
// INTERFACE IMPLEMENTATION METHODS |
352
|
|
|
|
353
|
|
|
public function count() |
354
|
|
|
{ |
355
|
|
|
return \strlen($this->raw); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
public function current() |
359
|
|
|
{ |
360
|
|
|
return $this->raw[$this->caret]; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
public function key() |
364
|
|
|
{ |
365
|
|
|
return $this->caret; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
public function next() |
369
|
|
|
{ |
370
|
|
|
$this->caret++; |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
public function rewind() |
374
|
|
|
{ |
375
|
|
|
$this->caret = 0; |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
public function valid() |
379
|
|
|
{ |
380
|
|
|
return ($this->caret < \strlen($this->raw)); |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
public function offsetExists($offset) |
384
|
|
|
{ |
385
|
|
|
$offset = (int) $offset; |
386
|
|
|
return ($offset >= 0 && $offset < \strlen($this->raw)); |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
public function offsetGet($offset) |
390
|
|
|
{ |
391
|
|
|
return $this->raw{$offset}; |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
public function offsetSet($offset, $value) |
395
|
|
|
{ |
396
|
|
|
throw new \LogicException('Cannot assign ' . $value . ' to immutable AString instance at index ' . $offset); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
public function offsetUnset($offset) |
400
|
|
|
{ |
401
|
|
|
throw new \LogicException('Cannot unset index ' . $offset . ' on immutable AString instance'); |
402
|
|
|
} |
403
|
|
|
} |
404
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.