1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace luya\helpers; |
4
|
|
|
|
5
|
|
|
use yii\helpers\BaseStringHelper; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Helper methods when dealing with Strings. |
9
|
|
|
* |
10
|
|
|
* Extends the {{yii\helpers\StringHelper}} class by some usefull functions like: |
11
|
|
|
* |
12
|
|
|
* + {{luya\helpers\StringHelper::typeCast()}} |
13
|
|
|
* + {{luya\helpers\StringHelper::isFloat()}} |
14
|
|
|
* + {{luya\helpers\StringHelper::replaceFirst()}} |
15
|
|
|
* + {{luya\helpers\StringHelper::contains()}} |
16
|
|
|
* + {{luya\helpers\StringHelper::startsWithWildcard()}} |
17
|
|
|
* + {{luya\helpers\StringHelper::typeCastNumeric()}} |
18
|
|
|
* |
19
|
|
|
* @author Basil Suter <[email protected]> |
20
|
|
|
* @since 1.0.0 |
21
|
|
|
*/ |
22
|
|
|
class StringHelper extends BaseStringHelper |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* TypeCast a string to its specific types. |
26
|
|
|
* |
27
|
|
|
* Arrays will passed to to the {{luya\helpers\ArrayHelper::typeCast()}} class. |
28
|
|
|
* |
29
|
|
|
* @param mixed $string The input string to type cast. Arrays will be passted to {{luya\helpers\ArrayHelper::typeCast()}}. |
30
|
|
|
* @return mixed The new type casted value, if the input is an array the output is the typecasted array. |
31
|
|
|
*/ |
32
|
|
|
public static function typeCast($string) |
33
|
|
|
{ |
34
|
|
|
if (is_numeric($string)) { |
35
|
|
|
return static::typeCastNumeric($string); |
36
|
|
|
} elseif (is_array($string)) { |
37
|
|
|
return ArrayHelper::typeCast($string); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
return $string; |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* String Wildcard Check. |
45
|
|
|
* |
46
|
|
|
* Checks whether a strings starts with the wildcard symbole and compares the string before the wild card symbol * |
47
|
|
|
* with the string provided, if there is NO wildcard symbold it always return false. |
48
|
|
|
* |
49
|
|
|
* |
50
|
|
|
* @param string $string The string which should be checked with $with comperator |
51
|
|
|
* @param string $with The with string which must end with the wildcard symbol * e.g. `foo*` would match string `foobar`. |
52
|
|
|
* @param boolean $caseSensitive Whether to compare the starts with string as case sensitive or not, defaults to true. |
53
|
|
|
* @return boolean Whether the string starts with the wildcard marked string or not, if no wildcard symbol is contained. |
54
|
|
|
* in the $with it always returns false. |
55
|
|
|
*/ |
56
|
|
|
public static function startsWithWildcard($string, $with, $caseSensitive = true) |
57
|
|
|
{ |
58
|
|
|
if (substr($with, -1) != "*") { |
59
|
|
|
return false; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
return self::startsWith($string, rtrim($with, '*'), $caseSensitive); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
|
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* See if filter conditions match the given value. |
69
|
|
|
* |
70
|
|
|
* Example filter conditions: |
71
|
|
|
* |
72
|
|
|
* + `cms_*` matches everything starting with "cms_". |
73
|
|
|
* + `cms_*,admin_*` matches booth cms_* and admin_* tables. |
74
|
|
|
* + `!cms_*` matches all not start with "cms_" |
75
|
|
|
* + `!cms_*,!admin_*` matches all not starting with "cms_" and not starting with "admin_" |
76
|
|
|
* + `cms_*,!admin_*` matches all start with "cms_" but not start with "admin_" |
77
|
|
|
* |
78
|
|
|
* Only first match is relevant: |
79
|
|
|
* |
80
|
|
|
* + "cms_*,!admin_*,admin_*" include all cms_* tables but exclude all admin_* tables (last match has no effect) |
81
|
|
|
* + "cms_*,admin_*,!admin_*" include all cms_* and admin_* tables (last match has no effect) |
82
|
|
|
* |
83
|
|
|
* Example using condition string: |
84
|
|
|
* |
85
|
|
|
* ```php |
86
|
|
|
* filterMatch('hello', 'he*'); // true |
87
|
|
|
* filterMatch('hello', 'ho,he*'); // true |
88
|
|
|
* filterMatch('hello', ['ho', 'he*']); // true |
89
|
|
|
* ``` |
90
|
|
|
* |
91
|
|
|
* @param $value The value on which the filter conditions should be applied on. |
92
|
|
|
* @param array|string $filters An array of filter conditions, if a string is given he will be exploded by commas. |
|
|
|
|
93
|
|
|
* @param boolean $caseSensitive Whether to match value even when lower/upper case is not correct/same. |
94
|
|
|
* @return bool Returns true if one of the given filter conditions matches. |
95
|
|
|
* @since 1.3.0 |
96
|
|
|
*/ |
97
|
|
|
public static function filterMatch($value, $conditions, $caseSensitive = true) |
98
|
|
|
{ |
99
|
|
|
if (is_scalar($conditions)) { |
100
|
|
|
$conditions = self::explode($conditions, ",", true, true); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
foreach ($conditions as $condition) { |
104
|
|
|
$isMatch = true; |
105
|
|
|
// negate |
106
|
|
|
if (substr($condition, 0, 1) == "!") { |
107
|
|
|
$isMatch = false; |
108
|
|
|
$condition = substr($condition, 1); |
109
|
|
|
} |
110
|
|
|
if ($caseSensitive) { |
111
|
|
|
$condition = strtolower($condition); |
112
|
|
|
$value = strtolower($value); |
113
|
|
|
} |
114
|
|
|
if ($condition == $value || self::startsWithWildcard($value, $condition)) { |
115
|
|
|
return $isMatch; |
116
|
|
|
} |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
return false; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* TypeCast a numeric value to float or integer. |
124
|
|
|
* |
125
|
|
|
* If the given value is not a numeric or float value it will be returned as it is. In order to find out whether its float |
126
|
|
|
* or not use {{luya\helpers\StringHelper::isFloat()}}. |
127
|
|
|
* |
128
|
|
|
* @param mixed $value The given value to parse. |
129
|
|
|
* @return mixed Returns the original value if not numeric or integer, float casted value. |
130
|
|
|
*/ |
131
|
|
|
public static function typeCastNumeric($value) |
132
|
|
|
{ |
133
|
|
|
if (!self::isFloat($value)) { |
134
|
|
|
return $value; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
if (intval($value) == $value) { |
138
|
|
|
return (int) $value; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
return (float) $value; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Checks whether a string is a float value. |
146
|
|
|
* |
147
|
|
|
* Compared to `is_float` function of php, it only ensures whether the input variable is type float. |
148
|
|
|
* |
149
|
|
|
* @param mixed $value The value to check whether its float or not. |
150
|
|
|
* @return boolean Whether its a float value or not. |
151
|
|
|
*/ |
152
|
|
|
public static function isFloat($value) |
153
|
|
|
{ |
154
|
|
|
if (is_float($value)) { |
155
|
|
|
return true; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
return ($value == (string)(float) $value); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Replace only the first occurance found inside the string. |
163
|
|
|
* |
164
|
|
|
* The replace first method is *case sensitive*. |
165
|
|
|
* |
166
|
|
|
* ```php |
167
|
|
|
* StringHelper::replaceFirst('abc', '123', 'abc abc abc'); // returns "123 abc abc" |
168
|
|
|
* ``` |
169
|
|
|
* |
170
|
|
|
* @param string $search Search string to look for. |
171
|
|
|
* @param string $replace Replacement value for the first found occurrence. |
172
|
|
|
* @param string $subject The string you want to look up to replace the first element. |
173
|
|
|
* @return mixed Replaced string |
174
|
|
|
*/ |
175
|
|
|
public static function replaceFirst($search, $replace, $subject) |
176
|
|
|
{ |
177
|
|
|
return preg_replace('/'.preg_quote($search, '/').'/', $replace, $subject, 1); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Check whether a char or word exists in a string or not. |
182
|
|
|
* |
183
|
|
|
* This method is case sensitive. The need can be an array with multiple chars or words who |
184
|
|
|
* are going to look up in the haystack string. |
185
|
|
|
* |
186
|
|
|
* If an array of needle words is provided the $strict parameter defines whether all need keys must be found |
187
|
|
|
* in the string to get the `true` response or if just one of the keys are found the response is already `true`. |
188
|
|
|
* |
189
|
|
|
* ```php |
190
|
|
|
* if (StringHelper::contains('foo', 'the foo bar Bar'')) { |
191
|
|
|
* echo "yes!"; |
192
|
|
|
* } |
193
|
|
|
* ``` |
194
|
|
|
* |
195
|
|
|
* check if one of the given needles exists: |
196
|
|
|
* |
197
|
|
|
* ```php |
198
|
|
|
* if (StringHelper::contains(['jungle', 'hell0], 'Welcome to the jungle!)) { |
199
|
|
|
* echo "yes!"; |
200
|
|
|
* } |
201
|
|
|
* ``` |
202
|
|
|
* |
203
|
|
|
* @param string|array $needle The char or word to find in the $haystack. Can be an array to multi find words or char in the string. |
204
|
|
|
* @param string $haystack The haystack where the $needle string should be looked up. A string or phrase with words. |
205
|
|
|
* @param boolean $strict If an array of needles is provided the $strict parameter defines whether all keys must be found ($strict = true) or just one result must be found ($strict = false). |
206
|
|
|
* @return boolean If an array of values is provided the response may change depending on $findAll. |
207
|
|
|
*/ |
208
|
|
|
public static function contains($needle, $haystack, $strict = false) |
209
|
|
|
{ |
210
|
|
|
$needles = (array) $needle; |
211
|
|
|
|
212
|
|
|
$state = false; |
213
|
|
|
|
214
|
|
|
foreach ($needles as $item) { |
215
|
|
|
$state = (strpos($haystack, $item) !== false); |
216
|
|
|
|
217
|
|
|
if ($strict && !$state) { |
218
|
|
|
return false; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
if (!$strict && $state) { |
222
|
|
|
return true; |
223
|
|
|
} |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
return $state; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* "Minify" html content. |
231
|
|
|
* |
232
|
|
|
* + remove space |
233
|
|
|
* + remove tabs |
234
|
|
|
* + remove newlines |
235
|
|
|
* + remove html comments |
236
|
|
|
* |
237
|
|
|
* @param string $content The content to minify. |
238
|
|
|
* @param array $options Optional arguments to provide for minification: |
239
|
|
|
* - comments: boolean, where html comments should be removed or not. defaults to false |
240
|
|
|
* @return mixed Returns the minified content. |
241
|
|
|
* @since 1.0.7 |
242
|
|
|
*/ |
243
|
|
|
public static function minify($content, array $options = []) |
244
|
|
|
{ |
245
|
|
|
$min = preg_replace(['/[\n\r]/', '/\>[^\S ]+/s', '/[^\S ]+\</s', '/(\s)+/s', ], ['', '>', '<', '\\1'], trim($content)); |
246
|
|
|
$min = str_replace(['> <'], ['><'], $min); |
247
|
|
|
|
248
|
|
|
if (ArrayHelper::getValue($options, 'comments', false)) { |
249
|
|
|
$min = preg_replace('/<!--(.*)-->/Uis', '', $min); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
return $min; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Cut the given word/string from the content. Its truncates to the left side and to the right side of the word. |
257
|
|
|
* |
258
|
|
|
* An example of how a sentenced is cut: |
259
|
|
|
* |
260
|
|
|
* ```php |
261
|
|
|
* $cut = StringHelper::truncateMiddle('the quick fox jumped over the lazy dog', 'jumped', 12); |
262
|
|
|
* echo $cut; // ..e quick fox jumped over the la.. |
263
|
|
|
* ``` |
264
|
|
|
* |
265
|
|
|
* @param string $content The content to cut the words from. |
266
|
|
|
* @param string $word The word which should be in the middle of the string |
267
|
|
|
* @param integer $length The amount of the chars to cut on the left and right side from the word. |
268
|
|
|
* @param string $affix The chars which should be used for prefix and suffix when string is cuted. |
269
|
|
|
* @param boolean $caseSensitive Whether the search word in the string even when lower/upper case is not correct. |
270
|
|
|
* @since 1.0.12 |
271
|
|
|
*/ |
272
|
|
|
public static function truncateMiddle($content, $word, $length, $affix = '..', $caseSensitive = false) |
273
|
|
|
{ |
274
|
|
|
$content = strip_tags($content); |
275
|
|
|
$array = self::mb_str_split($content); |
276
|
|
|
$first = mb_strpos($caseSensitive ? $content : mb_strtolower($content), $caseSensitive ? $word : mb_strtolower($word)); |
277
|
|
|
|
278
|
|
|
// we could not find any match, therefore use casual truncate method. |
279
|
|
|
if ($first === false) { |
280
|
|
|
// as the length value in truncate middle stands for to the left and to the right, we multiple this value with 2 |
281
|
|
|
return self::truncate($content, ($length*2), $affix); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
$last = $first + mb_strlen($word); |
285
|
|
|
|
286
|
|
|
// left and right array chars from word |
287
|
|
|
$left = array_slice($array, 0, $first, true); |
288
|
|
|
$right = array_slice($array, $last, null, true); |
289
|
|
|
$middle = array_splice($array, $first, mb_strlen($word)); |
290
|
|
|
|
291
|
|
|
// string before |
292
|
|
|
$before = (count($left) > $length) ? $affix.implode("", array_slice($left, -$length)) : implode("", $left); |
293
|
|
|
$after = (count($right) > $length) ? implode("", array_slice($right, 0, $length)) . $affix : implode("", $right); |
294
|
|
|
|
295
|
|
|
return $before . implode("", $middle) . $after; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* Highlight a word within a content. |
300
|
|
|
* |
301
|
|
|
* Since version 1.0.14 an array of words to highlight is possible. |
302
|
|
|
* |
303
|
|
|
* > This function IS NOT case sensitive! |
304
|
|
|
* |
305
|
|
|
* |
306
|
|
|
* |
307
|
|
|
* @param string $content The content to find the word. |
308
|
|
|
* @param string $word The word to find within the content. |
309
|
|
|
* @param string $markup The markup used wrap the word to highlight. |
310
|
|
|
* @since 1.0.12 |
311
|
|
|
*/ |
312
|
|
|
public static function highlightWord($content, $word, $markup = '<b>%s</b>') |
313
|
|
|
{ |
314
|
|
|
$word = (array) $word; |
315
|
|
|
$content = strip_tags($content); |
316
|
|
|
$latest = null; |
317
|
|
|
foreach ($word as $needle) { |
318
|
|
|
preg_match_all("/".preg_quote($needle, '/')."+/i", $content, $matches); |
319
|
|
|
if (is_array($matches[0]) && count($matches[0]) >= 1) { |
320
|
|
|
foreach ($matches[0] as $match) { |
321
|
|
|
// ensure if a word is found twice we don't replace again. |
322
|
|
|
if ($latest === $match) { |
323
|
|
|
continue; |
324
|
|
|
} |
325
|
|
|
$content = str_replace($match, sprintf($markup, $match), $content); |
326
|
|
|
$latest = $match; |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
return $content; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* Multibyte-safe str_split funciton. |
336
|
|
|
* |
337
|
|
|
* @param string $string The string to split into an array |
338
|
|
|
* @param integer $length The length of the chars to cut. |
339
|
|
|
* @since 1.0.12 |
340
|
|
|
* @see https://www.php.net/manual/de/function.str-split.php#115703 |
341
|
|
|
*/ |
342
|
|
|
public static function mb_str_split($string, $length = 1) |
343
|
|
|
{ |
344
|
|
|
return preg_split('/(.{'.$length.'})/us', $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* Check whether a value is numeric or not. |
349
|
|
|
* |
350
|
|
|
* There are situations where is_numeric does not provide the desried result, |
351
|
|
|
* like for example `is_numeric('3e30')` would return true, as e can be considered |
352
|
|
|
* as exponential operator. |
353
|
|
|
* |
354
|
|
|
* Therfore this function checks with regex whether values or 0-9 if strict is enabled, |
355
|
|
|
* which is default behavior. |
356
|
|
|
* |
357
|
|
|
* @param mixed $value The value to check. |
358
|
|
|
* @param boolean $strict |
359
|
|
|
* @return boolean |
360
|
|
|
* @since 1.2.0 |
361
|
|
|
*/ |
362
|
|
|
public static function isNummeric($value, $strict = true) |
363
|
|
|
{ |
364
|
|
|
if (!is_scalar($value)) { |
365
|
|
|
return false; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
if (is_bool($value)) { |
369
|
|
|
return false; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
if ($strict) { |
373
|
|
|
return preg_match('/^[0-9]+$/', $value) == 1 ? true : false; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
return is_numeric($value); |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* Templating a string with Variables |
381
|
|
|
* |
382
|
|
|
* The variables should be declared as `{{username}}` while the variables array key should contain `username`. |
383
|
|
|
* |
384
|
|
|
* Usage example: |
385
|
|
|
* |
386
|
|
|
* ```php |
387
|
|
|
* $content = StringHelper::template('<p>{{ name }}</p>', ['name' => 'John']); |
388
|
|
|
* |
389
|
|
|
* // output: <p>John</p> |
390
|
|
|
* ``` |
391
|
|
|
* |
392
|
|
|
* If a variable is not found, the original curly bracktes will be returned. |
393
|
|
|
* |
394
|
|
|
* @param string $template The template to parse. The template may contain double curly brackets variables. |
395
|
|
|
* @param array $variables The variables which should be available in the template. |
396
|
|
|
* @param boolean $removeEmpty Whether variables in double curly brackets should be removed event the have not be assigned by $variables array. |
397
|
|
|
* @return string |
398
|
|
|
* @since 1.5.0 |
399
|
|
|
*/ |
400
|
|
|
public static function template($template, array $variables = [], $removeEmpty = false) |
401
|
|
|
{ |
402
|
|
|
preg_match_all("/{{(.*?)}}/", $template, $matches, PREG_SET_ORDER); |
403
|
|
|
|
404
|
|
|
if (empty($matches)) { |
405
|
|
|
return $template; |
406
|
|
|
} |
407
|
|
|
|
408
|
|
|
foreach ($matches as $match) { |
409
|
|
|
if (array_key_exists(trim($match[1]), $variables)) { |
410
|
|
|
$template = str_replace($match[0], $variables[trim($match[1])], $template); |
411
|
|
|
} elseif ($removeEmpty) { |
412
|
|
|
$template = str_replace($match[0], '', $template); |
413
|
|
|
} |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
return $template; |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
/** |
420
|
|
|
* Convert a text with different seperators to an array. |
421
|
|
|
* |
422
|
|
|
* Its very common to use seperators when working with user input, for example a list of domains seperated by commas. Therefore |
423
|
|
|
* this function will use common seperators the generate an array from a text string. |
424
|
|
|
* |
425
|
|
|
* Explodes the string by: "Newline", ";", "," |
426
|
|
|
* |
427
|
|
|
* + newline |
428
|
|
|
* + comma |
429
|
|
|
* + point comma |
430
|
|
|
* |
431
|
|
|
* @param string $text A text which contains a list of items seperated by seperators like commas. |
432
|
|
|
* @return array |
433
|
|
|
* @since 1.7.1 |
434
|
|
|
*/ |
435
|
|
|
public static function textList($text, array $seperators = [PHP_EOL, "\n", "\r", "\n\r", ";", ","]) |
436
|
|
|
{ |
437
|
|
|
return StringHelper::explode(str_replace($seperators, ';', $text), ";", true, true); |
438
|
|
|
} |
439
|
|
|
} |
440
|
|
|
|
This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.
Consider the following example. The parameter
$italy
is not defined by the methodfinale(...)
.The most likely cause is that the parameter was removed, but the annotation was not.