Completed
Pull Request — master (#2016)
by Basil
02:21
created

StringHelper::filterMatch()   B

Complexity

Conditions 7
Paths 18

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 43
rs 8.2986
c 0
b 0
f 0
cc 7
nc 18
nop 3
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_*" include only cms_* tables
73
     * + "cms_*,admin_*" include only cms_* and admin_* tables
74
     * + "!cms_*" exclude all cms_* tables
75
     * + "!cms_*,!admin_*" exclude all cms_*and admin_* tables
76
     * + "cms_*,!admin_*" include all cms_* tables but exclude all admin_* tables
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 True if table can be skipped.
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
        $skip = true;
122
123
        foreach ($filters as $filter) {
124
            $exclude = false;
125
            if (substr($filter, 0, 1) == "!") {
126
                $exclude = true;
127
                $skip = false;
128
129
                $filter = substr($filter, 1);
130
            }
131
            
132
            if ($filter == $tableName || StringHelper::startsWithWildcard($tableName, $filter)) {
133
                return $exclude;
134
            }
135
        }
136
137
        return $skip;
138
        */
139
    }
140
    
141
    /**
142
     * TypeCast a numeric value to float or integer.
143
     *
144
     * 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
145
     * or not use {{luya\helpers\StringHelper::isFloat()}}.
146
     *
147
     * @param mixed $value The given value to parse.
148
     * @return mixed Returns the original value if not numeric or integer, float casted value.
149
     */
150
    public static function typeCastNumeric($value)
151
    {
152
        if (!self::isFloat($value)) {
153
            return $value;
154
        }
155
        
156
        if (intval($value) == $value) {
157
            return (int) $value;
158
        }
159
        
160
        return (float) $value;
161
    }
162
    
163
    /**
164
     * Checks whether a string is a float value.
165
     *
166
     * Compared to `is_float` function of php, it only ensures whether the input variable is type float.
167
     *
168
     * @param mixed $value The value to check whether its float or not.
169
     * @return boolean Whether its a float value or not.
170
     */
171
    public static function isFloat($value)
172
    {
173
        if (is_float($value)) {
174
            return true;
175
        }
176
        
177
        return ($value == (string)(float) $value);
178
    }
179
    
180
    /**
181
     * Replace only the first occurance found inside the string.
182
     *
183
     * The replace first method is *case sensitive*.
184
     *
185
     * ```php
186
     * StringHelper::replaceFirst('abc', '123', 'abc abc abc'); // returns "123 abc abc"
187
     * ```
188
     *
189
     * @param string $search Search string to look for.
190
     * @param string $replace Replacement value for the first found occurrence.
191
     * @param string $subject The string you want to look up to replace the first element.
192
     * @return mixed Replaced string
193
     */
194
    public static function replaceFirst($search, $replace, $subject)
195
    {
196
        return preg_replace('/'.preg_quote($search, '/').'/', $replace, $subject, 1);
197
    }
198
    
199
    /**
200
     * Check whether a char or word exists in a string or not.
201
     *
202
     * This method is case sensitive. The need can be an array with multiple chars or words who
203
     * are going to look up in the haystack string.
204
     *
205
     * If an array of needle words is provided the $strict parameter defines whether all need keys must be found
206
     * in the string to get the `true` response or if just one of the keys are found the response is already `true`.
207
     *
208
     * ```php
209
     * if (StringHelper::contains('foo', 'the foo bar Bar'')) {
210
     *    echo "yes!";
211
     * }
212
     * ```
213
     *
214
     * check if one of the given needles exists:
215
     *
216
     * ```php
217
     * if (StringHelper::contains(['jungle', 'hell0], 'Welcome to the jungle!)) {
218
     *    echo "yes!";
219
     * }
220
     * ```
221
     *
222
     * @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.
223
     * @param string $haystack The haystack where the $needle string should be looked up. A string or phrase with words.
224
     * @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).
225
     * @return boolean If an array of values is provided the response may change depending on $findAll.
226
     */
227
    public static function contains($needle, $haystack, $strict = false)
228
    {
229
        $needles = (array) $needle;
230
        
231
        $state = false;
232
        
233
        foreach ($needles as $item) {
234
            $state = (strpos($haystack, $item) !== false);
235
            
236
            if ($strict && !$state) {
237
                return false;
238
            }
239
            
240
            if (!$strict && $state) {
241
                return true;
242
            }
243
        }
244
245
        return $state;
246
    }
247
    
248
    /**
249
     * "Minify" html content.
250
     *
251
     * + remove space
252
     * + remove tabs
253
     * + remove newlines
254
     * + remove html comments
255
     *
256
     * @param string $content The content to minify.
257
     * @param array $options Optional arguments to provide for minification:
258
     * - comments: boolean, where html comments should be removed or not. defaults to false
259
     * @return mixed Returns the minified content.
260
     * @since 1.0.7
261
     */
262
    public static function minify($content, array $options = [])
