Completed
Push — master ( 9f6e5f...b0ab82 )
by Lars
02:20
created

Stringy::containsAny()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 15
Ratio 100 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 5
Bugs 0 Features 1
Metric Value
c 5
b 0
f 1
dl 15
loc 15
ccs 7
cts 7
cp 1
rs 9.2
cc 4
eloc 7
nc 4
nop 2
crap 4
1
<?php
2
3
namespace Stringy;
4
5
use voku\helper\AntiXSS;
6
use voku\helper\URLify;
7
use voku\helper\UTF8;
8
9
/**
10
 * Class Stringy
11
 *
12
 * @package Stringy
13
 */
14
class Stringy implements \Countable, \IteratorAggregate, \ArrayAccess
15
{
16
  /**
17
   * An instance's string.
18
   *
19
   * @var string
20
   */
21
  protected $str;
22
23
  /**
24
   * The string's encoding, which should be one of the mbstring module's
25
   * supported encodings.
26
   *
27
   * @var string
28
   */
29
  protected $encoding;
30
31
  /**
32
   * Initializes a Stringy object and assigns both str and encoding properties
33
   * the supplied values. $str is cast to a string prior to assignment, and if
34
   * $encoding is not specified, it defaults to mb_internal_encoding(). Throws
35
   * an InvalidArgumentException if the first argument is an array or object
36
   * without a __toString method.
37
   *
38
   * @param  mixed  $str      Value to modify, after being cast to string
39
   * @param  string $encoding The character encoding
40
   *
41
   * @throws \InvalidArgumentException if an array or object without a
42
   *         __toString method is passed as the first argument
43
   */
44 956
  public function __construct($str = '', $encoding = null)
45
  {
46 956
    if (is_array($str)) {
47 1
      throw new \InvalidArgumentException(
48 1
          'Passed value cannot be an array'
49
      );
50 955
    } elseif (is_object($str) && !method_exists($str, '__toString')) {
51 1
      throw new \InvalidArgumentException(
52 1
          'Passed object must have a __toString method'
53
      );
54
    }
55
56
57
    // don't throw a notice on PHP 5.3
58 954
    if (!defined('ENT_SUBSTITUTE')) {
59
      define('ENT_SUBSTITUTE', 8);
60
    }
61
62
    // init
63 954
    UTF8::checkForSupport();
64 954
    $this->str = (string)$str;
65
66 954
    if ($encoding) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encoding of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
67 782
      $this->encoding = $encoding;
68
    } else {
69 580
      UTF8::mbstring_loaded();
70 580
      $this->encoding = mb_internal_encoding();
71
    }
72
73 954
    if ($encoding) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encoding of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
74 782
      $this->encoding = $encoding;
75
    } else {
76 580
      $this->encoding = mb_internal_encoding();
77
    }
78 954
  }
79
80
  /**
81
   * Returns the value in $str.
82
   *
83
   * @return string The current value of the $str property
84
   */
85 915
  public function __toString()
86
  {
87 915
    return $this->str;
88
  }
89
90
  /**
91
   * Returns a new string with $string appended.
92
   *
93
   * @param  string $string The string to append
94
   *
95
   * @return Stringy Object with appended $string
96
   */
97 2
  public function append($string)
98
  {
99 2
    return static::create($this->str . $string, $this->encoding);
100
  }
101
102
  /**
103
   * Creates a Stringy object and assigns both str and encoding properties
104
   * the supplied values. $str is cast to a string prior to assignment, and if
105
   * $encoding is not specified, it defaults to mb_internal_encoding(). It
106
   * then returns the initialized object. Throws an InvalidArgumentException
107
   * if the first argument is an array or object without a __toString method.
108
   *
109
   * @param  mixed  $str      Value to modify, after being cast to string
110
   * @param  string $encoding The character encoding
111
   *
112
   * @return Stringy A Stringy object
113
   * @throws \InvalidArgumentException if an array or object without a
114
   *         __toString method is passed as the first argument
115
   */
116 946
  public static function create($str = '', $encoding = null)
117
  {
118 946
    return new static($str, $encoding);
119
  }
120
121
  /**
122
   * Returns the substring between $start and $end, if found, or an empty
123
   * string. An optional offset may be supplied from which to begin the
124
   * search for the start string.
125
   *
126
   * @param  string $start  Delimiter marking the start of the substring
127
   * @param  string $end    Delimiter marketing the end of the substring
128
   * @param  int    $offset Index from which to begin the search
129
   *
130
   * @return Stringy Object whose $str has been converted to an URL slug
131
   */
132 16
  public function between($start, $end, $offset = 0)
133
  {
134 16
    $startIndex = $this->indexOf($start, $offset);
135 16
    if ($startIndex === false) {
136 2
      return static::create('', $this->encoding);
137
    }
138
139 14
    $substrIndex = $startIndex + UTF8::strlen($start, $this->encoding);
140 14
    $endIndex = $this->indexOf($end, $substrIndex);
141 14
    if ($endIndex === false) {
142 2
      return static::create('', $this->encoding);
143
    }
144
145 12
    return $this->substr($substrIndex, $endIndex - $substrIndex);
146
  }
147
148
  /**
149
   * Returns the index of the first occurrence of $needle in the string,
150
   * and false if not found. Accepts an optional offset from which to begin
151
   * the search.
152
   *
153
   * @param  string $needle Substring to look for
154
   * @param  int    $offset Offset from which to search
155
   *
156
   * @return int|bool The occurrence's index if found, otherwise false
157
   */
158 26
  public function indexOf($needle, $offset = 0)
159
  {
160 26
    return UTF8::strpos($this->str, (string)$needle, (int)$offset, $this->encoding);
161
  }
162
163
  /**
164
   * Returns the substring beginning at $start with the specified $length.
165
   * It differs from the UTF8::substr() function in that providing a $length of
166
   * null will return the rest of the string, rather than an empty string.
167
   *
168
   * @param  int $start  Position of the first character to use
169
   * @param  int $length Maximum number of characters used
170
   *
171
   * @return Stringy Object with its $str being the substring
172
   */
173 66
  public function substr($start, $length = null)
174
  {
175 66
    if ($length === null) {
176 21
      $length = $this->length();
177
    }
178
179 66
    $str = UTF8::substr($this->str, $start, $length, $this->encoding);
180
181 66
    return static::create($str, $this->encoding);
182
  }
183
184
  /**
185
   * Returns the length of the string.
186
   *
187
   * @return int The number of characters in $str given the encoding
188
   */
189 247
  public function length()
190
  {
191 247
    return UTF8::strlen($this->str, $this->encoding);
192
  }
193
194
  /**
195
   * Trims the string and replaces consecutive whitespace characters with a
196
   * single space. This includes tabs and newline characters, as well as
197
   * multibyte whitespace such as the thin space and ideographic space.
198
   *
199
   * @return Stringy Object with a trimmed $str and condensed whitespace
200
   */
201 13
  public function collapseWhitespace()
202
  {
203 13
    return $this->regexReplace('[[:space:]]+', ' ')->trim();
204
  }
205
206
  /**
207
   * Returns a string with whitespace removed from the start and end of the
208
   * string. Supports the removal of unicode whitespace. Accepts an optional
209
   * string of characters to strip instead of the defaults.
210
   *
211
   * @param  string $chars Optional string of characters to strip
212
   *
213
   * @return Stringy Object with a trimmed $str
214
   */
215 114 View Code Duplication
  public function trim($chars = null)
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...
216
  {
217 114
    if (!$chars) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $chars of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
218 113
      $chars = '[:space:]';
219
    } else {
220 1
      $chars = preg_quote($chars, '/');
221
    }
222
223 114
    return $this->regexReplace("^[$chars]+|[$chars]+\$", '');
224
  }
225
226
  /**
227
   * Replaces all occurrences of $pattern in $str by $replacement.
228
   *
229
   * @param  string $pattern     The regular expression pattern
230
   * @param  string $replacement The string to replace with
231
   * @param  string $options     Matching conditions to be used
232
   *
233
   * @return Stringy Object with the result2ing $str after the replacements
234
   */
