Passed
Pull Request — master (#596)
by Richard
08:46
created

Mbstring::mb_convert_variables()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 12
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Symfony\Polyfill\Mbstring;
13
14
/**
15
 * Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
16
 *
17
 * Implemented:
18
 * - mb_chr                  - Returns a specific character from its Unicode code point
19
 * - mb_convert_encoding     - Convert character encoding
20
 * - mb_convert_variables    - Convert character code in variable(s)
21
 * - mb_decode_mimeheader    - Decode string in MIME header field
22
 * - mb_encode_mimeheader    - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
23
 * - mb_decode_numericentity - Decode HTML numeric string reference to character
24
 * - mb_encode_numericentity - Encode character to HTML numeric string reference
25
 * - mb_convert_case         - Perform case folding on a string
26
 * - mb_detect_encoding      - Detect character encoding
27
 * - mb_get_info             - Get internal settings of mbstring
28
 * - mb_http_input           - Detect HTTP input character encoding
29
 * - mb_http_output          - Set/Get HTTP output character encoding
30
 * - mb_internal_encoding    - Set/Get internal character encoding
31
 * - mb_list_encodings       - Returns an array of all supported encodings
32
 * - mb_ord                  - Returns the Unicode code point of a character
33
 * - mb_output_handler       - Callback function converts character encoding in output buffer
34
 * - mb_scrub                - Replaces ill-formed byte sequences with substitute characters
35
 * - mb_strlen               - Get string length
36
 * - mb_strpos               - Find position of first occurrence of string in a string
37
 * - mb_strrpos              - Find position of last occurrence of a string in a string
38
 * - mb_strtolower           - Make a string lowercase
39
 * - mb_strtoupper           - Make a string uppercase
40
 * - mb_substitute_character - Set/Get substitution character
41
 * - mb_substr               - Get part of string
42
 * - mb_stripos              - Finds position of first occurrence of a string within another, case insensitive
43
 * - mb_stristr              - Finds first occurrence of a string within another, case insensitive
44
 * - mb_strrchr              - Finds the last occurrence of a character in a string within another
45
 * - mb_strrichr             - Finds the last occurrence of a character in a string within another, case insensitive
46
 * - mb_strripos             - Finds position of last occurrence of a string within another, case insensitive
47
 * - mb_strstr               - Finds first occurrence of a string within another
48
 * - mb_strwidth             - Return width of string
49
 * - mb_substr_count         - Count the number of substring occurrences
50
 *
51
 * Not implemented:
52
 * - mb_convert_kana         - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
53
 * - mb_ereg_*               - Regular expression with multibyte support
54
 * - mb_parse_str            - Parse GET/POST/COOKIE data and set global variable
55
 * - mb_preferred_mime_name  - Get MIME charset string
56
 * - mb_regex_encoding       - Returns current encoding for multibyte regex as string
57
 * - mb_regex_set_options    - Set/Get the default options for mbregex functions
58
 * - mb_send_mail            - Send encoded mail
59
 * - mb_split                - Split multibyte string using regular expression
60
 * - mb_strcut               - Get part of string
61
 * - mb_strimwidth           - Get truncated string with specified width
62
 *
63
 * @author Nicolas Grekas <[email protected]>
64
 *
65
 * @internal
66
 */
67
final class Mbstring
68
{
69
    const MB_CASE_FOLD = PHP_INT_MAX;
70
71
    private static $encodingList = array('ASCII', 'UTF-8');
72
    private static $language = 'neutral';
73
    private static $internalEncoding = 'UTF-8';
74
    private static $caseFold = array(
75
        array('µ','ſ',"\xCD\x85",'ς',"\xCF\x90","\xCF\x91","\xCF\x95","\xCF\x96","\xCF\xB0","\xCF\xB1","\xCF\xB5","\xE1\xBA\x9B","\xE1\xBE\xBE"),
76
        array('μ','s','ι',       'σ','β',       'θ',       'φ',       'π',       'κ',       'ρ',       'ε',       "\xE1\xB9\xA1",'ι'),
77
    );
78
79
    public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null)
80
    {
81
        if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) {
82
            $fromEncoding = self::mb_detect_encoding($s, $fromEncoding);
83
        } else {
84
            $fromEncoding = self::getEncoding($fromEncoding);
85
        }
86
87
        $toEncoding = self::getEncoding($toEncoding);
88
89
        if ('BASE64' === $fromEncoding) {
90
            $s = base64_decode($s);
91
            $fromEncoding = $toEncoding;
92
        }
93
94
        if ('BASE64' === $toEncoding) {
95
            return base64_encode($s);
96
        }
97
98
        if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) {
99
            if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) {
100
                $fromEncoding = 'Windows-1252';
101
            }
102
            if ('UTF-8' !== $fromEncoding) {
103
                $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s);
104
            }
105
106
            return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s);
107
        }
108
109
        if ('HTML-ENTITIES' === $fromEncoding) {
110
            $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8');
111
            $fromEncoding = 'UTF-8';
112
        }
113
114
        return iconv($fromEncoding, $toEncoding.'//IGNORE', $s);
115
    }
116
117
    public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null)
118
    {
119
        $vars = array(&$a, &$b, &$c, &$d, &$e, &$f);
120
121
        $ok = true;
122
        array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
123
            if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
0 ignored issues
show
introduced by
The condition false === $v = Symfony\P...ncoding, $fromEncoding) is always false.
Loading history...
124
                $ok = false;
125
            }
126
        });
127
128
        return $ok ? $fromEncoding : false;
129
    }
130
131
    public static function mb_decode_mimeheader($s)
132
    {
133
        return iconv_mime_decode($s, 2, self::$internalEncoding);
134
    }
135
136
    public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)
0 ignored issues
show
Unused Code introduced by
The parameter $indent is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

136
    public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, /** @scrutinizer ignore-unused */ $indent = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $s is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

136
    public static function mb_encode_mimeheader(/** @scrutinizer ignore-unused */ $s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $linefeed is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

136
    public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, /** @scrutinizer ignore-unused */ $linefeed = null, $indent = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $transferEncoding is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

136
    public static function mb_encode_mimeheader($s, $charset = null, /** @scrutinizer ignore-unused */ $transferEncoding = null, $linefeed = null, $indent = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $charset is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

136
    public static function mb_encode_mimeheader($s, /** @scrutinizer ignore-unused */ $charset = null, $transferEncoding = null, $linefeed = null, $indent = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
137
    {
138
        trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING);
139
    }
140
141
    public static function mb_decode_numericentity($s, $convmap, $encoding = null)
142
    {
143
        if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
144
            trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
145
            return null;
146
        }
147
148
        if (!\is_array($convmap) || !$convmap) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $convmap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
149
            return false;
150
        }
151
152
        if (null !== $encoding && !\is_scalar($encoding)) {
153
            trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
154
            return '';  // Instead of null (cf. mb_encode_numericentity).
155
        }
156
157
        $s = (string) $s;
158
        if ('' === $s) {
159
            return '';
160
        }
161
162
        $encoding = self::getEncoding($encoding);
163
164
        if ('UTF-8' === $encoding) {
165
            $encoding = null;
166
            if (!preg_match('//u', $s)) {
167
                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
168
            }
169
        } else {
170
            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
171
        }
172
173
        $cnt = floor(\count($convmap) / 4) * 4;
174
175
        for ($i = 0; $i < $cnt; $i += 4) {
176
            // collector_decode_htmlnumericentity ignores $convmap[$i + 3]
177
            $convmap[$i] += $convmap[$i + 2];
178
            $convmap[$i + 1] += $convmap[$i + 2];
179
        }
180
181
        $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
182
            $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
183
            for ($i = 0; $i < $cnt; $i += 4) {
184
                if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
185
                    return Mbstring::mb_chr($c - $convmap[$i + 2]);
186
                }
187
            }
188
            return $m[0];
189
        }, $s);
190
191
        if (null === $encoding) {
192
            return $s;
193
        }
194
195
        return iconv('UTF-8', $encoding.'//IGNORE', $s);
196
    }
197
198
    public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
199
    {
200
        if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) {
201
            trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING);
202
            return null;
203
        }
204
205
        if (!\is_array($convmap) || !$convmap) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $convmap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
206
            return false;
207
        }
208
209
        if (null !== $encoding && !\is_scalar($encoding)) {
210
            trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING);
211
            return null;  // Instead of '' (cf. mb_decode_numericentity).
212
        }
