Completed
Push — master ( b43fe5...57a879 )
by Lars
04:07 queued 01:07
created

Stringy::at()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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 1011
  public function __construct($str = '', $encoding = null)
45
  {
46 1011
    if (is_array($str)) {
47 1
      throw new \InvalidArgumentException(
48
          'Passed value cannot be an array'
49 1
      );
50 1010
    } elseif (is_object($str) && !method_exists($str, '__toString')) {
51 1
      throw new \InvalidArgumentException(
52
          'Passed object must have a __toString method'
53 1
      );
54
    }
55
56
    // don't throw a notice on PHP 5.3
57 1009
    if (!defined('ENT_SUBSTITUTE')) {
58
      define('ENT_SUBSTITUTE', 8);
59
    }
60
61
    // init
62 1009
    UTF8::checkForSupport();
63 1009
    $this->str = (string)$str;
64
65 1009
    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...
66 798
      $this->encoding = $encoding;
67 798
    } else {
68 641
      UTF8::mbstring_loaded();
69 641
      $this->encoding = mb_internal_encoding();
70
    }
71
72 1009
    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...
73 798
      $this->encoding = $encoding;
74 798
    } else {
75 641
      $this->encoding = mb_internal_encoding();
76
    }
77 1009
  }
78
79
  /**
80
   * Returns the value in $str.
81
   *
82
   * @return string The current value of the $str property
83
   */
84 970
  public function __toString()
85
  {
86 970
    return $this->str;
87
  }
88
89
  /**
90
   * Returns a new string with $string appended.
91
   *
92
   * @param  string $string The string to append
93
   *
94
   * @return Stringy Object with appended $string
95
   */
96 2
  public function append($string)
97
  {
98 2
    return static::create($this->str . $string, $this->encoding);
99
  }
100
101
  /**
102
   * Creates a Stringy object and assigns both str and encoding properties
103
   * the supplied values. $str is cast to a string prior to assignment, and if
104
   * $encoding is not specified, it defaults to mb_internal_encoding(). It
105
   * then returns the initialized object. Throws an InvalidArgumentException
106
   * if the first argument is an array or object without a __toString method.
107
   *
108
   * @param  mixed  $str      Value to modify, after being cast to string
109
   * @param  string $encoding The character encoding
110
   *
111
   * @return Stringy A Stringy object
112
   * @throws \InvalidArgumentException if an array or object without a
113
   *         __toString method is passed as the first argument
114
   */
115 1001
  public static function create($str = '', $encoding = null)
116
  {
117 1001
    return new static($str, $encoding);
118
  }
119
120
  /**
121
   * Returns the substring between $start and $end, if found, or an empty
122
   * string. An optional offset may be supplied from which to begin the
123
   * search for the start string.
124
   *
125
   * @param  string $start  Delimiter marking the start of the substring
126
   * @param  string $end    Delimiter marketing the end of the substring
127
   * @param  int    $offset Index from which to begin the search
128
   *
129
   * @return Stringy Object whose $str has been converted to an URL slug
130
   */
131 16
  public function between($start, $end, $offset = 0)
132
  {
133 16
    $startIndex = $this->indexOf($start, $offset);
134 16
    if ($startIndex === false) {
135 2
      return static::create('', $this->encoding);
136
    }
137
138 14
    $substrIndex = $startIndex + UTF8::strlen($start, $this->encoding);
139 14
    $endIndex = $this->indexOf($end, $substrIndex);
140 14
    if ($endIndex === false) {
141 2
      return static::create('', $this->encoding);
142
    }
143
144 12
    return $this->substr($substrIndex, $endIndex - $substrIndex);
145
  }
146
147
  /**
148
   * Returns the index of the first occurrence of $needle in the string,
149
   * and false if not found. Accepts an optional offset from which to begin
150
   * the search.
151
   *
152
   * @param  string $needle Substring to look for
153
   * @param  int    $offset Offset from which to search
154
   *
155
   * @return int|bool The occurrence's index if found, otherwise false
156
   */
157 26
  public function indexOf($needle, $offset = 0)
158
  {
159 26
    return UTF8::strpos($this->str, (string)$needle, (int)$offset, $this->encoding);
160
  }
161
162
  /**
163
   * Returns the substring beginning at $start with the specified $length.
164
   * It differs from the UTF8::substr() function in that providing a $length of
165
   * null will return the rest of the string, rather than an empty string.
166
   *
167
   * @param  int $start  Position of the first character to use
168
   * @param  int $length Maximum number of characters used
169
   *
170
   * @return Stringy Object with its $str being the substring
171
   */
172 66
  public function substr($start, $length = null)
173
  {
174 66
    if ($length === null) {
175 21
      $length = $this->length();
176 21
    }
177
178 66
    $str = UTF8::substr($this->str, $start, $length, $this->encoding);
179
180 66
    return static::create($str, $this->encoding);
181
  }
182
183
  /**
184
   * Returns the length of the string.
185
   *
186
   * @return int The number of characters in $str given the encoding
187
   */
188 247
  public function length()
189
  {
190 247
    return UTF8::strlen($this->str, $this->encoding);
191
  }
192
193
  /**
194
   * Trims the string and replaces consecutive whitespace characters with a
195
   * single space. This includes tabs and newline characters, as well as
196
   * multibyte whitespace such as the thin space and ideographic space.
197
   *
198
   * @return Stringy Object with a trimmed $str and condensed whitespace
199
   */
200 13
  public function collapseWhitespace()
201
  {
202 13
    return $this->regexReplace('[[:space:]]+', ' ')->trim();
203
  }
204
205
  /**
206
   * Returns a string with whitespace removed from the start and end of the
207
   * string. Supports the removal of unicode whitespace. Accepts an optional
208
   * string of characters to strip instead of the defaults.
209
   *
210
   * @param  string $chars Optional string of characters to strip
211
   *
212
   * @return Stringy Object with a trimmed $str
213
   */
214 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...
215
  {
216 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...
217 113
      $chars = '[:space:]';
218 113
    } else {
219 1
      $chars = preg_quote($chars, '/');
220
    }
221
222 114
    return $this->regexReplace("^[$chars]+|[$chars]+\$", '');
223
  }
224
225
  /**
226
   * Replaces all occurrences of $pattern in $str by $replacement.
227
   *
228
   * @param  string $pattern     The regular expression pattern
229
   * @param  string $replacement The string to replace with
230
   * @param  string $options     Matching conditions to be used
231
   *
232
   * @return Stringy Object with the result2ing $str after the replacements
233
   */
234 184
  public function regexReplace($pattern, $replacement, $options = '')
235
  {
236 184
    if ($options === 'msr') {
237 8
      $options = 'ms';
238 8
    }
239
240 184
    $str = preg_replace(
241 184
        '/' . $pattern . '/u' . $options,
242 184
        $replacement,
243 184
        $this->str
244 184
    );
245
246 184
    return static::create($str, $this->encoding);
247
  }
248
249
  /**
250
   * Returns true if the string contains all $needles, false otherwise. By
251
   * default the comparison is case-sensitive, but can be made insensitive by
252
   * setting $caseSensitive to false.
253
   *
254
   * @param  array $needles       SubStrings to look for
255
   * @param  bool  $caseSensitive Whether or not to enforce case-sensitivity
256
   *
257
   * @return bool   Whether or not $str contains $needle
258
   */
259 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...
260
  {
261
    /** @noinspection IsEmptyFunctionUsageInspection */
262 43
    if (empty($needles)) {
263 1
      return false;
264
    }
265
266 42
    foreach ($needles as $needle) {
267 42
      if (!$this->contains($needle, $caseSensitive)) {
268 18
        return false;
269
      }
270 24
    }
271
272 24
    return true;
273
  }
274
275
  /**
276
   * Returns true if the string contains $needle, false otherwise. By default
277
   * the comparison is case-sensitive, but can be made insensitive by setting
278
   * $caseSensitive to false.
279
   *
280
   * @param  string $needle        Substring to look for
281
   * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
282
   *
283
   * @return bool   Whether or not $str contains $needle
284
   */
285 105
  public function contains($needle, $caseSensitive = true)
286
  {
287 105
    $encoding = $this->encoding;
288
289 105
    if ($caseSensitive) {
290 55
      return (UTF8::strpos($this->str, $needle, 0, $encoding) !== false);
291
    } else {
292 50
      return (UTF8::stripos($this->str, $needle, 0, $encoding) !== false);
293
    }
294
  }
295
296
  /**
297
   * Returns true if the string contains any $needles, false otherwise. By
298
   * default the comparison is case-sensitive, but can be made insensitive by
299
   * setting $caseSensitive to false.
300
   *
301
   * @param  array $needles       SubStrings to look for
302
   * @param  bool  $caseSensitive Whether or not to enforce case-sensitivity
303
   *
304
   * @return bool   Whether or not $str contains $needle
305
   */
306 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...
307
  {
308
    /** @noinspection IsEmptyFunctionUsageInspection */
309 43
    if (empty($needles)) {
310 1
      return false;
311
    }
312
313 42
    foreach ($needles as $needle) {
314 42
      if ($this->contains($needle, $caseSensitive)) {
315 24
        return true;
316
      }
317 18
    }
318
319 18
    return false;
320
  }
321
322
  /**
323
   * Returns the length of the string, implementing the countable interface.
324
   *
325
   * @return int The number of characters in the string, given the encoding
326
   */
327 1
  public function count()
328
  {
329 1
    return $this->length();
330
  }
331
332
  /**
333
   * Returns the number of occurrences of $substring in the given string.
334
   * By default, the comparison is case-sensitive, but can be made insensitive
335
   * by setting $caseSensitive to false.
336
   *
337
   * @param  string $substring     The substring to search for
338
   * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
339
   *
340
   * @return int    The number of $substring occurrences
341
   */
342 15
  public function countSubstr($substring, $caseSensitive = true)
343
  {
344 15
    if ($caseSensitive) {
345 9
      return UTF8::substr_count($this->str, $substring);
346
    }
347
348 6
    $str = UTF8::strtoupper($this->str, $this->encoding);
349 6
    $substring = UTF8::strtoupper($substring, $this->encoding);
350
351 6
    return UTF8::substr_count($str, $substring);
352
  }
353
354
  /**
355
   * Returns a lowercase and trimmed string separated by dashes. Dashes are
356
   * inserted before uppercase characters (with the exception of the first
357
   * character of the string), and in place of spaces as well as underscores.
358
   *
359
   * @return Stringy Object with a dasherized $str
360
   */
361 19
  public function dasherize()
362
  {
363 19
    return $this->delimit('-');
364
  }
365
366
  /**
367
   * Returns a lowercase and trimmed string separated by the given delimiter.
368
   * Delimiters are inserted before uppercase characters (with the exception
369
   * of the first character of the string), and in place of spaces, dashes,
370
   * and underscores. Alpha delimiters are not converted to lowercase.
371
   *
372
   * @param  string $delimiter Sequence used to separate parts of the string
373
   *
374
   * @return Stringy Object with a delimited $str
375
   */
376 49
  public function delimit($delimiter)
377
  {
378 49
    $str = $this->trim();
379
380 49
    $str = preg_replace('/\B([A-Z])/u', '-\1', $str);
381
382 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...
383
384 49
    $str = preg_replace('/[-_\s]+/u', $delimiter, $str);
385
386 49
    return static::create($str, $this->encoding);
387
  }
388
389
  /**
390
   * Ensures that the string begins with $substring. If it doesn't, it's
391
   * prepended.
392
   *
393
   * @param  string $substring The substring to add if not present
394
   *
395
   * @return Stringy Object with its $str prefixed by the $substring
396
   */
397 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...
398
  {
399 10
    $stringy = static::create($this->str, $this->encoding);
400
401 10
    if (!$stringy->startsWith($substring)) {
402 4
      $stringy->str = $substring . $stringy->str;
403 4
    }
404
405 10
    return $stringy;
406
  }
407
408
  /**
409
   * Returns true if the string begins with $substring, false otherwise. By
410
   * default, the comparison is case-sensitive, but can be made insensitive
411
   * by setting $caseSensitive to false.
412
   *
413
   * @param  string $substring     The substring to look for
414
   * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
415
   *
416
   * @return bool   Whether or not $str starts with $substring
417
   */
418 33
  public function startsWith($substring, $caseSensitive = true)
419
  {
420 33
    $substringLength = UTF8::strlen($substring, $this->encoding);
421 33
    $startOfStr = UTF8::substr(
422 33
        $this->str, 0, $substringLength,
423 33
        $this->encoding
424 33
    );
425
426 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...
427 4
      $substring = UTF8::strtolower($substring, $this->encoding);
428 4
      $startOfStr = UTF8::strtolower($startOfStr, $this->encoding);
429 4
    }
430
431 33
    return (string)$substring === $startOfStr;
432
  }
433
434
  /**
435
   * Ensures that the string ends with $substring. If it doesn't, it's
436
   * appended.
437
   *
438
   * @param  string $substring The substring to add if not present
439
   *
440
   * @return Stringy Object with its $str suffixed by the $substring
441
   */
442 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...
443
  {
444 10
    $stringy = static::create($this->str, $this->encoding);
445
446 10
    if (!$stringy->endsWith($substring)) {
447 4
      $stringy->str .= $substring;
448 4
    }
449
450 10
    return $stringy;
451
  }
452
453
  /**
454
   * Returns true if the string ends with $substring, false otherwise. By
455
   * default, the comparison is case-sensitive, but can be made insensitive
456
   * by setting $caseSensitive to false.
457
   *
458
   * @param  string $substring     The substring to look for
459
   * @param  bool   $caseSensitive Whether or not to enforce case-sensitivity
460
   *
461
   * @return bool   Whether or not $str ends with $substring
462
   */
463 33
  public function endsWith($substring, $caseSensitive = true)
464
  {
465 33
    $substringLength = UTF8::strlen($substring, $this->encoding);
466 33
    $strLength = $this->length();
467
468 33
    $endOfStr = UTF8::substr(
469 33
        $this->str,
470 33
        $strLength - $substringLength,
471 33
        $substringLength,
472 33
        $this->encoding
473 33
    );
474
475 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...
476 4
      $substring = UTF8::strtolower($substring, $this->encoding);
477 4
      $endOfStr = UTF8::strtolower($endOfStr, $this->encoding);
478 4
    }
479
480 33
    return (string)$substring === $endOfStr;
481
  }
482
483
  /**
484
   * Returns the first $n characters of the string.
485
   *
486
   * @param  int $n Number of characters to retrieve from the start
487
   *
488
   * @return Stringy Object with its $str being the first $n chars
489
   */
490 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...
491
  {
492 12
    $stringy = static::create($this->str, $this->encoding);
493
494 12
    if ($n < 0) {
495 2
      $stringy->str = '';
496 2
    } else {
497 10
      return $stringy->substr(0, $n);
498
    }
499
500 2
    return $stringy;
501
  }
502
503
  /**
504
   * Returns the encoding used by the Stringy object.
505
   *
506
   * @return string The current value of the $encoding property
507
   */
508 3
  public function getEncoding()
509
  {
510 3
    return $this->encoding;
511
  }
512
513
  /**
514
   * Returns a new ArrayIterator, thus implementing the IteratorAggregate
515
   * interface. The ArrayIterator's constructor is passed an array of chars
516
   * in the multibyte string. This enables the use of foreach with instances
517
   * of Stringy\Stringy.
518
   *
519
   * @return \ArrayIterator An iterator for the characters in the string
520
   */
521 1
  public function getIterator()
522
  {
523 1
    return new \ArrayIterator($this->chars());
524
  }
525
526
  /**
527
   * Returns an array consisting of the characters in the string.
528
   *
529
   * @return array An array of string chars
530
   */
531 4
  public function chars()
532
  {
533
    // init
534 4
    $chars = array();
535 4
    $l = $this->length();
536
537 4
    for ($i = 0; $i < $l; $i++) {
538 3
      $chars[] = $this->at($i)->str;
539 3
    }
540
541 4
    return $chars;
542
  }
543
544
  /**
545
   * Returns the character at $index, with indexes starting at 0.
546
   *
547
   * @param  int $index Position of the character
548
   *
549
   * @return Stringy The character at $index
550
   */