235 202
  public function regexReplace($pattern, $replacement, $options = '')
236
  {
237 202
    if ($options === 'msr') {
238 7
      $options = 'ms';
239
    }
240
241 202
    $str = preg_replace(
242 202
        '/' . $pattern . '/u' . $options,
243
        $replacement,
244 202
        $this->str
245
    );
246
247 202
    return static::create($str, $this->encoding);
248
  }
249
250
  /**
251
   * Returns true if the string contains all $needles, false otherwise. By
252
   * default the comparison is case-sensitive, but can be made insensitive by
253
   * setting $caseSensitive to false.
254
   *
255
   * @param  array $needles       SubStrings to look for
256
   * @param  bool  $caseSensitive Whether or not to enforce case-sensitivity
257
   *
258
   * @return bool   Whether or not $str contains $needle
259
   */
260 43 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...
261
  {
262
    /** @noinspection IsEmptyFunctionUsageInspection */
263 43
    if (empty($needles)) {
264 1
      return false;
265
    }
266
267 42
    foreach ($needles as $needle) {
268 42
      if (!$this->contains($needle, $caseSensitive)) {
269 42
        return false;
270
      }
271
    }
272
273 24
    return true;
274
  }
275
276
  /**
277
   * Returns true if the string contains $needle, false otherwise. By default
278
   * the comparison is case-sensitive, but can be made insensitive by setting
279
   * $caseSensitive to false.
280
   *
281
   * @param  string $needle        Substring to look for
282
   * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
283
   *
284
   * @return bool   Whether or not $str contains $needle
285
   */
286 105
  public function contains($needle, $caseSensitive = true)
287
  {
288 105
    $encoding = $this->encoding;
289
290 105
    if ($caseSensitive) {
291 55
      return (UTF8::strpos($this->str, $needle, 0, $encoding) !== false);
292
    } else {
293 50
      return (UTF8::stripos($this->str, $needle, 0, $encoding) !== false);
294
    }
295
  }
296
297
  /**
298
   * Returns true if the string contains any $needles, false otherwise. By
299
   * default the comparison is case-sensitive, but can be made insensitive by
300
   * setting $caseSensitive to false.
301
   *
302
   * @param  array $needles       SubStrings to look for
303
   * @param  bool  $caseSensitive Whether or not to enforce case-sensitivity
304
   *
305
   * @return bool   Whether or not $str contains $needle
306
   */
307 43 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...
308
  {
309
    /** @noinspection IsEmptyFunctionUsageInspection */
310 43
    if (empty($needles)) {
311 1
      return false;
312
    }
313
314 42
    foreach ($needles as $needle) {
315 42
      if ($this->contains($needle, $caseSensitive)) {
316 42
        return true;
317
      }
318
    }
319
320 18
    return false;
321
  }
322
323
  /**
324
   * Returns the length of the string, implementing the countable interface.
325
   *
326
   * @return int The number of characters in the string, given the encoding
327
   */
328 1
  public function count()
329
  {
330 1
    return $this->length();
331
  }
332
333
  /**
334
   * Returns the number of occurrences of $substring in the given string.
335
   * By default, the comparison is case-sensitive, but can be made insensitive
336
   * by setting $caseSensitive to false.
337
   *
338
   * @param  string $substring     The substring to search for
339
   * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
340
   *
341
   * @return int    The number of $substring occurrences
342
   */
343 15
  public function countSubstr($substring, $caseSensitive = true)
344
  {
345 15
    if ($caseSensitive) {
346 9
      return UTF8::substr_count($this->str, $substring, $this->encoding);
347
    }
348
349 6
    $str = UTF8::strtoupper($this->str, $this->encoding);
350 6
    $substring = UTF8::strtoupper($substring, $this->encoding);
351
352 6
    return UTF8::substr_count($str, $substring, $this->encoding);
353
  }
354
355
  /**
356
   * Returns a lowercase and trimmed string separated by dashes. Dashes are
357
   * inserted before uppercase characters (with the exception of the first
358
   * character of the string), and in place of spaces as well as underscores.
359
   *
360
   * @return Stringy Object with a dasherized $str
361
   */
362 19
  public function dasherize()
363
  {
364 19
    return $this->delimit('-');
365
  }
366
367
  /**
368
   * Returns a lowercase and trimmed string separated by the given delimiter.
369
   * Delimiters are inserted before uppercase characters (with the exception
370
   * of the first character of the string), and in place of spaces, dashes,
371
   * and underscores. Alpha delimiters are not converted to lowercase.
372
   *
373
   * @param  string $delimiter Sequence used to separate parts of the string
374
   *
375
   * @return Stringy Object with a delimited $str
376
   */
377 49
  public function delimit($delimiter)
378
  {
379 49
    $str = $this->trim();
380
381 49
    $str = preg_replace('/\B([A-Z])/u', '-\1', $str);
382
383 49
    $str = UTF8::strtolower($str, $this->encoding);
0 ignored issues
show
Bug introduced by
It seems like $str can also be of type array<integer,string>; however, voku\helper\UTF8::strtolower() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
384
385 49
    $str = preg_replace('/[-_\s]+/u', $delimiter, $str);
386
387 49
    return static::create($str, $this->encoding);
388
  }
389
390
  /**
391
   * Ensures that the string begins with $substring. If it doesn't, it's
392
   * prepended.
393
   *
394
   * @param  string $substring The substring to add if not present
395
   *
396
   * @return Stringy Object with its $str prefixed by the $substring
397
   */
398 10 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...
399
  {
400 10
    $stringy = static::create($this->str, $this->encoding);
401
402 10
    if (!$stringy->startsWith($substring)) {
403 4
      $stringy->str = $substring . $stringy->str;
404
    }
405
406 10
    return $stringy;
407
  }
408
409
  /**
410
   * Returns true if the string begins with $substring, false otherwise. By
411
   * default, the comparison is case-sensitive, but can be made insensitive
412
   * by setting $caseSensitive to false.
413
   *
414
   * @param  string $substring     The substring to look for
415
   * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
416
   *
417
   * @return bool   Whether or not $str starts with $substring
418
   */
419 33
  public function startsWith($substring, $caseSensitive = true)
420
  {
421 33
    $substringLength = UTF8::strlen($substring, $this->encoding);
422 33
    $startOfStr = UTF8::substr(
423 33
        $this->str, 0, $substringLength,
424 33
        $this->encoding
425
    );
426
427 33 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...
428 4
      $substring = UTF8::strtolower($substring, $this->encoding);
429 4
      $startOfStr = UTF8::strtolower($startOfStr, $this->encoding);
430
    }
431
432 33
    return (string)$substring === $startOfStr;
433
  }
434
435
  /**
436
   * Ensures that the string ends with $substring. If it doesn't, it's
437
   * appended.
438
   *
439
   * @param  string $substring The substring to add if not present
440
   *
441
   * @return Stringy Object with its $str suffixed by the $substring
442
   */
443 10 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...
444
  {
445 10
    $stringy = static::create($this->str, $this->encoding);
446
447 10
    if (!$stringy->endsWith($substring)) {
448 4
      $stringy->str .= $substring;
449
    }
450
451 10
    return $stringy;
452
  }
453
454
  /**
455
   * Returns true if the string ends with $substring, false otherwise. By
456
   * default, the comparison is case-sensitive, but can be made insensitive
457
   * by setting $caseSensitive to false.
458
   *
459
   * @param  string $substring     The substring to look for
460
   * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
461
   *
462
   * @return bool   Whether or not $str ends with $substring
463
   */
464 33
  public function endsWith($substring, $caseSensitive = true)
465
  {
466 33
    $substringLength = UTF8::strlen($substring, $this->encoding);
467 33
    $strLength = $this->length();
468
469 33
    $endOfStr = UTF8::substr(
470 33
        $this->str,
471 33
        $strLength - $substringLength,
472
        $substringLength,
473 33
        $this->encoding
474
    );
475
476 33 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...
477 4
      $substring = UTF8::strtolower($substring, $this->encoding);
478 4
      $endOfStr = UTF8::strtolower($endOfStr, $this->encoding);
479
    }