213
214
        if (null !== $is_hex && !\is_scalar($is_hex)) {
215
            trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.gettype($s).' given', E_USER_WARNING);
216
            return null;
217
        }
218
219
        $s = (string) $s;
220
        if ('' === $s) {
221
            return '';
222
        }
223
224
        $encoding = self::getEncoding($encoding);
225
226
        if ('UTF-8' === $encoding) {
227
            $encoding = null;
228
            if (!preg_match('//u', $s)) {
229
                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
230
            }
231
        } else {
232
            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
233
        }
234
235
        static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
236
237
        $cnt = floor(\count($convmap) / 4) * 4;
238
        $i = 0;
239
        $len = \strlen($s);
240
        $result = '';
241
242
        while ($i < $len) {
243
            $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
244
            $uchr = substr($s, $i, $ulen);
245
            $i += $ulen;
246
            $c = self::mb_ord($uchr);
247
248
            for ($j = 0; $j < $cnt; $j += 4) {
249
                if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
250
                    $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
251
                    $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
252
                    continue 2;
253
                }
254
            }
255
            $result .= $uchr;
256
        }
257
258
        if (null === $encoding) {
259
            return $result;
260
        }
261
262
        return iconv('UTF-8', $encoding.'//IGNORE', $result);
263
    }
264
265
    public static function mb_convert_case($s, $mode, $encoding = null)
266
    {
267
        $s = (string) $s;
268
        if ('' === $s) {
269
            return '';
270
        }
271
272
        $encoding = self::getEncoding($encoding);
273
274
        if ('UTF-8' === $encoding) {
275
            $encoding = null;
276
            if (!preg_match('//u', $s)) {
277
                $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s);
278
            }
279
        } else {
280
            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
281
        }
282
283
        if (MB_CASE_TITLE == $mode) {
284
            static $titleRegexp = null;
285
            if (null === $titleRegexp) {
286
                $titleRegexp = self::getData('titleCaseRegexp');
287
            }
288
            $s = preg_replace_callback($titleRegexp, array(__CLASS__, 'title_case'), $s);
289
        } else {
290
            if (MB_CASE_UPPER == $mode) {
291
                static $upper = null;
292
                if (null === $upper) {
293
                    $upper = self::getData('upperCase');
294
                }
295
                $map = $upper;
296
            } else {
297
                if (self::MB_CASE_FOLD === $mode) {
298
                    $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s);
299
                }
300
301
                static $lower = null;
302
                if (null === $lower) {
303
                    $lower = self::getData('lowerCase');
304
                }
305
                $map = $lower;
306
            }
307
308
            static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4);
309
310
            $i = 0;
311
            $len = \strlen($s);
312
313
            while ($i < $len) {
314
                $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"];
315
                $uchr = substr($s, $i, $ulen);
316
                $i += $ulen;
317
318
                if (isset($map[$uchr])) {
319
                    $uchr = $map[$uchr];
320
                    $nlen = \strlen($uchr);
321
322
                    if ($nlen == $ulen) {
323
                        $nlen = $i;
324
                        do {
325
                            $s[--$nlen] = $uchr[--$ulen];
326
                        } while ($ulen);
327
                    } else {
328
                        $s = substr_replace($s, $uchr, $i - $ulen, $ulen);
329
                        $len += $nlen - $ulen;
330
                        $i   += $nlen - $ulen;
331
                    }
332
                }
333
            }
334
        }
335
336
        if (null === $encoding) {
337
            return $s;
338
        }
339
340
        return iconv('UTF-8', $encoding.'//IGNORE', $s);
341
    }
342
343
    public static function mb_internal_encoding($encoding = null)
344
    {
345
        if (null === $encoding) {
346
            return self::$internalEncoding;
347
        }
348
349
        $encoding = self::getEncoding($encoding);
350
351
        if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) {
352
            self::$internalEncoding = $encoding;
353
354
            return true;
355
        }
356
357
        return false;
358
    }
359
360
    public static function mb_language($lang = null)
361
    {
362
        if (null === $lang) {
363
            return self::$language;
364
        }
365
366
        switch ($lang = strtolower($lang)) {
367
            case 'uni':
368
            case 'neutral':
369
                self::$language = $lang;
370
371
                return true;
372
        }
373
374
        return false;
375
    }
376
377
    public static function mb_list_encodings()
378
    {
379
        return array('UTF-8');
380
    }
381
382
    public static function mb_encoding_aliases($encoding)
383
    {
384
        switch (strtoupper($encoding)) {
385
            case 'UTF8':
386
            case 'UTF-8':
387
                return array('utf8');
388
        }
389
390
        return false;
391
    }
392
393
    public static function mb_check_encoding($var = null, $encoding = null)
394
    {
395
        if (null === $encoding) {
396
            if (null === $var) {
397
                return false;
398
            }
399
            $encoding = self::$internalEncoding;
400
        }
401
402
        return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var);
403
    }
404
405
    public static function mb_detect_encoding($str, $encodingList = null, $strict = false)
0 ignored issues
show
Unused Code introduced by
The parameter $strict is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

405
    public static function mb_detect_encoding($str, $encodingList = null, /** @scrutinizer ignore-unused */ $strict = false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
406
    {
407
        if (null === $encodingList) {
408
            $encodingList = self::$encodingList;
409
        } else {
410
            if (!\is_array($encodingList)) {
411
                $encodingList = array_map('trim', explode(',', $encodingList));
412
            }
413
            $encodingList = array_map('strtoupper', $encodingList);
414
        }
415
416
        foreach ($encodingList as $enc) {
417
            switch ($enc) {
418
                case 'ASCII':
419
                    if (!preg_match('/[\x80-\xFF]/', $str)) {
420
                        return $enc;
421
                    }
422
                    break;
423
424
                case 'UTF8':
425
                case 'UTF-8':
426
                    if (preg_match('//u', $str)) {
427
                        return 'UTF-8';
428
                    }
429
                    break;
430
431
                default:
432
                    if (0 === strncmp($enc, 'ISO-8859-', 9)) {
433
                        return $enc;
434
                    }
435
            }
436
        }
437
438
        return false;
439
    }
440
441
    public static function mb_detect_order($encodingList = null)
442
    {
443
        if (null === $encodingList) {
444
            return self::$encodingList;
445
        }
446
447
        if (!\is_array($encodingList)) {
448
            $encodingList = array_map('trim', explode(',', $encodingList));
449
        }
450
        $encodingList = array_map('strtoupper', $encodingList);
451
452
        foreach ($encodingList as $enc) {
453
            switch ($enc) {
454
                default:
455
                    if (strncmp($enc, 'ISO-8859-', 9)) {
456
                        return false;
457
                    }
458
                case 'ASCII':
459
                case 'UTF8':
460
                case 'UTF-8':
461
            }
462
        }
463
464
        self::$encodingList = $encodingList;
465
466
        return true;
467
    }
468
469
    public static function mb_strlen($s, $encoding = null)
470
    {
471
        $encoding = self::getEncoding($encoding);
472
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
473
            return \strlen($s);
474
        }
475
476
        return @iconv_strlen($s, $encoding);
477
    }
478
479
    public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null)
480
    {
481
        $encoding = self::getEncoding($encoding);
482
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
483
            return strpos($haystack, $needle, $offset);
484
        }
485
486
        $needle = (string) $needle;
487
        if ('' === $needle) {
488
            trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING);
489
490
            return false;
491
        }
492
493
        return iconv_strpos($haystack, $needle, $offset, $encoding);
494
    }
495
496
    public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null)
497
    {
498
        $encoding = self::getEncoding($encoding);
499
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
500
            return strrpos($haystack, $needle, $offset);
501
        }
502
503
        if ($offset != (int) $offset) {
504
            $offset = 0;
505
        } elseif ($offset = (int) $offset) {
506
            if ($offset < 0) {
507
                $haystack = self::mb_substr($haystack, 0, $offset, $encoding);
508
                $offset = 0;
509
            } else {
510
                $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding);
511
            }
512
        }
513
514
        $pos = iconv_strrpos($haystack, $needle, $encoding);
515
516
        return false !== $pos ? $offset + $pos : false;
517
    }
518
519
    public static function mb_strtolower($s, $encoding = null)
520
    {
521
        return self::mb_convert_case($s, MB_CASE_LOWER, $encoding);
522
    }
523
524
    public static function mb_strtoupper($s, $encoding = null)
525
    {
526
        return self::mb_convert_case($s, MB_CASE_UPPER, $encoding);
527
    }
