Test Failed
Push — master ( 7b17f2...cf58f0 )
by Julien
13:02
created

mb_vsprintf()   C

Complexity

Conditions 15
Paths 176

Size

Total Lines 85
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 33.1146

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 15
eloc 46
c 1
b 0
f 0
nc 176
nop 3
dl 0
loc 85
ccs 25
cts 44
cp 0.5682
crap 33.1146
rs 5.2833

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
if (!function_exists('implode_sprintf')) {
4
    /**
5
     * Will implode an array_map return of the sprintf or mb_sprintf results
6
     */
7
    function implode_sprintf(array $array = [], string $glue = ' ', string $format = '%s', $multibyte = false): string
8
    {
9
        return implode($glue, array_map(function ($value, $key) use ($format, $multibyte) {
10
            return $multibyte
11
                ? mb_sprintf($format, $value, $key)
12
                : sprintf($format, $value, $key);
13
        }, $array, array_keys($array)));
14
    }
15
}
16
17
if (!function_exists('implode_mb_sprintf')) {
18
    /**
19
     * Will implode an array_map return of the mb_sprintf results
20
     */
21
    function implode_mb_sprintf(array $array = [], string $glue = ' ', string $format = '%s'): string
22
    {
23
        return implode_sprintf($array, $glue, $format, true);
24
    }
25
}
26
27
if (!function_exists('sprintfn')) {
28
    /**
29
     * version of sprintf for cases where named arguments are desired (php syntax)
30
     *
31
     * with sprintf: sprintf('second: %2$s ; first: %1$s', '1st', '2nd');
32
     *
33
     * with sprintfn: sprintfn('second: %second$s ; first: %first$s', array(
34
     *  'first' => '1st',
35
     *  'second'=> '2nd'
36
     * ));
37
     *
38
     * @param string $format sprintf format string, with any number of named arguments
39
     * @param array $args array of [ 'arg_name' => 'arg value', ... ] replacements to be made
40
     * @return string|false result of sprintf call, or bool false on error
41
     */
42
    function sprintfn(string $format, array $args = [])
43
    {
44
        // map of argument names to their corresponding sprintf numeric argument value
45
        $array = array_keys($args);
46
        array_unshift($array, 0);
47
        $array = array_flip(array_slice($array, 1, null, true));
48
        
49
        // find the next named argument. each search starts at the end of the previous replacement.
50
        for ($pos = 0; preg_match('/(?<=%)([a-zA-Z_]\w*)(?=\$)/', $format, $match, PREG_OFFSET_CAPTURE, $pos);) {
51
            $position = intval($match[0][1]);
52
            $length = strlen($match[0][0]);
53
            $key = $match[1][0];
54
            
55
            // programmer did not supply a value for the named argument found in the format string
56
            if (!array_key_exists($key, $array)) {
57
                user_error("sprintfn(): Missing argument '${$key}'", E_USER_WARNING);
58
                return false;
59
            }
60
            
61
            // replace the named argument with the corresponding numeric one
62
            $format = substr_replace($format, $replace = $array[$key], $position, $length);
63
            $pos = $position + strlen($replace);
64
            
65
            // skip to end of replacement for next iteration
66
        }
67
        
68
        return vsprintf($format, array_values($args));
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type array; however, parameter $format of vsprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

68
        return vsprintf(/** @scrutinizer ignore-type */ $format, array_values($args));
Loading history...
69
    }
70
}
71
72
if (!function_exists('mb_sprintf')) {
73
    /**
74
     * Return a formatted multibyte string
75
     * A more complete and working version of mb_sprintf and mb_vsprintf.
76
     * It should work with any "ASCII preserving" encoding such as UTF-8 and all the ISO-8859 charsets.
77
     * It handles sign, padding, alignment, width and precision. Argument swapping is not handled.
78
     * @link http://php.net/manual/en/function.sprintf.php#89020
79
     */
80
    function mb_sprintf($format, ...$args): string
81
    {
82 1
        return mb_vsprintf($format, $args);
83
    }
84
}
85
86
if (!function_exists('mb_vsprintf')) {
87
    /**
88
     * Return a formatted string
89
     * It should work with any "ASCII preserving" encoding such as UTF-8 and all the ISO-8859 charsets.
90
     * It handles sign, padding, alignment, width and precision. Argument swapping is not handled.
91
     * Works with all encodings in format and arguments.
92
     * Supported: Sign, padding, alignment, width and precision.
93
     * Not supported: Argument swapping.
94
     * @link http://php.net/manual/en/function.sprintf.php#89020
95
     */
96
    function mb_vsprintf($format, $argv, $encoding = null): string
97
    {
98 1
        if (!is_array($argv)) {
99
            $argv = [$argv];
100
        }
101
        
102 1
        if (is_null($encoding)) {
103 1
            $encoding = mb_internal_encoding();
104
        }
105
        
106
        // Use UTF-8 in the format so we can use the u flag in preg_split
107 1
        $format = mb_convert_encoding($format, 'UTF-8', $encoding);
108 1
        $newFormat = '';
109
        
110
        // build a new format in UTF-8
111 1
        $newArgv = [];
112
        
113
        // unhandled args in unchanged encoding
114 1
        while ($format !== '') {
115
            
116
            // Split the format in two parts: $pre and $post by the first %-directive
117
            // We get also the matched groups
118 1
            $split = preg_split("!\%(\+?)('.|[0 ]|)(-?)([1-9][0-9]*|)(\.[1-9][0-9]*|)([%a-zA-Z])!u", $format, 2, PREG_SPLIT_DELIM_CAPTURE);
0 ignored issues
show
Bug introduced by
It seems like $format can also be of type array; however, parameter $subject of preg_split() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

118
            $split = preg_split("!\%(\+?)('.|[0 ]|)(-?)([1-9][0-9]*|)(\.[1-9][0-9]*|)([%a-zA-Z])!u", /** @scrutinizer ignore-type */ $format, 2, PREG_SPLIT_DELIM_CAPTURE);
Loading history...
119 1
            $pre = $split[0] ?? null;
120 1
            $sign = $split[1] ?? null;
121 1
            $filler = $split[2] ?? null;
122 1
            $align = $split[3] ?? null;
123 1
            $size = $split[4] ?? null;
124 1
            $precision = $split[5] ?? null;
125 1
            $type = $split[6] ?? null;
126 1
            $post = $split[7] ?? null;
127
            
128 1
            $newFormat .= mb_convert_encoding($pre, $encoding, 'UTF-8');
0 ignored issues
show
Bug introduced by
It seems like $pre can also be of type null; however, parameter $string of mb_convert_encoding() does only seem to accept array|string, maybe add an additional type check? ( Ignorable by Annotation )

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

128
            $newFormat .= mb_convert_encoding(/** @scrutinizer ignore-type */ $pre, $encoding, 'UTF-8');
Loading history...
129
            
130 1
            if ($type === '') {
131
                // didn't match. do nothing. this is the last iteration.
132
            }
133 1
            elseif ($type === '%') {
134
                // an escaped %
135
                $newFormat .= '%%';
136
            }
137 1
            elseif ($type === 's') {
138
                $arg = array_shift($argv);
139
                $arg = mb_convert_encoding($arg, 'UTF-8', $encoding);
140
                $paddingPre = '';
141
                $paddingPost = '';
142
                
143
                // truncate $arg
144
                if ($precision !== '') {
145
                    $precision = intval(substr($precision, 1));
0 ignored issues
show
Bug introduced by
It seems like $precision can also be of type null; however, parameter $string of substr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

145
                    $precision = intval(substr(/** @scrutinizer ignore-type */ $precision, 1));
Loading history...
146
                    if ($precision > 0 && mb_strlen($arg, $encoding) > $precision) {
0 ignored issues
show
Bug introduced by
It seems like $arg can also be of type array; however, parameter $string of mb_strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

146
                    if ($precision > 0 && mb_strlen(/** @scrutinizer ignore-type */ $arg, $encoding) > $precision) {
Loading history...
147
                        $arg = mb_substr($precision, 0, $precision, $encoding);
148
                    }
149
                }
150
                
151
                // define padding
152
                if ($size > 0) {
153
                    $argLength = mb_strlen($arg, $encoding);
154
                    if ($argLength < $size) {
155
                        if ($filler === '') {
156
                            $filler = ' ';
157
                        }
158
                        if ($align === '-') {
159
                            $paddingPost = str_repeat($filler, $size - $argLength);
160
                        }
161
                        else {
162
                            $paddingPre = str_repeat($filler, $size - $argLength);
163
                        }
164
                    }
165
                }
166
                
167
                // escape % and pass it forward
168
                $newFormat .= $paddingPre . str_replace('%', '%%', $arg) . $paddingPost;
169
            }
170
            else {
171
                // another type, pass forward
172 1
                $newFormat .= "%$sign$filler$align$size$precision$type";
173 1
                $newArgv[] = array_shift($argv);
174
            }
175
            
176 1
            $format = strval($post);
177
        }
178
        // Convert new format back from UTF-8 to the original encoding
179 1
        $newFormat = mb_convert_encoding($newFormat, $encoding, 'UTF-8');
180 1
        return !empty($newArgv) ? vsprintf($newFormat, $newArgv) : $newFormat;
0 ignored issues
show
Bug introduced by
It seems like $newFormat can also be of type array; however, parameter $format of vsprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

180
        return !empty($newArgv) ? vsprintf(/** @scrutinizer ignore-type */ $newFormat, $newArgv) : $newFormat;
Loading history...
Bug Best Practice introduced by
The expression return ! empty($newArgv)... $newArgv) : $newFormat could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
181
    }
182
}
183