551 11
  public function at($index)
552
  {
553 11
    return $this->substr($index, 1);
554
  }
555
556
  /**
557
   * Returns true if the string contains a lower case char, false
558
   * otherwise.
559
   *
560
   * @return bool Whether or not the string contains a lower case character.
561
   */
562 12
  public function hasLowerCase()
563
  {
564 12
    return $this->matchesPattern('.*[[:lower:]]');
565
  }
566
567
  /**
568
   * Returns true if $str matches the supplied pattern, false otherwise.
569
   *
570
   * @param  string $pattern Regex pattern to match against
571
   *
572
   * @return bool   Whether or not $str matches the pattern
573
   */
574 91
  private function matchesPattern($pattern)
575
  {
576 91
    if (preg_match('/' . $pattern . '/u', $this->str)) {
577 54
      return true;
578
    } else {
579 37
      return false;
580
    }
581
  }
582
583
  /**
584
   * Returns true if the string contains an upper case char, false
585
   * otherwise.
586
   *
587
   * @return bool Whether or not the string contains an upper case character.
588
   */
589 12
  public function hasUpperCase()
590
  {
591 12
    return $this->matchesPattern('.*[[:upper:]]');
592
  }
593
594
  /**
595
   * Convert all HTML entities to their applicable characters.
596
   *
597
   * @param  int|null $flags Optional flags
598
   *
599
   * @return Stringy  Object with the resulting $str after being html decoded.
600
   */
601 5
  public function htmlDecode($flags = ENT_COMPAT)
602
  {
603 5
    $str = UTF8::html_entity_decode($this->str, $flags, $this->encoding);
604
605 5
    return static::create($str, $this->encoding);
606
  }
607
608
  /**
609
   * Convert all applicable characters to HTML entities.
610
   *
611
   * @param  int|null $flags Optional flags
612
   *
613
   * @return Stringy  Object with the resulting $str after being html encoded.
614
   */
615 5
  public function htmlEncode($flags = ENT_COMPAT)
616
  {
617 5
    $str = UTF8::htmlentities($this->str, $flags, $this->encoding);
618
619 5
    return static::create($str, $this->encoding);
620
  }
621
622
  /**
623
   * Capitalizes the first word of the string, replaces underscores with
624
   * spaces, and strips '_id'.
625
   *
626
   * @return Stringy Object with a humanized $str
627
   */
628 3
  public function humanize()
629
  {
630 3
    $str = UTF8::str_replace(array('_id', '_'), array('', ' '), $this->str);
631
632 3
    return static::create($str, $this->encoding)->trim()->upperCaseFirst();
633
  }
634
635
  /**
636
   * Converts the first character of the supplied string to upper case.
637
   *
638
   * @return Stringy Object with the first character of $str being upper case
639
   */
640 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...
641
  {
642 27
    $first = UTF8::substr($this->str, 0, 1, $this->encoding);
643 27
    $rest = UTF8::substr(
644 27
        $this->str,
645 27
        1,
646 27
        $this->length() - 1,
647 27
        $this->encoding
648 27
    );
649
650 27
    $str = UTF8::strtoupper($first, $this->encoding) . $rest;
651
652 27
    return static::create($str, $this->encoding);
653
  }
654
655
  /**
656
   * Returns the index of the last occurrence of $needle in the string,
657
   * and false if not found. Accepts an optional offset from which to begin
658
   * the search. Offsets may be negative to count from the last character
659
   * in the string.
660
   *
661
   * @param  string $needle Substring to look for
662
   * @param  int    $offset Offset from which to search
663
   *
664
   * @return int|bool The last occurrence's index if found, otherwise false
665
   */
666 10
  public function indexOfLast($needle, $offset = 0)
667
  {
668 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...
669
  }
670
671
  /**
672
   * Inserts $substring into the string at the $index provided.
673
   *
674
   * @param  string $substring String to be inserted
675
   * @param  int    $index     The index at which to insert the substring
676
   *
677
   * @return Stringy Object with the resulting $str after the insertion
678
   */
679 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...
680
  {
681 8
    $stringy = static::create($this->str, $this->encoding);
682 8
    if ($index > $stringy->length()) {
683 1
      return $stringy;
684
    }
685
686 7
    $start = UTF8::substr($stringy->str, 0, $index, $stringy->encoding);
687 7
    $end = UTF8::substr(
688 7
        $stringy->str, $index, $stringy->length(),
689 7
        $stringy->encoding
690 7
    );
691
692 7
    $stringy->str = $start . $substring . $end;
693
694 7
    return $stringy;
695
  }
696
697
  /**
698
   * Returns true if the string contains only alphabetic chars, false
699
   * otherwise.
700
   *
701
   * @return bool Whether or not $str contains only alphabetic chars
702
   */
703 10
  public function isAlpha()
704
  {
705 10
    return $this->matchesPattern('^[[:alpha:]]*$');
706
  }
707
708
  /**
709
   * Returns true if the string contains only alphabetic and numeric chars,
710
   * false otherwise.
711
   *
712
   * @return bool Whether or not $str contains only alphanumeric chars
713
   */
714 13
  public function isAlphanumeric()
715
  {
716 13
    return $this->matchesPattern('^[[:alnum:]]*$');
717
  }
718
719
  /**
720
   * Returns true if the string contains only whitespace chars, false
721
   * otherwise.
722
   *
723
   * @return bool Whether or not $str contains only whitespace characters
724
   */
725 15
  public function isBlank()
726
  {
727 15
    return $this->matchesPattern('^[[:space:]]*$');
728
  }
729
730
  /**
731
   * Returns true if the string contains only hexadecimal chars, false
732
   * otherwise.
733
   *
734
   * @return bool Whether or not $str contains only hexadecimal chars
735
   */
736 13
  public function isHexadecimal()
737
  {
738 13
    return $this->matchesPattern('^[[:xdigit:]]*$');
739
  }
740
741
  /**
742
   * Returns true if the string is JSON, false otherwise. Unlike json_decode
743
   * in PHP 5.x, this method is consistent with PHP 7 and other JSON parsers,
744
   * in that an empty string is not considered valid JSON.
745
   *
746
   * @return bool Whether or not $str is JSON
747
   */
748 20
  public function isJson()
749
  {
750 20
    if (!isset($this->str[0])) {
751 1
      return false;
752
    }
753
754 19
    json_decode($this->str);
755
756 19
    if (json_last_error() === JSON_ERROR_NONE) {
757 11
      return true;
758
    } else {
759 8
      return false;
760
    }
761
  }
762
763
  /**
764
   * Returns true if the string contains only lower case chars, false
765
   * otherwise.
766
   *
767
   * @return bool Whether or not $str contains only lower case characters
768
   */
769 8
  public function isLowerCase()
770
  {
771 8
    if ($this->matchesPattern('^[[:lower:]]*$')) {
772 3
      return true;
773
    } else {
774 5
      return false;
775
    }
776
  }
777
778
  /**
779
   * Returns true if the string is serialized, false otherwise.
780
   *
781
   * @return bool Whether or not $str is serialized
782
   */
783 7
  public function isSerialized()
784
  {
785 7
    if (!isset($this->str[0])) {
786 1
      return false;
787
    }
788
789
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
790
    if (
791 6
        $this->str === 'b:0;'
792 6
        ||
793 6
        @unserialize($this->str) !== false
794 6
    ) {
795 4
      return true;
796
    } else {
797 2
      return false;
798
    }
799
  }
800
801
  /**
802
   * Returns true if the string contains only lower case chars, false
803
   * otherwise.
804
   *
805
   * @return bool Whether or not $str contains only lower case characters
806
   */
807 8
  public function isUpperCase()
808
  {
809 8
    return $this->matchesPattern('^[[:upper:]]*$');
810
  }
811
812
  /**
813
   * Returns the last $n characters of the string.
814
   *
815
   * @param  int $n Number of characters to retrieve from the end
816
   *
817
   * @return Stringy Object with its $str being the last $n chars
818
   */
819 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...
820
  {
821 12
    $stringy = static::create($this->str, $this->encoding);
822
823 12
    if ($n <= 0) {
824 4
      $stringy->str = '';
825 4
    } else {
826 8
      return $stringy->substr(-$n);
827
    }
828
829 4
    return $stringy;
830
  }