480
481 33
    return (string)$substring === $endOfStr;
482
  }
483
484
  /**
485
   * Returns the first $n characters of the string.
486
   *
487
   * @param  int $n Number of characters to retrieve from the start
488
   *
489
   * @return Stringy Object with its $str being the first $n chars
490
   */
491 12 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...
492
  {
493 12
    $stringy = static::create($this->str, $this->encoding);
494
495 12
    if ($n < 0) {
496 2
      $stringy->str = '';
497
    } else {
498 10
      return $stringy->substr(0, $n);
499
    }
500
501 2
    return $stringy;
502
  }
503
504
  /**
505
   * Returns the encoding used by the Stringy object.
506
   *
507
   * @return string The current value of the $encoding property
508
   */
509 3
  public function getEncoding()
510
  {
511 3
    return $this->encoding;
512
  }
513
514
  /**
515
   * Returns a new ArrayIterator, thus implementing the IteratorAggregate
516
   * interface. The ArrayIterator's constructor is passed an array of chars
517
   * in the multibyte string. This enables the use of foreach with instances
518
   * of Stringy\Stringy.
519
   *
520
   * @return \ArrayIterator An iterator for the characters in the string
521
   */
522 1
  public function getIterator()
523
  {
524 1
    return new \ArrayIterator($this->chars());
525
  }
526
527
  /**
528
   * Returns an array consisting of the characters in the string.
529
   *
530
   * @return array An array of string chars
531
   */
532 4
  public function chars()
533
  {
534
    // init
535 4
    $chars = array();
536 4
    $l = $this->length();
537
538 4
    for ($i = 0; $i < $l; $i++) {
539 3
      $chars[] = $this->at($i)->str;
540
    }
541
542 4
    return $chars;
543
  }
544
545
  /**
546
   * Returns the character at $index, with indexes starting at 0.
547
   *
548
   * @param  int $index Position of the character
549
   *
550
   * @return Stringy The character at $index
551
   */
552 11
  public function at($index)
553
  {
554 11
    return $this->substr($index, 1);
555
  }
556
557
  /**
558
   * Returns true if the string contains a lower case char, false
559
   * otherwise.
560
   *
561
   * @return bool Whether or not the string contains a lower case character.
562
   */
563 12
  public function hasLowerCase()
564
  {
565 12
    return $this->matchesPattern('.*[[:lower:]]');
566
  }
567
568
  /**
569
   * Returns true if $str matches the supplied pattern, false otherwise.
570
   *
571
   * @param  string $pattern Regex pattern to match against
572
   *
573
   * @return bool   Whether or not $str matches the pattern
574
   */
575 91
  private function matchesPattern($pattern)
576
  {
577 91
    if (preg_match('/' . $pattern . '/u', $this->str)) {
578 54
      return true;
579
    } else {
580 37
      return false;
581
    }
582
  }
583
584
  /**
585
   * Returns true if the string contains an upper case char, false
586
   * otherwise.
587
   *
588
   * @return bool Whether or not the string contains an upper case character.
589
   */
590 12
  public function hasUpperCase()
591
  {
592 12
    return $this->matchesPattern('.*[[:upper:]]');
593
  }
594
595
  /**
596
   * Convert all HTML entities to their applicable characters.
597
   *
598
   * @param  int|null $flags Optional flags
599
   *
600
   * @return Stringy  Object with the resulting $str after being html decoded.
601
   */
602 5
  public function htmlDecode($flags = ENT_COMPAT)
603
  {
604 5
    $str = UTF8::html_entity_decode($this->str, $flags, $this->encoding);
605
606 5
    return static::create($str, $this->encoding);
607
  }
608
609
  /**
610
   * Convert all applicable characters to HTML entities.
611
   *
612
   * @param  int|null $flags Optional flags
613
   *
614
   * @return Stringy  Object with the resulting $str after being html encoded.
615
   */
616 5
  public function htmlEncode($flags = ENT_COMPAT)
617
  {
618 5
    $str = UTF8::htmlentities($this->str, $flags, $this->encoding);
619
620 5
    return static::create($str, $this->encoding);
621
  }
622
623
  /**
624
   * Capitalizes the first word of the string, replaces underscores with
625
   * spaces, and strips '_id'.
626
   *
627
   * @return Stringy Object with a humanized $str
628
   */
629 3
  public function humanize()
630
  {
631 3
    $str = UTF8::str_replace(array('_id', '_'), array('', ' '), $this->str);
632
633 3
    return static::create($str, $this->encoding)->trim()->upperCaseFirst();
634
  }
635
636
  /**
637
   * Converts the first character of the supplied string to upper case.
638
   *
639
   * @return Stringy Object with the first character of $str being upper case
640
   */
641 27 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...
642
  {
643 27
    $first = UTF8::substr($this->str, 0, 1, $this->encoding);
644 27
    $rest = UTF8::substr(
645 27
        $this->str,
646 27
        1,
647 27
        $this->length() - 1,
648 27
        $this->encoding
649
    );
650
651 27
    $str = UTF8::strtoupper($first, $this->encoding) . $rest;
652
653 27
    return static::create($str, $this->encoding);
654
  }
655
656
  /**
657
   * Returns the index of the last occurrence of $needle in the string,
658
   * and false if not found. Accepts an optional offset from which to begin
659
   * the search. Offsets may be negative to count from the last character
660
   * in the string.
661
   *
662
   * @param  string $needle Substring to look for
663
   * @param  int    $offset Offset from which to search
664
   *
665
   * @return int|bool The last occurrence's index if found, otherwise false
666
   */
667 10
  public function indexOfLast($needle, $offset = 0)
668
  {
669 10
    return UTF8::strrpos($this->str, (string)$needle, (int)$offset, $this->encoding);
0 ignored issues
show
Documentation introduced by
$this->encoding is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
670
  }
671
672
  /**
673
   * Inserts $substring into the string at the $index provided.
674
   *
675
   * @param  string $substring String to be inserted
676
   * @param  int    $index     The index at which to insert the substring
677
   *
678
   * @return Stringy Object with the resulting $str after the insertion
679
   */
680 8 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...
681
  {
682 8
    $stringy = static::create($this->str, $this->encoding);
683 8
    if ($index > $stringy->length()) {
684 1
      return $stringy;
685
    }
686
687 7
    $start = UTF8::substr($stringy->str, 0, $index, $stringy->encoding);
688 7
    $end = UTF8::substr(
689 7
        $stringy->str, $index, $stringy->length(),
690 7
        $stringy->encoding
691
    );
692
693 7
    $stringy->str = $start . $substring . $end;
694
695 7
    return $stringy;
696
  }
697
698
  /**
699
   * Returns true if the string contains only alphabetic chars, false
700
   * otherwise.
701
   *
702
   * @return bool Whether or not $str contains only alphabetic chars
703
   */
704 10
  public function isAlpha()
705
  {
706 10
    return $this->matchesPattern('^[[:alpha:]]*$');
707
  }
708
709
  /**
710
   * Returns true if the string contains only alphabetic and numeric chars,
711
   * false otherwise.
712
   *
713
   * @return bool Whether or not $str contains only alphanumeric chars
714
   */
715 13
  public function isAlphanumeric()
716
  {
717 13
    return $this->matchesPattern('^[[:alnum:]]*$');
718
  }
719
720
  /**
721
   * Returns true if the string contains only whitespace chars, false
722
   * otherwise.
723
   *
724
   * @return bool Whether or not $str contains only whitespace characters
725
   */
726 15
  public function isBlank()
727
  {
728 15
    return $this->matchesPattern('^[[:space:]]*$');
729
  }
730
731
  /**
732
   * Returns true if the string contains only hexadecimal chars, false
733
   * otherwise.
734
   *
735
   * @return bool Whether or not $str contains only hexadecimal chars
736
   */
737 13
  public function isHexadecimal()
738
  {
739 13
    return $this->matchesPattern('^[[:xdigit:]]*$');
740
  }
741
742
  /**
743
   * Returns true if the string is JSON, false otherwise. Unlike json_decode
744
   * in PHP 5.x, this method is consistent with PHP 7 and other JSON parsers,
745
   * in that an empty string is not considered valid JSON.
746
   *
747
   * @return bool Whether or not $str is JSON
748
   */
749 20
  public function isJson()
750
  {
751 20
    if (!isset($this->str[0])) {
752 1
      return false;
753
    }
754
755 19
    json_decode($this->str);
756
757 19
    if (json_last_error() === JSON_ERROR_NONE) {
758 11
      return true;
759
    } else {
760 8
      return false;
761
    }
762
  }
763
764
  /**
765
   * Returns true if the string contains only lower case chars, false
766
   * otherwise.
767
   *
768
   * @return bool Whether or not $str contains only lower case characters
769
   */
770 8
  public function isLowerCase()
771
  {
772 8
    if ($this->matchesPattern('^[[:lower:]]*$')) {
773 3
      return true;
774
    } else {
775 5
      return false;
776
    }
777
  }
778
779
  /**
780
   * Returns true if the string is serialized, false otherwise.
781
   *
782
   * @return bool Whether or not $str is serialized
783
   */
784 7
  public function isSerialized()
785
  {
786 7
    if (!isset($this->str[0])) {
787 1
      return false;
788
    }
789
790
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
791
    if (
792 6
        $this->str === 'b:0;'
793
        ||
794 6
        @unserialize($this->str) !== false
795
    ) {
796 4
      return true;
797
    } else {
798 2
      return false;
799
    }
800
  }
801
802
  /**
803
   * Returns true if the string contains only lower case chars, false
804
   * otherwise.
805
   *
806
   * @return bool Whether or not $str contains only lower case characters
807
   */
808 8
  public function isUpperCase()
809
  {
810 8
    return $this->matchesPattern('^[[:upper:]]*$');
811
  }
812
813
  /**
814
   * Returns the last $n characters of the string.
815
   *
816
   * @param  int $n Number of characters to retrieve from the end
817
   *
818
   * @return Stringy Object with its $str being the last $n chars
819
   */
820 12 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...
821
  {
822 12
    $stringy = static::create($this->str, $this->encoding);
823
824 12
    if ($n <= 0) {
825 4
      $stringy->str = '';
826
    } else {
827 8
      return $stringy->substr(-$n);
828
    }
829
830 4
    return $stringy;
831
  }
832
833
  /**
834
   * Splits on newlines and carriage returns, returning an array of Stringy
835
   * objects corresponding to the lines in the string.
836
   *
837
   * @return Stringy[] An array of Stringy objects
838
   */
839 15
  public function lines()
840
  {
841 15
    $array = preg_split('/[\r\n]{1,2}/u', $this->str);
842
    /** @noinspection CallableInLoopTerminationConditionInspection */
843
    /** @noinspection ForeachInvariantsInspection */
844 15 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...
845 15
      $array[$i] = static::create($array[$i], $this->encoding);
846
    }
847
848 15
    return $array;
849
  }
850
851
  /**
852
   * Returns the longest common prefix between the string and $otherStr.
853
   *
854
   * @param  string $otherStr Second string for comparison
855
   *
856
   * @return Stringy Object with its $str being the longest common prefix
857
   */
858 10
  public function longestCommonPrefix($otherStr)
859
  {
860 10
    $encoding = $this->encoding;
861 10
    $maxLength = min($this->length(), UTF8::strlen($otherStr, $encoding));
862
863 10
    $longestCommonPrefix = '';
864 10
    for ($i = 0; $i < $maxLength; $i++) {
865 8
      $char = UTF8::substr($this->str, $i, 1, $encoding);
866
867 8
      if ($char == UTF8::substr($otherStr, $i, 1, $encoding)) {
868 6
        $longestCommonPrefix .= $char;
869
      } else {
870 6
        break;
871
      }
872
    }
873
874 10
    return static::create($longestCommonPrefix, $encoding);
875
  }
876
877
  /**
878
   * Returns the longest common suffix between the string and $otherStr.
879
   *
880
   * @param  string $otherStr Second string for comparison
881
   *
882
   * @return Stringy Object with its $str being the longest common suffix
883
   */
884 10
  public function longestCommonSuffix($otherStr)
885
  {
886 10
    $encoding = $this->encoding;
887 10
    $maxLength = min($this->length(), UTF8::strlen($otherStr, $encoding));
888
889 10
    $longestCommonSuffix = '';
890 10
    for ($i = 1; $i <= $maxLength; $i++) {
891 8
      $char = UTF8::substr($this->str, -$i, 1, $encoding);
892
893 8
      if ($char == UTF8::substr($otherStr, -$i, 1, $encoding)) {
894 6
        $longestCommonSuffix = $char . $longestCommonSuffix;
895
      } else {
896 6
        break;
897
      }
898
    }
899
900 10
    return static::create($longestCommonSuffix, $encoding);
901
  }
902
903
  /**
904
   * Returns the longest common substring between the string and $otherStr.
905
   * In the case of ties, it returns that which occurs first.
906
   *
907
   * @param  string $otherStr Second string for comparison
908
   *
909
   * @return Stringy Object with its $str being the longest common substring
910
   */
911 10
  public function longestCommonSubstring($otherStr)
912
  {
913
    // Uses dynamic programming to solve
914
    // http://en.wikipedia.org/wiki/Longest_common_substring_problem
915 10
    $encoding = $this->encoding;
916 10
    $stringy = static::create($this->str, $encoding);
917 10
    $strLength = $stringy->length();
918 10
    $otherLength = UTF8::strlen($otherStr, $encoding);
919
920
    // Return if either string is empty
921 10
    if ($strLength == 0 || $otherLength == 0) {
922 2
      $stringy->str = '';
923
924 2
      return $stringy;
925
    }
926
927 8
    $len = 0;
928 8
    $end = 0;
929 8
    $table = array_fill(
930 8
        0, $strLength + 1,
931 8
        array_fill(0, $otherLength + 1, 0)
932
    );
933
934 8
    for ($i = 1; $i <= $strLength; $i++) {
935 8
      for ($j = 1; $j <= $otherLength; $j++) {
936 8
        $strChar = UTF8::substr($stringy->str, $i - 1, 1, $encoding);
937 8
        $otherChar = UTF8::substr($otherStr, $j - 1, 1, $encoding);
938
939 8
        if ($strChar == $otherChar) {
940 8
          $table[$i][$j] = $table[$i - 1][$j - 1] + 1;
941 8
          if ($table[$i][$j] > $len) {
942 8
            $len = $table[$i][$j];
943 8
            $end = $i;
944
          }
945
        } else {
946 8
          $table[$i][$j] = 0;
947
        }
948
      }
949
    }
950
951 8
    $stringy->str = UTF8::substr($stringy->str, $end - $len, $len, $encoding);
952
953 8
    return $stringy;
954
  }
955
956
  /**
957
   * Returns whether or not a character exists at an index. Offsets may be
958
   * negative to count from the last character in the string. Implements
959
   * part of the ArrayAccess interface.
960
   *
961
   * @param  mixed $offset The index to check
962
   *
963
   * @return boolean Whether or not the index exists
964
   */
965 6
  public function offsetExists($offset)
966
  {
967
    // init
968 6
    $length = $this->length();
969 6
    $offset = (int)$offset;
970
971 6
    if ($offset >= 0) {
972 3
      return ($length > $offset);
973
    }
974
975 3
    return ($length >= abs($offset));
976
  }
977
978
  /**
979
   * Returns the character at the given index. Offsets may be negative to
980
   * count from the last character in the string. Implements part of the
981
   * ArrayAccess interface, and throws an OutOfBoundsException if the index
982
   * does not exist.
983
   *
984
   * @param  mixed $offset The index from which to retrieve the char
985
   *
986
   * @return string                 The character at the specified index
987
   * @throws \OutOfBoundsException If the positive or negative offset does
988
   *                               not exist
989
   */
990 2
  public function offsetGet($offset)