528
529
    public static function mb_substitute_character($c = null)
530
    {
531
        if (0 === strcasecmp($c, 'none')) {
532
            return true;
533
        }
534
535
        return null !== $c ? false : 'none';
536
    }
537
538
    public static function mb_substr($s, $start, $length = null, $encoding = null)
539
    {
540
        $encoding = self::getEncoding($encoding);
541
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
542
            return substr($s, $start, null === $length ? 2147483647 : $length);
543
        }
544
545
        if ($start < 0) {
546
            $start = iconv_strlen($s, $encoding) + $start;
547
            if ($start < 0) {
548
                $start = 0;
549
            }
550
        }
551
552
        if (null === $length) {
553
            $length = 2147483647;
554
        } elseif ($length < 0) {
555
            $length = iconv_strlen($s, $encoding) + $length - $start;
556
            if ($length < 0) {
557
                return '';
558
            }
559
        }
560
561
        return (string) iconv_substr($s, $start, $length, $encoding);
562
    }
563
564
    public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null)
565
    {
566
        $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
567
        $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
568
569
        return self::mb_strpos($haystack, $needle, $offset, $encoding);
570
    }
571
572
    public static function mb_stristr($haystack, $needle, $part = false, $encoding = null)
573
    {
574
        $pos = self::mb_stripos($haystack, $needle, 0, $encoding);
575
576
        return self::getSubpart($pos, $part, $haystack, $encoding);
577
    }
578
579
    public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null)
580
    {
581
        $encoding = self::getEncoding($encoding);
582
        if ('CP850' === $encoding || 'ASCII' === $encoding) {
583
            return strrchr($haystack, $needle, $part);
0 ignored issues
show
Unused Code introduced by
The call to strrchr() has too many arguments starting with $part. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

583
            return /** @scrutinizer ignore-call */ strrchr($haystack, $needle, $part);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
584
        }
585
        $needle = self::mb_substr($needle, 0, 1, $encoding);
586
        $pos = iconv_strrpos($haystack, $needle, $encoding);
587
588
        return self::getSubpart($pos, $part, $haystack, $encoding);
589
    }
590
591
    public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null)
592
    {
593
        $needle = self::mb_substr($needle, 0, 1, $encoding);
594
        $pos = self::mb_strripos($haystack, $needle, $encoding);
595
596
        return self::getSubpart($pos, $part, $haystack, $encoding);
597
    }
598
599
    public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null)
600
    {
601
        $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding);
602
        $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding);
603
604
        return self::mb_strrpos($haystack, $needle, $offset, $encoding);
605
    }
606
607
    public static function mb_strstr($haystack, $needle, $part = false, $encoding = null)
0 ignored issues
show
Unused Code introduced by
The parameter $encoding is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

607
    public static function mb_strstr($haystack, $needle, $part = false, /** @scrutinizer ignore-unused */ $encoding = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
608
    {
609
        $pos = strpos($haystack, $needle);
610
        if (false === $pos) {
611
            return false;
612
        }
613
        if ($part) {
614
            return substr($haystack, 0, $pos);
615
        }
616
617
        return substr($haystack, $pos);
618
    }
619
620
    public static function mb_get_info($type = 'all')
621
    {
622
        $info = array(
623
            'internal_encoding' => self::$internalEncoding,
624
            'http_output' => 'pass',
625
            'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)',
626
            'func_overload' => 0,
627
            'func_overload_list' => 'no overload',
628
            'mail_charset' => 'UTF-8',
629
            'mail_header_encoding' => 'BASE64',
630
            'mail_body_encoding' => 'BASE64',
631
            'illegal_chars' => 0,
632
            'encoding_translation' => 'Off',
633
            'language' => self::$language,
634
            'detect_order' => self::$encodingList,
635
            'substitute_character' => 'none',
636
            'strict_detection' => 'Off',
637
        );
638
639
        if ('all' === $type) {
640
            return $info;
641
        }
642
        if (isset($info[$type])) {
643
            return $info[$type];
644
        }
645
646
        return false;
647
    }
648
649
    public static function mb_http_input($type = '')
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

649
    public static function mb_http_input(/** @scrutinizer ignore-unused */ $type = '')

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
650
    {
651
        return false;
652
    }
653
654
    public static function mb_http_output($encoding = null)
655
    {
656
        return null !== $encoding ? 'pass' === $encoding : 'pass';
657
    }
658
659
    public static function mb_strwidth($s, $encoding = null)
660
    {
661
        $encoding = self::getEncoding($encoding);
662
663
        if ('UTF-8' !== $encoding) {
664
            $s = iconv($encoding, 'UTF-8//IGNORE', $s);
665
        }
666
667
        $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide);
668
669
        return ($wide << 1) + iconv_strlen($s, 'UTF-8');
670
    }