831
832
  /**
833
   * Splits on newlines and carriage returns, returning an array of Stringy
834
   * objects corresponding to the lines in the string.
835
   *
836
   * @return Stringy[] An array of Stringy objects
837
   */
838 15
  public function lines()
839
  {
840 15
    $array = preg_split('/[\r\n]{1,2}/u', $this->str);
841
    /** @noinspection CallableInLoopTerminationConditionInspection */
842
    /** @noinspection ForeachInvariantsInspection */
843 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...
844 15
      $array[$i] = static::create($array[$i], $this->encoding);
845 15
    }
846
847 15
    return $array;
848
  }
849
850
  /**
851
   * Returns the longest common prefix between the string and $otherStr.
852
   *
853
   * @param  string $otherStr Second string for comparison
854
   *
855
   * @return Stringy Object with its $str being the longest common prefix
856
   */
857 10
  public function longestCommonPrefix($otherStr)
858
  {
859 10
    $encoding = $this->encoding;
860 10
    $maxLength = min($this->length(), UTF8::strlen($otherStr, $encoding));
861
862 10
    $longestCommonPrefix = '';
863 10
    for ($i = 0; $i < $maxLength; $i++) {
864 8
      $char = UTF8::substr($this->str, $i, 1, $encoding);
865
866 8
      if ($char == UTF8::substr($otherStr, $i, 1, $encoding)) {
867 6
        $longestCommonPrefix .= $char;
868 6
      } else {
869 6
        break;
870
      }
871 6
    }
872
873 10
    return static::create($longestCommonPrefix, $encoding);
874
  }
875
876
  /**
877
   * Returns the longest common suffix between the string and $otherStr.
878
   *
879
   * @param  string $otherStr Second string for comparison
880
   *
881
   * @return Stringy Object with its $str being the longest common suffix
882
   */
883 10
  public function longestCommonSuffix($otherStr)
884
  {
885 10
    $encoding = $this->encoding;
886 10
    $maxLength = min($this->length(), UTF8::strlen($otherStr, $encoding));
887
888 10
    $longestCommonSuffix = '';
889 10
    for ($i = 1; $i <= $maxLength; $i++) {
890 8
      $char = UTF8::substr($this->str, -$i, 1, $encoding);
891
892 8
      if ($char == UTF8::substr($otherStr, -$i, 1, $encoding)) {
893 6
        $longestCommonSuffix = $char . $longestCommonSuffix;
894 6
      } else {
895 6
        break;
896
      }
897 6
    }
898
899 10
    return static::create($longestCommonSuffix, $encoding);
900
  }
901
902
  /**
903
   * Returns the longest common substring between the string and $otherStr.
904
   * In the case of ties, it returns that which occurs first.
905
   *
906
   * @param  string $otherStr Second string for comparison
907
   *
908
   * @return Stringy Object with its $str being the longest common substring
909
   */
910 10
  public function longestCommonSubstring($otherStr)
911
  {
912
    // Uses dynamic programming to solve
913
    // http://en.wikipedia.org/wiki/Longest_common_substring_problem
914 10
    $encoding = $this->encoding;
915 10
    $stringy = static::create($this->str, $encoding);
916 10
    $strLength = $stringy->length();
917 10
    $otherLength = UTF8::strlen($otherStr, $encoding);
918
919
    // Return if either string is empty
920 10
    if ($strLength == 0 || $otherLength == 0) {
921 2
      $stringy->str = '';
922
923 2
      return $stringy;
924
    }
925
926 8
    $len = 0;
927 8
    $end = 0;
928 8
    $table = array_fill(
929 8
        0, $strLength + 1,
930 8
        array_fill(0, $otherLength + 1, 0)
931 8
    );
932
933 8
    for ($i = 1; $i <= $strLength; $i++) {
934 8
      for ($j = 1; $j <= $otherLength; $j++) {
935 8
        $strChar = UTF8::substr($stringy->str, $i - 1, 1, $encoding);
936 8
        $otherChar = UTF8::substr($otherStr, $j - 1, 1, $encoding);
937
938 8
        if ($strChar == $otherChar) {
939 8
          $table[$i][$j] = $table[$i - 1][$j - 1] + 1;
940 8
          if ($table[$i][$j] > $len) {
941 8
            $len = $table[$i][$j];
942 8
            $end = $i;
943 8
          }
944 8
        } else {
945 8
          $table[$i][$j] = 0;
946
        }
947 8
      }
948 8
    }
949
950 8
    $stringy->str = UTF8::substr($stringy->str, $end - $len, $len, $encoding);
951
952 8
    return $stringy;
953
  }
954
955
  /**
956
   * Returns whether or not a character exists at an index. Offsets may be
957
   * negative to count from the last character in the string. Implements
958
   * part of the ArrayAccess interface.
959
   *
960
   * @param  mixed $offset The index to check
961
   *
962
   * @return boolean Whether or not the index exists
963
   */
964 6
  public function offsetExists($offset)
965
  {
966
    // init
967 6
    $length = $this->length();
968 6
    $offset = (int)$offset;
969
970 6
    if ($offset >= 0) {
971 3
      return ($length > $offset);
972
    }
973
974 3
    return ($length >= abs($offset));
975
  }
976
977
  /**
978
   * Returns the character at the given index. Offsets may be negative to
979
   * count from the last character in the string. Implements part of the
980
   * ArrayAccess interface, and throws an OutOfBoundsException if the index
981
   * does not exist.
982
   *
983
   * @param  mixed $offset The index from which to retrieve the char
984
   *
985
   * @return string                 The character at the specified index
986
   * @throws \OutOfBoundsException If the positive or negative offset does
987
   *                               not exist
988
   */
989 2
  public function offsetGet($offset)
990
  {
991
    // init
992 2
    $offset = (int)$offset;
993 2
    $length = $this->length();
994
995
    if (
996 2
        ($offset >= 0 && $length <= $offset)
997
        ||
998 1
        $length < abs($offset)
999 2
    ) {
1000 1
      throw new \OutOfBoundsException('No character exists at the index');
1001
    }
1002
1003 1
    return UTF8::substr($this->str, $offset, 1, $this->encoding);
1004
  }
1005
1006
  /**
1007
   * Implements part of the ArrayAccess interface, but throws an exception
1008
   * when called. This maintains the immutability of Stringy objects.
1009
   *
1010
   * @param  mixed $offset The index of the character
1011
   * @param  mixed $value  Value to set
1012
   *
1013
   * @throws \Exception When called
1014
   */
1015 1
  public function offsetSet($offset, $value)
1016
  {
1017
    // Stringy is immutable, cannot directly set char
1018 1
    throw new \Exception('Stringy object is immutable, cannot modify char');
1019
  }
1020
1021
  /**
1022
   * Implements part of the ArrayAccess interface, but throws an exception
1023
   * when called. This maintains the immutability of Stringy objects.
1024
   *
1025
   * @param  mixed $offset The index of the character
1026
   *
1027
   * @throws \Exception When called
1028
   */
1029 1
  public function offsetUnset($offset)
1030
  {
1031
    // Don't allow directly modifying the string
1032 1
    throw new \Exception('Stringy object is immutable, cannot unset char');
1033
  }
1034
1035
  /**
1036
   * Pads the string to a given length with $padStr. If length is less than
1037
   * or equal to the length of the string, no padding takes places. The
1038
   * default string used for padding is a space, and the default type (one of
1039
   * 'left', 'right', 'both') is 'right'. Throws an InvalidArgumentException
1040
   * if $padType isn't one of those 3 values.
1041
   *
1042
   * @param  int    $length  Desired string length after padding
1043
   * @param  string $padStr  String used to pad, defaults to space
1044
   * @param  string $padType One of 'left', 'right', 'both'
1045
   *
1046
   * @return Stringy Object with a padded $str
1047
   * @throws \InvalidArgumentException If $padType isn't one of 'right', 'left' or 'both'
1048
   */
1049 13
  public function pad($length, $padStr = ' ', $padType = 'right')
