Passed
Pull Request — master (#184)
by Christoffer
02:32
created

isEscapedTripleQuote()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.2
c 0
b 0
f 0
cc 4
eloc 4
nc 4
nop 3
1
<?php
2
3
namespace Digia\GraphQL\Language;
4
5
/**
6
 * Multi-byte compatible `chr`.
7
 *
8
 * Based on the Symfony's Mbstring polyfills.
9
 *
10
 * @param int $code
11
 * @return string
12
 */
13
function mbChr(int $code)
14
{
15
    if (0x80 > $code %= 0x200000) {
16
        return \chr($code);
17
    }
18
    if (0x800 > $code) {
19
        return \chr(0xC0 | $code >> 6) . \chr(0x80 | $code & 0x3F);
20
    }
21
    if (0x10000 > $code) {
22
        return \chr(0xE0 | $code >> 12) . \chr(0x80 | $code >> 6 & 0x3F) . \chr(0x80 | $code & 0x3F);
23
    }
24
25
    return \chr(0xF0 | $code >> 18) . \chr(0x80 | $code >> 12 & 0x3F) . \chr(0x80 | $code >> 6 & 0x3F) . \chr(0x80 | $code & 0x3F);
26
}
27
28
/**
29
 * Multi-byte compatible `ord`.
30
 *
31
 * Based on the Symfony's Mbstring polyfills.
32
 *
33
 * @param string $string
34
 * @param string $encoding
35
 * @return int
36
 */
37
function mbOrd(string $s)
38
{
39
    /** @noinspection CallableParameterUseCaseInTypeContextInspection */
40
    $code = ($s = \unpack('C*', \substr($s, 0, 4))) ? $s[1] : 0;
41
42
    if (0xF0 <= $code) {
43
        return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80;
44
    }
45
    if (0xE0 <= $code) {
46
        return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80;
47
    }
48
    if (0xC0 <= $code) {
49
        return (($code - 0xC0) << 6) + $s[2] - 0x80;
50
    }
51
52
    return $code;
53
}
54
55
/**
56
 * @param string $string
57
 * @param int    $position
58
 * @return int
59
 */
60
function charCodeAt(string $string, int $position): int
61
{
62
    return mbOrd(\mb_substr($string, $position, 1, 'UTF-8'));
63
}
64
65
/**
66
 * @param int $code
67
 * @return string
68
 */
69
function printCharCode(int $code): string
70
{
71
    if ($code === 0x0000) {
72
        return '<EOF>';
73
    }
74
75
    return $code < 0x007F
76
        // Trust JSON for ASCII.
77
        ? \json_encode(mbChr($code))
78
        // Otherwise print the escaped form.
79
        : '"\\u' . \dechex($code) . '"';
80
}
81
82
/**
83
 * @param string   $string
84
 * @param int      $start
85
 * @param int|null $end
86
 * @return string
87
 */
88
function sliceString(string $string, int $start, int $end = null): string
89
{
90
    $length = $end !== null ? $end - $start : \mb_strlen($string) - $start;
91
    return \mb_substr($string, $start, $length);
92
}
93
94
/**
95
 * @param int $code
96
 * @return bool
97
 */
98
function isLetter(int $code): bool
99
{
100
    return ($code >= 65 && $code <= 90) || ($code >= 97 && $code <= 122); // a-z or A-Z
101
}
102
103
/**
104
 * @param int $code
105
 * @return bool
106
 */
107
function isNumber(int $code): bool
108
{
109
    return $code >= 48 && $code <= 57; // 0-9
110
}
111
112
/**
113
 * @param int $code
114
 * @return bool
115
 */
116
function isUnderscore(int $code): bool
117
{
118
    return $code === 95; // _
119
}
120
121
/**
122
 * @param int $code
123
 * @return bool
124
 */
125
function isAlphaNumeric(int $code): bool
126
{
127
    return isLetter($code) || isNumber($code) || isUnderscore($code);
128
}
129
130
/**
131
 * @param int $code
132
 * @return bool
133
 */
134
function isLineTerminator(int $code): bool
135
{
136
    return $code === 0x000a || $code === 0x000d;
137
}
138
139
/**
140
 * @param int $code
141
 * @return bool
142
 */
143
function isSourceCharacter(int $code): bool
144
{
145
    return $code < 0x0020 && $code !== 0x0009; // any source character EXCEPT HT (Horizontal Tab)
146
}
147
148
/**
149
 * @param string $body
150
 * @param int    $code
151
 * @param int    $pos
152
 * @return bool
153
 */
154
function isSpread(string $body, int $code, int $pos): bool
155
{
156
    return $code === 46 &&
157
        charCodeAt($body, $pos + 1) === 46 &&
158
        charCodeAt($body, $pos + 2) === 46; // ...
159
}
160
161
/**
162
 * @param string $body
163
 * @param int    $code
164
 * @param int    $pos
165
 * @return bool
166
 */
167
function isString(string $body, int $code, int $pos): bool
168
{
169
    return $code === 34 && charCodeAt($body, $pos + 1) !== 34;
170
}
171
172
/**
173
 * @param string $body
174
 * @param int    $code
175
 * @param int    $pos
176
 * @return bool
177
 */
178
function isTripleQuote(string $body, int $code, int $pos): bool
179
{
180
    return $code === 34 &&
181
        charCodeAt($body, $pos + 1) === 34 &&
182
        charCodeAt($body, $pos + 2) === 34; // """
183
}
184
185
/**
186
 * @param string $body
187
 * @param int    $code
188
 * @param int    $pos
189
 * @return bool
190
 */
191
function isEscapedTripleQuote(
192
    string $body,
193
    int $code,
194
    int $pos
195
): bool {
196
    return $code === 92 &&
197
        charCodeAt($body, $pos + 1) === 34 &&
198
        charCodeAt($body, $pos + 2) === 34 &&
199
        charCodeAt($body, $pos + 3) === 34; // \"""
200
}
201
202
/**
203
 * @param string $value
204
 * @return bool
205
 */
206
function isOperation(string $value): bool
207
{
208
    return $value === 'query' || $value === 'mutation' || $value === 'subscription';
209
}
210
211
/**
212
 * @param array $location
213
 * @return array|null
214
 */
215
function locationShorthandToArray(array $location): ?array
216
{
217
    return isset($location[0], $location[1]) ? ['line' => $location[0], 'column' => $location[1]] : null;
218
}
219
220
/**
221
 * @param array $locations
222
 * @return array
223
 */
224
function locationsShorthandToArray(array $locations): array
225
{
226
    return array_map(function ($shorthand) {
227
        return locationShorthandToArray($shorthand);
228
    }, $locations);
229
}
230
231
/**
232
 * @param array $array
233
 * @return string
234
 */
235
function block(array $array): string
236
{
237
    return !empty($array) ? "{\n" . indent(implode("\n", $array)) . "\n}" : '';
238
}
239
240
/**
241
 * @param string      $start
242
 * @param null|string $maybeString
243
 * @param null|string $end
244
 * @return string
245
 */
246
function wrap(string $start, ?string $maybeString = null, ?string $end = null): string
247
{
248
    return null !== $maybeString ? ($start . $maybeString . ($end ?? '')) : '';
249
}
250
251
/**
252
 * @param null|string $maybeString
253
 * @return string
254
 */
255
function indent(?string $maybeString): string
256
{
257
    return null !== $maybeString ? '  ' . preg_replace("/\n/", "\n  ", $maybeString) : '';
258
}
259
260
/**
261
 * @param string $str
262
 * @return string
263
 */
264
function dedent(string $str): string
265
{
266
    $trimmed = \preg_replace("/^\n*|[ \t]*$/", '', $str); // Remove leading newline and trailing whitespace
267
    $matches = [];
268
    \preg_match("/^[ \t]*/", $trimmed, $matches); // Figure out indent
269
    $indent = $matches[0];
270
    return \str_replace($indent, '', $trimmed); // Remove indent
271
}
272