These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | declare(strict_types = 1); |
||
3 | |||
4 | namespace Innmind\Immutable; |
||
5 | |||
6 | use Innmind\Immutable\Exception\RegexException; |
||
7 | use Innmind\Immutable\Exception\SubstringException; |
||
8 | |||
9 | class StringPrimitive implements PrimitiveInterface, StringableInterface |
||
10 | { |
||
11 | const PAD_RIGHT = STR_PAD_RIGHT; |
||
12 | const PAD_LEFT = STR_PAD_LEFT; |
||
13 | const PAD_BOTH = STR_PAD_BOTH; |
||
14 | const PREG_NO_FLAGS = 0; |
||
15 | const PREG_SPLIT_NO_EMPTY = PREG_SPLIT_NO_EMPTY; |
||
16 | const PREG_SPLIT_DELIM_CAPTURE = PREG_SPLIT_DELIM_CAPTURE; |
||
17 | const PREG_SPLIT_OFFSET_CAPTURE = PREG_SPLIT_OFFSET_CAPTURE; |
||
18 | const PREG_OFFSET_CAPTURE = PREG_OFFSET_CAPTURE; |
||
19 | |||
20 | private $value; |
||
21 | |||
22 | 122 | public function __construct(string $value) |
|
23 | { |
||
24 | 122 | $this->value = $value; |
|
25 | 122 | } |
|
26 | |||
27 | /** |
||
28 | * {@inheritdoc} |
||
29 | */ |
||
30 | 1 | public function toPrimitive() |
|
31 | { |
||
32 | 1 | return $this->value; |
|
33 | } |
||
34 | |||
35 | /** |
||
36 | * {@inheritdoc} |
||
37 | */ |
||
38 | 59 | public function __toString(): string |
|
39 | { |
||
40 | 59 | return $this->value; |
|
41 | } |
||
42 | |||
43 | /** |
||
44 | * Split the string into a collection of ones |
||
45 | * |
||
46 | * @param string $delimiter |
||
47 | * |
||
48 | * @return TypedCollectionInterface |
||
49 | */ |
||
50 | 2 | public function split(string $delimiter = null): TypedCollectionInterface |
|
51 | { |
||
52 | 2 | $parts = empty($delimiter) ? |
|
53 | 2 | str_split($this->value) : explode($delimiter, $this->value); |
|
54 | 2 | $strings = []; |
|
55 | |||
56 | 2 | foreach ($parts as $part) { |
|
57 | 2 | $strings[] = new self($part); |
|
58 | } |
||
59 | |||
60 | 2 | return new TypedCollection( |
|
61 | 2 | self::class, |
|
62 | $strings |
||
63 | ); |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * Returns a collection of the string splitted by the given chunk size |
||
68 | * |
||
69 | * @param int $size |
||
70 | * |
||
71 | * @return TypedCollectionInterface |
||
72 | */ |
||
73 | 1 | public function chunk(int $size = 1): TypedCollectionInterface |
|
74 | { |
||
75 | 1 | $pieces = str_split($this->value, $size); |
|
76 | |||
77 | 1 | foreach ($pieces as &$piece) { |
|
78 | 1 | $piece = new self($piece); |
|
79 | } |
||
80 | |||
81 | 1 | return new TypedCollection(self::class, $pieces); |
|
82 | } |
||
83 | |||
84 | /** |
||
85 | * Returns the position of the first occurence of the string |
||
86 | * |
||
87 | * @param string $needle |
||
88 | * @param int $offset |
||
89 | * |
||
90 | * @throws SubstringException If the string is not found |
||
91 | * |
||
92 | * @return int |
||
93 | */ |
||
94 | 3 | public function pos(string $needle, int $offset = 0): int |
|
95 | { |
||
96 | 3 | $position = mb_strpos($this->value, $needle, $offset); |
|
97 | |||
98 | 3 | if ($position === false) { |
|
99 | 1 | throw new SubstringException(sprintf( |
|
100 | 1 | 'Substring "%s" not found', |
|
101 | $needle |
||
102 | )); |
||
103 | } |
||
104 | |||
105 | 2 | return (int) $position; |
|
106 | } |
||
107 | |||
108 | /** |
||
109 | * Replace all occurences of the search string with the replacement one |
||
110 | * |
||
111 | * @param string $search |
||
112 | * @param string $replacement |
||
113 | * |
||
114 | * @return self |
||
115 | */ |
||
116 | 1 | public function replace(string $search, string $replacement): self |
|
117 | { |
||
118 | 1 | return new self(str_replace( |
|
119 | 1 | (string) $search, |
|
120 | 1 | (string) $replacement, |
|
121 | 1 | $this->value |
|
122 | )); |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * Returns the string following the given delimiter |
||
127 | * |
||
128 | * @param string $delimiter |
||
129 | * |
||
130 | * @throws SubstringException If the string is not found |
||
131 | * |
||
132 | * @return self |
||
133 | */ |
||
134 | 2 | public function str(string $delimiter): self |
|
135 | { |
||
136 | 2 | $sub = mb_strstr($this->value, $delimiter); |
|
137 | |||
138 | 2 | if ($sub === false) { |
|
139 | 1 | throw new SubstringException(sprintf( |
|
140 | 1 | 'Substring "%s" not found', |
|
141 | $delimiter |
||
142 | )); |
||
143 | } |
||
144 | |||
145 | 1 | return new self($sub); |
|
146 | } |
||
147 | |||
148 | /** |
||
149 | * Return the string in upper case |
||
150 | * |
||
151 | * @return self |
||
152 | */ |
||
153 | 1 | public function toUpper(): self |
|
154 | { |
||
155 | 1 | return new self(mb_strtoupper($this->value)); |
|
156 | } |
||
157 | |||
158 | /** |
||
159 | * Return the string in lower case |
||
160 | * |
||
161 | * @return self |
||
162 | */ |
||
163 | 1 | public function toLower(): self |
|
164 | { |
||
165 | 1 | return new self(mb_strtolower($this->value)); |
|
166 | } |
||
167 | |||
168 | /** |
||
169 | * Return the string length |
||
170 | * |
||
171 | * @return int |
||
172 | */ |
||
173 | 2 | public function length(): int |
|
174 | { |
||
175 | 2 | return strlen($this->value); |
|
176 | } |
||
177 | |||
178 | /** |
||
179 | * Reverse the string |
||
180 | * |
||
181 | * @return self |
||
182 | */ |
||
183 | 1 | public function reverse(): self |
|
184 | { |
||
185 | 1 | return new self(strrev($this->value)); |
|
186 | } |
||
187 | |||
188 | /** |
||
189 | * Pad the string |
||
190 | * |
||
191 | * @param int $length |
||
192 | * @param string $character |
||
193 | * @param int $direction |
||
194 | * |
||
195 | * @return self |
||
196 | */ |
||
197 | 1 | public function pad(int $length, string $character = ' ', int $direction = self::PAD_RIGHT): self |
|
198 | { |
||
199 | 1 | return new self(str_pad( |
|
200 | 1 | $this->value, |
|
201 | $length, |
||
202 | $character, |
||
203 | $direction |
||
204 | )); |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * Pad to the right |
||
209 | * |
||
210 | * @param int $length |
||
211 | * @param string $character |
||
212 | * |
||
213 | * @return self |
||
214 | */ |
||
215 | 1 | public function rightPad(int $length, string $character = ' '): self |
|
216 | { |
||
217 | 1 | return $this->pad($length, $character, self::PAD_RIGHT); |
|
218 | } |
||
219 | |||
220 | /** |
||
221 | * Pad to the left |
||
222 | * |
||
223 | * @param int $length |
||
224 | * @param string $character |
||
225 | * |
||
226 | * @return self |
||
227 | */ |
||
228 | 1 | public function leftPad(int $length, string $character = ' '): self |
|
229 | { |
||
230 | 1 | return $this->pad($length, $character, self::PAD_LEFT); |
|
231 | } |
||
232 | |||
233 | /** |
||
234 | * Pad both sides |
||
235 | * |
||
236 | * @param int $length |
||
237 | * @param string $character |
||
238 | * |
||
239 | * @return self |
||
240 | */ |
||
241 | 1 | public function uniPad(int $length, string $character = ' '): self |
|
242 | { |
||
243 | 1 | return $this->pad($length, $character, self::PAD_BOTH); |
|
244 | } |
||
245 | |||
246 | /** |
||
247 | * Find length of initial segment not matching mask |
||
248 | * |
||
249 | * @param string $mask |
||
250 | * @param int $start |
||
251 | * @param int $length |
||
252 | * |
||
253 | * @return int |
||
254 | */ |
||
255 | 1 | public function cspn(string $mask, int $start = 0, int $length = null): int |
|
256 | { |
||
257 | 1 | View Code Duplication | if ($length === null) { |
0 ignored issues
–
show
|
|||
258 | 1 | $value = strcspn($this->value, $mask, $start); |
|
259 | } else { |
||
260 | 1 | $value = strcspn( |
|
261 | 1 | $this->value, |
|
262 | $mask, |
||
263 | $start, |
||
264 | $length |
||
265 | ); |
||
266 | } |
||
267 | |||
268 | 1 | return (int) $value; |
|
269 | } |
||
270 | |||
271 | /** |
||
272 | * Repeat the string n times |
||
273 | * |
||
274 | * @param int $repeat |
||
275 | * |
||
276 | * @return self |
||
277 | */ |
||
278 | 1 | public function repeat(int $repeat): self |
|
279 | { |
||
280 | 1 | return new self(str_repeat($this->value, $repeat)); |
|
281 | } |
||
282 | |||
283 | /** |
||
284 | * Shuffle the string |
||
285 | * |
||
286 | * @return self |
||
287 | */ |
||
288 | 1 | public function shuffle(): self |
|
289 | { |
||
290 | 1 | return new self(str_shuffle($this->value)); |
|
291 | } |
||
292 | |||
293 | /** |
||
294 | * Strip slashes |
||
295 | * |
||
296 | * @return self |
||
297 | */ |
||
298 | 1 | public function stripSlashes(): self |
|
299 | { |
||
300 | 1 | return new self(stripslashes($this->value)); |
|
301 | } |
||
302 | |||
303 | /** |
||
304 | * Strip C-like slashes |
||
305 | * |
||
306 | * @return self |
||
307 | */ |
||
308 | 1 | public function stripCSlashes(): self |
|
309 | { |
||
310 | 1 | return new self(stripcslashes($this->value)); |
|
311 | } |
||
312 | |||
313 | /** |
||
314 | * Return the word count |
||
315 | * |
||
316 | * @param string $charlist |
||
317 | * |
||
318 | * @return int |
||
319 | */ |
||
320 | 1 | public function wordCount(string $charlist = ''): int |
|
321 | { |
||
322 | 1 | return (int) str_word_count( |
|
323 | 1 | $this->value, |
|
324 | 1 | 0, |
|
325 | $charlist |
||
326 | ); |
||
327 | } |
||
328 | |||
329 | /** |
||
330 | * Return the collection of words |
||
331 | * |
||
332 | * @param string $charlist |
||
333 | * |
||
334 | * @return TypedCollectionInterface |
||
335 | */ |
||
336 | 1 | public function words(string $charlist = ''): TypedCollectionInterface |
|
337 | { |
||
338 | 1 | $words = str_word_count($this->value, 2, $charlist); |
|
339 | |||
340 | 1 | foreach ($words as &$word) { |
|
341 | 1 | $word = new self($word); |
|
342 | } |
||
343 | |||
344 | 1 | return new TypedCollection( |
|
345 | 1 | self::class, |
|
346 | $words |
||
347 | ); |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * Split the string using a regular expression |
||
352 | * |
||
353 | * @param string $regex |
||
354 | * @param int $limit |
||
355 | * @param int $flags |
||
356 | * |
||
357 | * @return TypedCollectionInterface |
||
358 | */ |
||
359 | 2 | public function pregSplit(string $regex, int $limit = -1, int $flags = self::PREG_NO_FLAGS): TypedCollectionInterface |
|
360 | { |
||
361 | 2 | $strings = preg_split($regex, $this->value, $limit, $flags); |
|
362 | |||
363 | 2 | foreach ($strings as &$string) { |
|
364 | 2 | $string = new self($string); |
|
365 | } |
||
366 | |||
367 | 2 | return new TypedCollection( |
|
368 | 2 | self::class, |
|
369 | $strings |
||
370 | ); |
||
371 | } |
||
372 | |||
373 | /** |
||
374 | * Check if the string match the given regular expression |
||
375 | * |
||
376 | * @param string $regex |
||
377 | * @param int $offset |
||
378 | * |
||
379 | * @throws Exception If the regex failed |
||
380 | * |
||
381 | * @return bool |
||
382 | */ |
||
383 | 2 | public function match(string $regex, int $offset = 0): bool |
|
384 | { |
||
385 | 2 | $matches = []; |
|
386 | 2 | $value = preg_match($regex, $this->value, $matches, 0, $offset); |
|
387 | |||
388 | 2 | if ($value === false) { |
|
389 | 1 | throw new RegexException('', preg_last_error()); |
|
390 | } |
||
391 | |||
392 | 1 | return (bool) $value; |
|
393 | } |
||
394 | |||
395 | /** |
||
396 | * Return a collection of the elements matching the regex |
||
397 | * |
||
398 | * @param string $regex |
||
399 | * @param int $offset |
||
400 | * @param int $flags |
||
401 | * |
||
402 | * @throws Exception If the regex failed |
||
403 | * |
||
404 | * @return TypedCollectionInterface |
||
405 | */ |
||
406 | 2 | public function getMatches(string $regex, int $offset = 0, int $flags = self::PREG_NO_FLAGS): TypedCollectionInterface |
|
407 | { |
||
408 | 2 | $matches = []; |
|
409 | 2 | $value = preg_match( |
|
410 | $regex, |
||
411 | 2 | $this->value, |
|
412 | $matches, |
||
413 | $flags, |
||
414 | $offset |
||
415 | ); |
||
416 | |||
417 | 2 | foreach ($matches as &$match) { |
|
418 | 1 | $match = new self($match); |
|
419 | } |
||
420 | |||
421 | 2 | if ($value === false) { |
|
422 | 1 | throw new RegexException('', preg_last_error()); |
|
423 | } |
||
424 | |||
425 | 1 | return new TypedCollection(self::class, $matches); |
|
426 | } |
||
427 | |||
428 | /** |
||
429 | * Replace part of the string by using a regular expression |
||
430 | * |
||
431 | * @param string $regex |
||
432 | * @param string $replacement |
||
433 | * @param int $limit |
||
434 | * |
||
435 | * @throws Exception If the regex failed |
||
436 | * |
||
437 | * @return self |
||
438 | */ |
||
439 | 1 | public function pregReplace(string $regex, string $replacement, int $limit = -1): self |
|
440 | { |
||
441 | 1 | $value = preg_replace( |
|
442 | $regex, |
||
443 | $replacement, |
||
444 | 1 | $this->value, |
|
445 | $limit |
||
446 | ); |
||
447 | |||
448 | 1 | if ($value === null) { |
|
449 | throw new RegexException('', preg_last_error()); |
||
450 | } |
||
451 | |||
452 | 1 | return new self($value); |
|
453 | } |
||
454 | |||
455 | /** |
||
456 | * Return part of the string |
||
457 | * |
||
458 | * @param int $start |
||
459 | * @param int $length |
||
460 | * |
||
461 | * @return self |
||
462 | */ |
||
463 | 1 | public function substring(int $start, int $length = null): self |
|
464 | { |
||
465 | 1 | View Code Duplication | if ($length === null) { |
0 ignored issues
–
show
This code seems to be duplicated across your project.
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. ![]() |
|||
466 | 1 | $sub = substr($this->value, $start); |
|
467 | } else { |
||
468 | 1 | $sub = substr($this->value, $start, $length); |
|
469 | } |
||
470 | |||
471 | 1 | return new self($sub); |
|
472 | } |
||
473 | |||
474 | /** |
||
475 | * Return a formatted string |
||
476 | * |
||
477 | * @return self |
||
478 | */ |
||
479 | 1 | public function sprintf(): self |
|
480 | { |
||
481 | 1 | $params = func_get_args(); |
|
482 | 1 | array_unshift($params, $this->value); |
|
483 | 1 | $formatted = call_user_func_array('sprintf', $params); |
|
484 | |||
485 | 1 | return new self($formatted); |
|
486 | } |
||
487 | |||
488 | /** |
||
489 | * Return the string with the first letter as uppercase |
||
490 | * |
||
491 | * @return self |
||
492 | */ |
||
493 | 2 | public function ucfirst(): self |
|
494 | { |
||
495 | 2 | return new self(ucfirst($this->value)); |
|
496 | } |
||
497 | |||
498 | /** |
||
499 | * Return the string with the first letter as lowercase |
||
500 | * |
||
501 | * @return self |
||
502 | */ |
||
503 | 1 | public function lcfirst(): self |
|
504 | { |
||
505 | 1 | return new self(lcfirst($this->value)); |
|
506 | } |
||
507 | |||
508 | /** |
||
509 | * Return a CamelCase representation of the string |
||
510 | * |
||
511 | * @return self |
||
512 | */ |
||
513 | 1 | public function camelize(): self |
|
514 | { |
||
515 | 1 | return new self( |
|
516 | $this |
||
517 | 1 | ->pregSplit('/_| /') |
|
518 | 1 | ->map(function(self $part) { |
|
519 | 1 | return $part->ucfirst(); |
|
520 | 1 | }) |
|
521 | 1 | ->join('') |
|
522 | ); |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * Append a string at the end of the current one |
||
527 | * |
||
528 | * @param string $string |
||
529 | * |
||
530 | * @return self |
||
531 | */ |
||
532 | 2 | public function append(string $string): self |
|
533 | { |
||
534 | 2 | return new self((string) $this.$string); |
|
535 | } |
||
536 | |||
537 | /** |
||
538 | * Prepend a string at the beginning of the current one |
||
539 | * |
||
540 | * @param string $string |
||
541 | * |
||
542 | * @return self |
||
543 | */ |
||
544 | 1 | public function prepend(string $string): self |
|
545 | { |
||
546 | 1 | return new self($string.(string) $this); |
|
547 | } |
||
548 | |||
549 | /** |
||
550 | * Check if the 2 strings are equal |
||
551 | * |
||
552 | * @param self $string |
||
553 | * |
||
554 | * @return bool |
||
555 | */ |
||
556 | 11 | public function equals(self $string): bool |
|
557 | { |
||
558 | 11 | return (string) $this === (string) $string; |
|
559 | } |
||
560 | |||
561 | /** |
||
562 | * Trim the string |
||
563 | * |
||
564 | * @param string $mask |
||
565 | * |
||
566 | * @return self |
||
567 | */ |
||
568 | 1 | public function trim(string $mask = null): self |
|
569 | { |
||
570 | 1 | return new self($mask === null ? trim((string) $this) : trim((string) $this, $mask)); |
|
571 | } |
||
572 | |||
573 | /** |
||
574 | * Trim the right side of the string |
||
575 | * |
||
576 | * @param string $mask |
||
577 | * |
||
578 | * @return self |
||
579 | */ |
||
580 | 1 | public function rightTrim(string $mask = null): self |
|
581 | { |
||
582 | 1 | return new self($mask === null ? rtrim((string) $this) : rtrim((string) $this, $mask)); |
|
583 | } |
||
584 | |||
585 | /** |
||
586 | * Trim the left side of the string |
||
587 | * |
||
588 | * @param string $mask |
||
589 | * |
||
590 | * @return self |
||
591 | */ |
||
592 | 1 | public function leftTrim(string $mask = null): self |
|
593 | { |
||
594 | 1 | return new self($mask === null ? ltrim((string) $this) : ltrim((string) $this, $mask)); |
|
595 | } |
||
596 | } |
||
597 |
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.