991
  {
992
    // init
993 2
    $offset = (int)$offset;
994 2
    $length = $this->length();
995
996
    if (
997 2
        ($offset >= 0 && $length <= $offset)
998
        ||
999 2
        $length < abs($offset)
1000
    ) {
1001 1
      throw new \OutOfBoundsException('No character exists at the index');
1002
    }
1003
1004 1
    return UTF8::substr($this->str, $offset, 1, $this->encoding);
1005
  }
1006
1007
  /**
1008
   * Implements part of the ArrayAccess interface, but throws an exception
1009
   * when called. This maintains the immutability of Stringy objects.
1010
   *
1011
   * @param  mixed $offset The index of the character
1012
   * @param  mixed $value  Value to set
1013
   *
1014
   * @throws \Exception When called
1015
   */
1016 1
  public function offsetSet($offset, $value)
1017
  {
1018
    // Stringy is immutable, cannot directly set char
1019 1
    throw new \Exception('Stringy object is immutable, cannot modify char');
1020
  }
1021
1022
  /**
1023
   * Implements part of the ArrayAccess interface, but throws an exception
1024
   * when called. This maintains the immutability of Stringy objects.
1025
   *
1026
   * @param  mixed $offset The index of the character
1027
   *
1028
   * @throws \Exception When called
1029
   */
1030 1
  public function offsetUnset($offset)
1031
  {
1032
    // Don't allow directly modifying the string
1033 1
    throw new \Exception('Stringy object is immutable, cannot unset char');
1034
  }
1035
1036
  /**
1037
   * Pads the string to a given length with $padStr. If length is less than
1038
   * or equal to the length of the string, no padding takes places. The
1039
   * default string used for padding is a space, and the default type (one of
1040
   * 'left', 'right', 'both') is 'right'. Throws an InvalidArgumentException
1041
   * if $padType isn't one of those 3 values.
1042
   *
1043
   * @param  int    $length  Desired string length after padding
1044
   * @param  string $padStr  String used to pad, defaults to space
1045
   * @param  string $padType One of 'left', 'right', 'both'
1046
   *
1047
   * @return Stringy Object with a padded $str
1048
   * @throws \InvalidArgumentException If $padType isn't one of 'right', 'left' or 'both'
1049
   */
1050 13
  public function pad($length, $padStr = ' ', $padType = 'right')
1051
  {
1052 13
    if (!in_array($padType, array('left', 'right', 'both'), true)) {
1053 1
      throw new \InvalidArgumentException(
1054 1
          'Pad expects $padType ' . "to be one of 'left', 'right' or 'both'"
1055
      );
1056
    }
1057
1058
    switch ($padType) {
1059 12
      case 'left':
1060 3
        return $this->padLeft($length, $padStr);
1061 3
      case 'right':
1062 6
        return $this->padRight($length, $padStr);
1063
      default:
1064 3
        return $this->padBoth($length, $padStr);
1065
    }
1066
  }
1067
1068
  /**
1069
   * Returns a new string of a given length such that the beginning of the
1070
   * string is padded. Alias for pad() with a $padType of 'left'.
1071
   *
1072
   * @param  int    $length Desired string length after padding
1073
   * @param  string $padStr String used to pad, defaults to space
1074
   *
1075
   * @return Stringy String with left padding
1076
   */
1077 10
  public function padLeft($length, $padStr = ' ')
1078
  {
1079 10
    return $this->applyPadding($length - $this->length(), 0, $padStr);
1080
  }
1081
1082
  /**
1083
   * Adds the specified amount of left and right padding to the given string.
1084
   * The default character used is a space.
1085
   *
1086
   * @param  int    $left   Length of left padding
1087
   * @param  int    $right  Length of right padding
1088
   * @param  string $padStr String used to pad
1089
   *
1090
   * @return Stringy String with padding applied
1091
   */
1092 37
  private function applyPadding($left = 0, $right = 0, $padStr = ' ')
1093
  {
1094 37
    $stringy = static::create($this->str, $this->encoding);
1095
1096 37
    $length = UTF8::strlen($padStr, $stringy->encoding);
1097
1098 37
    $strLength = $stringy->length();
1099 37
    $paddedLength = $strLength + $left + $right;
1100
1101 37
    if (!$length || $paddedLength <= $strLength) {
1102 3
      return $stringy;
1103
    }
1104
1105 34
    $leftPadding = UTF8::substr(
1106 34
        UTF8::str_repeat(
1107
            $padStr,
1108 34
            ceil($left / $length)
1109
        ),
1110 34
        0,
1111
        $left,
1112 34
        $stringy->encoding
1113
    );
1114
1115 34
    $rightPadding = UTF8::substr(
1116 34
        UTF8::str_repeat(
1117
            $padStr,
1118 34
            ceil($right / $length)
1119
        ),
1120 34
        0,
1121
        $right,
1122 34
        $stringy->encoding
1123
    );
1124
1125 34
    $stringy->str = $leftPadding . $stringy->str . $rightPadding;
1126
1127 34
    return $stringy;
1128
  }
1129
1130
  /**
1131
   * Returns a new string of a given length such that the end of the string
1132
   * is padded. Alias for pad() with a $padType of 'right'.
1133
   *
1134
   * @param  int    $length Desired string length after padding
1135
   * @param  string $padStr String used to pad, defaults to space
1136
   *
1137
   * @return Stringy String with right padding
1138
   */
1139 13
  public function padRight($length, $padStr = ' ')
1140
  {
1141 13
    return $this->applyPadding(0, $length - $this->length(), $padStr);
1142
  }
1143
1144
  /**
1145
   * Returns a new string of a given length such that both sides of the
1146
   * string are padded. Alias for pad() with a $padType of 'both'.
1147
   *
1148
   * @param  int    $length Desired string length after padding
1149
   * @param  string $padStr String used to pad, defaults to space
1150
   *
1151
   * @return Stringy String with padding applied
1152
   */
1153 14
  public function padBoth($length, $padStr = ' ')
1154
  {
1155 14
    $padding = $length - $this->length();
1156
1157 14
    return $this->applyPadding(floor($padding / 2), ceil($padding / 2), $padStr);
1158
  }
1159
1160
  /**
1161
   * Returns a new string starting with $string.
1162
   *
1163
   * @param  string $string The string to append
1164
   *
1165
   * @return Stringy Object with appended $string
1166
   */
1167 2
  public function prepend($string)
1168
  {
1169 2
    return static::create($string . $this->str, $this->encoding);
1170
  }
1171
1172
  /**
1173
   * Returns a new string with the prefix $substring removed, if present.
1174
   *
1175
   * @param  string $substring The prefix to remove
1176
   *
1177
   * @return Stringy Object having a $str without the prefix $substring
1178
   */
1179 12 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...
1180
  {
1181 12
    $stringy = static::create($this->str, $this->encoding);
1182
1183 12
    if ($stringy->startsWith($substring)) {
1184 8
      $substringLength = UTF8::strlen($substring, $stringy->encoding);
1185
1186 8
      return $stringy->substr($substringLength);
1187
    }
1188
1189 4
    return $stringy;
1190
  }
1191
1192
  /**
1193
   * Returns a new string with the suffix $substring removed, if present.
1194
   *
1195
   * @param  string $substring The suffix to remove
1196
   *
1197
   * @return Stringy Object having a $str without the suffix $substring
1198
   */
1199 12 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...
1200
  {
1201 12
    $stringy = static::create($this->str, $this->encoding);
1202
1203 12
    if ($stringy->endsWith($substring)) {
1204 8
      $substringLength = UTF8::strlen($substring, $stringy->encoding);
1205
1206 8
      return $stringy->substr(0, $stringy->length() - $substringLength);
1207
    }
1208
1209 4
    return $stringy;
1210
  }
1211
1212
  /**
1213
   * Returns a repeated string given a multiplier.
1214
   *
1215
   * @param  int $multiplier The number of times to repeat the string
1216
   *
1217
   * @return Stringy Object with a repeated str
1218
   */
1219 7
  public function repeat($multiplier)
