Passed
Push — master ( faa50a...691133 )
by Julien
17:31
created

mb_vsprintf()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 45
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 18.9765

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 28
c 2
b 0
f 0
dl 0
loc 45
ccs 12
cts 27
cp 0.4444
rs 8.4444
cc 8
nc 8
nop 3
crap 18.9765
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 1
        return implode($glue, array_map(function ($value, $key) use ($format, $multibyte) {
10 1
            return $multibyte
11
                ? mb_sprintf($format, $value, $key)
12 1
                : sprintf($format, $value, $key);
13 1
        }, $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 = []): false|string
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 = (string)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));
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(string $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(string $format, array $argv, ?string $encoding = null): string
97
    {
98 1
        if (is_null($encoding)) {
99 1
            $encoding = mb_internal_encoding();
100
        }
101
        
102 1
        $format = (string)mb_convert_encoding($format, 'UTF-8', $encoding);
0 ignored issues
show
Bug introduced by
It seems like $encoding can also be of type true; however, parameter $from_encoding of mb_convert_encoding() does only seem to accept array|null|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

102
        $format = (string)mb_convert_encoding($format, 'UTF-8', /** @scrutinizer ignore-type */ $encoding);
Loading history...
103 1
        $newFormat = '';
104 1
        $newArgv = [];
105
        
106 1
        while ($format !== '') {
107
            // Corrected regular expression pattern
108 1
            $split = preg_split("!(%[0-9]*\$)?%([-+ 0]|'.)?(-?[0-9]*)?(\.\d+)?([bcdeEfFgGosuxX])!u", $format, 2, PREG_SPLIT_DELIM_CAPTURE);
109
            
110 1
            if (empty($split) || count($split) < 7) {
111
                // If split is empty or does not contain all expected parts, append the rest of the format string as-is.
112 1
                $newFormat .= mb_convert_encoding($format, $encoding, 'UTF-8');
0 ignored issues
show
Bug introduced by
It seems like $encoding can also be of type true; however, parameter $to_encoding of mb_convert_encoding() 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

112
                $newFormat .= mb_convert_encoding($format, /** @scrutinizer ignore-type */ $encoding, 'UTF-8');
Loading history...
113 1
                break;
114
            }
115
            
116
            $pre = $split[0];
117
            $numberedArg = $split[1] ?? '';
118
            $sign = $split[2] ?? '';
119
            $size = $split[3] ?? '';
120
            $precision = $split[4] ?? '';
121
            $type = $split[5] ?? '';
122
            $post = $split[6] ?? '';
123
            
124
            $newFormat .= mb_convert_encoding($pre, $encoding, 'UTF-8');
125
            
126
            if ($type === '') {
127
                // Incomplete specifier at the end of the string, escape it.
128
                $newFormat .= '%';
129
            } elseif ($type === '%') {
130
                $newFormat .= '%%';
131
            } else {
132
                $newFormat .= "%$numberedArg$sign$size$precision$type";
133
                $newArgv[] = array_shift($argv);
134
            }
135
            
136
            $format = $post;
137
        }
138
        
139 1
        $newFormat = (string)mb_convert_encoding($newFormat, $encoding, 'UTF-8');
140 1
        return !empty($newArgv) ? vsprintf($newFormat, $newArgv) : $newFormat;
141
    }
142
}
143