1050
  {
1051 13
    if (!in_array($padType, array('left', 'right', 'both'), true)) {
1052 1
      throw new \InvalidArgumentException(
1053
          'Pad expects $padType ' . "to be one of 'left', 'right' or 'both'"
1054 1
      );
1055
    }
1056
1057
    switch ($padType) {
1058 12
      case 'left':
1059 3
        return $this->padLeft($length, $padStr);
1060 9
      case 'right':
1061 6
        return $this->padRight($length, $padStr);
1062 3
      default:
1063 3
        return $this->padBoth($length, $padStr);
1064 3
    }
1065
  }
1066
1067
  /**
1068
   * Returns a new string of a given length such that the beginning of the
1069
   * string is padded. Alias for pad() with a $padType of 'left'.
1070
   *
1071
   * @param  int    $length Desired string length after padding
1072
   * @param  string $padStr String used to pad, defaults to space
1073
   *
1074
   * @return Stringy String with left padding
1075
   */
1076 10
  public function padLeft($length, $padStr = ' ')
1077
  {
1078 10
    return $this->applyPadding($length - $this->length(), 0, $padStr);
1079
  }
1080
1081
  /**
1082
   * Adds the specified amount of left and right padding to the given string.
1083
   * The default character used is a space.
1084
   *
1085
   * @param  int    $left   Length of left padding
1086
   * @param  int    $right  Length of right padding
1087
   * @param  string $padStr String used to pad
1088
   *
1089
   * @return Stringy String with padding applied
1090
   */
1091 37
  private function applyPadding($left = 0, $right = 0, $padStr = ' ')
1092
  {
1093 37
    $stringy = static::create($this->str, $this->encoding);
1094
1095 37
    $length = UTF8::strlen($padStr, $stringy->encoding);
1096
1097 37
    $strLength = $stringy->length();
1098 37
    $paddedLength = $strLength + $left + $right;
1099
1100 37
    if (!$length || $paddedLength <= $strLength) {
1101 3
      return $stringy;
1102
    }
1103
1104 34
    $leftPadding = UTF8::substr(
1105 34
        UTF8::str_repeat(
1106 34
            $padStr,
1107 34
            ceil($left / $length)
1108 34
        ),
1109 34
        0,
1110 34
        $left,
1111 34
        $stringy->encoding
1112 34
    );
1113
1114 34
    $rightPadding = UTF8::substr(
1115 34
        UTF8::str_repeat(
1116 34
            $padStr,
1117 34
            ceil($right / $length)
1118 34
        ),
1119 34
        0,
1120 34
        $right,
1121 34
        $stringy->encoding
1122 34
    );
1123
1124 34
    $stringy->str = $leftPadding . $stringy->str . $rightPadding;
1125
1126 34
    return $stringy;
1127
  }
1128
1129
  /**
1130
   * Returns a new string of a given length such that the end of the string
1131
   * is padded. Alias for pad() with a $padType of 'right'.
1132
   *
1133
   * @param  int    $length Desired string length after padding
1134
   * @param  string $padStr String used to pad, defaults to space
1135
   *
1136
   * @return Stringy String with right padding
1137
   */
1138 13
  public function padRight($length, $padStr = ' ')
1139
  {
1140 13
    return $this->applyPadding(0, $length - $this->length(), $padStr);
1141
  }
1142
1143
  /**
1144
   * Returns a new string of a given length such that both sides of the
1145
   * string are padded. Alias for pad() with a $padType of 'both'.
1146
   *
1147
   * @param  int    $length Desired string length after padding
1148
   * @param  string $padStr String used to pad, defaults to space
1149
   *
1150
   * @return Stringy String with padding applied
1151
   */
1152 14
  public function padBoth($length, $padStr = ' ')
1153
  {
1154 14
    $padding = $length - $this->length();
1155
1156 14
    return $this->applyPadding(floor($padding / 2), ceil($padding / 2), $padStr);
1157
  }
1158
1159
  /**
1160
   * Returns a new string starting with $string.
1161
   *
1162
   * @param  string $string The string to append
1163
   *
1164
   * @return Stringy Object with appended $string
1165
   */
1166 2
  public function prepend($string)
1167
  {
1168 2
    return static::create($string . $this->str, $this->encoding);
1169
  }
1170
1171
  /**
1172
   * Returns a new string with the prefix $substring removed, if present.
1173
   *
1174
   * @param  string $substring The prefix to remove
1175
   *
1176
   * @return Stringy Object having a $str without the prefix $substring
1177
   */
1178 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...
1179
  {
1180 12
    $stringy = static::create($this->str, $this->encoding);
1181
1182 12
    if ($stringy->startsWith($substring)) {
1183 8
      $substringLength = UTF8::strlen($substring, $stringy->encoding);
1184
1185 8
      return $stringy->substr($substringLength);
1186
    }
1187
1188 4
    return $stringy;
1189
  }
1190
1191
  /**
1192
   * Returns a new string with the suffix $substring removed, if present.
1193
   *
1194
   * @param  string $substring The suffix to remove
1195
   *
1196
   * @return Stringy Object having a $str without the suffix $substring
1197
   */
1198 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...
1199
  {
1200 12
    $stringy = static::create($this->str, $this->encoding);
1201
1202 12
    if ($stringy->endsWith($substring)) {
1203 8
      $substringLength = UTF8::strlen($substring, $stringy->encoding);
1204
1205 8
      return $stringy->substr(0, $stringy->length() - $substringLength);
1206
    }
1207
1208 4
    return $stringy;
1209
  }
1210
1211
  /**
1212
   * Returns a repeated string given a multiplier.
1213
   *
1214
   * @param  int $multiplier The number of times to repeat the string
1215
   *
1216
   * @return Stringy Object with a repeated str
1217
   */
1218 7
  public function repeat($multiplier)
1219
  {
1220 7
    $repeated = UTF8::str_repeat($this->str, $multiplier);
1221
1222 7
    return static::create($repeated, $this->encoding);
1223
  }
1224
1225
  /**
1226
   * Replaces all occurrences of $search in $str by $replacement.
1227
   *
1228
   * @param string $search        The needle to search for
1229
   * @param string $replacement   The string to replace with
1230
   * @param bool   $caseSensitive Whether or not to enforce case-sensitivity
1231
   *
1232
   * @return Stringy Object with the resulting $str after the replacements
1233
   */