671
672
    public static function mb_substr_count($haystack, $needle, $encoding = null)
0 ignored issues
show
Unused Code introduced by
The parameter $encoding is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

672
    public static function mb_substr_count($haystack, $needle, /** @scrutinizer ignore-unused */ $encoding = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
673
    {
674
        return substr_count($haystack, $needle);
675
    }
676
677
    public static function mb_output_handler($contents, $status)
0 ignored issues
show
Unused Code introduced by
The parameter $status is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

677
    public static function mb_output_handler($contents, /** @scrutinizer ignore-unused */ $status)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
678
    {
679
        return $contents;
680
    }
681
682
    public static function mb_chr($code, $encoding = null)
683
    {
684
        if (0x80 > $code %= 0x200000) {
685
            $s = \chr($code);
686
        } elseif (0x800 > $code) {
687
            $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F);
688
        } elseif (0x10000 > $code) {
689
            $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
690
        } else {
691
            $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F);
692
        }
693
694
        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
695
            $s = mb_convert_encoding($s, $encoding, 'UTF-8');
696
        }
697
698
        return $s;
699
    }
700
701
    public static function mb_ord($s, $encoding = null)
702
    {
703
        if ('UTF-8' !== $encoding = self::getEncoding($encoding)) {
704
            $s = mb_convert_encoding($s, 'UTF-8', $encoding);
705
        }
706
707
        $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0;
0 ignored issues
show
Bug introduced by
$s of type array is incompatible with the type string expected by parameter $string of substr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

707
        $code = ($s = unpack('C*', substr(/** @scrutinizer ignore-type */ $s, 0, 4))) ? $s[1] : 0;
Loading history...
708
        if (0xF0 <= $code) {
709
            return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
710
        }
711
        if (0xE0 <= $code) {
712
            return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
713
        }
714
        if (0xC0 <= $code) {
715
            return (($code - 0xC0) << 6) + $s[2] - 0x80;
716
        }
717
718
        return $code;
719
    }
720
721
    private static function getSubpart($pos, $part, $haystack, $encoding)
722
    {
723
        if (false === $pos) {
724
            return false;
725
        }
726
        if ($part) {
727
            return self::mb_substr($haystack, 0, $pos, $encoding);
728
        }
729
730
        return self::mb_substr($haystack, $pos, null, $encoding);
731
    }
732
733
    private static function html_encoding_callback(array $m)
734
    {
735
        $i = 1;
736
        $entities = '';
737
        $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8'));
738
739
        while (isset($m[$i])) {
740
            if (0x80 > $m[$i]) {
741
                $entities .= \chr($m[$i++]);
742
                continue;
743
            }
744
            if (0xF0 <= $m[$i]) {
745
                $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
746
            } elseif (0xE0 <= $m[$i]) {
747
                $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80;
748
            } else {
749
                $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80;
750
            }
751
752
            $entities .= '&#'.$c.';';
753
        }
754
755
        return $entities;
756
    }
757
758
    private static function title_case(array $s)
759
    {
760
        return self::mb_convert_case($s[1], MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], MB_CASE_LOWER, 'UTF-8');
761
    }
762
763
    private static function getData($file)
764
    {
765
        if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) {
766
            return require $file;
767
        }
768
769
        return false;
770
    }
771
772
    private static function getEncoding($encoding)
773
    {
774
        if (null === $encoding) {
775
            return self::$internalEncoding;
776
        }
777
778
        $encoding = strtoupper($encoding);
779
780
        if ('8BIT' === $encoding || 'BINARY' === $encoding) {
781
            return 'CP850';
782
        }
783
        if ('UTF8' === $encoding) {
784
            return 'UTF-8';
785
        }
786
787
        return $encoding;
788
    }
789
}
790