Completed
Push — master ( e12837...80e432 )
by Basil
02:48
created

StringHelper::textList()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
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.
0 ignored issues
show
Bug introduced by
There is no parameter named $filters. Was it maybe removed?

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 method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
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