1234 28 View Code Duplication
  public function replace($search, $replacement, $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...
1235
  {
1236 28
    if ($caseSensitive) {
1237 21
      $return = UTF8::str_replace($search, $replacement, $this->str);
1238 21
    } else {
1239 7
      $return = UTF8::str_ireplace($search, $replacement, $this->str);
1240
    }
1241
1242 28
    return static::create($return);
1243
  }
1244
1245
  /**
1246
   * Replaces all occurrences of $search in $str by $replacement.
1247
   *
1248
   * @param array        $search        The elements to search for
1249
   * @param string|array $replacement   The string to replace with
1250
   * @param bool         $caseSensitive Whether or not to enforce case-sensitivity
1251
   *
1252
   * @return Stringy Object with the resulting $str after the replacements
1253
   */
1254 30 View Code Duplication
  public function replaceAll(array $search, $replacement, $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...
1255
  {
1256 30
    if ($caseSensitive) {
1257 23
      $return = UTF8::str_replace($search, $replacement, $this->str);
1258 23
    } else {
1259 7
      $return = UTF8::str_ireplace($search, $replacement, $this->str);
1260
    }
1261
1262 30
    return static::create($return);
1263
  }
1264
1265
  /**
1266
   * Replaces all occurrences of $search from the beginning of string with $replacement
1267
   *
1268
   * @param string $search
1269
   * @param string $replacement
1270
   *
1271
   * @return Stringy Object with the resulting $str after the replacements
1272
   */
1273 16
  public function replaceBeginning($search, $replacement)
1274
  {
1275 16
    $str = $this->regexReplace('^' . preg_quote($search, '/'), UTF8::str_replace('\\', '\\\\', $replacement));
1276
1277 16
    return static::create($str, $this->encoding);
1278
  }
1279
1280
  /**
1281
   * Replaces all occurrences of $search from the ending of string with $replacement
1282
   *
1283
   * @param string $search
1284
   * @param string $replacement
1285
   *
1286
   * @return Stringy Object with the resulting $str after the replacements
1287
   */
1288 16
  public function replaceEnding($search, $replacement)
1289
  {
1290 16
    $str = $this->regexReplace(preg_quote($search, '/') . '$', UTF8::str_replace('\\', '\\\\', $replacement));
1291
1292 16
    return static::create($str, $this->encoding);
1293
  }
1294
1295
  /**
1296
   * Returns a reversed string. A multibyte version of strrev().
1297
   *
1298
   * @return Stringy Object with a reversed $str
1299
   */
1300 5
  public function reverse()
1301
  {
1302 5
    $reversed = UTF8::strrev($this->str);
1303
1304 5
    return static::create($reversed, $this->encoding);
1305
  }
1306
1307
  /**
1308
   * Truncates the string to a given length, while ensuring that it does not
1309
   * split words. If $substring is provided, and truncating occurs, the
1310
   * string is further truncated so that the substring may be appended without
1311
   * exceeding the desired length.
1312
   *
1313
   * @param  int    $length    Desired length of the truncated string
1314
   * @param  string $substring The substring to append if it can fit
1315
   *
1316
   * @return Stringy Object with the resulting $str after truncating
1317
   */
1318 22
  public function safeTruncate($length, $substring = '')
1319
  {
1320 22
    $stringy = static::create($this->str, $this->encoding);
1321 22
    if ($length >= $stringy->length()) {
1322 4
      return $stringy;
1323
    }
1324
1325
    // Need to further trim the string so we can append the substring
1326 18
    $encoding = $stringy->encoding;
1327 18
    $substringLength = UTF8::strlen($substring, $encoding);
1328 18
    $length -= $substringLength;
1329
1330 18
    $truncated = UTF8::substr($stringy->str, 0, $length, $encoding);
1331
1332
    // If the last word was truncated
1333 18
    if (UTF8::strpos($stringy->str, ' ', $length - 1, $encoding) != $length) {
1334
      // Find pos of the last occurrence of a space, get up to that
1335 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...
1336 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 1335 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...
1337 11
    }
1338
1339 18
    $stringy->str = $truncated . $substring;
1340
1341 18
    return $stringy;
1342
  }
1343
1344
  /**
1345
   * A multibyte string shuffle function. It returns a string with its
1346
   * characters in random order.
1347
   *
1348
   * @return Stringy Object with a shuffled $str
1349
   */
1350 3
  public function shuffle()
1351
  {
1352 3
    $shuffledStr = UTF8::str_shuffle($this->str);
1353
1354 3
    return static::create($shuffledStr, $this->encoding);
1355
  }
1356
1357
  /**
1358
   * Converts the string into an URL slug. This includes replacing non-ASCII
1359
   * characters with their closest ASCII equivalents, removing remaining
1360
   * non-ASCII and non-alphanumeric characters, and replacing whitespace with
1361
   * $replacement. The replacement defaults to a single dash, and the string
1362
   * is also converted to lowercase.
1363
   *
1364
   * @param string $replacement The string used to replace whitespace
1365
   * @param string $language    The language for the url
1366
   * @param bool   $strToLower  string to lower
1367
   *
1368
   * @return Stringy Object whose $str has been converted to an URL slug
1369
   */
1370 15
  public function slugify($replacement = '-', $language = 'de', $strToLower = true)
1371
  {
1372 15
    $slug = URLify::slug($this->str, $language, $replacement, $strToLower);
1373
1374 15
    return static::create($slug, $this->encoding);
1375
  }
1376
1377
  /**
1378
   * Converts the string into an valid UTF-8 string.
1379
   *
1380
   * @return Stringy
1381
   */
1382 1
  public function utf8ify()
1383
  {
1384 1
    return static::create(UTF8::cleanup($this->str));
1385
  }
1386
1387
  /**
1388
   * escape html
1389
   *
1390
   * @return Stringy
1391
   */
1392 6
  public function escape()
1393
  {
1394 6
    $str = UTF8::htmlspecialchars(
1395 6
        $this->str,
1396 6
        ENT_QUOTES | ENT_SUBSTITUTE,
1397 6
        $this->encoding
1398 6
    );
1399
1400 6
    return static::create($str, $this->encoding);
1401
  }
1402
1403
  /**
1404
   * remove xss from html
1405
   *
1406
   * @return Stringy
1407
   */
1408 6
  public function removeXss()
1409
  {
1410 6
    static $antiXss = null;
1411
1412 6
    if ($antiXss === null) {
1413 1
      $antiXss = new AntiXSS();
1414 1
    }
1415
1416 6
    $str = $antiXss->xss_clean($this->str);
1417
1418 6
    return static::create($str, $this->encoding);
1419
  }
1420
1421
  /**
1422
   * remove html-break [br | \r\n | \r | \n | ...]
1423
   *
1424
   * @param string $replacement
1425
   *
1426
   * @return Stringy
1427
   */
1428 6
  public function removeHtmlBreak($replacement = '')
1429
  {
1430 6
    $str = preg_replace('#/\r\n|\r|\n|<br.*/?>#isU', $replacement, $this->str);
1431
1432 6
    return static::create($str, $this->encoding);
1433
  }
1434
1435
  /**
1436
   * remove html
1437
   *
1438
   * @param $allowableTags
1439
   *
1440
   * @return Stringy
1441
   */
1442 6
  public function removeHtml($allowableTags = null)
1443
  {
1444 6
    $str = strip_tags($this->str, $allowableTags);
1445
1446 6
    return static::create($str, $this->encoding);
1447
  }
1448
1449
  /**
1450
   * Returns the substring beginning at $start, and up to, but not including
1451
   * the index specified by $end. If $end is omitted, the function extracts
1452
   * the remaining string. If $end is negative, it is computed from the end
1453
   * of the string.
1454
   *
1455
   * @param  int $start Initial index from which to begin extraction
1456
   * @param  int $end   Optional index at which to end extraction
1457
   *
1458
   * @return Stringy Object with its $str being the extracted substring
1459
   */
1460 18
  public function slice($start, $end = null)
1461
  {
1462 18
    if ($end === null) {
1463 4
      $length = $this->length();
1464 18
    } elseif ($end >= 0 && $end <= $start) {
1465 4
      return static::create('', $this->encoding);
1466 10
    } elseif ($end < 0) {
1467 2
      $length = $this->length() + $end - $start;
1468 2
    } else {
1469 8
      $length = $end - $start;
1470
    }
1471
1472 14
    $str = UTF8::substr($this->str, $start, $length, $this->encoding);
1473
1474 14
    return static::create($str, $this->encoding);
1475
  }
1476
1477
  /**
1478
   * Splits the string with the provided regular expression, returning an
1479
   * array of Stringy objects. An optional integer $limit will truncate the
1480
   * results.
1481
   *
1482
   * @param  string $pattern The regex with which to split the string
1483
   * @param  int    $limit   Optional maximum number of results to return
1484
   *
1485
   * @return Stringy[] An array of Stringy objects
1486
   */
1487 19
  public function split($pattern, $limit = null)
1488
  {
1489 19
    if ($limit === 0) {
1490 2
      return array();
1491
    }
1492
1493
    // UTF8::split errors when supplied an empty pattern in < PHP 5.4.13
1494
    // and current versions of HHVM (3.8 and below)
1495 17
    if ($pattern === '') {
1496 1
      return array(static::create($this->str, $this->encoding));
1497
    }
1498
1499
    // UTF8::split returns the remaining unsplit string in the last index when
1500
    // supplying a limit
1501 16
    if ($limit > 0) {
1502 8
      $limit += 1;
1503 8
    } else {
1504 8
      $limit = -1;
1505
    }
1506
1507 16
    $array = preg_split('/' . preg_quote($pattern, '/') . '/u', $this->str, $limit);
1508
1509 16
    if ($limit > 0 && count($array) === $limit) {
1510 4
      array_pop($array);
1511 4
    }
1512
1513
    /** @noinspection CallableInLoopTerminationConditionInspection */
1514
    /** @noinspection ForeachInvariantsInspection */
1515 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...
1516 16
      $array[$i] = static::create($array[$i], $this->encoding);
1517 16
    }
1518
1519 16
    return $array;
1520
  }
1521
1522
  /**
1523
   * Surrounds $str with the given substring.
1524
   *
1525
   * @param  string $substring The substring to add to both sides
1526
   *
1527
   * @return Stringy Object whose $str had the substring both prepended and
1528
   *                 appended
1529
   */
1530 5
  public function surround($substring)
1531
  {
1532 5
    $str = implode('', array($substring, $this->str, $substring));
1533
1534 5
    return static::create($str, $this->encoding);
1535
  }
1536
1537
  /**
1538
   * Returns a case swapped version of the string.
1539
   *
1540
   * @return Stringy Object whose $str has each character's case swapped
1541
   */
1542 5
  public function swapCase()
1543
  {
1544 5
    $stringy = static::create($this->str, $this->encoding);
1545
1546 5
    $stringy->str = UTF8::swapCase($stringy->str, $stringy->encoding);
1547
1548 5
    return $stringy;
1549
  }
1550
1551
  /**
1552
   * Returns a string with smart quotes, ellipsis characters, and dashes from
1553
   * Windows-1252 (commonly used in Word documents) replaced by their ASCII
1554
   * equivalents.
1555
   *
1556
   * @return Stringy Object whose $str has those characters removed
1557
   */
1558 4
  public function tidy()
1559
  {
1560 4
    $str = UTF8::normalize_msword($this->str);
1561
1562 4
    return static::create($str, $this->encoding);
1563
  }
1564
1565
  /**
1566
   * Returns a trimmed string with the first letter of each word capitalized.
1567
   * Also accepts an array, $ignore, allowing you to list words not to be
1568
   * capitalized.
1569
   *
1570
   * @param  array $ignore An array of words not to capitalize
1571
   *
1572
   * @return Stringy Object with a titleized $str
1573
   */
1574 5
  public function titleize($ignore = null)
1575
  {
1576 5
    $stringy = static::create($this->trim(), $this->encoding);
1577 5
    $encoding = $this->encoding;
1578
1579 5
    $stringy->str = preg_replace_callback(
1580 5
        '/([\S]+)/u',
1581
        function ($match) use ($encoding, $ignore) {
1582 5
          if ($ignore && in_array($match[0], $ignore, true)) {
1583 2
            return $match[0];
1584
          } else {
1585 5
            $stringy = new Stringy($match[0], $encoding);
1586
1587 5
            return (string)$stringy->toLowerCase()->upperCaseFirst();
1588
          }
1589 5
        },
1590 5
        $stringy->str
1591 5
    );
1592
1593 5
    return $stringy;
1594
  }
1595
1596
  /**
1597
   * Converts all characters in the string to lowercase. An alias for PHP's
1598
   * UTF8::strtolower().
1599
   *
1600
   * @return Stringy Object with all characters of $str being lowercase
1601
   */
1602 27
  public function toLowerCase()
1603
  {
1604 27
    $str = UTF8::strtolower($this->str, $this->encoding);
1605
1606 27
    return static::create($str, $this->encoding);
1607
  }
1608
1609
  /**
1610
   * Returns true if the string is base64 encoded, false otherwise.
1611
   *
1612
   * @return bool Whether or not $str is base64 encoded
1613
   */
1614 7
  public function isBase64()
1615
  {
1616 7
    return UTF8::is_base64($this->str);
1617
  }
1618
1619
  /**
1620
   * Returns an ASCII version of the string. A set of non-ASCII characters are
1621
   * replaced with their closest ASCII counterparts, and the rest are removed
1622
   * unless instructed otherwise.
1623
   *
1624
   * @return Stringy Object whose $str contains only ASCII characters
1625
   */
1626 16
  public function toAscii()
1627
  {
1628 16
    $str = UTF8::toAscii($this->str);
1629
1630 16
    return static::create($str, $this->encoding);
1631
  }
1632
1633
  /**
1634
   * Returns a boolean representation of the given logical string value.
1635
   * For example, 'true', '1', 'on' and 'yes' will return true. 'false', '0',
1636
   * 'off', and 'no' will return false. In all instances, case is ignored.
1637
   * For other numeric strings, their sign will determine the return value.
1638
   * In addition, blank strings consisting of only whitespace will return
1639
   * false. For all other strings, the return value is a result of a
1640
   * boolean cast.
1641
   *
1642
   * @return bool A boolean value for the string
1643
   */
1644 15
  public function toBoolean()
1645
  {
1646 15
    $key = $this->toLowerCase()->str;
1647
    $map = array(
1648 15
        'true'  => true,
1649 15
        '1'     => true,
1650 15
        'on'    => true,
1651 15
        'yes'   => true,
1652 15
        'false' => false,
1653 15
        '0'     => false,
1654 15
        'off'   => false,
1655 15
        'no'    => false,
1656 15
    );
1657
1658 15
    if (array_key_exists($key, $map)) {
1659 10
      return $map[$key];
1660 5
    } elseif (is_numeric($this->str)) {
1661 2
      return ((int)$this->str > 0);
1662
    } else {
1663 3
      return (bool)$this->regexReplace('[[:space:]]', '')->str;
1664
    }
1665
  }
1666
1667
  /**
1668
   * Return Stringy object as string, but you can also use (string) for automatically casting the object into a string.
1669
   *
1670
   * @return string
1671
   */
1672 7
  public function toString()
1673
  {
1674 7
    return (string)$this->str;
1675
  }
1676
1677
  /**
1678
   * Converts each tab in the string to some number of spaces, as defined by
1679
   * $tabLength. By default, each tab is converted to 4 consecutive spaces.
1680
   *
1681
   * @param  int $tabLength Number of spaces to replace each tab with
1682
   *
1683
   * @return Stringy Object whose $str has had tabs switched to spaces
1684
   */
1685 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...
1686
  {
1687 6
    $spaces = UTF8::str_repeat(' ', $tabLength);
1688 6
    $str = UTF8::str_replace("\t", $spaces, $this->str);
1689
1690 6
    return static::create($str, $this->encoding);
1691
  }
1692
1693
  /**
1694
   * Converts each occurrence of some consecutive number of spaces, as
1695
   * defined by $tabLength, to a tab. By default, each 4 consecutive spaces
1696
   * are converted to a tab.
1697
   *
1698
   * @param  int $tabLength Number of spaces to replace with a tab
1699
   *
1700
   * @return Stringy Object whose $str has had spaces switched to tabs
1701
   */
1702 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...
1703
  {
1704 5
    $spaces = UTF8::str_repeat(' ', $tabLength);
1705 5
    $str = UTF8::str_replace($spaces, "\t", $this->str);
1706
1707 5
    return static::create($str, $this->encoding);
1708
  }
1709
1710
  /**
1711
   * Converts the first character of each word in the string to uppercase.
1712
   *
1713
   * @return Stringy Object with all characters of $str being title-cased
1714
   */
1715 5
  public function toTitleCase()
1716
  {
1717
    // "mb_convert_case()" used a polyfill from the "UTF8"-Class
1718 5
    $str = mb_convert_case($this->str, MB_CASE_TITLE, $this->encoding);
1719
1720 5
    return static::create($str, $this->encoding);
1721
  }
1722
1723
  /**
1724
   * Converts all characters in the string to uppercase. An alias for PHP's
1725
   * UTF8::strtoupper().
1726
   *
1727
   * @return Stringy Object with all characters of $str being uppercase
1728
   */
1729 5
  public function toUpperCase()
1730
  {
1731 5
    $str = UTF8::strtoupper($this->str, $this->encoding);
1732
1733 5
    return static::create($str, $this->encoding);
1734
  }
1735
1736
  /**
1737
   * Returns a string with whitespace removed from the start of the string.
1738
   * Supports the removal of unicode whitespace. Accepts an optional
1739
   * string of characters to strip instead of the defaults.
1740
   *
1741
   * @param  string $chars Optional string of characters to strip
1742
   *
1743
   * @return Stringy Object with a trimmed $str
1744
   */
1745 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...
1746
  {
1747 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...
1748 11
      $chars = '[:space:]';
1749 11
    } else {
1750 2
      $chars = preg_quote($chars, '/');
1751
    }
1752
1753 13
    return $this->regexReplace("^[$chars]+", '');
1754
  }