1220
  {
1221 7
    $repeated = UTF8::str_repeat($this->str, $multiplier);
1222
1223 7
    return static::create($repeated, $this->encoding);
1224
  }
1225
1226
  /**
1227
   * Replaces all occurrences of $search in $str by $replacement.
1228
   *
1229
   * @param  string $search      The needle to search for
1230
   * @param  string $replacement The string to replace with
1231
   *
1232
   * @return Stringy Object with the resulting $str after the replacements
1233
   */
1234 19
  public function replace($search, $replacement)
1235
  {
1236 19
    return $this->regexReplace(preg_quote($search, '/'), UTF8::str_replace('\\', '\\\\', $replacement));
1237
  }
1238
1239
  /**
1240
   * Replaces all occurrences of $search from the beginning of string with $replacement
1241
   *
1242
   * @param string $search
1243
   * @param string $replacement
1244
   *
1245
   * @return Stringy Object with the resulting $str after the replacements
1246
   */
1247 16
  public function replaceBeginning($search, $replacement)
1248
  {
1249 16
    $str = $this->regexReplace('^' . preg_quote($search, '/'), UTF8::str_replace('\\', '\\\\', $replacement));
1250
1251 16
    return static::create($str, $this->encoding);
1252
  }
1253
1254
  /**
1255
   * Replaces all occurrences of $search from the ending of string with $replacement
1256
   *
1257
   * @param string $search
1258
   * @param string $replacement
1259
   *
1260
   * @return Stringy Object with the resulting $str after the replacements
1261
   */
1262 16
  public function replaceEnding($search, $replacement)
1263
  {
1264 16
    $str = $this->regexReplace(preg_quote($search, '/') . '$', UTF8::str_replace('\\', '\\\\', $replacement));
1265
1266 16
    return static::create($str, $this->encoding);
1267
  }
1268
1269
  /**
1270
   * Returns a reversed string. A multibyte version of strrev().
1271
   *
1272
   * @return Stringy Object with a reversed $str
1273
   */
1274 5
  public function reverse()
1275
  {
1276 5
    $reversed = UTF8::strrev($this->str);
1277
1278 5
    return static::create($reversed, $this->encoding);
1279
  }
1280
1281
  /**
1282
   * Truncates the string to a given length, while ensuring that it does not
1283
   * split words. If $substring is provided, and truncating occurs, the
1284
   * string is further truncated so that the substring may be appended without
1285
   * exceeding the desired length.
1286
   *
1287
   * @param  int    $length    Desired length of the truncated string
1288
   * @param  string $substring The substring to append if it can fit
1289
   *
1290
   * @return Stringy Object with the resulting $str after truncating
1291
   */
1292 22
  public function safeTruncate($length, $substring = '')
1293
  {
1294 22
    $stringy = static::create($this->str, $this->encoding);
1295 22
    if ($length >= $stringy->length()) {
1296 4
      return $stringy;
1297
    }
1298
1299
    // Need to further trim the string so we can append the substring
1300 18
    $encoding = $stringy->encoding;
1301 18
    $substringLength = UTF8::strlen($substring, $encoding);
1302 18
    $length -= $substringLength;
1303
1304 18
    $truncated = UTF8::substr($stringy->str, 0, $length, $encoding);
1305
1306
    // If the last word was truncated
1307 18
    if (UTF8::strpos($stringy->str, ' ', $length - 1, $encoding) != $length) {
1308
      // Find pos of the last occurrence of a space, get up to that
1309 11
      $lastPos = UTF8::strrpos($truncated, ' ', 0, $encoding);
0 ignored issues
show
Documentation introduced by
$encoding is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1310 11
      $truncated = UTF8::substr($truncated, 0, $lastPos, $encoding);
0 ignored issues
show
Bug introduced by
It seems like $lastPos defined by \voku\helper\UTF8::strrp...ted, ' ', 0, $encoding) on line 1309 can also be of type double or false; however, voku\helper\UTF8::substr() does only seem to accept integer|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1311
    }
1312
1313 18
    $stringy->str = $truncated . $substring;
1314
1315 18
    return $stringy;
1316
  }
1317
1318
  /**
1319
   * A multibyte string shuffle function. It returns a string with its
1320
   * characters in random order.
1321
   *
1322
   * @return Stringy Object with a shuffled $str
1323
   */
1324 3
  public function shuffle()
1325
  {
1326 3
    $shuffledStr = UTF8::str_shuffle($this->str);
1327
1328 3
    return static::create($shuffledStr, $this->encoding);
1329
  }
1330
1331
  /**
1332
   * Converts the string into an URL slug. This includes replacing non-ASCII
1333
   * characters with their closest ASCII equivalents, removing remaining
1334
   * non-ASCII and non-alphanumeric characters, and replacing whitespace with
1335
   * $replacement. The replacement defaults to a single dash, and the string
1336
   * is also converted to lowercase.
1337
   *
1338
   * @param string $replacement The string used to replace whitespace
1339
   * @param string $language    The language for the url
1340
   * @param bool   $strToLower  string to lower
1341
   *
1342
   * @return Stringy Object whose $str has been converted to an URL slug
1343
   */
1344 15
  public function slugify($replacement = '-', $language = 'de', $strToLower = true)
1345
  {
1346 15
    $slug = URLify::slug($this->str, $language, $replacement, $strToLower);
1347
1348 15
    return static::create($slug, $this->encoding);
1349
  }
1350
1351
  /**
1352
   * escape html
1353
   *
1354
   * @return Stringy
1355
   */
1356 6
  public function escape()
1357
  {
1358 6
    $str = UTF8::htmlspecialchars(
1359 6
        $this->str,
1360 6
        ENT_QUOTES | ENT_SUBSTITUTE,
1361 6
        $this->encoding
1362
    );
1363
1364 6
    return static::create($str, $this->encoding);
1365
  }
1366
1367
  /**
1368
   * remove xss from html
1369
   *
1370
   * @return Stringy
1371
   */
1372 6
  public function removeXss()
1373
  {
1374 6
    static $antiXss = null;
1375
1376 6
    if ($antiXss === null) {
1377 1
      $antiXss = new AntiXSS();
1378
    }
1379
1380 6
    $str = $antiXss->xss_clean($this->str);
1381
1382 6
    return static::create($str, $this->encoding);
1383
  }
1384
1385
  /**
1386
   * remove html
1387
   *
1388
   * @param $allowableTags
1389
   *
1390
   * @return Stringy
1391
   */
1392 6
  public function removeHtml($allowableTags = null)
1393
  {
1394 6
    $str = UTF8::strip_tags($this->str, $allowableTags);
1395
1396 6
    return static::create($str, $this->encoding);
1397
  }
1398
1399
  /**
1400
   * Returns the substring beginning at $start, and up to, but not including
1401
   * the index specified by $end. If $end is omitted, the function extracts
1402
   * the remaining string. If $end is negative, it is computed from the end
1403
   * of the string.
1404
   *
1405
   * @param  int $start Initial index from which to begin extraction
1406
   * @param  int $end   Optional index at which to end extraction
1407
   *
1408
   * @return Stringy Object with its $str being the extracted substring
1409
   */
1410 18
  public function slice($start, $end = null)
1411
  {
1412 18
    if ($end === null) {
1413 4
      $length = $this->length();
1414 14
    } elseif ($end >= 0 && $end <= $start) {
1415 4
      return static::create('', $this->encoding);
1416 10
    } elseif ($end < 0) {
1417 2
      $length = $this->length() + $end - $start;
1418
    } else {
1419 8
      $length = $end - $start;
1420
    }
1421
1422 14
    $str = UTF8::substr($this->str, $start, $length, $this->encoding);
1423
1424 14
    return static::create($str, $this->encoding);
1425
  }
1426
1427
  /**
1428
   * Splits the string with the provided regular expression, returning an
1429
   * array of Stringy objects. An optional integer $limit will truncate the
1430
   * results.
1431
   *
1432
   * @param  string $pattern The regex with which to split the string
1433
   * @param  int    $limit   Optional maximum number of results to return
1434
   *
1435
   * @return Stringy[] An array of Stringy objects
1436
   */
