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() |
||
128 | // and self::from() instead. |
||
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)) { |
|
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)) { |
|
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++) { |
|
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 |
|
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 |
|
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) { |
|
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) { |
|
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) { |
||
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--) { |
|
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)) { |
|
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)) { |
|
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)) { |
||
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
|
|||
1263 | } |
||
1264 | |||
1265 | return $haystack; |
||
1266 | } |
||
1267 | } |
||
1268 |
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:
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
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: