Passed
Push — master ( fbdae9...66097f )
by Divine Niiquaye
10:37
created

EscaperHelper::initializeEscapers()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 70
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 13.776

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 24
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 70
ccs 10
cts 25
cp 0.4
crap 13.776
rs 8.9137

How to fix   Long Method   

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
declare(strict_types=1);
4
5
/*
6
 * This file is part of Biurad opensource projects.
7
 *
8
 * PHP version 7.2 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Biurad\UI\Helper;
19
20
class EscaperHelper extends AbstractHelper
21
{
22
    /** @var array<string,callable> */
23
    protected $escapers = [];
24
25
    /** @var array<string,array<int|bool|string,mixed>> */
26
    protected static $escaperCache = [];
27
28 6
    public function __construct()
29
    {
30 6
        $this->initializeEscapers();
31 6
    }
32
33
    /**
34
     * {@inheritdoc}
35
     */
36 6
    public function getName(): string
37
    {
38 6
        return 'escape';
39
    }
40
41
    /**
42
     * Adds an escaper for the given context.
43
     */
44
    public function setEscaper(string $context, callable $escaper): void
45
    {
46
        $this->escapers[$context] = $escaper;
47
        self::$escaperCache[$context] = [];
48
    }
49
50
    /**
51
     * Gets an escaper for a given context.
52
     *
53
     * @throws \InvalidArgumentException
54
     *
55
     * @return callable A PHP callable
56
     */
57 4
    public function getEscaper(string $context)
58
    {
59 4
        if (!isset($this->escapers[$context])) {
60
            throw new \InvalidArgumentException(\sprintf('No registered escaper for context "%s".', $context));
61
        }
62
63 4
        return $this->escapers[$context];
64
    }
65
66
    /**
67
     * Runs the PHP function htmlspecialchars on the value passed.
68
     *
69
     * @param mixed $value The value to escape
70
     *
71
     * @return mixed the escaped value
72
     */
73 4
    public function html($value)
74
    {
75 4
        return $this->encode($value, __FUNCTION__);
76
    }
77
78
    /**
79
     * A function that escape all non-alphanumeric characters
80
     * into their \xHH or \uHHHH representations.
81
     *
82
     * @param mixed $value The value to escape
83
     *
84
     * @return mixed the escaped value
85
     */
86
    public function js($value)
87
    {
88
        return $this->encode($value, __FUNCTION__);
89
    }
90
91
    /**
92
     * Escapes string for use inside CSS template.
93
     *
94
     * @param mixed $value The value to escape
95
     *
96
     * @return mixed the escaped value
97
     */
98
    public function css($value)
99
    {
100
        return $this->encode($value, __FUNCTION__);
101
    }
102
103
    /**
104
     * Escapes a string by using the current charset.
105
     *
106
     * @param mixed $value A variable to escape
107
     *
108
     * @return mixed The escaped value
109
     */
110 4
    public function encode($value, string $context = 'html')
111
    {
112 4
        if (\is_numeric($value)) {
113
            return $value;
114
        }
115
116
        // If we deal with a scalar value, we can cache the result to increase
117
        // the performance when the same value is escaped multiple times (e.g. loops)
118 4
        if (\is_scalar($value)) {
119 4
            if (!isset(self::$escaperCache[$context][$value])) {
120 4
                self::$escaperCache[$context][$value] = $this->getEscaper($context)($value);
121
            }
122
123 4
            return self::$escaperCache[$context][$value];
124
        }
125
126
        return $this->getEscaper($context)($value);
127
    }
128
129
    /**
130
     * Initializes the built-in escapers.
131
     *
132
     * Each function specifies a way for applying a transformation to a string
133
     * passed to it. The purpose is for the string to be "escaped" so it is
134
     * suitable for the format it is being displayed in.
135
     *
136
     * For example, the string: "It's required that you enter a username & password.\n"
137
     * If this were to be displayed as HTML it would be sensible to turn the
138
     * ampersand into '&amp;' and the apostrophe into '&aps;'. However if it were
139
     * going to be used as a string in JavaScript to be displayed in an alert box
140
     * it would be right to leave the string as-is, but c-escape the apostrophe and
141
     * the new line.
142
     *
143
     * For each function there is a define to avoid problems with strings being
144
     * incorrectly specified.
145
     */
146 6
    protected function initializeEscapers(): void
147
    {
148 6
        $flags = \ENT_NOQUOTES | \ENT_SUBSTITUTE;
149
150
        $this->escapers = [
151
            'html' =>
152
                /**
153
                 * Runs the PHP function htmlspecialchars on the value passed.
154
                 *
155
                 * @param string $value The value to escape
156
                 *
157
                 * @return string the escaped value
158
                 */
159 6
                function ($value) use ($flags) {
160
                    // Numbers and Boolean values get turned into strings which can cause problems
161
                    // with type comparisons (e.g. === or is_int() etc).
162 4
                    return \is_string($value) ? \htmlspecialchars($value, $flags, $this->getCharset()) : $value;
0 ignored issues
show
introduced by
The condition is_string($value) is always true.
Loading history...
163 6
                },
164
165
            'js' =>
166
                /**
167
                 * A function that escape all non-alphanumeric characters
168
                 * into their \xHH or \uHHHH representations.
169
                 *
170
                 * @param string $value The value to escape
171
                 *
172
                 * @return string the escaped value
173
                 */
174 6
                function ($value) {
175
                    if ('UTF-8' != $this->getCharset()) {
176
                        $value = \iconv($this->getCharset(), 'UTF-8', $value);
177
                    }
178
179
                    $callback = static function ($matches): string {
180
                        $char = $matches[0];
181
182
                        // \xHH
183
                        if (!isset($char[1])) {
184
                            return '\\x' . \substr('00' . \bin2hex($char), -2);
185
                        }
186
187
                        // \uHHHH
188
                        $char = \iconv('UTF-8', 'UTF-16BE', $char);
189
190
                        return '\\u' . \substr('0000' . \bin2hex($char), -4);
191
                    };
192
193
                    if (null === $value = \preg_replace_callback('#[^\p{L}\p{N} ]#u', $callback, $value)) {
194
                        throw new \InvalidArgumentException('The string to escape is not a valid UTF-8 string.');
195
                    }
196
197
                    if ('UTF-8' != $this->getCharset()) {
198
                        $value = \iconv('UTF-8', $this->getCharset(), $value);
199
                    }
200
201
                    return $value;
202 6
                },
203
            'css' =>
204
                /**
205
                 * Escapes string for use inside CSS template.
206
                 *
207
                 * @param mixed $value The value to escape
208
                 */
209 6
                function ($value) {
210
                    // http://www.w3.org/TR/2006/WD-CSS21-20060411/syndata.html#q6
211
                    return \addcslashes($value, "\x00..\x1F!\"#$%&'()*+,./:;<=>?@[\\]^`{|}~");
212 6
                },
213
        ];
214
215 6
        self::$escaperCache = [];
216 6
    }
217
}
218