1437 19
  public function split($pattern, $limit = null)
1438
  {
1439 19
    if ($limit === 0) {
1440 2
      return array();
1441
    }
1442
1443
    // UTF8::split errors when supplied an empty pattern in < PHP 5.4.13
1444
    // and current versions of HHVM (3.8 and below)
1445 17
    if ($pattern === '') {
1446 1
      return array(static::create($this->str, $this->encoding));
1447
    }
1448
1449
    // UTF8::split returns the remaining unsplit string in the last index when
1450
    // supplying a limit
1451 16
    if ($limit > 0) {
1452 8
      $limit += 1;
1453
    } else {
1454 8
      $limit = -1;
1455
    }
1456
1457 16
    $array = preg_split('/' . preg_quote($pattern, '/') . '/u', $this->str, $limit);
1458
1459 16
    if ($limit > 0 && count($array) === $limit) {
1460 4
      array_pop($array);
1461
    }
1462
1463
    /** @noinspection CallableInLoopTerminationConditionInspection */
1464
    /** @noinspection ForeachInvariantsInspection */
1465 16 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...
1466 16
      $array[$i] = static::create($array[$i], $this->encoding);
1467
    }
1468
1469 16
    return $array;
1470
  }
1471
1472
  /**
1473
   * Surrounds $str with the given substring.
1474
   *
1475
   * @param  string $substring The substring to add to both sides
1476
   *
1477
   * @return Stringy Object whose $str had the substring both prepended and
1478
   *                 appended
1479
   */
1480 5
  public function surround($substring)
1481
  {
1482 5
    $str = implode('', array($substring, $this->str, $substring));
1483
1484 5
    return static::create($str, $this->encoding);
1485
  }
1486
1487
  /**
1488
   * Returns a case swapped version of the string.
1489
   *
1490
   * @return Stringy Object whose $str has each character's case swapped
1491
   */
1492 5
  public function swapCase()
1493
  {
1494 5
    $stringy = static::create($this->str, $this->encoding);
1495 5
    $encoding = $stringy->encoding;
1496
1497 5
    $stringy->str = preg_replace_callback(
1498 5
        '/[\S]/u',
1499
        function ($match) use ($encoding) {
1500 5
          $marchToUpper = UTF8::strtoupper($match[0], $encoding);
1501
1502 5
          if ($match[0] == $marchToUpper) {
1503 5
            return UTF8::strtolower($match[0], $encoding);
1504
          } else {
1505 5
            return $marchToUpper;
1506
          }
1507 5
        },
1508 5
        $stringy->str
1509
    );
1510
1511 5
    return $stringy;
1512
  }
1513
1514
  /**
1515
   * Returns a string with smart quotes, ellipsis characters, and dashes from
1516
   * Windows-1252 (commonly used in Word documents) replaced by their ASCII
1517
   * equivalents.
1518
   *
1519
   * @return Stringy Object whose $str has those characters removed
1520
   */
1521 4
  public function tidy()
1522
  {
1523 4
    $str = UTF8::normalize_msword($this->str);
1524
1525 4
    return static::create($str, $this->encoding);
1526
  }
1527
1528
  /**
1529
   * Returns a trimmed string with the first letter of each word capitalized.
1530
   * Also accepts an array, $ignore, allowing you to list words not to be
1531
   * capitalized.
1532
   *
1533
   * @param  array $ignore An array of words not to capitalize
1534
   *
1535
   * @return Stringy Object with a titleized $str
1536
   */
1537 5
  public function titleize($ignore = null)
1538
  {
1539 5
    $stringy = static::create($this->trim(), $this->encoding);
1540 5
    $encoding = $this->encoding;
1541
1542 5
    $stringy->str = preg_replace_callback(
1543 5
        '/([\S]+)/u',
1544
        function ($match) use ($encoding, $ignore) {
1545 5
          if ($ignore && in_array($match[0], $ignore, true)) {
1546 2
            return $match[0];
1547
          } else {
1548 5
            $stringy = new Stringy($match[0], $encoding);
1549
1550 5
            return (string)$stringy->toLowerCase()->upperCaseFirst();
1551
          }
1552 5
        },
1553 5
        $stringy->str
1554
    );
1555
1556 5
    return $stringy;
1557
  }
1558
1559
  /**
1560
   * Converts all characters in the string to lowercase. An alias for PHP's
1561
   * UTF8::strtolower().
1562
   *
1563
   * @return Stringy Object with all characters of $str being lowercase
1564
   */
1565 27
  public function toLowerCase()
1566
  {
1567 27
    $str = UTF8::strtolower($this->str, $this->encoding);
1568
1569 27
    return static::create($str, $this->encoding);
1570
  }
1571
1572
  /**
1573
   * Returns true if the string is base64 encoded, false otherwise.
1574
   *
1575
   * @return bool Whether or not $str is base64 encoded
1576
   */
1577 7
  public function isBase64()
1578
  {
1579
    if (
1580 7
        $this->str !== ''
1581
        &&
1582 7
        base64_encode(base64_decode($this->str, true)) === $this->str
1583
    ) {
1584 4
      return true;
1585
    } else {
1586 3
      return false;
1587
    }
1588
  }
1589
1590
  /**
1591
   * Returns an ASCII version of the string. A set of non-ASCII characters are
1592
   * replaced with their closest ASCII counterparts, and the rest are removed
1593
   * unless instructed otherwise.
1594
   *
1595
   * @return Stringy Object whose $str contains only ASCII characters
1596
   */
1597 16
  public function toAscii()
1598
  {
1599 16
    $str = UTF8::toAscii($this->str);
1600
1601 16
    return static::create($str, $this->encoding);
1602
  }
1603
1604
  /**
1605
   * Returns a boolean representation of the given logical string value.
1606
   * For example, 'true', '1', 'on' and 'yes' will return true. 'false', '0',
1607
   * 'off', and 'no' will return false. In all instances, case is ignored.
1608
   * For other numeric strings, their sign will determine the return value.
1609
   * In addition, blank strings consisting of only whitespace will return
1610
   * false. For all other strings, the return value is a result of a
1611
   * boolean cast.
1612
   *
1613
   * @return bool A boolean value for the string
1614
   */
1615 15
  public function toBoolean()
1616
  {
1617 15
    $key = $this->toLowerCase()->str;
1618
    $map = array(
1619 15
        'true'  => true,
1620
        '1'     => true,
1621
        'on'    => true,
1622
        'yes'   => true,
1623
        'false' => false,
1624
        '0'     => false,
1625
        'off'   => false,
1626
        'no'    => false,
1627
    );
1628
1629 15
    if (array_key_exists($key, $map)) {
1630 10
      return $map[$key];
1631 5
    } elseif (is_numeric($this->str)) {
1632 2
      return ((int)$this->str > 0);
1633
    } else {
1634 3
      return (bool)$this->regexReplace('[[:space:]]', '')->str;
1635
    }
1636
  }
1637
1638
  /**
1639
   * Converts each tab in the string to some number of spaces, as defined by
1640
   * $tabLength. By default, each tab is converted to 4 consecutive spaces.
1641
   *
1642
   * @param  int $tabLength Number of spaces to replace each tab with
1643
   *
1644
   * @return Stringy Object whose $str has had tabs switched to spaces
1645
   */
1646 6 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...
1647
  {
1648 6
    $spaces = UTF8::str_repeat(' ', $tabLength);
1649 6
    $str = UTF8::str_replace("\t", $spaces, $this->str);
1650
1651 6
    return static::create($str, $this->encoding);
1652
  }
1653
1654
  /**
1655
   * Converts each occurrence of some consecutive number of spaces, as
1656
   * defined by $tabLength, to a tab. By default, each 4 consecutive spaces
1657
   * are converted to a tab.
1658
   *
1659
   * @param  int $tabLength Number of spaces to replace with a tab
1660
   *
1661
   * @return Stringy Object whose $str has had spaces switched to tabs
1662
   */