263
    {
264
        $min = preg_replace(['/[\n\r]/', '/\>[^\S ]+/s', '/[^\S ]+\</s', '/(\s)+/s', ], ['', '>', '<', '\\1'], trim($content));
265
        $min = str_replace(['> <'], ['><'], $min);
266
        
267
        if (ArrayHelper::getValue($options, 'comments', false)) {
268
            $min = preg_replace('/<!--(.*)-->/Uis', '', $min);
269
        }
270
        
271
        return $min;
272
    }
273
274
    /**
275
     * Cut the given word/string from the content. Its truncates to the left side and to the right side of the word.
276
     *
277
     * An example of how a sentenced is cut:
278
     *
279
     * ```php
280
     * $cut = StringHelper::truncateMiddle('the quick fox jumped over the lazy dog', 'jumped', 12);
281
     * echo $cut; // ..e quick fox jumped over the la..
282
     * ```
283
     *
284
     * @param string $content The content to cut the words from.
285
     * @param string $word The word which should be in the middle of the string
286
     * @param integer $length The amount of the chars to cut on the left and right side from the word.
287
     * @param string $affix The chars which should be used for prefix and suffix when string is cuted.
288
     * @param boolean $caseSensitive Whether the search word in the string even when lower/upper case is not correct.
289
     * @since 1.0.12
290
     */
291
    public static function truncateMiddle($content, $word, $length, $affix = '..', $caseSensitive = false)
292
    {
293
        $content = strip_tags($content);
294
        $array = self::mb_str_split($content);
295
        $first = mb_strpos($caseSensitive ? $content : mb_strtolower($content), $caseSensitive ? $word : mb_strtolower($word));
296
297
        // we could not find any match, therefore use casual truncate method.
298
        if ($first === false) {
299
            // as the length value in truncate middle stands for to the left and to the right, we multiple this value with 2
300
            return self::truncate($content, ($length*2), $affix);
301
        }
302
303
        $last = $first + mb_strlen($word);
304
305
        // left and right array chars from word
306
        $left = array_slice($array, 0, $first, true);
307
        $right = array_slice($array, $last, null, true);
308
        $middle = array_splice($array, $first, mb_strlen($word));
309
310
        // string before
311
        $before = (count($left) > $length) ? $affix.implode("", array_slice($left, -$length)) : implode("", $left);
312
        $after = (count($right) > $length) ? implode("", array_slice($right, 0, $length)) . $affix : implode("", $right);
313
314
        return $before . implode("", $middle) . $after;
315
    }
316
317
    /**
318
     * Highlight a word within a content.
319
     *
320
     * Since version 1.0.14 an array of words to highlight is possible.
321
     *
322
     * > This function IS NOT case sensitive!
323
     *
324
     *
325
     *
326
     * @param string $content The content to find the word.
327
     * @param string $word The word to find within the content.
328
     * @param string $markup The markup used wrap the word to highlight.
329
     * @since 1.0.12
330
     */
331
    public static function highlightWord($content, $word, $markup = '<b>%s</b>')
332
    {
333
        $word = (array) $word;
334
        $content = strip_tags($content);
335
        $latest = null;
336
        foreach ($word as $needle) {
337
            preg_match_all("/".preg_quote($needle, '/')."+/i", $content, $matches);
338
            if (is_array($matches[0]) && count($matches[0]) >= 1) {
339
                foreach ($matches[0] as $match) {
340
                    // ensure if a word is found twice we don't replace again.
341
                    if ($latest === $match) {
342
                        continue;
343
                    }
344
                    $content = str_replace($match, sprintf($markup, $match), $content);
345
                    $latest = $match;
346
                }
347
            }
348
        }
349
350
        return $content;
351
    }
352
353
    /**
354
     * Multibyte-safe str_split funciton.
355
     *
356
     * @param string $string The string to split into an array
357
     * @param integer $length The length of the chars to cut.
358
     * @since 1.0.12
359
     * @see https://www.php.net/manual/de/function.str-split.php#115703
360
     */
361
    public static function mb_str_split($string, $length = 1)
362
    {
363
        return preg_split('/(.{'.$length.'})/us', $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
364
    }
365
366
    /**
367
     * Check whether a value is numeric or not.
368
     * 
369
     * There are situations where is_numeric does not provide the desried result,
370
     * like for example `is_numeric('3e30')` would return true, as e can be considered
371
     * as exponential operator.
372
     * 
373
     * Therfore this function checks with regex whether values or 0-9 if strict is enabled,
374
     * which is default behavior.
375
     *
376
     * @param mixed $value The value to check.
377
     * @param boolean $strict
378
     * @return boolean
379
     * @since 1.2.0
380
     */
381
    public static function isNummeric($value, $strict = true)
382
    {
383
        if (!is_scalar($value)) {
384
            return false;
385
        }
386
387
        if (is_bool($value)) {
388
            return false;
389
        }
390
391
        if ($strict) {
392
            return preg_match('/^[0-9]+$/', $value) == 1 ? true : false;
393
        }    
394
395
        return is_numeric($value);
396
    }
397
}
398