This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php namespace nyx\utils; |
||
2 | |||
3 | /** |
||
4 | * Str |
||
5 | * |
||
6 | * Helper methods for dealing with strings. The class is based on Laravel, FuelPHP, Patchwork/UTF-8 and a few others. |
||
7 | * Some minor performance-related improvements were made. |
||
8 | * |
||
9 | * Note: Many of the methods can be circumvented by falling back directly to the builtin functions of the mbstring |
||
10 | * extension as long as you don't need the additional layer of abstraction and feel comfortable managing the |
||
11 | * encoding and input parameter validity on your own. |
||
12 | * |
||
13 | * Suggestions: |
||
14 | * If you need an instance-based fluent OO wrapper for strings with similar manipulation capabilities. then |
||
15 | * you should take a look at Stringy {@see https://github.com/danielstjules/Stringy} |
||
16 | * |
||
17 | * Requires: |
||
18 | * - ext-mbstring |
||
19 | * - ext-intl (Normalizer) |
||
20 | * - ext-iconv |
||
21 | * |
||
22 | * @package Nyx\Utils\Strings |
||
23 | * @version 0.1.0 |
||
24 | * @author Michal Chojnacki <[email protected]> |
||
25 | * @copyright 2012-2016 Nyx Dev Team |
||
26 | * @link http://docs.muyo.io/nyx/utils/strings.html |
||
27 | * @todo Add afterFirst/Last, beforeFirst/Last instead of the current after/before? |
||
28 | */ |
||
29 | class Str |
||
30 | { |
||
31 | /** |
||
32 | * The traits of the Str class. |
||
33 | */ |
||
34 | use traits\StaticallyExtendable; |
||
35 | |||
36 | /** |
||
37 | * @var string The default encoding to use when we fail to determine it based on a given string. UTF-8 will |
||
38 | * be used when the above is true and this property is null. |
||
39 | */ |
||
40 | public static $encoding; |
||
41 | |||
42 | /** |
||
43 | * @var bool Whether self::encoding(), used internally by all methods which accept an encoding, |
||
44 | * should attempt to determine the encoding from the string given to it, if it's not |
||
45 | * explicitly specified. Note: This adds (relatively big) overhead to most methods and |
||
46 | * is therefore set to false, since the default of UTF-8 should satisfy most use-cases. |
||
47 | */ |
||
48 | public static $autoDetectEncoding = false; |
||
49 | |||
50 | /** |
||
51 | * @var array The transliteration table used by the toAscii() method once fetched from a file. |
||
52 | */ |
||
53 | protected static $ascii; |
||
54 | |||
55 | /** |
||
56 | * Finds the first occurrence of $needle within $haystack and returns the part of $haystack |
||
57 | * after the $needle (excluding the $needle). |
||
58 | * |
||
59 | * @param string $haystack The string to search in. |
||
60 | * @param string $needle The substring to search for. |
||
61 | * @param bool $strict Whether to use case-sensitive comparisons. True by default. |
||
62 | * @param string|null $encoding The encoding to use. |
||
63 | * @return string The part of $haystack after $needle. |
||
64 | * @throws \RuntimeException Upon failing to find $needle in $haystack at all. |
||
65 | */ |
||
66 | public static function after(string $haystack, string $needle, bool $strict = true, string $encoding = null) : string |
||
67 | { |
||
68 | $encoding = $encoding ?: static::encoding($haystack); |
||
69 | |||
70 | // We're gonna use strstr internally to grab the part of $haystack starting at $needle |
||
71 | // and including the $needle, and then simply remove the starting $needle. |
||
72 | // Note: Removing 1 from the length of $needle since we're using it as start offset |
||
73 | // for mb_substr which is 0-indexed. |
||
74 | return mb_substr(static::partOf($haystack, $needle, $strict, false, $encoding), mb_strlen($needle, $encoding) - 1); |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Finds the first occurrence of $needle within $haystack and returns the part of $haystack |
||
79 | * *before* the $needle. |
||
80 | * |
||
81 | * @param string $haystack The string to search in. |
||
82 | * @param string $needle The substring to search for. |
||
83 | * @param bool $strict Whether to use case-sensitive comparisons. True by default. |
||
84 | * @param string|null $encoding The encoding to use. |
||
85 | * @return string The part of $haystack before $needle. |
||
86 | * @throws \RuntimeException Upon failing to find $needle in $haystack at all. |
||
87 | */ |
||
88 | public static function before(string $haystack, string $needle, bool $strict = true, string $encoding = null) : string |
||
89 | { |
||
90 | return static::partOf($haystack, $needle, $strict, true, $encoding); |
||
91 | } |
||
92 | |||
93 | /** |
||
94 | * Ensures the given $haystack begins with a single instance of the given $needle. For single characters |
||
95 | * use PHP's native ltrim() instead. |
||
96 | * |
||
97 | * @param string $haystack The string to cap. |
||
98 | * @param string $needle The substring to begin with. |
||
99 | * @return string The resulting string. |
||
100 | */ |
||
101 | public static function beginWith(string $haystack, string $needle) : string |
||
102 | { |
||
103 | return $needle . preg_replace('/^(?:'.preg_quote($needle, '/').')+/', '', $haystack); |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Returns the substring of $haystack between $startNeedle and $endNeedle. |
||
108 | * |
||
109 | * If all you need is to return part of a string when the offsets you are looking for are known, |
||
110 | * you should use {@see self::sub()} instead. |
||
111 | * |
||
112 | * @param string $haystack The string to search in. |
||
113 | * @param string $startNeedle The needle marking the start of the substring to return. |
||
114 | * @param string $endNeedle The needle marking the end of the substring to return. |
||
115 | * @param int $offset The 0-based index at which to start the search for the first needle. |
||
116 | * Can be negative, in which case the search will start $offset characters |
||
117 | * from the end of the $haystack. |
||
118 | * @param bool $strict Whether to use strict comparisons when searching for the needles. |
||
119 | * @param string|null $encoding The encoding to use. |
||
120 | * @return string The substring between the needles. |
||
121 | * @throws \InvalidArgumentException When $haystack or either of the needles is an empty string. |
||
122 | * @throws \OutOfBoundsException Upon failing to find either $startNeedle or $endNeedle in $haystack. |
||
123 | */ |
||
124 | public static function between(string $haystack, string $startNeedle, string $endNeedle, int $offset = 0, bool $strict = true, string $encoding = null) : string |
||
125 | { |
||
126 | // Note: We're throwing here because this method will return unpredictable results otherwise. |
||
127 | // If you want to omit $firstNeedle or $secondNeedle, turn to self::after(), self::before() |
||
0 ignored issues
–
show
|
|||
128 | // and self::from() instead. |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
46% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
129 | // We're not validating input args any further than the below, however, as there are a lot of |
||
130 | // error-inducing combinations of invalid input which all will be caught when we attempt |
||
131 | // to actually look for the indices of the needles. Anything else is just way too much overhead. |
||
132 | if ($haystack === '' || $startNeedle === '' || $endNeedle === '') { |
||
133 | $endNeedle === '' && $arg = '$endNeedle'; |
||
134 | $startNeedle === '' && $arg = '$startNeedle'; |
||
135 | $haystack === '' && $arg = '$haystack'; |
||
136 | |||
137 | throw new \InvalidArgumentException($arg.' must not be an empty string.'); |
||
138 | } |
||
139 | |||
140 | $encoding = $encoding ?: static::encoding($haystack); |
||
141 | $funcIndexOf = $strict ? 'mb_strpos' : 'mb_stripos'; |
||
142 | |||
143 | // Find the offset of the first needle. |
||
144 | View Code Duplication | if (false === $firstIndex = $funcIndexOf($haystack, $startNeedle, $offset, $encoding)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
145 | throw new \OutOfBoundsException('Failed to find $startNeedle ['.$startNeedle.'] in $haystack ['.static::truncate($haystack, 20, '...', false, $encoding).'].'); |
||
146 | } |
||
147 | |||
148 | // We're going to adjust the offset for the position of the first needle, ie. we're gonna |
||
149 | // start searching for the second one right at the end of the first one. |
||
150 | $offset = $firstIndex + mb_strlen($startNeedle, $encoding); |
||
151 | |||
152 | // Find the offset of the second needle. |
||
153 | View Code Duplication | if (false === $secondIndex = $funcIndexOf($haystack, $endNeedle, $offset, $encoding)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
154 | throw new \OutOfBoundsException('Failed to find $endNeedle ['.$endNeedle.'] in $haystack ['.static::truncate($haystack, 20, '...', false, $encoding).'].'); |
||
155 | } |
||
156 | |||
157 | // Return the substring between the needles. |
||
158 | return mb_substr($haystack, $offset, $secondIndex - $offset); |
||
159 | } |
||
160 | |||
161 | /** |
||
162 | * Returns the characters of the given $haystack as an array, in an offset => character format. |
||
163 | * |
||
164 | * @param string $haystack The string to iterate over. |
||
165 | * @param string|null $encoding The encoding to use. |
||
166 | * @return array The array of characters. |
||
167 | */ |
||
168 | public static function characters(string $haystack, string $encoding = null) : array |
||
169 | { |
||
170 | $result = []; |
||
171 | $length = mb_strlen($haystack, $encoding ?: static::encoding($haystack)); |
||
172 | |||
173 | View Code Duplication | for ($idx = 0; $idx < $length; $idx++) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
174 | $result[] = mb_substr($haystack, $idx, 1, $encoding); |
||
175 | } |
||
176 | |||
177 | return $result; |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Trims the given string and replaces multiple consecutive whitespaces with a single whitespace. |
||
182 | * |
||
183 | * Note: This includes multi-byte whitespace characters, tabs and newlines, which effectively means |
||
184 | * that the string may also be collapsed down. This is mostly a utility for processing natural language |
||
185 | * user input for displaying. |
||
186 | * |
||
187 | * @param string $str The string to collapse. |
||
188 | * @param string|null $encoding The encoding to use. |
||
189 | * @return string The resulting string. |
||
190 | */ |
||
191 | public static function collapse(string $str, string $encoding = null) : string |
||
192 | { |
||
193 | $encoding = $encoding ?: static::encoding($str); |
||
194 | |||
195 | return static::trim(static::replace($str, '[[:space:]]+', ' ', 'msr', $encoding), null, $encoding); |
||
196 | } |
||
197 | |||
198 | /** |
||
199 | * Determines whether the given $haystack contains the given $needle. |
||
200 | * |
||
201 | * If $needle is an array, this method will check whether at least one of the substrings is contained |
||
202 | * in the $haystack. If $all is set to true, all needles in the $needle array must be contained in the |
||
203 | * $haystack for this method to return true. |
||
204 | * |
||
205 | * @param string $haystack The string to check in. |
||
206 | * @param string|array $needle A string or an array of strings. If an array is given, the method returns |
||
207 | * true if at least one of the values is contained within the $haystack. |
||
208 | * @param bool $all Set this to true to ensure all elements of the $needle array (if provided) |
||
209 | * are contained within the $haystack. |
||
210 | * @param bool $strict Whether to use case-sensitive comparisons. |
||
211 | * @param string|null $encoding The encoding to use. |
||
212 | * @return bool |
||
213 | */ |
||
214 | public static function contains(string $haystack, $needle, bool $all = false, bool $strict = true, string $encoding = null) : bool |
||
215 | { |
||
216 | $func = $strict ? 'mb_strpos' : 'mb_stripos'; |
||
217 | $encoding = $encoding ?: static::encoding($haystack); |
||
218 | |||
219 | foreach ((array) $needle as $value) { |
||
220 | if ($func($haystack, $value, 0, $encoding) === false) { |
||
221 | if ($all) { |
||
222 | return false; |
||
223 | } |
||
224 | } elseif (!$all) { |
||
225 | return true; |
||
226 | } |
||
227 | } |
||
228 | |||
229 | // When we reach this point, if we were ensuring all needles are contained within the haystack, it means |
||
230 | // that we didn't fail on a single one. However, when looking for at least one of them, it means that none |
||
231 | // returned true up to this point, so none of them is contained in the haystack. |
||
232 | return $all; |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Determines whether the given $haystack contains all of the $needles. Alias for self::contains() with an |
||
237 | * array of needles and the $all parameter set to true. |
||
238 | * |
||
239 | * @see Str::contains() |
||
240 | */ |
||
241 | public static function containsAll(string $haystack, array $needles, bool $strict = true, string $encoding = null) : bool |
||
242 | { |
||
243 | return static::contains($haystack, $needles, true, $strict, $encoding); |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * Determines whether the given $haystack contains any of the $needles. Alias for self::contains() with an |
||
248 | * array of needles and the $all parameter set to false. |
||
249 | * |
||
250 | * @see Str::contains() |
||
251 | */ |
||
252 | public static function containsAny(string $haystack, array $needles, bool $strict = true, string $encoding = null) : bool |
||
253 | { |
||
254 | return static::contains($haystack, $needles, false, $strict, $encoding); |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * Runs the given callable over each line of the given string and returns the resulting string. |
||
259 | * |
||
260 | * The callable should accept two arguments (in this order): the contents of the line (string) |
||
261 | * and the line's number (int). Additional arguments may also be added and will be appended to the |
||
262 | * callable in the order given. The callable should return a string or a value castable to a string. |
||
263 | * |
||
264 | * @param string $str The string over which to run the callable. |
||
265 | * @param callable $callable The callable to apply. |
||
266 | * @param mixed ...$args Additional arguments to pass to the callable. |
||
267 | * @return string The string after applying the callable to each of its lines. |
||
268 | */ |
||
269 | public static function eachLine(string $str, callable $callable, ...$args) : string |
||
270 | { |
||
271 | if ($str === '') { |
||
272 | return $str; |
||
273 | } |
||
274 | |||
275 | $lines = mb_split('[\r\n]{1,2}', $str); |
||
276 | |||
277 | foreach ($lines as $number => &$line) { |
||
278 | $lines[$number] = (string) call_user_func($callable, $line, $number, ...$args); |
||
279 | } |
||
280 | |||
281 | return implode("\n", $lines); |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Attempts to determine the encoding of a string if a string is given. |
||
286 | * |
||
287 | * Upon failure or when no string is given, returns the static encoding set in this class or if that is not set, |
||
288 | * the hardcoded default of 'utf-8'. |
||
289 | * |
||
290 | * @param string|null $str |
||
291 | * @return string |
||
292 | */ |
||
293 | public static function encoding(string $str = null) : string |
||
294 | { |
||
295 | // If a string was given, we attempt to detect the encoding of the string if we are told to do so. |
||
296 | // If we succeed, just return the determined type. |
||
297 | if (true === static::$autoDetectEncoding && null !== $str && false !== $encoding = mb_detect_encoding($str)) { |
||
298 | return $encoding; |
||
299 | } |
||
300 | |||
301 | // Otherwise let's return one of the defaults. |
||
302 | return static::$encoding ?: 'utf-8'; |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Determines whether the given $haystack ends with the given needle or one of the given needles |
||
307 | * if $needles is an array. |
||
308 | * |
||
309 | * @param string $haystack The string to search in. |
||
310 | * @param string|array $needles The needle(s) to look for. |
||
311 | * @param bool $strict Whether to use case-sensitive comparisons. |
||
312 | * @param string|null $encoding The encoding to use. |
||
313 | * @return bool True when the string ends with one of the given needles, false otherwise. |
||
314 | */ |
||
315 | public static function endsWith(string $haystack, $needles, bool $strict = true, string $encoding = null) : bool |
||
316 | { |
||
317 | if ($haystack === '') { |
||
318 | return false; |
||
319 | } |
||
320 | |||
321 | $encoding = $encoding ?: static::encoding($haystack); |
||
322 | |||
323 | foreach ((array) $needles as $needle) { |
||
324 | |||
325 | // Empty needles are invalid. For philosophical reasons. |
||
326 | if ($needle === '') { |
||
327 | continue; |
||
328 | } |
||
329 | |||
330 | // Grab the substring of the haystack at an offset $needle would be at if the haystack |
||
331 | // ended with it. |
||
332 | $end = mb_substr($haystack, -mb_strlen($needle, $encoding), null, $encoding); |
||
333 | |||
334 | // For case-insensitive comparisons we need a common denominator. |
||
335 | if (!$strict) { |
||
336 | $needle = mb_strtolower($needle, $encoding); |
||
337 | $end = mb_strtolower($end, $encoding); |
||
338 | } |
||
339 | |||
340 | // Stop looping on the first hit. Obviously we're not checking whether the $haystack |
||
341 | // ends with *all* $needles, d'oh. |
||
342 | if ($needle === $end) { |
||
343 | return true; |
||
344 | } |
||
345 | } |
||
346 | |||
347 | return false; |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * Ensures the given $haystack ends with a single instance of the given $needle. For single characters |
||
352 | * use PHP's native rtrim() instead. |
||
353 | * |
||
354 | * @param string $haystack The string to cap. |
||
355 | * @param string $needle The substring to end with. |
||
356 | * @return string The resulting string. |
||
357 | */ |
||
358 | public static function finishWith(string $haystack, string $needle) : string |
||
359 | { |
||
360 | return preg_replace('/(?:'.preg_quote($needle, '/').')+$/', '', $haystack) . $needle; |
||
361 | } |
||
362 | |||
363 | /** |
||
364 | * Finds the first occurrence of $needle within $haystack and returns the part of $haystack |
||
365 | * starting where the $needle starts (ie. including the $needle). |
||
366 | * |
||
367 | * @param string $haystack The string to search in. |
||
368 | * @param string $needle The substring to search for. |
||
369 | * @param bool $strict Whether to use case-sensitive comparisons. True by default. |
||
370 | * @param string|null $encoding The encoding to use. |
||
371 | * @return string The part of $haystack from where $needle starts, including $needle. |
||
372 | * @throws \RuntimeException Upon failing to find $needle in $haystack at all. |
||
373 | */ |
||
374 | public static function from(string $haystack, string $needle, bool $strict = true, string $encoding = null) : string |
||
375 | { |
||
376 | return static::partOf($haystack, $needle, $strict, false, $encoding); |
||
377 | } |
||
378 | |||
379 | /** |
||
380 | * Returns the index (0-indexed) of the first occurrence of $needle in the $haystack. |
||
381 | * |
||
382 | * Important note: Differs from native PHP strpos() in that if the $needle could not be found, it returns -1 |
||
383 | * instead of false. |
||
384 | * |
||
385 | * @param string $haystack The string to search in. |
||
386 | * @param string $needle The substring to search for. |
||
387 | * @param int $offset The offset from which to search. Negative offsets will start the search $offset |
||
388 | * characters from the end of the $haystack. 0-indexed. |
||
389 | * @param bool $strict Whether to use case-sensitive comparisons. True by default. |
||
390 | * @param string|null $encoding The encoding to use. |
||
391 | * @return int The index of the first occurrence if found, -1 otherwise. |
||
392 | */ |
||
393 | View Code Duplication | public static function indexOf(string $haystack, string $needle, int $offset = 0, bool $strict = true, string $encoding = null) : int |
|
0 ignored issues
–
show
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. ![]() |
|||
394 | { |
||
395 | $func = $strict ? 'mb_strpos' : 'mb_stripos'; |
||
396 | $encoding = $encoding ?: static::encoding($haystack); |
||
397 | |||
398 | if (false === $result = $func($haystack, $needle, $offset, $encoding)) { |
||
399 | return -1; |
||
400 | } |
||
401 | |||
402 | return $result; |
||
403 | } |
||
404 | |||
405 | /** |
||
406 | * Returns the index (0-indexed) of the last occurrence of $needle in the $haystack. |
||
407 | * |
||
408 | * Important note: Differs from native PHP strrpos() in that if the $needle could not be found, it returns -1 |
||
409 | * instead of false. |
||
410 | * |
||
411 | * @param string $haystack The string to search in. |
||
412 | * @param string $needle The substring to search for. |
||
413 | * @param int $offset The offset from which to search. Negative offsets will start the search $offset |
||
414 | * characters from the end of the $haystack. 0-indexed. |
||
415 | * @param bool $strict Whether to use case-sensitive comparisons. True by default. |
||
416 | * @param string $encoding The encoding to use. |
||
417 | * @return int The index of the last occurrence if found, -1 otherwise. |
||
418 | */ |
||
419 | View Code Duplication | public static function indexOfLast(string $haystack, string $needle, int $offset = 0, bool $strict = true, string $encoding = null) : int |
|
0 ignored issues
–
show
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. ![]() |
|||
420 | { |
||
421 | $func = $strict ? 'mb_strrpos' : 'mb_strripos'; |
||
422 | $encoding = $encoding ?: static::encoding($haystack); |
||
423 | |||
424 | if (false === $result = $func($haystack, $needle, $offset, $encoding)) { |
||
425 | return -1; |
||
426 | } |
||
427 | |||
428 | return $result; |
||
429 | } |
||
430 | |||
431 | /** |
||
432 | * Inserts the given $needle into the $haystack at the provided offset. |
||
433 | * |
||
434 | * @param string $haystack The string to insert into. |
||
435 | * @param string $needle The string to be inserted. |
||
436 | * @param int $offset The offset at which to insert the substring. If negative, the $substring |
||
437 | * will be inserted $offset characters from the end of $str. |
||
438 | * @param string|null $encoding The encoding to use. |
||
439 | * @return string The resulting string. |
||
440 | * @throws \OutOfBoundsException When trying to insert a substring at an index above the length of the |
||
441 | * initial string. |
||
442 | */ |
||
443 | public static function insert(string $haystack, string $needle, int $offset, string $encoding = null) : string |
||
444 | { |
||
445 | if ($needle === '') { |
||
446 | return $haystack; |
||
447 | } |
||
448 | |||
449 | $encoding = $encoding ?: static::encoding($haystack); |
||
450 | $length = mb_strlen($haystack, $encoding); |
||
451 | |||
452 | // Make sure the offset is contained in the initial string. |
||
453 | View Code Duplication | if (abs($offset) >= $length) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
454 | throw new \OutOfBoundsException('The given $offset ['.$offset.'] does not exist within the string ['.static::truncate($haystack, 20, '...', false, $encoding).'].'); |
||
455 | } |
||
456 | |||
457 | // With a negative offset, we'll convert it to a positive one for the initial part (before the inserted |
||
458 | // substring), since we'll be using that as a length actually. |
||
459 | return mb_substr($haystack, 0, $offset < 0 ? $length + $offset : $offset, $encoding) . $needle . mb_substr($haystack, $offset, null, $encoding); |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * Determines the length of a given string. Counts multi-byte characters as single characters. |
||
464 | * |
||
465 | * @param string $str The string to count characters in. |
||
466 | * @param string $encoding The encoding to use. |
||
467 | * @return int The length of the string. |
||
468 | */ |
||
469 | public static function length(string $str, string $encoding = null) : int |
||
470 | { |
||
471 | return mb_strlen($str, $encoding ?: static::encoding($str)); |
||
472 | } |
||
473 | |||
474 | /** |
||
475 | * Returns all lines contained in the given string as an array, ie. splits the string on newline characters |
||
476 | * into separate strings as items in an array. |
||
477 | * |
||
478 | * @param string $str The string to split into separate lines. |
||
479 | * @param int $limit The maximal number of lines to return. If null, all lines will be returned. |
||
480 | * @return array An array of all lines contained in $str. An empty array when $str contains |
||
481 | * no lines. |
||
482 | */ |
||
483 | public static function lines(string $str, int $limit = null) : array |
||
484 | { |
||
485 | return mb_split('[\r\n]{1,2}', $str, $limit); |
||
486 | } |
||
487 | |||
488 | /** |
||
489 | * Determines whether the given string matches the given pattern. Asterisks are translated into zero or more |
||
490 | * regexp wildcards, allowing for glob-style patterns. |
||
491 | * |
||
492 | * @param string $str The string to match. |
||
493 | * @param string $pattern The pattern to match the string against. |
||
494 | * @return bool |
||
495 | */ |
||
496 | public static function matches(string $str, string $pattern) : bool |
||
497 | { |
||
498 | if ($pattern === $str) { |
||
499 | return true; |
||
500 | } |
||
501 | |||
502 | return (bool) preg_match('#^'.str_replace('\*', '.*', preg_quote($pattern, '#')).'\z'.'#', $str); |
||
503 | } |
||
504 | |||
505 | /** |
||
506 | * Returns an array containing the offsets of all occurrences of $needle in $haystack. The offsets |
||
507 | * are 0-indexed. If no occurrences could be found, an empty array will be returned. |
||
508 | * |
||
509 | * If all you need is to count the number of occurrences, mb_substr_count() should be your |
||
510 | * function of choice. Str::occurrences() however has got you covered if you need case-(in)sensitivity |
||
511 | * or a count from a specific offset. To count the number of occurrences of $needle in $haystack using |
||
512 | * this method, a simple `count(Str::occurrences($needle, $haystack));` will do the trick. |
||
513 | * |
||
514 | * @param string $haystack The string to search in. |
||
515 | * @param string $needle The substring to search for. |
||
516 | * @param int $offset The offset from which to start the search. Can be negative, in which |
||
517 | * case this method will start searching for the occurrences $offset |
||
518 | * characters from the end of the $haystack. Starts from 0 by default. |
||
519 | * @param bool $strict Whether to use case-sensitive comparisons. True by default. |
||
520 | * @param string|null $encoding The encoding to use. |
||
521 | * @return array An array containing the 0-indexed offsets of all found occurrences |
||
522 | * or an empty array if none were found. |
||
523 | * @throws \OutOfBoundsException When the $offset index is not contained in the input string. |
||
524 | */ |
||
525 | public static function occurrences(string $haystack, string $needle, int $offset = 0, bool $strict = true, string $encoding = null) : array |
||
526 | { |
||
527 | // Early return in obvious circumstances. |
||
528 | if ($haystack === '' || $needle === '') { |
||
529 | return []; |
||
530 | } |
||
531 | |||
532 | $encoding = $encoding ?: static::encoding($haystack); |
||
533 | $length = mb_strlen($haystack, $encoding); |
||
534 | |||
535 | // Make sure the offset given exists within the $haystack. |
||
536 | View Code Duplication | if (abs($offset) >= $length) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
537 | throw new \OutOfBoundsException('The given $offset ['.$offset.'] does not exist within the string ['.static::truncate($haystack, 20, '...', false, $encoding).'].'); |
||
538 | } |
||
539 | |||
540 | $func = $strict ? 'mb_strpos' : 'mb_stripos'; |
||
541 | $result = []; |
||
542 | |||
543 | while (false !== $offset = $func($haystack, $needle, $offset, $encoding)) { |
||
544 | // We could count the length of $needle here but just going +1 ensures we don't catch |
||
545 | // the same needle again while at the same time we're avoiding the overhead of mb_strlen. |
||
546 | $result[] = $offset++; |
||
547 | } |
||
548 | |||
549 | return $result; |
||
550 | } |
||
551 | |||
552 | /** |
||
553 | * Pads the given string to a given $length using $with as padding. This has no effect on input strings which are |
||
554 | * longer than or equal in length to the requested $length. |
||
555 | * |
||
556 | * Padding can be applied to the left, the right or both sides of the input string simultaneously depending |
||
557 | * on the $type chosen (one of PHP's native STR_PAD_* constants). |
||
558 | * |
||
559 | * @param string $str The string to pad. |
||
560 | * @param int $length The desired length of the string after padding. |
||
561 | * @param string $with The character(s) to pad the string with. |
||
562 | * @param int $type One of the STR_PAD_* constants supported by PHP's native str_pad(). |
||
563 | * @param string|null $encoding The encoding to use. |
||
564 | * @return string The resulting string. |
||
565 | * @throws \InvalidArgumentException when an unrecognized $type is given. |
||
566 | */ |
||
567 | public static function pad(string $str, int $length, string $with = ' ', int $type = STR_PAD_RIGHT, string $encoding = null) : string |
||
568 | { |
||
569 | $encoding = $encoding ?: static::encoding($str); |
||
570 | |||
571 | // Get the length of the input string - we'll need it to determine how much padding to apply. |
||
572 | // Note: We're not returning early when the input string is empty - this is acceptable input for this |
||
573 | // method. We will, however, return early when $with (the padding) is empty. |
||
574 | $strLen = mb_strlen($str, $encoding); |
||
575 | $padding = $length - $strLen; |
||
576 | |||
577 | // Determine how much padding to apply to either of the sides depending on which padding type |
||
578 | // we were asked to perform. |
||
579 | switch ($type) { |
||
580 | case STR_PAD_LEFT: |
||
581 | $left = $padding; |
||
582 | $right = 0; |
||
583 | break; |
||
584 | |||
585 | case STR_PAD_RIGHT: |
||
586 | $left = 0; |
||
587 | $right = $padding; |
||
588 | break; |
||
589 | |||
590 | case STR_PAD_BOTH: |
||
591 | $left = floor($padding / 2); |
||
592 | $right = ceil($padding / 2); |
||
593 | break; |
||
594 | |||
595 | default: |
||
596 | throw new \InvalidArgumentException('Expected $type to be one of [STR_PAD_RIGHT|STR_PAD_LEFT|STR_PAD_BOTH], got ['.$type.'] instead.'); |
||
597 | } |
||
598 | |||
599 | // If there's no actual padding or if the final length of the string would be longer than the |
||
600 | // input string, we'll do nothing and return the input string. |
||
601 | if (0 === $padLen = mb_strlen($with, $encoding) || $strLen >= $paddedLength = $strLen + $left + $right) { |
||
0 ignored issues
–
show
|
|||
602 | return $str; |
||
603 | } |
||
604 | |||
605 | // Construct the requested padding strings. |
||
606 | $leftPadding = 0 === $left ? '' : mb_substr(str_repeat($with, ceil($left / $padLen)), 0, $left, $encoding); |
||
607 | $rightPadding = 0 === $right ? '' : mb_substr(str_repeat($with, ceil($right / $padLen)), 0, $right, $encoding); |
||
608 | |||
609 | // Apply the padding and return the glued string. |
||
610 | return $leftPadding . $str . $rightPadding; |
||
611 | } |
||
612 | |||
613 | /** |
||
614 | * Alias for self::pad() with the $type set to apply padding to both sides of the input string. |
||
615 | * |
||
616 | * @see Str::pad() |
||
617 | */ |
||
618 | public static function padBoth(string $str, int $length, string $with = ' ', string $encoding = null) : string |
||
619 | { |
||
620 | return static::pad($str, $length, $with, STR_PAD_BOTH, $encoding); |
||
621 | } |
||
622 | |||
623 | /** |
||
624 | * Alias for self::pad() with the $type set to apply padding only to the left side of the input string. |
||
625 | * |
||
626 | * @see Str::pad() |
||
627 | */ |
||
628 | public static function padLeft(string $str, int $length, string $with = ' ', string $encoding = null) : string |
||
629 | { |
||
630 | return static::pad($str, $length, $with, STR_PAD_LEFT, $encoding); |
||
631 | } |
||
632 | |||
633 | /** |
||
634 | * Alias for self::pad() with the $type set to apply padding only to the left side of the input string. |
||
635 | * |
||
636 | * Note: Self::pad() performs padding on the right side only by default - this alias is provided mostly |
||
637 | * for consistency and verbosity. |
||
638 | * |
||
639 | * @see Str::pad() |
||
640 | */ |
||
641 | public static function padRight(string $str, int $length, string $with = ' ', string $encoding = null) : string |
||
642 | { |
||
643 | return static::pad($str, $length, $with, STR_PAD_RIGHT, $encoding); |
||
644 | } |
||
645 | |||
646 | /** -- |
||
647 | * Do *not* use this method in a cryptographic context without passing in a higher $strength |
||
648 | * parameter or better yet, use Random::string() directly instead. |
||
649 | * -- |
||
650 | * |
||
651 | * Generates a pseudo-random string of the specified length using alpha-numeric characters |
||
652 | * or the characters provided. |
||
653 | * |
||
654 | * @see Random::string() |
||
655 | * |
||
656 | * @param int $length The expected length of the generated string. |
||
657 | * @param string|int $characters The character list to use. Can be either a string |
||
658 | * with the characters to use or an int | nyx\core\Mask |
||
659 | * to generate a list (@see self::buildCharacterSet()). |
||
660 | * If not provided or an invalid mask, the method will fall |
||
661 | * back to a base alphanumeric character set. |
||
662 | * @param int $strength The requested strength of entropy (one of the Random::STRENGTH_* |
||
663 | * class constants) |
||
664 | * @return string The generated string. |
||
665 | */ |
||
666 | public static function random(int $length = 8, $characters = null, int $strength = Random::STRENGTH_NONE) : string |
||
667 | { |
||
668 | // Note: We're duplicating ourselves by specifying the character pool directly instead of |
||
669 | // relying on str\Character::buildSet(), but this skips this process in Random::string() |
||
670 | // and is therefore faster. |
||
671 | return Random::string($length, $characters ?: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', $strength); |
||
672 | } |
||
673 | |||
674 | /** |
||
675 | * Removes the given $needle(s) from the $haystack. |
||
676 | * |
||
677 | * @param string $haystack The string to remove from. |
||
678 | * @param string|string[] $needles The substrings to remove. |
||
679 | * @return string The resulting string. |
||
680 | */ |
||
681 | public static function remove(string $haystack, $needles) : string |
||
682 | { |
||
683 | return static::replace($haystack, $needles, ''); |
||
684 | } |
||
685 | |||
686 | /** |
||
687 | * Removes the given $needle from the beginning (only) of the $haystack. Only the first occurrence of the $needle |
||
688 | * will be removed. If the $haystack does not start with the specified $needle, nothing will be removed. |
||
689 | * |
||
690 | * @param string $haystack The string to remove from. |
||
691 | * @param string $needle The substring to remove from the beginning. |
||
692 | * @param string|null $encoding The encoding to use. |
||
693 | * @return string The resulting string. |
||
694 | */ |
||
695 | public static function removeLeft(string $haystack, string $needle, string $encoding = null) : string |
||
696 | { |
||
697 | // Early return in obvious circumstances. |
||
698 | if ($haystack === '' || $needle === '') { |
||
699 | return $haystack; |
||
700 | } |
||
701 | |||
702 | $encoding = $encoding ?: static::encoding($haystack); |
||
703 | |||
704 | // This is a non-DRY version of self::startsWith(). If $haystack doesn't even start |
||
705 | // with the given substring, we'll just return $haystack. |
||
706 | if (0 !== mb_strpos($haystack, $needle, 0, $encoding) || 0 === $needleLen = mb_strlen($needle, $encoding)) { |
||
707 | return $haystack; |
||
708 | } |
||
709 | |||
710 | // Grab a substring of the full initial string starting from the end of the prefix |
||
711 | // we're cutting off... and return it. |
||
712 | return mb_substr($haystack, $needleLen, null, $encoding); |
||
713 | } |
||
714 | |||
715 | /** |
||
716 | * Removes the given $needle from the end (only) of the $haystack. Only the last occurrence of the $needle |
||
717 | * will be removed. If the $haystack does not end with the specified $needle, nothing will be removed. |
||
718 | * |
||
719 | * @param string $haystack The string to remove from. |
||
720 | * @param string $needle The substring to remove from the end. |
||
721 | * @param string|null $encoding The encoding to use. |
||
722 | * @return string The resulting string. |
||
723 | */ |
||
724 | public static function removeRight(string $haystack, string $needle, string $encoding = null) : string |
||
725 | { |
||
726 | // Early return in obvious circumstances. |
||
727 | if ($haystack === '' || $needle === '') { |
||
728 | return $haystack; |
||
729 | } |
||
730 | |||
731 | $encoding = $encoding ?: static::encoding($haystack); |
||
732 | $needleLen = mb_strlen($needle, $encoding); |
||
733 | |||
734 | // This is a non-DRY version of self::endsWith(). If $haystack doesn't even end |
||
735 | // with the given substring, we'll just return $haystack. |
||
736 | if (0 === $needleLen || $needle !== mb_substr($haystack, -$needleLen, null, $encoding)) { |
||
737 | return $haystack; |
||
738 | } |
||
739 | |||
740 | // Grab a substring of the full initial string ending at the beginning of the suffix |
||
741 | // we're cutting off... and return it. |
||
742 | return mb_substr($haystack, 0, mb_strlen($haystack, $encoding) - $needleLen, $encoding); |
||
743 | } |
||
744 | |||
745 | /** |
||
746 | * Replaces the $needles within the given $haystack with the $replacement. If $needles is a string, |
||
747 | * it will be treated as a regular expression. If it is an array, it will be treated as an array |
||
748 | * of substrings to replace (an appropriate regular expression will be constructed). |
||
749 | * |
||
750 | * Acts as an utility alias for mb_ereg_replace(). |
||
751 | * |
||
752 | * @param string $haystack The string to replace $needles in. |
||
753 | * @param string|string[] $needles What to replace in the string. |
||
754 | * @param string $replacement The replacement value. |
||
755 | * @param string $options The matching conditions as a string. |
||
756 | * {@link http://php.net/manual/en/function.mb-ereg-replace.php} |
||
757 | * @param string|null $encoding The encoding to use. |
||
758 | * @return string The resulting string. |
||
759 | */ |
||
760 | public static function replace(string $haystack, $needles, string $replacement, string $options = 'msr', string $encoding = null) : string |
||
761 | { |
||
762 | // If multiple values to replace were passed. |
||
763 | if (is_array($needles)) { |
||
764 | $needles = '(' .implode('|', $needles). ')'; |
||
765 | } |
||
766 | |||
767 | // Keep track of the internal encoding as we'll change it temporarily and then revert back to it. |
||
768 | $internalEncoding = mb_regex_encoding(); |
||
769 | |||
770 | // Swap out the internal encoding for what we want... |
||
771 | mb_regex_encoding($encoding ?: static::encoding($haystack)); |
||
772 | |||
773 | // ... and perform the replacement. |
||
774 | $result = mb_ereg_replace($needles, $replacement, $haystack, $options); |
||
775 | |||
776 | // Restore the initial internal encoding. |
||
777 | mb_regex_encoding($internalEncoding); |
||
778 | |||
779 | return $result; |
||
780 | } |
||
781 | |||
782 | /** |
||
783 | * Replaces the first occurrence of each of the $needles in $haystack with $replacement. |
||
784 | * |
||
785 | * This method will search from the beginning of $haystack after processing each needle and replacing it, |
||
786 | * meaning subsequent iterations may replace substrings resulting from previous iterations. |
||
787 | * |
||
788 | * @param string $haystack The string to replace $needles in. |
||
789 | * @param string|string[] $needles What to replace in the string. |
||
790 | * @param string $replacement The replacement value for each found (first) needle. |
||
791 | * @param bool $strict Whether to use case-sensitive comparisons. |
||
792 | * @param string|null $encoding The encoding to use. |
||
793 | * @return string The resulting string. |
||
794 | */ |
||
795 | public static function replaceFirst(string $haystack, $needles, string $replacement, bool $strict = true, string $encoding = null) : string |
||
796 | { |
||
797 | return static::replaceOccurrence($haystack, $needles, $replacement, $strict, true, $encoding); |
||
798 | } |
||
799 | |||
800 | /** |
||
801 | * Replaces the last occurrence of each of the $needles in $haystack with $replacement. |
||
802 | * |
||
803 | * This method will search from the end of $haystack after processing each needle and replacing it, |
||
804 | * meaning subsequent iterations may replace substrings resulting from previous iterations. |
||
805 | * |
||
806 | * @param string $haystack The string to replace $needles in. |
||
807 | * @param string|string[] $needles What to replace in the string. |
||
808 | * @param string $replacement The replacement value for each found (last) needle. |
||
809 | * @param bool $strict Whether to use case-sensitive comparisons. |
||
810 | * @param string|null $encoding The encoding to use. |
||
811 | * @return string The resulting string. |
||
812 | */ |
||
813 | public static function replaceLast(string $haystack, $needles, string $replacement, bool $strict = true, string $encoding = null) : string |
||
814 | { |
||
815 | return static::replaceOccurrence($haystack, $needles, $replacement, $strict, false, $encoding); |
||
816 | } |
||
817 | |||
818 | /** |
||
819 | * Reverses a string. Multi-byte-safe equivalent of strrev(). |
||
820 | * |
||
821 | * @param string $str The string to reverse. |
||
822 | * @param string|null $encoding The encoding to use. |
||
823 | * @return string The resulting string. |
||
824 | */ |
||
825 | public static function reverse(string $str, string $encoding = null) : string |
||
826 | { |
||
827 | $encoding = $encoding ?: static::encoding($str); |
||
828 | $length = mb_strlen($str, $encoding); |
||
829 | $result = ''; |
||
830 | |||
831 | // Return early under obvious circumstances. |
||
832 | if ($length === 0) { |
||
833 | return $result; |
||
834 | } |
||
835 | |||
836 | // Reverse one character after the other, counting from the end. |
||
837 | View Code Duplication | for ($i = $length - 1; $i >= 0; $i--) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
838 | $result .= mb_substr($str, $i, 1, $encoding); |
||
839 | } |
||
840 | |||
841 | return $result; |
||
842 | } |
||
843 | |||
844 | /** |
||
845 | * Randomizes the order of characters in the given string. Multi-byte-safe equivalent |
||
846 | * of str_shuffle(). |
||
847 | * |
||
848 | * @param string $str The string to shuffle. |
||
849 | * @param string|null $encoding The encoding to use. |
||
850 | * @return string The resulting string. |
||
851 | */ |
||
852 | public static function shuffle(string $str, string $encoding = null) : string |
||
853 | { |
||
854 | if ($str === '') { |
||
855 | return $str; |
||
856 | } |
||
857 | |||
858 | $result = ''; |
||
859 | $encoding = $encoding ?: static::encoding($str); |
||
860 | $indices = range(0, mb_strlen($str, $encoding) - 1); |
||
861 | |||
862 | shuffle($indices); |
||
863 | |||
864 | foreach ($indices as $i) { |
||
865 | $result .= mb_substr($str, $i, 1, $encoding); |
||
866 | } |
||
867 | |||
868 | return $result; |
||
869 | } |
||
870 | |||
871 | /** |
||
872 | * Generates a URL-friendly slug from the given string. |
||
873 | * |
||
874 | * @param string $str The string to slugify. |
||
875 | * @param string $delimiter The delimiter to replace non-alphanumeric characters with. |
||
876 | * @return string The resulting slug. |
||
877 | */ |
||
878 | public static function slug(string $str, string $delimiter = '-') : string |
||
879 | { |
||
880 | $str = static::toAscii($str); |
||
881 | |||
882 | // Remove all characters that are neither alphanumeric, nor the separator nor a whitespace. |
||
883 | $str = preg_replace('![^'.preg_quote($delimiter).'\pL\pN\s]+!u', '', mb_strtolower($str)); |
||
884 | |||
885 | // Standardize the separator. |
||
886 | $flip = $delimiter == '-' ? '_' : '-'; |
||
887 | $str = preg_replace('!['.preg_quote($flip).']+!u', $delimiter, $str); |
||
888 | |||
889 | // Replace all separator characters and whitespace by a single separator. |
||
890 | $str = preg_replace('!['.preg_quote($delimiter).'\s]+!u', $delimiter, $str); |
||
891 | |||
892 | return trim($str, $delimiter); |
||
893 | } |
||
894 | |||
895 | /** |
||
896 | * Determines whether the given $haystack starts with the given needle or one of the given needles |
||
897 | * if $needles is an array. |
||
898 | * |
||
899 | * @param string $haystack The string to search in. |
||
900 | * @param string|string[] $needles The needle(s) to look for. |
||
901 | * @param bool $strict Whether to use case-sensitive comparisons. |
||
902 | * @param string|null $encoding The encoding to use. |
||
903 | * @return bool True when the string starts with one of the given needles, false otherwise. |
||
904 | */ |
||
905 | public static function startsWith(string $haystack, $needles, bool $strict = true, string $encoding = null) : bool |
||
906 | { |
||
907 | if ($haystack === '') { |
||
908 | return false; |
||
909 | } |
||
910 | |||
911 | $encoding = $encoding ?: static::encoding($haystack); |
||
912 | $func = $strict ? 'mb_strpos' : 'mb_stripos'; |
||
913 | |||
914 | foreach ((array) $needles as $needle) { |
||
915 | if ($needle !== '' && 0 === $func($haystack, $needle, 0, $encoding)) { |
||
916 | return true; |
||
917 | } |
||
918 | } |
||
919 | |||
920 | return false; |
||
921 | } |
||
922 | |||
923 | /** |
||
924 | * Returns a part of the given $haystack starting at the given $offset. |
||
925 | * |
||
926 | * @param string $haystack The input string. |
||
927 | * @param int $offset The offset at which to start the slice. If a negative offset is given, |
||
928 | * the slice will start at the $offset-th character counting from the end |
||
929 | * of the input string. |
||
930 | * @param int|null $length The length of the slice. Must be a positive integer or null. If null, |
||
931 | * the full string starting from $offset will be returned. If a length |
||
932 | * which is longer than the input string is requested the method will |
||
933 | * silently ignore this and will act as if null was passed as length. |
||
934 | * @param string|null $encoding The encoding to use. |
||
935 | * @return string The resulting string. |
||
936 | * @throws \InvalidArgumentException When $length is negative. |
||
937 | * @throws \OutOfBoundsException When the $start index is not contained in the input string. |
||
938 | */ |
||
939 | public static function sub(string $haystack, int $offset, int $length = null, string $encoding = null) : string |
||
940 | { |
||
941 | if ($length === 0) { |
||
942 | return ''; |
||
943 | } |
||
944 | |||
945 | // We could silently return the initial string, but a negative $length may be an indicator of |
||
946 | // mismatching $start with $length in the method call. |
||
947 | if ($length < 0) { |
||
948 | throw new \InvalidArgumentException('The length of the requested substring must be > 0, ['.$length.'] requested.'); |
||
949 | } |
||
950 | |||
951 | $encoding = $encoding ?: static::encoding($haystack); |
||
952 | |||
953 | // Check if the absolute starting index (to account for negative indices) + 1 (since it's 0-indexed |
||
954 | // while length is > 1 at this point) is within the length of the string. |
||
955 | View Code Duplication | if (abs($offset) >= mb_strlen($haystack, $encoding)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
956 | throw new \OutOfBoundsException('The given $offset ['.$offset.'] does not exist within the string ['.static::truncate($haystack, 20, '...', false, $encoding).'].'); |
||
957 | } |
||
958 | |||
959 | return mb_substr($haystack, $offset, $length, $encoding); |
||
960 | } |
||
961 | |||
962 | /** |
||
963 | * Surrounds the $haystack with the given $needle. |
||
964 | * |
||
965 | * Works consistent with Str::begin() and Str::finish() in that it ensures only single instances of |
||
966 | * the $needle will be present at the beginning and end of the $haystack. |
||
967 | * |
||
968 | * @param string $haystack The string to surround. |
||
969 | * @param string $needle The substring to surround the string with. |
||
970 | * @return string The resulting string. |
||
971 | */ |
||
972 | public static function surroundWith(string $haystack, string $needle) : string |
||
973 | { |
||
974 | return static::beginWith($haystack, $needle) . $needle . static::finishWith($haystack, $needle); |
||
975 | } |
||
976 | |||
977 | /** |
||
978 | * Alias for @see Str::characters() |
||
979 | */ |
||
980 | public static function toArray(string $str, string $encoding = null) : array |
||
981 | { |
||
982 | return static::characters($str, $encoding); |
||
983 | } |
||
984 | |||
985 | /** |
||
986 | * Transliterates an UTF-8 encoded string to its ASCII equivalent. |
||
987 | * |
||
988 | * @param string $str The UTF-8 encoded string to transliterate. |
||
989 | * @return string The ASCII equivalent of the input string. |
||
990 | */ |
||
991 | public static function toAscii(string $str) : string |
||
992 | { |
||
993 | if (preg_match("/[\x80-\xFF]/", $str)) { |
||
994 | // Grab the transliteration table since we'll need it. |
||
995 | if (null === static::$ascii) { |
||
996 | static::$ascii = unserialize(file_get_contents(__DIR__ . '/str/resources/transliteration_table.ser')); |
||
997 | } |
||
998 | |||
999 | $str = \Normalizer::normalize($str, \Normalizer::NFKD); |
||
1000 | $str = preg_replace('/\p{Mn}+/u', '', $str); |
||
1001 | $str = str_replace(static::$ascii[0], static::$ascii[1], $str); |
||
1002 | $str = iconv('UTF-8', 'ASCII' . ('glibc' !== ICONV_IMPL ? '//IGNORE' : '') . '//TRANSLIT', $str); |
||
1003 | } |
||
1004 | |||
1005 | return $str; |
||
1006 | } |
||
1007 | |||
1008 | /** |
||
1009 | * Checks whether the given string represents a boolean value. Case insensitive. |
||
1010 | * |
||
1011 | * Works different than simply casting a string to a bool in that strings like "yes"/"no", |
||
1012 | * "y"/"n", "on"/"off", "1"/"0" and "true"/"false" are interpreted based on the natural language |
||
1013 | * value they represent. |
||
1014 | * |
||
1015 | * Numeric strings are left as in native PHP typecasting, ie. only 0 represents false. Every |
||
1016 | * other numeric string, including negative numbers, will be treated as a truthy value. |
||
1017 | * |
||
1018 | * Non-empty strings containing only whitespaces, tabs or newlines will also be interpreted |
||
1019 | * as empty (false) strings. Other than that normal typecasting rules apply. |
||
1020 | * |
||
1021 | * @param string $str The string to check. |
||
1022 | * @param string|null $encoding The encoding to use. |
||
1023 | * @return bool True when the string represents a boolean value, false otherwise. |
||
1024 | * @todo Grab the map from a separate method to allow easier extending with locale specific values? |
||
1025 | * @todo Rename to asBool() to make a more explicit distinction between this and normal typecasting? |
||
1026 | */ |
||
1027 | public static function toBool(string $str, string $encoding = null) : bool |
||
1028 | { |
||
1029 | static $map = [ |
||
1030 | 'true' => true, |
||
1031 | 'false' => false, |
||
1032 | '1' => true, |
||
1033 | '0' => false, |
||
1034 | 'on' => true, |
||
1035 | 'off' => false, |
||
1036 | 'yes' => true, |
||
1037 | 'no' => false, |
||
1038 | 'y' => true, |
||
1039 | 'n' => false |
||
1040 | ]; |
||
1041 | |||
1042 | $encoding = $encoding ?: static::encoding($str); |
||
1043 | |||
1044 | return $map[mb_strtolower($str, $encoding)] ?? (bool) static::replace($str, '[[:space:]]', '', 'msr', $encoding); |
||
1045 | } |
||
1046 | |||
1047 | /** |
||
1048 | * Converts each tab in the given string to the defined number of spaces (4 by default). |
||
1049 | * |
||
1050 | * @param string $str The string in which to convert the tabs to whitespaces. |
||
1051 | * @param int $length The number of spaces to replace each tab with. |
||
1052 | * @return string The resulting string. |
||
1053 | */ |
||
1054 | public static function toSpaces(string $str, int $length = 4) : string |
||
1055 | { |
||
1056 | return str_replace("\t", str_repeat(' ', $length), $str); |
||
1057 | } |
||
1058 | |||
1059 | /** |
||
1060 | * Converts each occurrence of the defined consecutive number of spaces (4 by default) to a tab. |
||
1061 | * |
||
1062 | * @param string $str The string in which to convert the whitespaces to tabs. |
||
1063 | * @param int $length The number of consecutive spaces to replace with a tab. |
||
1064 | * @return string The resulting string. |
||
1065 | */ |
||
1066 | public static function toTabs(string $str, int $length = 4) : string |
||
1067 | { |
||
1068 | return str_replace(str_repeat(' ', $length), "\t", $str); |
||
1069 | } |
||
1070 | |||
1071 | /** |
||
1072 | * Removes whitespaces (or other characters, if given) from the beginning and end of the given string. |
||
1073 | * Handles multi-byte whitespaces. |
||
1074 | * |
||
1075 | * Note: If you simply want to remove whitespace and multi-byte whitespaces are of no concern, |
||
1076 | * use PHP's native trim() instead, for obvious performance reasons. |
||
1077 | * |
||
1078 | * @param string $str The string in which to convert the whitespaces to tabs. |
||
1079 | * @param string $characters Optional characters to strip off (instead of the default whitespace chars). |
||
1080 | * @param string|null $encoding The encoding to use. |
||
1081 | * @return string The resulting string. |
||
1082 | */ |
||
1083 | public static function trim(string $str, string $characters = null, string $encoding = null) : string |
||
1084 | { |
||
1085 | $characters = null !== $characters ? preg_quote($characters) : '[:space:]'; |
||
1086 | |||
1087 | return static::replace($str, "^[$characters]+|[$characters]+\$", '', 'msr', $encoding); |
||
1088 | } |
||
1089 | |||
1090 | /** |
||
1091 | * Removes whitespaces (or other characters, if given) from the beginning of the given string. |
||
1092 | * Handles multi-byte whitespaces. |
||
1093 | * |
||
1094 | * Note: If you simply want to remove whitespace and multi-byte whitespaces are of no concern, |
||
1095 | * use PHP's native ltrim() instead, for obvious performance reasons. |
||
1096 | * |
||
1097 | * @param string $str The string in which to convert the whitespaces to tabs. |
||
1098 | * @param string $characters Optional characters to strip off (instead of the default whitespace chars). |
||
1099 | * @param string|null $encoding The encoding to use. |
||
1100 | * @return string The resulting string. |
||
1101 | */ |
||
1102 | public static function trimLeft(string $str, string $characters = null, string $encoding = null) : string |
||
1103 | { |
||
1104 | $characters = null !== $characters ? preg_quote($characters) : '[:space:]'; |
||
1105 | |||
1106 | return static::replace($str, "^[$characters]+", '', 'msr', $encoding); |
||
1107 | } |
||
1108 | |||
1109 | /** |
||
1110 | * Removes whitespaces (or other characters, if given) from the end of the given string. |
||
1111 | * Handles multi-byte whitespaces. |
||
1112 | * |
||
1113 | * Note: If you simply want to remove whitespace and multi-byte whitespaces are of no concern, |
||
1114 | * use PHP's native rtrim() instead, for obvious performance reasons. |
||
1115 | * |
||
1116 | * @param string $str The string in which to convert the whitespaces to tabs. |
||
1117 | * @param string $characters Optional characters to strip off (instead of the default whitespace chars). |
||
1118 | * @param string|null $encoding The encoding to use. |
||
1119 | * @return string The resulting string. |
||
1120 | */ |
||
1121 | public static function trimRight(string $str, string $characters = null, string $encoding = null) : string |
||
1122 | { |
||
1123 | $characters = null !== $characters ? preg_quote($characters) : '[:space:]'; |
||
1124 | |||
1125 | return static::replace($str, "[$characters]+\$", '', 'msr', $encoding); |
||
1126 | } |
||
1127 | |||
1128 | /** |
||
1129 | * Trims the string to the given length, replacing the cut off characters from the end with an optional |
||
1130 | * substring ("..." by default). The final length of the string, including the optionally appended $end |
||
1131 | * substring, will not exceed $limit. |
||
1132 | * |
||
1133 | * @param string $str The string to truncate. |
||
1134 | * @param int $limit The maximal number of characters to be contained in the string. Must be |
||
1135 | * a positive integer. If 0 is given, an empty string will be returned. |
||
1136 | * @param string $end The replacement for the whole of the cut off string (if any). |
||
1137 | * @param bool $preserveWords Whether to preserve words, ie. allow splitting only on whitespace |
||
1138 | * characters. |
||
1139 | * @param string|null $encoding The encoding to use. |
||
1140 | * @return string The resulting string. |
||
1141 | * @throws \InvalidArgumentException When $limit is a negative integer. |
||
1142 | */ |
||
1143 | public static function truncate(string $str, int $limit = 100, string $end = '...', bool $preserveWords = false, string $encoding = null) : string |
||
1144 | { |
||
1145 | if ($limit < 0) { |
||
1146 | throw new \InvalidArgumentException('The $limit must be a positive integer but ['.$limit.'] was given.'); |
||
1147 | } |
||
1148 | |||
1149 | if ($limit === 0) { |
||
1150 | return ''; |
||
1151 | } |
||
1152 | |||
1153 | $encoding = $encoding ?: static::encoding($str); |
||
1154 | |||
1155 | // Is there anything to actually truncate? |
||
1156 | if (mb_strlen($str, $encoding) <= $limit) { |
||
1157 | return $str; |
||
1158 | } |
||
1159 | |||
1160 | // Determine the final length of the substring of $str we might return and grab it. |
||
1161 | $length = $limit - mb_strlen($end, $encoding); |
||
1162 | $result = mb_substr($str, 0, $length, $encoding); |
||
1163 | |||
1164 | // If we are to preserve words, see whether the last word got truncated by checking if |
||
1165 | // the truncated string would've been directly followed by a whitespace or not. If not, |
||
1166 | // we're going to get the position of the last whitespace in the truncated string and |
||
1167 | // cut the whole thing off at that offset instead. |
||
1168 | if (true === $preserveWords && $length !== mb_strpos($str, ' ', $length - 1, $encoding)) { |
||
1169 | $result = mb_substr($result, 0, mb_strrpos($result, ' ', 0, $encoding), $encoding); |
||
1170 | } |
||
1171 | |||
1172 | return $result . $end; |
||
1173 | } |
||
1174 | |||
1175 | /** |
||
1176 | * Limits the number of words in the given string. |
||
1177 | * |
||
1178 | * @param string $str The string to limit. |
||
1179 | * @param int $words The maximal number of words to be contained in the string, not counting |
||
1180 | * the replacement. |
||
1181 | * @param string|null $encoding The encoding to use. |
||
1182 | * @param string $end The replacement for the whole of the cut off string (if any). |
||
1183 | * @return string The resulting string. |
||
1184 | */ |
||
1185 | public static function words(string $str, int $words = 100, string $encoding = null, string $end = '...') : string |
||
1186 | { |
||
1187 | $encoding = $encoding ?: static::encoding($str); |
||
1188 | |||
1189 | preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $str, $matches); |
||
1190 | |||
1191 | if (!isset($matches[0]) || mb_strlen($str, $encoding) === mb_strlen($matches[0], $encoding)) { |
||
1192 | return $str; |
||
1193 | } |
||
1194 | |||
1195 | return rtrim($matches[0]).$end; |
||
1196 | } |
||
1197 | |||
1198 | /** |
||
1199 | * Finds the first occurrence of $needle within $haystack and returns the part of $haystack |
||
1200 | * before up to the beginning of the $needle or from the beginning of the $needle and including it, |
||
1201 | * depending on whether $before is true or false. |
||
1202 | * |
||
1203 | * Used internally by {@see self::after()}, {@see self::before()} and {@see self::from()}. |
||
1204 | * |
||
1205 | * @param string $haystack The string to search in. |
||
1206 | * @param string $needle The substring to search for. |
||
1207 | * @param bool $strict Whether to use case-sensitive comparisons. True by default. |
||
1208 | * @param bool $before Whether to return the part of $haystack before $needle or part of |
||
1209 | * $haystack starting at $needle and including $needle. |
||
1210 | * @param string|null $encoding The encoding to use. |
||
1211 | * @return string The part of $haystack before/from $needle. |
||
1212 | * @throws \RuntimeException Upon failing to find $needle in $haystack at all. |
||
1213 | */ |
||
1214 | protected static function partOf(string $haystack, string $needle, bool $strict, bool $before, string $encoding = null) : string |
||
1215 | { |
||
1216 | $func = $strict ? 'mb_strstr' : 'mb_stristr'; |
||
1217 | $encoding = $encoding ?: static::encoding($haystack); |
||
1218 | |||
1219 | View Code Duplication | if (false === $result = $func($haystack, $needle, $before, $encoding)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
1220 | throw new \RuntimeException('Failed to find $needle ['.$needle.'] in $haystack ['.static::truncate($haystack, 20, '...', false, $encoding).'].'); |
||
1221 | } |
||
1222 | |||
1223 | return $result; |
||
1224 | } |
||
1225 | |||
1226 | /** |
||
1227 | * Replaces a single occurrence of each of the $needles in $haystack with $replacement - either the first or |
||
1228 | * the last occurrence, depending whether $first is true or false. |
||
1229 | * |
||
1230 | * This method will search from the beginning/end of $haystack after processing each needle and replacing it, |
||
1231 | * meaning subsequent iterations may replace substrings resulting from previous iterations. |
||
1232 | * |
||
1233 | * Used internally by {@see self::replaceFirst()} and {@see self::replaceLast()}. |
||
1234 | * |
||
1235 | * @param string $haystack The string to replace $needles in. |
||
1236 | * @param string|string[] $needles What to replace in the string. |
||
1237 | * @param string $replacement The replacement value for each found (last) needle. |
||
1238 | * @param bool $strict Whether to use case-sensitive comparisons. |
||
1239 | * @param bool $first Whether to replace the first or the last occurrence of the needle(s). |
||
1240 | * @param string|null $encoding The encoding to use. |
||
1241 | * @return string The resulting string. |
||
1242 | */ |
||
1243 | protected static function replaceOccurrence(string $haystack, $needles, string $replacement, bool $strict, bool $first, string $encoding = null) : string |
||
1244 | { |
||
1245 | if ($haystack === '') { |
||
1246 | return ''; |
||
1247 | } |
||
1248 | |||
1249 | $encoding = $encoding ?: static::encoding($haystack); |
||
1250 | $method = $first ? 'indexOf' : 'indexOfLast'; |
||
1251 | |||
1252 | foreach ((array) $needles as $needle) { |
||
1253 | |||
1254 | // Pass to the next needle if this one is an empty string or if it couldn't be found |
||
1255 | // in the haystack at all. |
||
1256 | if ($needle === '' || -1 === $offset = static::$method($haystack, $needle, 0, $strict, $encoding) || 0 === $needleLen = mb_strlen($needle, $encoding)) { |
||
0 ignored issues
–
show
|
|||
1257 | continue; |
||
1258 | } |
||
1259 | |||
1260 | // Grab the substrings before and after the needle occurs, insert the replacement in between |
||
1261 | // and glue it together omitting the needle. |
||
1262 | $haystack = mb_substr($haystack, 0, $offset, $encoding) . $replacement . mb_substr($haystack, $offset + $needleLen, null, $encoding); |
||
0 ignored issues
–
show
The variable
$needleLen does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
![]() |
|||
1263 | } |
||
1264 | |||
1265 | return $haystack; |
||
1266 | } |
||
1267 | } |
||
1268 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.