6
6
7
7
namespace PHP_CodeSniffer \Tokenizers ;
8
8
9
+ use GraphQL \Language \Lexer ;
10
+ use GraphQL \Language \Source ;
11
+ use GraphQL \Language \Token ;
9
12
use PHP_CodeSniffer \Config ;
13
+ use PHP_CodeSniffer \Exceptions \TokenizerException ;
10
14
11
15
/**
12
16
* Implements a tokenizer for GraphQL files.
17
+ *
18
+ * @todo Reimplement using the official GraphQL implementation
13
19
*/
14
- class GraphQL extends JS
20
+ class GraphQL extends Tokenizer
15
21
{
16
22
17
- protected $ additionalTokenValues = [
18
- 'type ' => 'T_CLASS ' ,
19
- 'interface ' => 'T_CLASS ' ,
20
- 'enum ' => 'T_CLASS ' ,
21
- '# ' => 'T_COMMENT ' ,
23
+ /**
24
+ * Defines how GraphQL token types are mapped to PHP token types.
25
+ *
26
+ * @var array
27
+ */
28
+ private $ tokenTypeMap = [
29
+ Token::AMP => null , //TODO
30
+ Token::AT => 'T_DOC_COMMENT_TAG ' ,
31
+ Token::BANG => null , //TODO
32
+ Token::BLOCK_STRING => 'T_COMMENT ' ,
33
+ Token::BRACE_L => 'T_OPEN_CURLY_BRACKET ' ,
34
+ Token::BRACE_R => 'T_CLOSE_CURLY_BRACKET ' ,
35
+ Token::BRACKET_L => 'T_OPEN_SQUARE_BRACKET ' ,
36
+ Token::BRACKET_R => 'T_CLOSE_CURLY_BRACKET ' ,
37
+ Token::COLON => 'T_COLON ' ,
38
+ Token::COMMENT => 'T_COMMENT ' ,
39
+ Token::DOLLAR => 'T_DOLLAR ' ,
40
+ Token::EOF => 'T_CLOSE_TAG ' ,
41
+ Token::EQUALS => 'T_EQUAL ' ,
42
+ Token::FLOAT => null , //TODO
43
+ Token::INT => null , //TODO
44
+ Token::NAME => 'T_STRING ' ,
45
+ Token::PAREN_L => 'T_OPEN_PARENTHESIS ' ,
46
+ Token::PAREN_R => 'T_CLOSE_PARENTHESIS ' ,
47
+ Token::PIPE => null , //TODO
48
+ Token::SPREAD => 'T_ELLIPSIS ' ,
49
+ Token::SOF => 'T_OPEN_TAG ' ,
50
+ Token::STRING => 'T_STRING ' ,
51
+ ];
52
+
53
+ /**
54
+ * Defines how special keywords are mapped to PHP token types
55
+ *
56
+ * @var array
57
+ */
58
+ private $ keywordTokenTypeMap = [
59
+ 'enum ' => 'T_CLASS ' ,
60
+ 'extend ' => 'T_EXTENDS ' , //TODO This might not be the appropriate equivalent
61
+ 'interface ' => 'T_INTERFACE ' ,
62
+ 'implements ' => 'T_IMPLEMENTS ' ,
63
+ 'type ' => 'T_CLASS ' ,
64
+ 'union ' => 'T_CLASS ' ,
65
+ //TODO Add further types
22
66
];
23
67
24
68
/**
@@ -27,17 +71,13 @@ class GraphQL extends JS
27
71
* @param string $content
28
72
* @param Config $config
29
73
* @param string $eolChar
30
- * @throws \PHP_CodeSniffer\Exceptions\ TokenizerException
74
+ * @throws TokenizerException
31
75
*/
32
76
public function __construct ($ content , Config $ config , $ eolChar = '\n ' )
33
77
{
34
- //add our token values
35
- $ this ->tokenValues = array_merge (
36
- $ this ->tokenValues ,
37
- $ this ->additionalTokenValues
38
- );
78
+ //TODO We might want to delete this unless we need the constructor to work totally different
39
79
40
- //let parent do its job (which will start tokenizing)
80
+ //let parent do its job
41
81
parent ::__construct ($ content , $ config , $ eolChar );
42
82
}
43
83
@@ -46,7 +86,110 @@ public function __construct($content, Config $config, $eolChar = '\n')
46
86
*/
47
87
public function processAdditional ()
48
88
{
49
- //NOP Does nothing intentionally
89
+ //NOP: Does nothing intentionally
50
90
}
51
91
92
+ /**
93
+ * {@inheritDoc}
94
+ *
95
+ * @throws TokenizerException
96
+ */
97
+ protected function tokenize ($ string )
98
+ {
99
+ $ this ->logVerbose ('*** START GRAPHQL TOKENIZING *** ' );
100
+
101
+ $ string = str_replace ($ this ->eolChar , "\n" , $ string );
102
+ $ tokens = [];
103
+ $ lexer = new Lexer (
104
+ new Source ($ string )
105
+ );
106
+
107
+ do {
108
+ $ kind = $ lexer ->token ->kind ;
109
+ $ value = $ lexer ->token ->value ?: '' ;
110
+
111
+ //if we have encountered a keyword, we convert it
112
+ //otherwise we translate the token or default it to T_STRING
113
+ if ($ kind === Token::NAME && isset ($ this ->keywordTokenTypeMap [$ value ])) {
114
+ $ tokenType = $ this ->keywordTokenTypeMap [$ value ];
115
+ } elseif (isset ($ this ->tokenTypeMap [$ kind ])) {
116
+ $ tokenType = $ this ->tokenTypeMap [$ kind ];
117
+ } else {
118
+ $ tokenType = 'T_STRING ' ;
119
+ }
120
+
121
+ //some GraphQL tokens need special handling
122
+ switch ($ kind ) {
123
+ case Token::AT :
124
+ case Token::BRACE_L :
125
+ case Token::BRACE_R :
126
+ case Token::PAREN_L :
127
+ case Token::PAREN_R :
128
+ $ value = $ kind ;
129
+ break ;
130
+ default :
131
+ //NOP
132
+ }
133
+
134
+ //finally we create the PHP token
135
+ $ token = [
136
+ 'code ' => constant ($ tokenType ),
137
+ 'type ' => $ tokenType ,
138
+ 'content ' => $ value ,
139
+ ];
140
+ $ line = $ lexer ->token ->line ;
141
+
142
+ $ lexer ->advance ();
143
+
144
+ //if line has changed (and we're not on start of file) we have to append at least one line break to current
145
+ //tokens content otherwise PHP_CodeSniffer will screw up line numbers
146
+ if ($ lexer ->token ->line !== $ line && $ kind !== Token::SOF ) {
147
+ $ token ['content ' ] .= $ this ->eolChar ;
148
+ }
149
+ $ tokens [] = $ token ;
150
+ $ tokens = array_merge (
151
+ $ tokens ,
152
+ $ this ->getNewLineTokens ($ line , $ lexer ->token ->line )
153
+ );
154
+ } while ($ lexer ->token ->kind !== Token::EOF );
155
+
156
+ $ this ->logVerbose ('*** END GRAPHQL TOKENIZING *** ' );
157
+ return $ tokens ;
158
+ }
159
+
160
+ /**
161
+ * Returns tokens of empty new lines for the range <var>$lineStart</var> to <var>$lineEnd</var>
162
+ *
163
+ * @param int $lineStart
164
+ * @param int $lineEnd
165
+ * @return array
166
+ */
167
+ private function getNewLineTokens ($ lineStart , $ lineEnd )
168
+ {
169
+ $ amount = ($ lineEnd - $ lineStart ) - 1 ;
170
+ $ tokens = [];
171
+
172
+ for ($ i = 0 ; $ i < $ amount ; ++$ i ) {
173
+ $ tokens [] = [
174
+ 'code ' => T_WHITESPACE ,
175
+ 'type ' => 'T_WHITESPACE ' ,
176
+ 'content ' => $ this ->eolChar ,
177
+ ];
178
+ }
179
+
180
+ return $ tokens ;
181
+ }
182
+
183
+ /**
184
+ * Logs <var>$message</var> if {@link PHP_CODESNIFFER_VERBOSITY} is greater than <var>$level</var>.
185
+ *
186
+ * @param string $message
187
+ * @param int $level
188
+ */
189
+ private function logVerbose ($ message , $ level = 1 )
190
+ {
191
+ if (PHP_CODESNIFFER_VERBOSITY > $ level ) {
192
+ printf ("\t%s " . PHP_EOL , $ message );
193
+ }
194
+ }
52
195
}
0 commit comments