Completed
Push — master ( 93f6e0...a7ea34 )
by Aydin
27:25 queued 18:55
created

Stringy   D

Complexity

Total Complexity 161

Size/Duplication

Total Lines 1745
Duplicated Lines 9.91 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 161
c 2
b 0
f 1
lcom 1
cbo 0
dl 173
loc 1745
rs 4.4103

87 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 15 5
A create() 0 4 1
A __toString() 0 4 1
A append() 0 4 1
A at() 0 4 1
A between() 0 15 3
B camelize() 0 28 2
A chars() 0 9 2
A collapseWhitespace() 0 4 1
A contains() 0 10 2
A containsAll() 14 14 4
A containsAny() 14 14 4
A count() 0 4 1
A countSubstr() 0 11 2
A dasherize() 0 4 1
A delimit() 0 14 1
A endsWith() 4 15 2
A ensureLeft() 10 10 2
A ensureRight() 10 10 2
A first() 12 12 2
A getEncoding() 0 4 1
A getIterator() 0 4 1
A hasLowerCase() 0 4 1
A hasUpperCase() 0 4 1
A htmlDecode() 0 6 1
A htmlEncode() 0 6 1
A humanize() 0 6 1
A indexOf() 0 5 1
A indexOfLast() 0 5 1
A insert() 15 15 2
A isAlpha() 0 4 1
A isAlphanumeric() 0 4 1
A isBlank() 0 4 1
A isHexadecimal() 0 4 1
A isJson() 0 6 1
A isLowerCase() 0 4 1
A isSerialized() 0 4 2
A isUpperCase() 0 4 1
A last() 12 12 2
A length() 0 4 1
A lines() 3 9 2
A longestCommonPrefix() 0 18 3
A longestCommonSuffix() 0 18 3
C longestCommonSubstring() 0 41 7
A lowerCaseFirst() 10 10 1
A offsetExists() 0 11 2
A offsetGet() 0 11 4
A offsetSet() 0 5 1
A offsetUnset() 0 5 1
A pad() 0 16 4
A padBoth() 0 7 1
A padLeft() 0 4 1
A padRight() 0 4 1
A prepend() 0 4 1
A regexReplace() 0 10 1
A removeLeft() 11 11 2
A removeRight() 11 11 2
A repeat() 0 6 1
A replace() 0 4 1
A reverse() 0 12 2
B safeTruncate() 0 25 3
A shuffle() 0 12 2
A slugify() 0 11 1
A startsWith() 4 13 2
B slice() 0 16 5
C split() 3 31 7
A substr() 0 7 2
A surround() 0 6 1
A swapCase() 0 19 2
A tidy() 0 16 1
A titleize() 0 20 3
A toAscii() 0 14 3
A toBoolean() 0 22 3
A toLowerCase() 0 6 1
A toSpaces() 7 7 1
A toTabs() 7 7 1
A toTitleCase() 0 6 1
A toUpperCase() 0 6 1
A trim() 0 6 2
A trimLeft() 0 6 2
A trimRight() 0 6 2
A truncate() 16 16 2
A underscored() 0 4 1
A upperCamelize() 0 4 1
A upperCaseFirst() 10 10 1
A applyPadding() 0 21 3
A matchesPattern() 0 10 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Stringy often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Stringy, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Stringy;
4
5
class Stringy implements \Countable, \IteratorAggregate, \ArrayAccess
6
{
7
    /**
8
     * An instance's string.
9
     *
10
     * @var string
11
     */
12
    protected $str;
13
14
    /**
15
     * The string's encoding, which should be one of the mbstring module's
16
     * supported encodings.
17
     *
18
     * @var string
19
     */
20
    protected $encoding;
21
22
    /**
23
     * Initializes a Stringy object and assigns both str and encoding properties
24
     * the supplied values. $str is cast to a string prior to assignment, and if
25
     * $encoding is not specified, it defaults to mb_internal_encoding(). Throws
26
     * an InvalidArgumentException if the first argument is an array or object
27
     * without a __toString method.
28
     *
29
     * @param  mixed  $str      Value to modify, after being cast to string
30
     * @param  string $encoding The character encoding
31
     * @throws \InvalidArgumentException if an array or object without a
32
     *         __toString method is passed as the first argument
33
     */
34
    public function __construct($str = '', $encoding = null)
35
    {
36
        if (is_array($str)) {
37
            throw new \InvalidArgumentException(
38
                'Passed value cannot be an array'
39
            );
40
        } elseif (is_object($str) && !method_exists($str, '__toString')) {
41
            throw new \InvalidArgumentException(
42
                'Passed object must have a __toString method'
43
            );
44
        }
45
46
        $this->str = (string) $str;
47
        $this->encoding = $encoding ?: mb_internal_encoding();
48
    }
49
50
    /**
51
     * Creates a Stringy object and assigns both str and encoding properties
52
     * the supplied values. $str is cast to a string prior to assignment, and if
53
     * $encoding is not specified, it defaults to mb_internal_encoding(). It
54
     * then returns the initialized object. Throws an InvalidArgumentException
55
     * if the first argument is an array or object without a __toString method.
56
     *
57
     * @param  mixed   $str      Value to modify, after being cast to string
58
     * @param  string  $encoding The character encoding
59
     * @return Stringy A Stringy object
60
     * @throws \InvalidArgumentException if an array or object without a
61
     *         __toString method is passed as the first argument
62
     */
63
    public static function create($str = '', $encoding = null)
64
    {
65
        return new static($str, $encoding);
66
    }
67
68
    /**
69
     * Returns the value in $str.
70
     *
71
     * @return string The current value of the $str property
72
     */
73
    public function __toString()
74
    {
75
        return $this->str;
76
    }
77
78
    /**
79
     * Returns a new string with $string appended.
80
     *
81
     * @param  string  $string The string to append
82
     * @return Stringy Object with appended $string
83
     */
84
    public function append($string)
85
    {
86
        return static::create($this->str . $string, $this->encoding);
87
    }
88
89
    /**
90
     * Returns the character at $index, with indexes starting at 0.
91
     *
92
     * @param  int     $index Position of the character
93
     * @return Stringy The character at $index
94
     */
95
    public function at($index)
96
    {
97
        return $this->substr($index, 1);
98
    }
99
100
    /**
101
     * Returns the substring between $start and $end, if found, or an empty
102
     * string. An optional offset may be supplied from which to begin the
103
     * search for the start string.
104
     *
105
     * @param  string $start  Delimiter marking the start of the substring
106
     * @param  string $end    Delimiter marketing the end of the substring
107
     * @param  string $offset Index from which to begin the search
108
     * @return Stringy Object whose $str has been converted to an URL slug
109
     */
110
    public function between($start, $end, $offset = 0)
111
    {
112
        $startIndex = $this->indexOf($start, $offset);
113
        if ($startIndex === false) {
114
            return static::create('', $this->encoding);
115
        }
116
117
        $substrIndex = $startIndex + mb_strlen($start, $this->encoding);
118
        $endIndex = $this->indexOf($end, $substrIndex);
119
        if ($endIndex === false) {
120
            return static::create('', $this->encoding);
121
        }
122
123
        return $this->substr($substrIndex, $endIndex - $substrIndex);
124
    }
125
126
    /**
127
     * Returns a camelCase version of the string. Trims surrounding spaces,
128
     * capitalizes letters following digits, spaces, dashes and underscores,
129
     * and removes spaces, dashes, as well as underscores.
130
     *
131
     * @return Stringy Object with $str in camelCase
132
     */
133
    public function camelize()
134
    {
135
        $encoding = $this->encoding;
136
        $stringy = $this->trim()->lowerCaseFirst();
137
        $stringy->str = preg_replace('/^[-_]+/', '', $stringy->str);
138
139
        $stringy->str = preg_replace_callback(
140
            '/[-_\s]+(.)?/u',
141
            function ($match) use ($encoding) {
142
                if (isset($match[1])) {
143
                    return mb_strtoupper($match[1], $encoding);
144
                } else {
145
                    return '';
146
                }
147
            },
148
            $stringy->str
149
        );
150
151
        $stringy->str = preg_replace_callback(
152
            '/[\d]+(.)?/u',
153
            function ($match) use ($encoding) {
154
                return mb_strtoupper($match[0], $encoding);
155
            },
156
            $stringy->str
157
        );
158
159
        return $stringy;
160
    }
161
162
    /**
163
     * Returns an array consisting of the characters in the string.
164
     *
165
     * @return array An array of string chars
166
     */
167
    public function chars()
168
    {
169
        $chars = array();
170
        for ($i = 0, $l = $this->length(); $i < $l; $i++) {
171
            $chars[] = $this->at($i)->str;
172
        }
173
174
        return $chars;
175
    }
176
177
    /**
178
     * Trims the string and replaces consecutive whitespace characters with a
179
     * single space. This includes tabs and newline characters, as well as
180
     * multibyte whitespace such as the thin space and ideographic space.
181
     *
182
     * @return Stringy Object with a trimmed $str and condensed whitespace
183
     */
184
    public function collapseWhitespace()
185
    {
186
        return $this->regexReplace('[[:space:]]+', ' ')->trim();
187
    }
188
189
    /**
190
     * Returns true if the string contains $needle, false otherwise. By default
191
     * the comparison is case-sensitive, but can be made insensitive by setting
192
     * $caseSensitive to false.
193
     *
194
     * @param  string $needle        Substring to look for
195
     * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
196
     * @return bool   Whether or not $str contains $needle
197
     */
198
    public function contains($needle, $caseSensitive = true)
199
    {
200
        $encoding = $this->encoding;
201
202
        if ($caseSensitive) {
203
            return (mb_strpos($this->str, $needle, 0, $encoding) !== false);
204
        } else {
205
            return (mb_stripos($this->str, $needle, 0, $encoding) !== false);
206
        }
207
    }
208
209
    /**
210
     * Returns true if the string contains all $needles, false otherwise. By
211
     * default the comparison is case-sensitive, but can be made insensitive by
212
     * setting $caseSensitive to false.
213
     *
214
     * @param  array  $needles       Substrings to look for
215
     * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
216
     * @return bool   Whether or not $str contains $needle
217
     */
218 View Code Duplication
    public function containsAll($needles, $caseSensitive = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
219
    {
220
        if (empty($needles)) {
221
            return false;
222
        }
223
224
        foreach ($needles as $needle) {
225
            if (!$this->contains($needle, $caseSensitive)) {
226
                return false;
227
            }
228
        }
229
230
        return true;
231
    }
232
233
    /**
234
     * Returns true if the string contains any $needles, false otherwise. By
235
     * default the comparison is case-sensitive, but can be made insensitive by
236
     * setting $caseSensitive to false.
237
     *
238
     * @param  array  $needles       Substrings to look for
239
     * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
240
     * @return bool   Whether or not $str contains $needle
241
     */
242 View Code Duplication
    public function containsAny($needles, $caseSensitive = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
243
    {
244
        if (empty($needles)) {
245
            return false;
246
        }
247
248
        foreach ($needles as $needle) {
249
            if ($this->contains($needle, $caseSensitive)) {
250
                return true;
251
            }
252
        }
253
254
        return false;
255
    }
256
257
    /**
258
     * Returns the length of the string, implementing the countable interface.
259
     *
260
     * @return int The number of characters in the string, given the encoding
261
     */
262
    public function count()
263
    {
264
        return $this->length();
265
    }
266
267
    /**
268
     * Returns the number of occurrences of $substring in the given string.
269
     * By default, the comparison is case-sensitive, but can be made insensitive
270
     * by setting $caseSensitive to false.
271
     *
272
     * @param  string $substring     The substring to search for
273
     * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
274
     * @return int    The number of $substring occurrences
275
     */
276
    public function countSubstr($substring, $caseSensitive = true)
277
    {
278
        if ($caseSensitive) {
279
            return mb_substr_count($this->str, $substring, $this->encoding);
280
        }
281
282
        $str = mb_strtoupper($this->str, $this->encoding);
283
        $substring = mb_strtoupper($substring, $this->encoding);
284
285
        return mb_substr_count($str, $substring, $this->encoding);
286
    }
287
288
    /**
289
     * Returns a lowercase and trimmed string separated by dashes. Dashes are
290
     * inserted before uppercase characters (with the exception of the first
291
     * character of the string), and in place of spaces as well as underscores.
292
     *
293
     * @return Stringy Object with a dasherized $str
294
     */
295
    public function dasherize()
296
    {
297
        return $this->delimit('-');
298
    }
299
300
    /**
301
     * Returns a lowercase and trimmed string separated by the given delimiter.
302
     * Delimiters are inserted before uppercase characters (with the exception
303
     * of the first character of the string), and in place of spaces, dashes,
304
     * and underscores. Alpha delimiters are not converted to lowercase.
305
     *
306
     * @param  string  $delimiter Sequence used to separate parts of the string
307
     * @return Stringy Object with a delimited $str
308
     */
309
    public function delimit($delimiter)
310
    {
311
        // Save current regex encoding so we can reset it after
312
        $regexEncoding = mb_regex_encoding();
313
        mb_regex_encoding($this->encoding);
314
315
        $str = mb_ereg_replace('\B([A-Z])', '-\1', $this->trim());
316
        $str = mb_strtolower($str, $this->encoding);
317
        $str = mb_ereg_replace('[-_\s]+', $delimiter, $str);
318
319
        mb_regex_encoding($regexEncoding);
320
321
        return static::create($str, $this->encoding);
322
    }
323
324
    /**
325
     * Returns true if the string ends with $substring, false otherwise. By
326
     * default, the comparison is case-sensitive, but can be made insensitive
327
     * by setting $caseSensitive to false.
328
     *
329
     * @param  string $substring     The substring to look for
330
     * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
331
     * @return bool   Whether or not $str ends with $substring
332
     */
333
    public function endsWith($substring, $caseSensitive = true)
334
    {
335
        $substringLength = mb_strlen($substring, $this->encoding);
336
        $strLength = $this->length();
337
338
        $endOfStr = mb_substr($this->str, $strLength - $substringLength,
339
            $substringLength, $this->encoding);
340
341 View Code Duplication
        if (!$caseSensitive) {
0 ignored issues
show
Duplication introduced by
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.

Loading history...
342
            $substring = mb_strtolower($substring, $this->encoding);
343
            $endOfStr = mb_strtolower($endOfStr, $this->encoding);
344
        }
345
346
        return (string) $substring === $endOfStr;
347
    }
348
349
    /**
350
     * Ensures that the string begins with $substring. If it doesn't, it's
351
     * prepended.
352
     *
353
     * @param  string  $substring The substring to add if not present
354
     * @return Stringy Object with its $str prefixed by the $substring
355
     */
356 View Code Duplication
    public function ensureLeft($substring)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
357
    {
358
        $stringy = static::create($this->str, $this->encoding);
359
360
        if (!$stringy->startsWith($substring)) {
361
            $stringy->str = $substring . $stringy->str;
362
        }
363
364
        return $stringy;
365
    }
366
367
    /**
368
     * Ensures that the string begins with $substring. If it doesn't, it's
369
     * appended.
370
     *
371
     * @param  string  $substring The substring to add if not present
372
     * @return Stringy Object with its $str suffixed by the $substring
373
     */
374 View Code Duplication
    public function ensureRight($substring)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
375
    {
376
        $stringy = static::create($this->str, $this->encoding);
377
378
        if (!$stringy->endsWith($substring)) {
379
            $stringy->str .= $substring;
380
        }
381
382
        return $stringy;
383
    }
384
385
    /**
386
     * Returns the first $n characters of the string.
387
     *
388
     * @param  int     $n Number of characters to retrieve from the start
389
     * @return Stringy Object with its $str being the first $n chars
390
     */
391 View Code Duplication
    public function first($n)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
392
    {
393
        $stringy = static::create($this->str, $this->encoding);
394
395
        if ($n < 0) {
396
            $stringy->str = '';
397
        } else {
398
            return $stringy->substr(0, $n);
399
        }
400
401
        return $stringy;
402
    }
403
404
    /**
405
     * Returns the encoding used by the Stringy object.
406
     *
407
     * @return string The current value of the $encoding property
408
     */
409
    public function getEncoding()
410
    {
411
        return $this->encoding;
412
    }
413
414
    /**
415
     * Returns a new ArrayIterator, thus implementing the IteratorAggregate
416
     * interface. The ArrayIterator's constructor is passed an array of chars
417
     * in the multibyte string. This enables the use of foreach with instances
418
     * of Stringy\Stringy.
419
     *
420
     * @return \ArrayIterator An iterator for the characters in the string
421
     */
422
    public function getIterator()
423
    {
424
        return new \ArrayIterator($this->chars());
425
    }
426
427
    /**
428
     * Returns true if the string contains a lower case char, false
429
     * otherwise.
430
     *
431
     * @return bool Whether or not the string contains a lower case character.
432
     */
433
    public function hasLowerCase()
434
    {
435
        return $this->matchesPattern('.*[[:lower:]]');
436
    }
437
438
    /**
439
     * Returns true if the string contains an upper case char, false
440
     * otherwise.
441
     *
442
     * @return bool Whether or not the string contains an upper case character.
443
     */
444
    public function hasUpperCase()
445
    {
446
        return $this->matchesPattern('.*[[:upper:]]');
447
    }
448
449
450
    /**
451
     * Convert all HTML entities to their applicable characters. An alias of
452
     * html_entity_decode. For a list of flags, refer to
453
     * http://php.net/manual/en/function.html-entity-decode.php
454
     *
455
     * @param  int|null $flags Optional flags
456
     * @return Stringy  Object with the resulting $str after being html decoded.
457
     */
458
    public function htmlDecode($flags = ENT_COMPAT)
459
    {
460
        $str = html_entity_decode($this->str, $flags, $this->encoding);
461
462
        return static::create($str, $this->encoding);
463
    }
464
465
    /**
466
     * Convert all applicable characters to HTML entities. An alias of
467
     * htmlentities. Refer to http://php.net/manual/en/function.htmlentities.php
468
     * for a list of flags.
469
     *
470
     * @param  int|null $flags Optional flags
471
     * @return Stringy  Object with the resulting $str after being html encoded.
472
     */
473
    public function htmlEncode($flags = ENT_COMPAT)
474
    {
475
        $str = htmlentities($this->str, $flags, $this->encoding);
476
477
        return static::create($str, $this->encoding);
478
    }
479
480
    /**
481
     * Capitalizes the first word of the string, replaces underscores with
482
     * spaces, and strips '_id'.
483
     *
484
     * @return Stringy Object with a humanized $str
485
     */
486
    public function humanize()
487
    {
488
        $str = str_replace(array('_id', '_'), array('', ' '), $this->str);
489
490
        return static::create($str, $this->encoding)->trim()->upperCaseFirst();
491
    }
492
493
    /**
494
     * Returns the index of the first occurrence of $needle in the string,
495
     * and false if not found. Accepts an optional offset from which to begin
496
     * the search.
497
     *
498
     * @param  string   $needle Substring to look for
499
     * @param  int      $offset Offset from which to search
500
     * @return int|bool The occurrence's index if found, otherwise false
501
     */
502
    public function indexOf($needle, $offset = 0)
503
    {
504
        return mb_strpos($this->str, (string) $needle,
505
            (int) $offset, $this->encoding);
506
    }
507
508
    /**
509
     * Returns the index of the last occurrence of $needle in the string,
510
     * and false if not found. Accepts an optional offset from which to begin
511
     * the search. Offsets may be negative to count from the last character
512
     * in the string.
513
     *
514
     * @param  string   $needle Substring to look for
515
     * @param  int      $offset Offset from which to search
516
     * @return int|bool The last occurrence's index if found, otherwise false
517
     */
518
    public function indexOfLast($needle, $offset = 0)
519
    {
520
        return mb_strrpos($this->str, (string) $needle,
521
            (int) $offset, $this->encoding);
522
    }
523
524
    /**
525
     * Inserts $substring into the string at the $index provided.
526
     *
527
     * @param  string  $substring String to be inserted
528
     * @param  int     $index     The index at which to insert the substring
529
     * @return Stringy Object with the resulting $str after the insertion
530
     */
531 View Code Duplication
    public function insert($substring, $index)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
532
    {
533
        $stringy = static::create($this->str, $this->encoding);
534
        if ($index > $stringy->length()) {
535
            return $stringy;
536
        }
537
538
        $start = mb_substr($stringy->str, 0, $index, $stringy->encoding);
539
        $end = mb_substr($stringy->str, $index, $stringy->length(),
540
            $stringy->encoding);
541
542
        $stringy->str = $start . $substring . $end;
543
544
        return $stringy;
545
    }
546
547
    /**
548
     * Returns true if the string contains only alphabetic chars, false
549
     * otherwise.
550
     *
551
     * @return bool Whether or not $str contains only alphabetic chars
552
     */
553
    public function isAlpha()
554
    {
555
        return $this->matchesPattern('^[[:alpha:]]*$');
556
    }
557
558
    /**
559
     * Returns true if the string contains only alphabetic and numeric chars,
560
     * false otherwise.
561
     *
562
     * @return bool Whether or not $str contains only alphanumeric chars
563
     */
564
    public function isAlphanumeric()
565
    {
566
        return $this->matchesPattern('^[[:alnum:]]*$');
567
    }
568
569
    /**
570
     * Returns true if the string contains only whitespace chars, false
571
     * otherwise.
572
     *
573
     * @return bool Whether or not $str contains only whitespace characters
574
     */
575
    public function isBlank()
576
    {
577
        return $this->matchesPattern('^[[:space:]]*$');
578
    }
579
580
    /**
581
     * Returns true if the string contains only hexadecimal chars, false
582
     * otherwise.
583
     *
584
     * @return bool Whether or not $str contains only hexadecimal chars
585
     */
586
    public function isHexadecimal()
587
    {
588
        return $this->matchesPattern('^[[:xdigit:]]*$');
589
    }
590
591
    /**
592
     * Returns true if the string is JSON, false otherwise.
593
     *
594
     * @return bool Whether or not $str is JSON
595
     */
596
    public function isJson()
597
    {
598
        json_decode($this->str);
599
600
        return (json_last_error() === JSON_ERROR_NONE);
601
    }
602
603
    /**
604
     * Returns true if the string contains only lower case chars, false
605
     * otherwise.
606
     *
607
     * @return bool Whether or not $str contains only lower case characters
608
     */
609
    public function isLowerCase()
610
    {
611
        return $this->matchesPattern('^[[:lower:]]*$');
612
    }
613
614
    /**
615
     * Returns true if the string is serialized, false otherwise.
616
     *
617
     * @return bool Whether or not $str is serialized
618
     */
619
    public function isSerialized()
620
    {
621
        return $this->str === 'b:0;' || @unserialize($this->str) !== false;
622
    }
623
624
    /**
625
     * Returns true if the string contains only lower case chars, false
626
     * otherwise.
627
     *
628
     * @return bool Whether or not $str contains only lower case characters
629
     */
630
    public function isUpperCase()
631
    {
632
        return $this->matchesPattern('^[[:upper:]]*$');
633
    }
634
635
    /**
636
     * Returns the last $n characters of the string.
637
     *
638
     * @param  int     $n Number of characters to retrieve from the end
639
     * @return Stringy Object with its $str being the last $n chars
640
     */
641 View Code Duplication
    public function last($n)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
642
    {
643
        $stringy = static::create($this->str, $this->encoding);
644
645
        if ($n <= 0) {
646
            $stringy->str = '';
647
        } else {
648
            return $stringy->substr(-$n);
649
        }
650
651
        return $stringy;
652
    }
653
654
    /**
655
     * Returns the length of the string. An alias for PHP's mb_strlen() function.
656
     *
657
     * @return int The number of characters in $str given the encoding
658
     */
659
    public function length()
660
    {
661
        return mb_strlen($this->str, $this->encoding);
662
    }
663
664
    /**
665
     * Splits on newlines and carriage returns, returning an array of Stringy
666
     * objects corresponding to the lines in the string.
667
     *
668
     * @return Stringy[] An array of Stringy objects
669
     */
670
    public function lines()
671
    {
672
        $array = mb_split('[\r\n]{1,2}', $this->str);
673 View Code Duplication
        for ($i = 0; $i < count($array); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
Duplication introduced by
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.

Loading history...
674
            $array[$i] = static::create($array[$i], $this->encoding);
675
        }
676
677
        return $array;
678
    }
679
680
    /**
681
     * Returns the longest common prefix between the string and $otherStr.
682
     *
683
     * @param  string  $otherStr Second string for comparison
684
     * @return Stringy Object with its $str being the longest common prefix
685
     */
686
    public function longestCommonPrefix($otherStr)
687
    {
688
        $encoding = $this->encoding;
689
        $maxLength = min($this->length(), mb_strlen($otherStr, $encoding));
690
691
        $longestCommonPrefix = '';
692
        for ($i = 0; $i < $maxLength; $i++) {
693
            $char = mb_substr($this->str, $i, 1, $encoding);
694
695
            if ($char == mb_substr($otherStr, $i, 1, $encoding)) {
696
                $longestCommonPrefix .= $char;
697
            } else {
698
                break;
699
            }
700
        }
701
702
        return static::create($longestCommonPrefix, $encoding);
703
    }
704
705
    /**
706
     * Returns the longest common suffix between the string and $otherStr.
707
     *
708
     * @param  string  $otherStr Second string for comparison
709
     * @return Stringy Object with its $str being the longest common suffix
710
     */
711
    public function longestCommonSuffix($otherStr)
712
    {
713
        $encoding = $this->encoding;
714
        $maxLength = min($this->length(), mb_strlen($otherStr, $encoding));
715
716
        $longestCommonSuffix = '';
717
        for ($i = 1; $i <= $maxLength; $i++) {
718
            $char = mb_substr($this->str, -$i, 1, $encoding);
719
720
            if ($char == mb_substr($otherStr, -$i, 1, $encoding)) {
721
                $longestCommonSuffix = $char . $longestCommonSuffix;
722
            } else {
723
                break;
724
            }
725
        }
726
727
        return static::create($longestCommonSuffix, $encoding);
728
    }
729
730
    /**
731
     * Returns the longest common substring between the string and $otherStr.
732
     * In the case of ties, it returns that which occurs first.
733
     *
734
     * @param  string  $otherStr Second string for comparison
735
     * @return Stringy Object with its $str being the longest common substring
736
     */
737
    public function longestCommonSubstring($otherStr)
738
    {
739
        // Uses dynamic programming to solve
740
        // http://en.wikipedia.org/wiki/Longest_common_substring_problem
741
        $encoding = $this->encoding;
742
        $stringy = static::create($this->str, $encoding);
743
        $strLength = $stringy->length();
744
        $otherLength = mb_strlen($otherStr, $encoding);
745
746
        // Return if either string is empty
747
        if ($strLength == 0 || $otherLength == 0) {
748
            $stringy->str = '';
749
            return $stringy;
750
        }
751
752
        $len = 0;
753
        $end = 0;
754
        $table = array_fill(0, $strLength + 1,
755
            array_fill(0, $otherLength + 1, 0));
756
757
        for ($i = 1; $i <= $strLength; $i++) {
758
            for ($j = 1; $j <= $otherLength; $j++) {
759
                $strChar = mb_substr($stringy->str, $i - 1, 1, $encoding);
760
                $otherChar = mb_substr($otherStr, $j - 1, 1, $encoding);
761
762
                if ($strChar == $otherChar) {
763
                    $table[$i][$j] = $table[$i - 1][$j - 1] + 1;
764
                    if ($table[$i][$j] > $len) {
765
                        $len = $table[$i][$j];
766
                        $end = $i;
767
                    }
768
                } else {
769
                    $table[$i][$j] = 0;
770
                }
771
            }
772
        }
773
774
        $stringy->str = mb_substr($stringy->str, $end - $len, $len, $encoding);
775
776
        return $stringy;
777
    }
778
779
    /**
780
     * Converts the first character of the string to lower case.
781
     *
782
     * @return Stringy Object with the first character of $str being lower case
783
     */
784 View Code Duplication
    public function lowerCaseFirst()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
785
    {
786
        $first = mb_substr($this->str, 0, 1, $this->encoding);
787
        $rest = mb_substr($this->str, 1, $this->length() - 1,
788
            $this->encoding);
789
790
        $str = mb_strtolower($first, $this->encoding) . $rest;
791
792
        return static::create($str, $this->encoding);
793
    }
794
795
    /**
796
     * Returns whether or not a character exists at an index. Offsets may be
797
     * negative to count from the last character in the string. Implements
798
     * part of the ArrayAccess interface.
799
     *
800
     * @param  mixed   $offset The index to check
801
     * @return boolean Whether or not the index exists
802
     */
803
    public function offsetExists($offset)
804
    {
805
        $length = $this->length();
806
        $offset = (int) $offset;
807
808
        if ($offset >= 0) {
809
            return ($length > $offset);
810
        }
811
812
        return ($length >= abs($offset));
813
    }
814
815
    /**
816
     * Returns the character at the given index. Offsets may be negative to
817
     * count from the last character in the string. Implements part of the
818
     * ArrayAccess interface, and throws an OutOfBoundsException if the index
819
     * does not exist.
820
     *
821
     * @param  mixed $offset         The index from which to retrieve the char
822
     * @return mixed                 The character at the specified index
823
     * @throws \OutOfBoundsException If the positive or negative offset does
824
     *                               not exist
825
     */
826
    public function offsetGet($offset)
827
    {
828
        $offset = (int) $offset;
829
        $length = $this->length();
830
831
        if (($offset >= 0 && $length <= $offset) || $length < abs($offset)) {
832
            throw new \OutOfBoundsException('No character exists at the index');
833
        }
834
835
        return mb_substr($this->str, $offset, 1, $this->encoding);
836
    }
837
838
    /**
839
     * Implements part of the ArrayAccess interface, but throws an exception
840
     * when called. This maintains the immutability of Stringy objects.
841
     *
842
     * @param  mixed      $offset The index of the character
843
     * @param  mixed      $value  Value to set
844
     * @throws \Exception When called
845
     */
846
    public function offsetSet($offset, $value)
847
    {
848
        // Stringy is immutable, cannot directly set char
849
        throw new \Exception('Stringy object is immutable, cannot modify char');
850
    }
851
852
    /**
853
     * Implements part of the ArrayAccess interface, but throws an exception
854
     * when called. This maintains the immutability of Stringy objects.
855
     *
856
     * @param  mixed      $offset The index of the character
857
     * @throws \Exception When called
858
     */
859
    public function offsetUnset($offset)
860
    {
861
        // Don't allow directly modifying the string
862
        throw new \Exception('Stringy object is immutable, cannot unset char');
863
    }
864
865
    /**
866
     * Pads the string to a given length with $padStr. If length is less than
867
     * or equal to the length of the string, no padding takes places. The
868
     * default string used for padding is a space, and the default type (one of
869
     * 'left', 'right', 'both') is 'right'. Throws an InvalidArgumentException
870
     * if $padType isn't one of those 3 values.
871
     *
872
     * @param  int     $length  Desired string length after padding
873
     * @param  string  $padStr  String used to pad, defaults to space
874
     * @param  string  $padType One of 'left', 'right', 'both'
875
     * @return Stringy Object with a padded $str
876
     * @throws InvalidArgumentException If $padType isn't one of 'right',
877
     *         'left' or 'both'
878
     */
879
    public function pad($length, $padStr = ' ', $padType = 'right')
880
    {
881
        if (!in_array($padType, array('left', 'right', 'both'))) {
882
            throw new \InvalidArgumentException('Pad expects $padType ' .
883
                "to be one of 'left', 'right' or 'both'");
884
        }
885
886
        switch ($padType) {
887
            case 'left':
888
                return $this->padLeft($length, $padStr);
889
            case 'right':
890
                return $this->padRight($length, $padStr);
891
            default:
892
                return $this->padBoth($length, $padStr);
893
        }
894
    }
895
896
    /**
897
     * Returns a new string of a given length such that both sides of the
898
     * string are padded. Alias for pad() with a $padType of 'both'.
899
     *
900
     * @param  int     $length Desired string length after padding
901
     * @param  string  $padStr String used to pad, defaults to space
902
     * @return Stringy String with padding applied
903
     */
904
    public function padBoth($length, $padStr = ' ')
905
    {
906
        $padding = $length - $this->length();
907
908
        return $this->applyPadding(floor($padding / 2), ceil($padding / 2),
909
            $padStr);
910
    }
911
912
    /**
913
     * Returns a new string of a given length such that the beginning of the
914
     * string is padded. Alias for pad() with a $padType of 'left'.
915
     *
916
     * @param  int     $length Desired string length after padding
917
     * @param  string  $padStr String used to pad, defaults to space
918
     * @return Stringy String with left padding
919
     */
920
    public function padLeft($length, $padStr = ' ')
921
    {
922
        return $this->applyPadding($length - $this->length(), 0, $padStr);
923
    }
924
925
    /**
926
     * Returns a new string of a given length such that the end of the string
927
     * is padded. Alias for pad() with a $padType of 'right'.
928
     *
929
     * @param  int     $length Desired string length after padding
930
     * @param  string  $padStr String used to pad, defaults to space
931
     * @return Stringy String with right padding
932
     */
933
    public function padRight($length, $padStr = ' ')
934
    {
935
        return $this->applyPadding(0, $length - $this->length(), $padStr);
936
    }
937
938
    /**
939
     * Returns a new string starting with $string.
940
     *
941
     * @param  string  $string The string to append
942
     * @return Stringy Object with appended $string
943
     */
944
    public function prepend($string)
945
    {
946
        return static::create($string . $this->str, $this->encoding);
947
    }
948
949
    /**
950
     * Replaces all occurrences of $pattern in $str by $replacement. An alias
951
     * for mb_ereg_replace(). Note that the 'i' option with multibyte patterns
952
     * in mb_ereg_replace() requires PHP 5.6+ for correct results. This is due
953
     * to a lack of support in the bundled version of Oniguruma in PHP < 5.6,
954
     * and current versions of HHVM (3.8 and below).
955
     *
956
     * @param  string  $pattern     The regular expression pattern
957
     * @param  string  $replacement The string to replace with
958
     * @param  string  $options     Matching conditions to be used
959
     * @return Stringy Object with the resulting $str after the replacements
960
     */
961
    public function regexReplace($pattern, $replacement, $options = 'msr')
962
    {
963
        $regexEncoding = mb_regex_encoding();
964
        mb_regex_encoding($this->encoding);
965
966
        $str = mb_ereg_replace($pattern, $replacement, $this->str, $options);
967
        mb_regex_encoding($regexEncoding);
968
969
        return static::create($str, $this->encoding);
970
    }
971
972
    /**
973
     * Returns a new string with the prefix $substring removed, if present.
974
     *
975
     * @param  string  $substring The prefix to remove
976
     * @return Stringy Object having a $str without the prefix $substring
977
     */
978 View Code Duplication
    public function removeLeft($substring)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
979
    {
980
        $stringy = static::create($this->str, $this->encoding);
981
982
        if ($stringy->startsWith($substring)) {
983
            $substringLength = mb_strlen($substring, $stringy->encoding);
984
            return $stringy->substr($substringLength);
985
        }
986
987
        return $stringy;
988
    }
989
990
    /**
991
     * Returns a new string with the suffix $substring removed, if present.
992
     *
993
     * @param  string  $substring The suffix to remove
994
     * @return Stringy Object having a $str without the suffix $substring
995
     */
996 View Code Duplication
    public function removeRight($substring)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
997
    {
998
        $stringy = static::create($this->str, $this->encoding);
999
1000
        if ($stringy->endsWith($substring)) {
1001
            $substringLength = mb_strlen($substring, $stringy->encoding);
1002
            return $stringy->substr(0, $stringy->length() - $substringLength);
1003
        }
1004
1005
        return $stringy;
1006
    }
1007
1008
    /**
1009
     * Returns a repeated string given a multiplier. An alias for str_repeat.
1010
     *
1011
     * @param  int     $multiplier The number of times to repeat the string
1012
     * @return Stringy Object with a repeated str
1013
     */
1014
    public function repeat($multiplier)
1015
    {
1016
        $repeated = str_repeat($this->str, $multiplier);
1017
1018
        return static::create($repeated, $this->encoding);
1019
    }
1020
1021
    /**
1022
     * Replaces all occurrences of $search in $str by $replacement.
1023
     *
1024
     * @param  string  $search      The needle to search for
1025
     * @param  string  $replacement The string to replace with
1026
     * @return Stringy Object with the resulting $str after the replacements
1027
     */
1028
    public function replace($search, $replacement)
1029
    {
1030
        return $this->regexReplace(preg_quote($search), $replacement);
1031
    }
1032
1033
    /**
1034
     * Returns a reversed string. A multibyte version of strrev().
1035
     *
1036
     * @return Stringy Object with a reversed $str
1037
     */
1038
    public function reverse()
1039
    {
1040
        $strLength = $this->length();
1041
        $reversed = '';
1042
1043
        // Loop from last index of string to first
1044
        for ($i = $strLength - 1; $i >= 0; $i--) {
1045
            $reversed .= mb_substr($this->str, $i, 1, $this->encoding);
1046
        }
1047
1048
        return static::create($reversed, $this->encoding);
1049
    }
1050
1051
    /**
1052
     * Truncates the string to a given length, while ensuring that it does not
1053
     * split words. If $substring is provided, and truncating occurs, the
1054
     * string is further truncated so that the substring may be appended without
1055
     * exceeding the desired length.
1056
     *
1057
     * @param  int     $length    Desired length of the truncated string
1058
     * @param  string  $substring The substring to append if it can fit
1059
     * @return Stringy Object with the resulting $str after truncating
1060
     */
1061
    public function safeTruncate($length, $substring = '')
1062
    {
1063
        $stringy = static::create($this->str, $this->encoding);
1064
        if ($length >= $stringy->length()) {
1065
            return $stringy;
1066
        }
1067
1068
        // Need to further trim the string so we can append the substring
1069
        $encoding = $stringy->encoding;
1070
        $substringLength = mb_strlen($substring, $encoding);
1071
        $length = $length - $substringLength;
1072
1073
        $truncated = mb_substr($stringy->str, 0, $length, $encoding);
1074
1075
        // If the last word was truncated
1076
        if (mb_strpos($stringy->str, ' ', $length - 1, $encoding) != $length) {
1077
            // Find pos of the last occurrence of a space, get up to that
1078
            $lastPos = mb_strrpos($truncated, ' ', 0, $encoding);
1079
            $truncated = mb_substr($truncated, 0, $lastPos, $encoding);
1080
        }
1081
1082
        $stringy->str = $truncated . $substring;
1083
1084
        return $stringy;
1085
    }
1086
1087
    /*
1088
     * A multibyte str_shuffle() function. It returns a string with its
1089
     * characters in random order.
1090
     *
1091
     * @return Stringy Object with a shuffled $str
1092
     */
1093
    public function shuffle()
1094
    {
1095
        $indexes = range(0, $this->length() - 1);
1096
        shuffle($indexes);
1097
1098
        $shuffledStr = '';
1099
        foreach ($indexes as $i) {
1100
            $shuffledStr .= mb_substr($this->str, $i, 1, $this->encoding);
1101
        }
1102
1103
        return static::create($shuffledStr, $this->encoding);
1104
    }
1105
1106
    /**
1107
     * Converts the string into an URL slug. This includes replacing non-ASCII
1108
     * characters with their closest ASCII equivalents, removing remaining
1109
     * non-ASCII and non-alphanumeric characters, and replacing whitespace with
1110
     * $replacement. The replacement defaults to a single dash, and the string
1111
     * is also converted to lowercase.
1112
     *
1113
     * @param  string  $replacement The string used to replace whitespace
1114
     * @return Stringy Object whose $str has been converted to an URL slug
1115
     */
1116
    public function slugify($replacement = '-')
1117
    {
1118
        $stringy = $this->toAscii();
1119
1120
        $quotedReplacement = preg_quote($replacement);
1121
        $pattern = "/[^a-zA-Z\d\s-_$quotedReplacement]/u";
1122
        $stringy->str = preg_replace($pattern, '', $stringy);
0 ignored issues
show
Documentation Bug introduced by
It seems like preg_replace($pattern, '', $stringy) can also be of type array<integer,string>. However, the property $str is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1123
1124
        return $stringy->toLowerCase()->delimit($replacement)
1125
                       ->removeLeft($replacement)->removeRight($replacement);
1126
    }
1127
1128
    /**
1129
     * Returns true if the string begins with $substring, false otherwise. By
1130
     * default, the comparison is case-sensitive, but can be made insensitive
1131
     * by setting $caseSensitive to false.
1132
     *
1133
     * @param  string $substring     The substring to look for
1134
     * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
1135
     * @return bool   Whether or not $str starts with $substring
1136
     */
1137
    public function startsWith($substring, $caseSensitive = true)
1138
    {
1139
        $substringLength = mb_strlen($substring, $this->encoding);
1140
        $startOfStr = mb_substr($this->str, 0, $substringLength,
1141
            $this->encoding);
1142
1143 View Code Duplication
        if (!$caseSensitive) {
0 ignored issues
show
Duplication introduced by
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.

Loading history...
1144
            $substring = mb_strtolower($substring, $this->encoding);
1145
            $startOfStr = mb_strtolower($startOfStr, $this->encoding);
1146
        }
1147
1148
        return (string) $substring === $startOfStr;
1149
    }
1150
1151
    /**
1152
     * Returns the substring beginning at $start, and up to, but not including
1153
     * the index specified by $end. If $end is omitted, the function extracts
1154
     * the remaining string. If $end is negative, it is computed from the end
1155
     * of the string.
1156
     *
1157
     * @param  int     $start Initial index from which to begin extraction
1158
     * @param  int     $end   Optional index at which to end extraction
1159
     * @return Stringy Object with its $str being the extracted substring
1160
     */
1161
    public function slice($start, $end = null)
1162
    {
1163
        if ($end === null) {
1164
            $length = $this->length();
1165
        } elseif ($end >= 0 && $end <= $start) {
1166
            return static::create('', $this->encoding);
1167
        } elseif ($end < 0) {
1168
            $length = $this->length() + $end - $start;
1169
        } else {
1170
            $length = $end - $start;
1171
        }
1172
1173
        $str = mb_substr($this->str, $start, $length, $this->encoding);
1174
1175
        return static::create($str, $this->encoding);
1176
    }
1177
1178
    /**
1179
     * Splits the string with the provided regular expression, returning an
1180
     * array of Stringy objects. An optional integer $limit will truncate the
1181
     * results.
1182
     *
1183
     * @param  string    $pattern The regex with which to split the string
1184
     * @param  int       $limit   Optional maximum number of results to return
1185
     * @return Stringy[] An array of Stringy objects
1186
     */
1187
    public function split($pattern, $limit = null)
1188
    {
1189
        if ($limit === 0) {
1190
            return array();
1191
        }
1192
1193
        // mb_split errors when supplied an empty pattern in < PHP 5.4.13
1194
        // and current versions of HHVM (3.8 and below)
1195
        if ($pattern === '') {
1196
            return array(static::create($this->str, $this->encoding));
1197
        }
1198
1199
        $regexEncoding = mb_regex_encoding();
1200
        mb_regex_encoding($this->encoding);
1201
1202
        // mb_split returns the remaining unsplit string in the last index when
1203
        // supplying a limit
1204
        $limit = ($limit > 0) ? $limit += 1 : -1;
1205
        $array = mb_split($pattern, $this->str, $limit);
1206
        mb_regex_encoding($regexEncoding);
1207
1208
        if ($limit > 0 && count($array) === $limit) {
1209
            array_pop($array);
1210
        }
1211
1212 View Code Duplication
        for ($i = 0; $i < count($array); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
Duplication introduced by
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.

Loading history...
1213
            $array[$i] = static::create($array[$i], $this->encoding);
1214
        }
1215
1216
        return $array;
1217
    }
1218
1219
    /**
1220
     * Returns the substring beginning at $start with the specified $length.
1221
     * It differs from the mb_substr() function in that providing a $length of
1222
     * null will return the rest of the string, rather than an empty string.
1223
     *
1224
     * @param  int     $start  Position of the first character to use
1225
     * @param  int     $length Maximum number of characters used
1226
     * @return Stringy Object with its $str being the substring
1227
     */
1228
    public function substr($start, $length = null)
1229
    {
1230
        $length = $length === null ? $this->length() : $length;
1231
        $str = mb_substr($this->str, $start, $length, $this->encoding);
1232
1233
        return static::create($str, $this->encoding);
1234
    }
1235
1236
    /**
1237
     * Surrounds $str with the given substring.
1238
     *
1239
     * @param  string  $substring The substring to add to both sides
1240
     * @return Stringy Object whose $str had the substring both prepended and
1241
     *                 appended
1242
     */
1243
    public function surround($substring)
1244
    {
1245
        $str = implode('', array($substring, $this->str, $substring));
1246
1247
        return static::create($str, $this->encoding);
1248
    }
1249
1250
    /**
1251
     * Returns a case swapped version of the string.
1252
     *
1253
     * @return Stringy Object whose $str has each character's case swapped
1254
     */
1255
    public function swapCase()
1256
    {
1257
        $stringy = static::create($this->str, $this->encoding);
1258
        $encoding = $stringy->encoding;
1259
1260
        $stringy->str = preg_replace_callback(
1261
            '/[\S]/u',
1262
            function ($match) use ($encoding) {
1263
                if ($match[0] == mb_strtoupper($match[0], $encoding)) {
1264
                    return mb_strtolower($match[0], $encoding);
1265
                } else {
1266
                    return mb_strtoupper($match[0], $encoding);
1267
                }
1268
            },
1269
            $stringy->str
1270
        );
1271
1272
        return $stringy;
1273
    }
1274
1275
    /**
1276
     * Returns a string with smart quotes, ellipsis characters, and dashes from
1277
     * Windows-1252 (commonly used in Word documents) replaced by their ASCII
1278
     * equivalents.
1279
     *
1280
     * @return Stringy Object whose $str has those characters removed
1281
     */
1282
    public function tidy()
1283
    {
1284
        $str = preg_replace(array(
1285
            '/\x{2026}/u',
1286
            '/[\x{201C}\x{201D}]/u',
1287
            '/[\x{2018}\x{2019}]/u',
1288
            '/[\x{2013}\x{2014}]/u',
1289
        ), array(
1290
            '...',
1291
            '"',
1292
            "'",
1293
            '-',
1294
        ), $this->str);
1295
1296
        return static::create($str, $this->encoding);
1297
    }
1298
1299
    /**
1300
     * Returns a trimmed string with the first letter of each word capitalized.
1301
     * Also accepts an array, $ignore, allowing you to list words not to be
1302
     * capitalized.
1303
     *
1304
     * @param  array   $ignore An array of words not to capitalize
1305
     * @return Stringy Object with a titleized $str
1306
     */
1307
    public function titleize($ignore = null)
1308
    {
1309
        $stringy = static::create($this->trim(), $this->encoding);
1310
        $encoding = $this->encoding;
1311
1312
        $stringy->str = preg_replace_callback(
1313
            '/([\S]+)/u',
1314
            function ($match) use ($encoding, $ignore) {
1315
                if ($ignore && in_array($match[0], $ignore)) {
1316
                    return $match[0];
1317
                } else {
1318
                    $stringy = new Stringy($match[0], $encoding);
1319
                    return (string) $stringy->toLowerCase()->upperCaseFirst();
1320
                }
1321
            },
1322
            $stringy->str
1323
        );
1324
1325
        return $stringy;
1326
    }
1327
1328
    /**
1329
     * Returns an ASCII version of the string. A set of non-ASCII characters are
1330
     * replaced with their closest ASCII counterparts, and the rest are removed
1331
     * unless instructed otherwise.
1332
     *
1333
     * @param  bool    $removeUnsupported Whether or not to remove the
1334
     *                                    unsupported characters
1335
     * @return Stringy Object whose $str contains only ASCII characters
1336
     */
1337
    public function toAscii($removeUnsupported = true)
1338
    {
1339
        $str = $this->str;
1340
1341
        foreach ($this->charsArray() as $key => $value) {
1342
            $str = str_replace($value, $key, $str);
1343
        }
1344
1345
        if ($removeUnsupported) {
1346
            $str = preg_replace('/[^\x20-\x7E]/u', '', $str);
1347
        }
1348
1349
        return static::create($str, $this->encoding);
1350
    }
1351
1352
    /**
1353
     * Returns a boolean representation of the given logical string value.
1354
     * For example, 'true', '1', 'on' and 'yes' will return true. 'false', '0',
1355
     * 'off', and 'no' will return false. In all instances, case is ignored.
1356
     * For other numeric strings, their sign will determine the return value.
1357
     * In addition, blank strings consisting of only whitespace will return
1358
     * false. For all other strings, the return value is a result of a
1359
     * boolean cast.
1360
     *
1361
     * @return bool A boolean value for the string
1362
     */
1363
    public function toBoolean()
1364
    {
1365
        $key = $this->toLowerCase()->str;
1366
        $map = array(
1367
            'true'  => true,
1368
            '1'     => true,
1369
            'on'    => true,
1370
            'yes'   => true,
1371
            'false' => false,
1372
            '0'     => false,
1373
            'off'   => false,
1374
            'no'    => false
1375
        );
1376
1377
        if (array_key_exists($key, $map)) {
1378
            return $map[$key];
1379
        } elseif (is_numeric($this->str)) {
1380
           return (intval($this->str) > 0);
1381
        } else {
1382
            return (bool) $this->regexReplace('[[:space:]]', '')->str;
1383
        }
1384
    }
1385
1386
    /**
1387
     * Converts all characters in the string to lowercase. An alias for PHP's
1388
     * mb_strtolower().
1389
     *
1390
     * @return Stringy Object with all characters of $str being lowercase
1391
     */
1392
    public function toLowerCase()
1393
    {
1394
        $str = mb_strtolower($this->str, $this->encoding);
1395
1396
        return static::create($str, $this->encoding);
1397
    }
1398
1399
    /**
1400
     * Converts each tab in the string to some number of spaces, as defined by
1401
     * $tabLength. By default, each tab is converted to 4 consecutive spaces.
1402
     *
1403
     * @param  int     $tabLength Number of spaces to replace each tab with
1404
     * @return Stringy Object whose $str has had tabs switched to spaces
1405
     */
1406 View Code Duplication
    public function toSpaces($tabLength = 4)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
1407
    {
1408
        $spaces = str_repeat(' ', $tabLength);
1409
        $str = str_replace("\t", $spaces, $this->str);
1410
1411
        return static::create($str, $this->encoding);
1412
    }
1413
1414
    /**
1415
     * Converts each occurrence of some consecutive number of spaces, as
1416
     * defined by $tabLength, to a tab. By default, each 4 consecutive spaces
1417
     * are converted to a tab.
1418
     *
1419
     * @param  int     $tabLength Number of spaces to replace with a tab
1420
     * @return Stringy Object whose $str has had spaces switched to tabs
1421
     */
1422 View Code Duplication
    public function toTabs($tabLength = 4)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
1423
    {
1424
        $spaces = str_repeat(' ', $tabLength);
1425
        $str = str_replace($spaces, "\t", $this->str);
1426
1427
        return static::create($str, $this->encoding);
1428
    }
1429
1430
    /**
1431
     * Converts the first character of each word in the string to uppercase.
1432
     *
1433
     * @return Stringy Object with all characters of $str being title-cased
1434
     */
1435
    public function toTitleCase()
1436
    {
1437
        $str = mb_convert_case($this->str, MB_CASE_TITLE, $this->encoding);
1438
1439
        return static::create($str, $this->encoding);
1440
    }
1441
1442
    /**
1443
     * Converts all characters in the string to uppercase. An alias for PHP's
1444
     * mb_strtoupper().
1445
     *
1446
     * @return Stringy Object with all characters of $str being uppercase
1447
     */
1448
    public function toUpperCase()
1449
    {
1450
        $str = mb_strtoupper($this->str, $this->encoding);
1451
1452
        return static::create($str, $this->encoding);
1453
    }
1454
1455
    /**
1456
     * Returns a string with whitespace removed from the start and end of the
1457
     * string. Supports the removal of unicode whitespace. Accepts an optional
1458
     * string of characters to strip instead of the defaults.
1459
     *
1460
     * @param  string  $chars Optional string of characters to strip
1461
     * @return Stringy Object with a trimmed $str
1462
     */
1463
    public function trim($chars = null)
1464
    {
1465
        $chars = ($chars) ? preg_quote($chars) : '[:space:]';
1466
1467
        return $this->regexReplace("^[$chars]+|[$chars]+\$", '');
1468
    }
1469
1470
    /**
1471
     * Returns a string with whitespace removed from the start of the string.
1472
     * Supports the removal of unicode whitespace. Accepts an optional
1473
     * string of characters to strip instead of the defaults.
1474
     *
1475
     * @param  string  $chars Optional string of characters to strip
1476
     * @return Stringy Object with a trimmed $str
1477
     */
1478
    public function trimLeft($chars = null)
1479
    {
1480
        $chars = ($chars) ? preg_quote($chars) : '[:space:]';
1481
1482
        return $this->regexReplace("^[$chars]+", '');
1483
    }
1484
1485
    /**
1486
     * Returns a string with whitespace removed from the end of the string.
1487
     * Supports the removal of unicode whitespace. Accepts an optional
1488
     * string of characters to strip instead of the defaults.
1489
     *
1490
     * @param  string  $chars Optional string of characters to strip
1491
     * @return Stringy Object with a trimmed $str
1492
     */
1493
    public function trimRight($chars = null)
1494
    {
1495
        $chars = ($chars) ? preg_quote($chars) : '[:space:]';
1496
1497
        return $this->regexReplace("[$chars]+\$", '');
1498
    }
1499
1500
    /**
1501
     * Truncates the string to a given length. If $substring is provided, and
1502
     * truncating occurs, the string is further truncated so that the substring
1503
     * may be appended without exceeding the desired length.
1504
     *
1505
     * @param  int     $length    Desired length of the truncated string
1506
     * @param  string  $substring The substring to append if it can fit
1507
     * @return Stringy Object with the resulting $str after truncating
1508
     */
1509 View Code Duplication
    public function truncate($length, $substring = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
1510
    {
1511
        $stringy = static::create($this->str, $this->encoding);
1512
        if ($length >= $stringy->length()) {
1513
            return $stringy;
1514
        }
1515
1516
        // Need to further trim the string so we can append the substring
1517
        $substringLength = mb_strlen($substring, $stringy->encoding);
1518
        $length = $length - $substringLength;
1519
1520
        $truncated = mb_substr($stringy->str, 0, $length, $stringy->encoding);
1521
        $stringy->str = $truncated . $substring;
1522
1523
        return $stringy;
1524
    }
1525
1526
    /**
1527
     * Returns a lowercase and trimmed string separated by underscores.
1528
     * Underscores are inserted before uppercase characters (with the exception
1529
     * of the first character of the string), and in place of spaces as well as
1530
     * dashes.
1531
     *
1532
     * @return Stringy Object with an underscored $str
1533
     */
1534
    public function underscored()
1535
    {
1536
        return $this->delimit('_');
1537
    }
1538
1539
    /**
1540
     * Returns an UpperCamelCase version of the supplied string. It trims
1541
     * surrounding spaces, capitalizes letters following digits, spaces, dashes
1542
     * and underscores, and removes spaces, dashes, underscores.
1543
     *
1544
     * @return Stringy Object with $str in UpperCamelCase
1545
     */
1546
    public function upperCamelize()
1547
    {
1548
        return $this->camelize()->upperCaseFirst();
1549
    }
1550
1551
    /**
1552
     * Converts the first character of the supplied string to upper case.
1553
     *
1554
     * @return Stringy Object with the first character of $str being upper case
1555
     */
1556 View Code Duplication
    public function upperCaseFirst()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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.

Loading history...
1557
    {
1558
        $first = mb_substr($this->str, 0, 1, $this->encoding);
1559
        $rest = mb_substr($this->str, 1, $this->length() - 1,
1560
            $this->encoding);
1561
1562
        $str = mb_strtoupper($first, $this->encoding) . $rest;
1563
1564
        return static::create($str, $this->encoding);
1565
    }
1566
1567
    /**
1568
     * Returns the replacements for the toAscii() method.
1569
     *
1570
     * @return array An array of replacements.
1571
     */
1572
    protected function charsArray()
1573
    {
1574
        static $charsArray;
1575
        if (isset($charsArray)) return $charsArray;
1576
1577
        return $charsArray = array(
1578
            'a'    => array(
1579
                            'à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ',
1580
                            'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ä', 'ā', 'ą',
1581
                            'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ',
1582
                            'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ',
1583
                            'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ'),
1584
            'b'    => array('б', 'β', 'Ъ', 'Ь', 'ب'),
1585
            'c'    => array('ç', 'ć', 'č', 'ĉ', 'ċ'),
1586
            'd'    => array('ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ',
1587
                            'д', 'δ', 'د', 'ض'),
1588
            'e'    => array('é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ',
1589
                            'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ',
1590
                            'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э',
1591
                            'є', 'ə'),
1592
            'f'    => array('ф', 'φ', 'ف'),
1593
            'g'    => array('ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ج'),
1594
            'h'    => array('ĥ', 'ħ', 'η', 'ή', 'ح', 'ه'),
1595
            'i'    => array('í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į',
1596
                            'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ',
1597
                            'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ',
1598
                            'ῗ', 'і', 'ї', 'и'),
1599
            'j'    => array('ĵ', 'ј', 'Ј'),
1600
            'k'    => array('ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك'),
1601
            'l'    => array('ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل'),
1602
            'm'    => array('м', 'μ', 'م'),
1603
            'n'    => array('ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن'),
1604
            'o'    => array('ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ',
1605
                            'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő',
1606
                            'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό',
1607
                            'ö', 'о', 'و', 'θ'),
1608
            'p'    => array('п', 'π'),
1609
            'r'    => array('ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر'),
1610
            's'    => array('ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص'),
1611
            't'    => array('ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط'),
1612
            'u'    => array('ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ',
1613
                            'ự', 'ü', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у'),
1614
            'v'    => array('в'),
1615
            'w'    => array('ŵ', 'ω', 'ώ'),
1616
            'x'    => array('χ'),
1617
            'y'    => array('ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ',
1618
                            'ϋ', 'ύ', 'ΰ', 'ي'),
1619
            'z'    => array('ź', 'ž', 'ż', 'з', 'ζ', 'ز'),
1620
            'aa'   => array('ع'),
1621
            'ae'   => array('æ'),
1622
            'ch'   => array('ч'),
1623
            'dj'   => array('ђ', 'đ'),
1624
            'dz'   => array('џ'),
1625
            'gh'   => array('غ'),
1626
            'kh'   => array('х', 'خ'),
1627
            'lj'   => array('љ'),
1628
            'nj'   => array('њ'),
1629
            'oe'   => array('œ'),
1630
            'ps'   => array('ψ'),
1631
            'sh'   => array('ш'),
1632
            'shch' => array('щ'),
1633
            'ss'   => array('ß'),
1634
            'th'   => array('þ', 'ث', 'ذ', 'ظ'),
1635
            'ts'   => array('ц'),
1636
            'ya'   => array('я'),
1637
            'yu'   => array('ю'),
1638
            'zh'   => array('ж'),
1639
            '(c)'  => array('©'),
1640
            'A'    => array('Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ',
1641
                            'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Ä', 'Å', 'Ā',
1642
                            'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ',
1643
                            'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ',
1644
                            'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А'),
1645
            'B'    => array('Б', 'Β'),
1646
            'C'    => array('Ç','Ć', 'Č', 'Ĉ', 'Ċ'),
1647
            'D'    => array('Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ'),
1648
            'E'    => array('É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ',
1649
                            'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ',
1650
                            'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э',
1651
                            'Є', 'Ə'),
1652
            'F'    => array('Ф', 'Φ'),
1653
            'G'    => array('Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ'),
1654
            'H'    => array('Η', 'Ή'),
1655
            'I'    => array('Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į',
1656
                            'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ',
1657
                            'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї'),
1658
            'K'    => array('К', 'Κ'),
1659
            'L'    => array('Ĺ', 'Ł', 'Л', 'Λ', 'Ļ'),
1660
            'M'    => array('М', 'Μ'),
1661
            'N'    => array('Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν'),
1662
            'O'    => array('Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ',
1663
                            'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ö', 'Ø', 'Ō',
1664
                            'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ',
1665
                            'Ὸ', 'Ό', 'О', 'Θ', 'Ө'),
1666
            'P'    => array('П', 'Π'),
1667
            'R'    => array('Ř', 'Ŕ', 'Р', 'Ρ'),
1668
            'S'    => array('Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ'),
1669
            'T'    => array('Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ'),
1670
            'U'    => array('Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ',
1671
                            'Ự', 'Û', 'Ü', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У'),
1672
            'V'    => array('В'),
1673
            'W'    => array('Ω', 'Ώ'),
1674
            'X'    => array('Χ'),
1675
            'Y'    => array('Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ',
1676
                            'Ы', 'Й', 'Υ', 'Ϋ'),
1677
            'Z'    => array('Ź', 'Ž', 'Ż', 'З', 'Ζ'),
1678
            'AE'   => array('Æ'),
1679
            'CH'   => array('Ч'),
1680
            'DJ'   => array('Ђ'),
1681
            'DZ'   => array('Џ'),
1682
            'KH'   => array('Х'),
1683
            'LJ'   => array('Љ'),
1684
            'NJ'   => array('Њ'),
1685
            'PS'   => array('Ψ'),
1686
            'SH'   => array('Ш'),
1687
            'SHCH' => array('Щ'),
1688
            'SS'   => array('ẞ'),
1689
            'TH'   => array('Þ'),
1690
            'TS'   => array('Ц'),
1691
            'YA'   => array('Я'),
1692
            'YU'   => array('Ю'),
1693
            'ZH'   => array('Ж'),
1694
            ' '    => array("\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81",
1695
                            "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84",
1696
                            "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87",
1697
                            "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A",
1698
                            "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80"),
1699
        );
1700
    }
1701
1702
    /**
1703
     * Adds the specified amount of left and right padding to the given string.
1704
     * The default character used is a space.
1705
     *
1706
     * @param  int     $left   Length of left padding
1707
     * @param  int     $right  Length of right padding
1708
     * @param  string  $padStr String used to pad
1709
     * @return Stringy String with padding applied
1710
     */
1711
    private function applyPadding($left = 0, $right = 0, $padStr = ' ')
1712
    {
1713
        $stringy = static::create($this->str, $this->encoding);
1714
        $length = mb_strlen($padStr, $stringy->encoding);
1715
1716
        $strLength = $stringy->length();
1717
        $paddedLength = $strLength + $left + $right;
1718
1719
        if (!$length || $paddedLength <= $strLength) {
1720
            return $stringy;
1721
        }
1722
1723
        $leftPadding = mb_substr(str_repeat($padStr, ceil($left / $length)), 0,
1724
            $left, $stringy->encoding);
1725
        $rightPadding = mb_substr(str_repeat($padStr, ceil($right / $length)),
1726
            0, $right, $stringy->encoding);
1727
1728
        $stringy->str = $leftPadding . $stringy->str . $rightPadding;
1729
1730
        return $stringy;
1731
    }
1732
1733
    /**
1734
     * Returns true if $str matches the supplied pattern, false otherwise.
1735
     *
1736
     * @param  string $pattern Regex pattern to match against
1737
     * @return bool   Whether or not $str matches the pattern
1738
     */
1739
    private function matchesPattern($pattern)
1740
    {
1741
        $regexEncoding = mb_regex_encoding();
1742
        mb_regex_encoding($this->encoding);
1743
1744
        $match = mb_ereg_match($pattern, $this->str);
1745
        mb_regex_encoding($regexEncoding);
1746
1747
        return $match;
1748
    }
1749
}
1750