Test Failed
Push — master ( b7ab7b...94ee03 )
by Kirill
02:59
created

StringValueNode::renderSpecialCharacters()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
nc 1
nop 1
dl 0
loc 23
ccs 0
cts 20
cp 0
crap 42
rs 8.9297
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\SDL\Frontend\AST\Value;
11
12
use Railt\Parser\Ast\LeafInterface;
13
14
/**
15
 * Class StringValue
16
 */
17
class StringValueNode extends AbstractAstValueNode
18
{
19
    /**
20
     * @var string
21
     */
22
    private const UTF_SEQUENCE_PATTERN = '/(?<!\\\\)\\\\u([0-9a-f]{4})/ui';
23
24
    /**
25
     * @var string
26
     */
27
    private const CHAR_SEQUENCE_PATTERN = '/(?<!\\\\)\\\\(b|f|n|r|t)/u';
28
29
    /**
30
     * @return string
31
     */
32
    private function getStringValue(): string
33
    {
34
        /** @var LeafInterface $leaf */
35
        $leaf = $this->getChild(0);
36
37
        return $leaf->getValue(1);
38
    }
39
40
    /**
41
     * @return string
42
     */
43
    public function parse(): string
44
    {
45
        // "..."
46
        $result = $this->getStringValue();
47
48
        // Encode slashes to special "pattern" chars
49
        $result = $this->encodeSlashes($result);
50
51
        // Transform utf char \uXXXX -> X
52
        $result = $this->renderUtfSequences($result);
53
54
        // Transform special chars
55
        $result = $this->renderSpecialCharacters($result);
56
57
        // Decode special patterns to source chars (rollback)
58
        $result = $this->decodeSlashes($result);
59
60
        return $result;
61
    }
62
63
    /**
64
     * @param string $value
65
     * @return string
66
     */
67
    private function encodeSlashes(string $value): string
68
    {
69
        return \str_replace(['\\\\', '\\"'], ["\0", '"'], $value);
70
    }
71
72
    /**
73
     * Method for parsing and decode utf-8 character
74
     * sequences like "\uXXXX" type.
75
     *
76
     * @see http://facebook.github.io/graphql/October2016/#sec-String-Value
77
     * @param string $body
78
     * @return string
79
     */
80
    private function renderUtfSequences(string $body): string
81
    {
82
        $callee = function (array $matches): string {
83
            [$char, $code] = [$matches[0], $matches[1]];
0 ignored issues
show
Bug introduced by
The variable $char does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $code does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
84
85
            try {
86
                return $this->forwardRenderUtfSequences($code);
87
            } catch (\Error | \ErrorException $error) {
0 ignored issues
show
Bug introduced by
The class ErrorException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
88
                return $this->fallbackRenderUtfSequences($char);
89
            }
90
        };
91
92
        return @\preg_replace_callback(self::UTF_SEQUENCE_PATTERN, $callee, $body) ?? $body;
93
    }
94
95
    /**
96
     * @param string $body
97
     * @return string
98
     */
99
    private function forwardRenderUtfSequences(string $body): string
100
    {
101
        return \mb_convert_encoding(\pack('H*', $body), 'UTF-8', 'UCS-2BE');
102
    }
103
104
    /**
105
     * @param string $body
106
     * @return string
107
     */
108
    private function fallbackRenderUtfSequences(string $body): string
109
    {
110
        try {
111
            if (\function_exists('\\json_decode')) {
112
                $result = @\json_decode('{"char": "' . $body . '"}')->char;
113
114
                if (\json_last_error() === \JSON_ERROR_NONE) {
115
                    $body = $result;
116
                }
117
            }
118
        } finally {
119
            return $body;
120
        }
121
    }
122
123
    /**
124
     * Method for parsing special control characters.
125
     *
126
     * @see http://facebook.github.io/graphql/October2016/#sec-String-Value
127
     * @param string $body
128
     * @return string
129
     */
130
    private function renderSpecialCharacters(string $body): string
131
    {
132
        $callee = function (array $matches): string {
133
            [$char, $code] = [$matches[0], $matches[1]];
0 ignored issues
show
Bug introduced by
The variable $char does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $code does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
134
135
            switch ($code) {
136
                case 'b':
137
                    return "\u{0008}";
138
                case 'f':
139
                    return "\u{000C}";
140
                case 'n':
141
                    return "\u{000A}";
142
                case 'r':
143
                    return "\u{000D}";
144
                case 't':
145
                    return "\u{0009}";
146
            }
147
148
            return $char;
149
        };
150
151
        return @\preg_replace_callback(self::CHAR_SEQUENCE_PATTERN, $callee, $body) ?? $body;
152
    }
153
154
    /**
155
     * @param string $value
156
     * @return string
157
     */
158
    private function decodeSlashes(string $value): string
159
    {
160
        return \str_replace("\0", '\\', $value);
161
    }
162
}
163