1755
1756
  /**
1757
   * Returns a string with whitespace removed from the end of the string.
1758
   * Supports the removal of unicode whitespace. Accepts an optional
1759
   * string of characters to strip instead of the defaults.
1760
   *
1761
   * @param  string $chars Optional string of characters to strip
1762
   *
1763
   * @return Stringy Object with a trimmed $str
1764
   */
1765 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...
1766
  {
1767 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...
1768 11
      $chars = '[:space:]';
1769 11
    } else {
1770 2
      $chars = preg_quote($chars, '/');
1771
    }
1772
1773 13
    return $this->regexReplace("[$chars]+\$", '');
1774
  }
1775
1776
  /**
1777
   * Truncates the string to a given length. If $substring is provided, and
1778
   * truncating occurs, the string is further truncated so that the substring
1779
   * may be appended without exceeding the desired length.
1780
   *
1781
   * @param  int    $length    Desired length of the truncated string
1782
   * @param  string $substring The substring to append if it can fit
1783
   *
1784
   * @return Stringy Object with the resulting $str after truncating
1785
   */
1786 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...
1787
  {
1788 22
    $stringy = static::create($this->str, $this->encoding);
1789 22
    if ($length >= $stringy->length()) {
1790 4
      return $stringy;
1791
    }
1792
1793
    // Need to further trim the string so we can append the substring
1794 18
    $substringLength = UTF8::strlen($substring, $stringy->encoding);
1795 18
    $length -= $substringLength;
1796
1797 18
    $truncated = UTF8::substr($stringy->str, 0, $length, $stringy->encoding);
1798 18
    $stringy->str = $truncated . $substring;
1799
1800 18
    return $stringy;
1801
  }