1663 5 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...
1664
  {
1665 5
    $spaces = UTF8::str_repeat(' ', $tabLength);
1666 5
    $str = UTF8::str_replace($spaces, "\t", $this->str);
1667
1668 5
    return static::create($str, $this->encoding);
1669
  }
1670
1671
  /**
1672
   * Converts the first character of each word in the string to uppercase.
1673
   *
1674
   * @return Stringy Object with all characters of $str being title-cased
1675
   */
1676 5
  public function toTitleCase()
1677
  {
1678
    // "mb_convert_case()" used a polyfill from the "UTF8"-Class
1679 5
    $str = mb_convert_case($this->str, MB_CASE_TITLE, $this->encoding);
1680
1681 5
    return static::create($str, $this->encoding);
1682
  }
1683
1684
  /**
1685
   * Converts all characters in the string to uppercase. An alias for PHP's
1686
   * UTF8::strtoupper().
1687
   *
1688
   * @return Stringy Object with all characters of $str being uppercase
1689
   */
1690 5
  public function toUpperCase()
1691
  {
1692 5
    $str = UTF8::strtoupper($this->str, $this->encoding);
1693
1694 5
    return static::create($str, $this->encoding);
1695
  }
1696
1697
  /**
1698
   * Returns a string with whitespace removed from the start of the string.
1699
   * Supports the removal of unicode whitespace. Accepts an optional
1700
   * string of characters to strip instead of the defaults.
1701
   *
1702
   * @param  string $chars Optional string of characters to strip
1703
   *
1704
   * @return Stringy Object with a trimmed $str
1705
   */
1706 13 View Code Duplication
  public function trimLeft($chars = null)
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...
1707
  {
1708 13
    if (!$chars) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $chars of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1709 11
      $chars = '[:space:]';
1710
    } else {
1711 2
      $chars = preg_quote($chars, '/');
1712
    }
1713
1714 13
    return $this->regexReplace("^[$chars]+", '');
1715
  }
1716
1717
  /**
1718
   * Returns a string with whitespace removed from the end of the string.
1719
   * Supports the removal of unicode whitespace. Accepts an optional
1720
   * string of characters to strip instead of the defaults.
1721
   *
1722
   * @param  string $chars Optional string of characters to strip
1723
   *
1724
   * @return Stringy Object with a trimmed $str
1725
   */
1726 13 View Code Duplication
  public function trimRight($chars = null)
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...
1727
  {
1728 13
    if (!$chars) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $chars of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1729 11
      $chars = '[:space:]';
1730
    } else {
1731 2
      $chars = preg_quote($chars, '/');
1732
    }
1733
1734 13
    return $this->regexReplace("[$chars]+\$", '');
1735
  }
1736
1737
  /**
1738
   * Truncates the string to a given length. If $substring is provided, and
1739
   * truncating occurs, the string is further truncated so that the substring
1740
   * may be appended without exceeding the desired length.
1741
   *
1742
   * @param  int    $length    Desired length of the truncated string
1743
   * @param  string $substring The substring to append if it can fit
1744
   *
1745
   * @return Stringy Object with the resulting $str after truncating
1746
   */
1747 22 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...
1748
  {
1749 22
    $stringy = static::create($this->str, $this->encoding);
1750 22
    if ($length >= $stringy->length()) {
1751 4
      return $stringy;
1752
    }
1753
1754
    // Need to further trim the string so we can append the substring
1755 18
    $substringLength = UTF8::strlen($substring, $stringy->encoding);
1756 18
    $length -= $substringLength;
1757
1758 18
    $truncated = UTF8::substr($stringy->str, 0, $length, $stringy->encoding);
1759 18
    $stringy->str = $truncated . $substring;
1760
1761 18
    return $stringy;
1762
  }
1763
1764
  /**
1765
   * Returns a lowercase and trimmed string separated by underscores.
1766
   * Underscores are inserted before uppercase characters (with the exception
1767
   * of the first character of the string), and in place of spaces as well as
1768
   * dashes.
1769
   *
1770
   * @return Stringy Object with an underscored $str
1771
   */
1772 16
  public function underscored()
1773
  {
1774 16
    return $this->delimit('_');
1775
  }
1776
1777
  /**
1778
   * Returns an UpperCamelCase version of the supplied string. It trims
1779
   * surrounding spaces, capitalizes letters following digits, spaces, dashes
1780
   * and underscores, and removes spaces, dashes, underscores.
1781
   *
1782
   * @return Stringy Object with $str in UpperCamelCase
1783
   */
1784 13
  public function upperCamelize()
1785
  {
1786 13
    return $this->camelize()->upperCaseFirst();
1787
  }
1788
1789
  /**
1790
   * Returns a camelCase version of the string. Trims surrounding spaces,
1791
   * capitalizes letters following digits, spaces, dashes and underscores,
1792
   * and removes spaces, dashes, as well as underscores.
1793
   *
1794
   * @return Stringy Object with $str in camelCase
1795
   */
1796 32
  public function camelize()
1797
  {
1798 32
    $encoding = $this->encoding;
1799 32
    $stringy = $this->trim()->lowerCaseFirst();
1800 32
    $stringy->str = preg_replace('/^[-_]+/', '', $stringy->str);
1801
1802 32
    $stringy->str = preg_replace_callback(
1803 32
        '/[-_\s]+(.)?/u',
1804
        function ($match) use ($encoding) {
1805 27
          if (isset($match[1])) {
1806 27
            return UTF8::strtoupper($match[1], $encoding);
1807
          } else {
1808 1
            return '';
1809
          }
1810 32
        },
1811 32
        $stringy->str
1812
    );
1813
1814 32
    $stringy->str = preg_replace_callback(
1815 32
        '/[\d]+(.)?/u',
1816
        function ($match) use ($encoding) {
1817 6
          return UTF8::strtoupper($match[0], $encoding);
1818 32
        },
1819 32
        $stringy->str
1820
    );
1821
1822 32
    return $stringy;
1823
  }
1824
1825
  /**
1826
   * Convert a string to e.g.: "snake_case"
1827
   *
1828
   * @return Stringy Object with $str in snake_case
1829
   */
1830 20
  public function snakeize()
1831
  {
1832 20
    $str = $this->str;
1833
1834 20
    $str = UTF8::normalize_whitespace($str);
1835 20
    $str = str_replace('-', '_', $str);
1836
1837 20
    $str = preg_replace_callback(
1838 20
        '/([\d|A-Z])/u',
1839 20
        function ($matches) {
1840 8
          $match = $matches[1];
1841 8
          $matchInt = (int)$match;
1842
1843 8
          if ("$matchInt" == $match) {
1844 4
            return '_' . $match . '_';
1845
          } else {
1846 4
            return '_' . UTF8::strtolower($match);
1847
          }
1848 20
        },
1849
        $str
1850
    );
1851
1852 20
    $str = preg_replace(
1853
        array(
1854
1855 20
            '/\s+/',      // convert spaces to "_"
1856
            '/^\s+|\s+$/',  // trim leading & trailing spaces
1857
            '/_+/',         // remove double "_"
1858
        ),
1859
        array(
1860 20
            '_',
1861
            '',
1862
            '_'
1863
        ),
1864
        $str
1865
    );
1866
1867 20
    $str = UTF8::trim($str, '_'); // trim leading & trailing "_"
1868 20
    $str = UTF8::trim($str); // trim leading & trailing whitespace
1869
1870 20
    return static::create($str, $this->encoding);
1871
  }
1872
1873
  /**
1874
   * Converts the first character of the string to lower case.
1875
   *
1876
   * @return Stringy Object with the first character of $str being lower case
1877
   */
1878 37 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...
1879
  {
1880 37
    $first = UTF8::substr($this->str, 0, 1, $this->encoding);
1881 37
    $rest = UTF8::substr(
1882 37
        $this->str, 1, $this->length() - 1,
1883 37
        $this->encoding
1884
    );
1885
1886 37
    $str = UTF8::strtolower($first, $this->encoding) . $rest;
1887
1888 37
    return static::create($str, $this->encoding);
1889
  }
1890
}
1891