1802
1803
  /**
1804
   * Returns a lowercase and trimmed string separated by underscores.
1805
   * Underscores are inserted before uppercase characters (with the exception
1806
   * of the first character of the string), and in place of spaces as well as
1807
   * dashes.
1808
   *
1809
   * @return Stringy Object with an underscored $str
1810
   */
1811 16
  public function underscored()
1812
  {
1813 16
    return $this->delimit('_');
1814
  }
1815
1816
  /**
1817
   * Returns an UpperCamelCase version of the supplied string. It trims
1818
   * surrounding spaces, capitalizes letters following digits, spaces, dashes
1819
   * and underscores, and removes spaces, dashes, underscores.
1820
   *
1821
   * @return Stringy Object with $str in UpperCamelCase
1822
   */
1823 13
  public function upperCamelize()
1824
  {
1825 13
    return $this->camelize()->upperCaseFirst();
1826
  }
1827
1828
  /**
1829
   * Returns a camelCase version of the string. Trims surrounding spaces,
1830
   * capitalizes letters following digits, spaces, dashes and underscores,
1831
   * and removes spaces, dashes, as well as underscores.
1832
   *
1833
   * @return Stringy Object with $str in camelCase
1834
   */
1835 32
  public function camelize()
1836
  {
1837 32
    $encoding = $this->encoding;
1838 32
    $stringy = $this->trim()->lowerCaseFirst();
1839 32
    $stringy->str = preg_replace('/^[-_]+/', '', $stringy->str);
1840
1841 32
    $stringy->str = preg_replace_callback(
1842 32
        '/[-_\s]+(.)?/u',
1843
        function ($match) use ($encoding) {
1844 27
          if (isset($match[1])) {
1845 27
            return UTF8::strtoupper($match[1], $encoding);
1846
          } else {
1847 1
            return '';
1848
          }
1849 32
        },
1850 32
        $stringy->str
1851 32
    );
1852
1853 32
    $stringy->str = preg_replace_callback(
1854 32
        '/[\d]+(.)?/u',
1855
        function ($match) use ($encoding) {
1856 6
          return UTF8::strtoupper($match[0], $encoding);
1857 32
        },
1858 32
        $stringy->str
1859 32
    );
1860
1861 32
    return $stringy;
1862
  }
1863
1864
  /**
1865
   * Convert a string to e.g.: "snake_case"
1866
   *
1867
   * @return Stringy Object with $str in snake_case
1868
   */
1869 20
  public function snakeize()
1870
  {
1871 20
    $str = $this->str;
1872
1873 20
    $str = UTF8::normalize_whitespace($str);
1874 20
    $str = str_replace('-', '_', $str);
1875
1876 20
    $str = preg_replace_callback(
1877 20
        '/([\d|A-Z])/u',
1878 20
        function ($matches) {
1879 8
          $match = $matches[1];
1880 8
          $matchInt = (int)$match;
1881
1882 8
          if ("$matchInt" == $match) {
1883 4
            return '_' . $match . '_';
1884
          } else {
1885 4
            return '_' . UTF8::strtolower($match);
1886
          }
1887 20
        },
1888
        $str
1889 20
    );
1890
1891 20
    $str = preg_replace(
1892
        array(
1893
1894 20
            '/\s+/',      // convert spaces to "_"
1895 20
            '/^\s+|\s+$/',  // trim leading & trailing spaces
1896 20
            '/_+/',         // remove double "_"
1897 20
        ),
1898
        array(
1899 20
            '_',
1900 20
            '',
1901 20
            '_',
1902 20
        ),
1903
        $str
1904 20
    );
1905
1906 20
    $str = UTF8::trim($str, '_'); // trim leading & trailing "_"
1907 20
    $str = UTF8::trim($str); // trim leading & trailing whitespace
1908
1909 20
    return static::create($str, $this->encoding);
1910
  }
1911
1912
  /**
1913
   * Converts the first character of the string to lower case.
1914
   *
1915
   * @return Stringy Object with the first character of $str being lower case
1916
   */
1917 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...
1918
  {
1919 37
    $first = UTF8::substr($this->str, 0, 1, $this->encoding);
1920 37
    $rest = UTF8::substr(
1921 37
        $this->str, 1, $this->length() - 1,
1922 37
        $this->encoding
1923 37
    );
1924
1925 37
    $str = UTF8::strtolower($first, $this->encoding) . $rest;
1926
1927 37
    return static::create($str, $this->encoding);
1928
  }
1929
1930
  /**
1931
   * Shorten the string after $length, but also after the next word.
1932
   *
1933
   * @param int    $length
1934
   * @param string $strAddOn
1935
   *
1936
   * @return Stringy
1937
   */
1938 4
  public function shortenAfterWord($length, $strAddOn = '...')
1939
  {
1940 4
    $string = UTF8::str_limit_after_word($this->str, $length, $strAddOn);
1941
1942 4
    return static::create($string);
1943
  }
1944
1945
  /**
1946
   * Line-Wrap the string after $limit, but also after the next word.
1947
   *
1948
   * @param int $limit
1949
   *
1950
   * @return Stringy
1951
   */
1952 1
  public function lineWrapAfterWord($limit)
1953
  {
1954 1
    $strings = preg_split('/\\r\\n|\\r|\\n/', $this->str);
1955
1956 1
    $string = '';
1957 1
    foreach ($strings as $value) {
1958 1
      $string .= wordwrap($value, $limit);
1959 1
      $string .= "\n";
1960 1
    }
1961
1962 1
    return static::create($string);
1963
  }
1964
1965
  /**
1966
   * Remove empty html-tag.
1967
   *
1968
   * e.g.: <tag></tag>
1969
   *
1970
   * @return Stringy
1971
   */
1972 1
  public function stripeEmptyHtmlTags()
1973
  {
1974 1
    $pattern = "/<[^\/>]*>(([\s]?)*|)<\/[^>]*>/i";
1975
1976 1
    return static::create(preg_replace($pattern, '', $this->str));
1977
  }
1978
1979
  /**
1980
   * Remove css media-queries.
1981
   *
1982
   * @return Stringy
1983
   */
1984 1
  public function stripeCssMediaQueries()
1985
  {
1986 1
    $pattern = '#@media\\s+(?:only\\s)?(?:[\\s{\\(]|screen|all)\\s?[^{]+{.*}\\s*}\\s*#misU';
1987 1
    return static::create(preg_replace($pattern, '', $this->str));
1988
  }
1989
}
1990