1
2 /*
3 +------------------------------------------------------------------------+
4 | Phalcon Framework |
5 +------------------------------------------------------------------------+
6 | Copyright (c) 2011-2016 Phalcon Team (http://www.phalconphp.com) |
7 +------------------------------------------------------------------------+
8 | This source file is subject to the New BSD License that is bundled |
9 | with this package in the file docs/LICENSE.txt. |
10 | |
11 | If you did not receive a copy of the license and are unable to |
12 | obtain it through the world-wide-web, please send an email |
13 | to license@phalconphp.com so we can send you a copy immediately. |
14 +------------------------------------------------------------------------+
15 | Authors: Andres Gutierrez <andres@phalconphp.com> |
16 | Eduar Carvajal <eduar@phalconphp.com> |
17 +------------------------------------------------------------------------+
18 */
19
20 const phannot_token_names phannot_tokens[] =
21 {
22 { "INTEGER", PHANNOT_T_INTEGER },
23 { "DOUBLE", PHANNOT_T_DOUBLE },
24 { "STRING", PHANNOT_T_STRING },
25 { "IDENTIFIER", PHANNOT_T_IDENTIFIER },
26 { "@", PHANNOT_T_AT },
27 { ",", PHANNOT_T_COMMA },
28 { "=", PHANNOT_T_EQUALS },
29 { ":", PHANNOT_T_COLON },
30 { "(", PHANNOT_T_PARENTHESES_OPEN },
31 { ")", PHANNOT_T_PARENTHESES_CLOSE },
32 { "{", PHANNOT_T_BRACKET_OPEN },
33 { "}", PHANNOT_T_BRACKET_CLOSE },
34 { "[", PHANNOT_T_SBRACKET_OPEN },
35 { "]", PHANNOT_T_SBRACKET_CLOSE },
36 { "ARBITRARY TEXT", PHANNOT_T_ARBITRARY_TEXT },
37 { NULL, 0 }
38 };
39
40 /**
41 * Wrapper to alloc memory within the parser
42 */
phannot_wrapper_alloc(size_t bytes)43 static void *phannot_wrapper_alloc(size_t bytes){
44 return emalloc(bytes);
45 }
46
47 /**
48 * Wrapper to free memory within the parser
49 */
phannot_wrapper_free(void * pointer)50 static void phannot_wrapper_free(void *pointer){
51 efree(pointer);
52 }
53
54 /**
55 * Creates a parser_token to be passed to the parser
56 */
phannot_parse_with_token(void * phannot_parser,int opcode,int parsercode,phannot_scanner_token * token,phannot_parser_status * parser_status)57 static void phannot_parse_with_token(void* phannot_parser, int opcode, int parsercode, phannot_scanner_token *token, phannot_parser_status *parser_status){
58
59 phannot_parser_token *pToken;
60
61 pToken = emalloc(sizeof(phannot_parser_token));
62 pToken->opcode = opcode;
63 pToken->token = token->value;
64 pToken->token_len = token->len;
65 pToken->free_flag = 1;
66
67 phannot_(phannot_parser, parsercode, pToken, parser_status);
68
69 token->value = NULL;
70 token->len = 0;
71 }
72
73 /**
74 * Creates an error message when it's triggered by the scanner
75 */
phannot_scanner_error_msg(phannot_parser_status * parser_status,char ** error_msg TSRMLS_DC)76 static void phannot_scanner_error_msg(phannot_parser_status *parser_status, char **error_msg TSRMLS_DC){
77
78 phannot_scanner_state *state = parser_status->scanner_state;
79
80 if (state->start) {
81 if (state->start_length > 16) {
82 spprintf(error_msg, 0, "Scanning error before '%.16s...' in %s on line %d", state->start, state->active_file, state->active_line);
83 } else {
84 spprintf(error_msg, 0, "Scanning error before '%s' in %s on line %d", state->start, state->active_file, state->active_line);
85 }
86 } else {
87 spprintf(error_msg, 0, "Scanning error near to EOF in %s", state->active_file);
88 }
89 }
90
91 /**
92 * Receives the comment tokenizes and parses it
93 */
phannot_parse_annotations(zval * result,zval * comment,zval * file_path,zval * line TSRMLS_DC)94 int phannot_parse_annotations(zval *result, zval *comment, zval *file_path, zval *line TSRMLS_DC) {
95
96 char *comment_str;
97 int comment_len;
98 char *file_path_str;
99 int line_num;
100
101 char *error_msg = NULL;
102
103 ZVAL_NULL(result);
104
105 if (Z_TYPE_P(comment) == IS_STRING) {
106 comment_str = Z_STRVAL_P(comment);
107 comment_len = Z_STRLEN_P(comment);
108 } else {
109 comment_str = "";
110 comment_len = 0;
111 }
112
113 if (Z_TYPE_P(file_path) == IS_STRING) {
114 file_path_str = Z_STRVAL_P(file_path);
115 } else {
116 file_path_str = "eval";
117 }
118
119 if (Z_TYPE_P(line) == IS_LONG) {
120 line_num = Z_LVAL_P(line);
121 } else {
122 line_num = 0;
123 }
124
125 if (phannot_internal_parse_annotations(&result, comment_str, comment_len, file_path_str, line_num, &error_msg TSRMLS_CC) == FAILURE) {
126 if (likely(error_msg != NULL)) {
127 zephir_throw_exception_string(phalcon_annotations_exception_ce, error_msg, strlen(error_msg) TSRMLS_CC);
128 efree(error_msg);
129 } else {
130 zephir_throw_exception_string(phalcon_annotations_exception_ce, SL("There was an error parsing annotation") TSRMLS_CC);
131 }
132
133 return FAILURE;
134 }
135
136 return SUCCESS;
137 }
138
139 /**
140 * Remove comment separators from a docblock
141 */
phannot_remove_comment_separators(char ** ret,int * ret_len,const char * comment,int length,int * start_lines)142 static void phannot_remove_comment_separators(char **ret, int *ret_len, const char *comment, int length, int *start_lines)
143 {
144 char ch;
145 int start_mode = 1, j, i, open_parentheses;
146 smart_str processed_str = {0};
147
148 (*start_lines) = 0;
149
150 for (i = 0; i < length; i++) {
151
152 ch = comment[i];
153
154 if (start_mode) {
155 if (ch == ' ' || ch == '*' || ch == '/' || ch == '\t' || ch == 11) {
156 continue;
157 }
158 start_mode = 0;
159 }
160
161 if (ch == '@') {
162
163 smart_str_appendc(&processed_str, ch);
164 i++;
165
166 open_parentheses = 0;
167 for (j = i; j < length; j++) {
168
169 ch = comment[j];
170
171 if (start_mode) {
172 if (ch == ' ' || ch == '*' || ch == '/' || ch == '\t' || ch == 11) {
173 continue;
174 }
175 start_mode = 0;
176 }
177
178 if (open_parentheses == 0) {
179
180 if (isalnum(ch) || '_' == ch || '\\' == ch) {
181 smart_str_appendc(&processed_str, ch);
182 continue;
183 }
184
185 if (ch == '(') {
186 smart_str_appendc(&processed_str, ch);
187 open_parentheses++;
188 continue;
189 }
190
191 } else {
192
193 smart_str_appendc(&processed_str, ch);
194
195 if (ch == '(') {
196 open_parentheses++;
197 } else {
198 if (ch == ')') {
199 open_parentheses--;
200 } else {
201 if (ch == '\n') {
202 (*start_lines)++;
203 start_mode = 1;
204 }
205 }
206 }
207
208 if (open_parentheses > 0) {
209 continue;
210 }
211 }
212
213 i = j;
214 smart_str_appendc(&processed_str, ' ');
215 break;
216 }
217 }
218
219 if (ch == '\n') {
220 (*start_lines)++;
221 start_mode = 1;
222 }
223 }
224
225 smart_str_0(&processed_str);
226
227 #if PHP_VERSION_ID < 70000
228 if (processed_str.len) {
229 *ret = processed_str.c;
230 *ret_len = processed_str.len;
231 } else {
232 *ret = NULL;
233 *ret_len = 0;
234 }
235 #else
236 if (processed_str.s) {
237 *ret = estrndup(ZSTR_VAL(processed_str.s), ZSTR_LEN(processed_str.s));
238 *ret_len = ZSTR_LEN(processed_str.s);
239 smart_str_free(&processed_str);
240 } else {
241 *ret = NULL;
242 *ret_len = 0;
243 }
244 #endif
245 }
246
247 /**
248 * Parses a comment returning an intermediate array representation
249 */
phannot_internal_parse_annotations(zval ** result,const char * comment,int comment_len,const char * file_path,int line,char ** error_msg TSRMLS_DC)250 int phannot_internal_parse_annotations(zval **result, const char *comment, int comment_len, const char *file_path, int line, char **error_msg TSRMLS_DC)
251 {
252 phannot_scanner_state *state;
253 phannot_scanner_token token;
254 int start_lines;
255 int scanner_status, status = SUCCESS;
256 phannot_parser_status *parser_status = NULL;
257 void* phannot_parser;
258 char *processed_comment;
259 int processed_comment_len;
260
261 *error_msg = NULL;
262
263 /**
264 * Check if the comment has content
265 */
266 if (UNEXPECTED(!comment)) {
267 ZVAL_BOOL(*result, 0);
268 spprintf(error_msg, 0, "Empty annotation");
269 return FAILURE;
270 }
271
272 if (comment_len < 2) {
273 ZVAL_BOOL(*result, 0);
274 return SUCCESS;
275 }
276
277 /**
278 * Remove comment separators
279 */
280 phannot_remove_comment_separators(&processed_comment, &processed_comment_len, comment, comment_len, &start_lines);
281
282 if (processed_comment_len < 2) {
283 ZVAL_BOOL(*result, 0);
284 if (processed_comment) {
285 efree(processed_comment);
286 }
287
288 return SUCCESS;
289 }
290
291 /**
292 * Start the reentrant parser
293 */
294 phannot_parser = phannot_Alloc(phannot_wrapper_alloc);
295 if (unlikely(!phannot_parser)) {
296 ZVAL_BOOL(*result, 0);
297 return FAILURE;
298 }
299
300 parser_status = emalloc(sizeof(phannot_parser_status) + sizeof(phannot_scanner_state));
301 state = (phannot_scanner_state*)((char*)parser_status + sizeof(phannot_parser_status));
302
303 parser_status->status = PHANNOT_PARSING_OK;
304 parser_status->scanner_state = state;
305 #if PHP_VERSION_ID < 70000
306 parser_status->ret = NULL;
307 #endif
308 parser_status->token = &token;
309 parser_status->syntax_error = NULL;
310
311 /**
312 * Initialize the scanner state
313 */
314 state->active_token = 0;
315 state->start = processed_comment;
316 state->start_length = 0;
317 state->mode = PHANNOT_MODE_RAW;
318 state->active_file = file_path;
319
320 token.value = NULL;
321 token.len = 0;
322
323 /**
324 * Possible start line
325 */
326 if (line) {
327 state->active_line = line - start_lines;
328 } else {
329 state->active_line = 1;
330 }
331
332 state->end = state->start;
333
334 while(0 <= (scanner_status = phannot_get_token(state, &token))) {
335
336 state->active_token = token.opcode;
337
338 state->start_length = processed_comment + processed_comment_len - state->start;
339
340 switch (token.opcode) {
341
342 case PHANNOT_T_IGNORE:
343 break;
344
345 case PHANNOT_T_AT:
346 phannot_(phannot_parser, PHANNOT_AT, NULL, parser_status);
347 break;
348 case PHANNOT_T_COMMA:
349 phannot_(phannot_parser, PHANNOT_COMMA, NULL, parser_status);
350 break;
351 case PHANNOT_T_EQUALS:
352 phannot_(phannot_parser, PHANNOT_EQUALS, NULL, parser_status);
353 break;
354 case PHANNOT_T_COLON:
355 phannot_(phannot_parser, PHANNOT_COLON, NULL, parser_status);
356 break;
357
358 case PHANNOT_T_PARENTHESES_OPEN:
359 phannot_(phannot_parser, PHANNOT_PARENTHESES_OPEN, NULL, parser_status);
360 break;
361 case PHANNOT_T_PARENTHESES_CLOSE:
362 phannot_(phannot_parser, PHANNOT_PARENTHESES_CLOSE, NULL, parser_status);
363 break;
364
365 case PHANNOT_T_BRACKET_OPEN:
366 phannot_(phannot_parser, PHANNOT_BRACKET_OPEN, NULL, parser_status);
367 break;
368 case PHANNOT_T_BRACKET_CLOSE:
369 phannot_(phannot_parser, PHANNOT_BRACKET_CLOSE, NULL, parser_status);
370 break;
371
372 case PHANNOT_T_SBRACKET_OPEN:
373 phannot_(phannot_parser, PHANNOT_SBRACKET_OPEN, NULL, parser_status);
374 break;
375 case PHANNOT_T_SBRACKET_CLOSE:
376 phannot_(phannot_parser, PHANNOT_SBRACKET_CLOSE, NULL, parser_status);
377 break;
378
379 case PHANNOT_T_NULL:
380 phannot_(phannot_parser, PHANNOT_NULL, NULL, parser_status);
381 break;
382 case PHANNOT_T_TRUE:
383 phannot_(phannot_parser, PHANNOT_TRUE, NULL, parser_status);
384 break;
385 case PHANNOT_T_FALSE:
386 phannot_(phannot_parser, PHANNOT_FALSE, NULL, parser_status);
387 break;
388
389 case PHANNOT_T_INTEGER:
390 phannot_parse_with_token(phannot_parser, PHANNOT_T_INTEGER, PHANNOT_INTEGER, &token, parser_status);
391 break;
392 case PHANNOT_T_DOUBLE:
393 phannot_parse_with_token(phannot_parser, PHANNOT_T_DOUBLE, PHANNOT_DOUBLE, &token, parser_status);
394 break;
395 case PHANNOT_T_STRING:
396 phannot_parse_with_token(phannot_parser, PHANNOT_T_STRING, PHANNOT_STRING, &token, parser_status);
397 break;
398 case PHANNOT_T_IDENTIFIER:
399 phannot_parse_with_token(phannot_parser, PHANNOT_T_IDENTIFIER, PHANNOT_IDENTIFIER, &token, parser_status);
400 break;
401 /*case PHANNOT_T_ARBITRARY_TEXT:
402 phannot_parse_with_token(phannot_parser, PHANNOT_T_ARBITRARY_TEXT, PHANNOT_ARBITRARY_TEXT, &token, parser_status);
403 break;*/
404
405 default:
406 parser_status->status = PHANNOT_PARSING_FAILED;
407 if (!*error_msg) {
408 spprintf(error_msg, 0, "Scanner: unknown opcode %d on in %s line %d", token.opcode, state->active_file, state->active_line);
409 }
410 break;
411 }
412
413 if (parser_status->status != PHANNOT_PARSING_OK) {
414 status = FAILURE;
415 break;
416 }
417
418 state->end = state->start;
419 }
420
421 if (status != FAILURE) {
422 switch (scanner_status) {
423
424 case PHANNOT_SCANNER_RETCODE_ERR:
425 case PHANNOT_SCANNER_RETCODE_IMPOSSIBLE:
426 if (!*error_msg) {
427 phannot_scanner_error_msg(parser_status, error_msg TSRMLS_CC);
428 }
429 status = FAILURE;
430 break;
431
432 default:
433 phannot_(phannot_parser, 0, NULL, parser_status);
434 }
435 }
436
437 state->active_token = 0;
438 state->start = NULL;
439
440 if (parser_status->status != PHANNOT_PARSING_OK) {
441 status = FAILURE;
442 if (parser_status->syntax_error) {
443 if (!*error_msg) {
444 *error_msg = parser_status->syntax_error;
445 } else {
446 efree(parser_status->syntax_error);
447 }
448 }
449 }
450
451 phannot_Free(phannot_parser, phannot_wrapper_free);
452
453 if (status != FAILURE) {
454 if (parser_status->status == PHANNOT_PARSING_OK) {
455 #if PHP_VERSION_ID < 70000
456 if (parser_status->ret) {
457 ZVAL_ZVAL(*result, parser_status->ret, 0, 0);
458 ZVAL_NULL(parser_status->ret);
459 zval_ptr_dtor(&parser_status->ret);
460 } else {
461 array_init(*result);
462 }
463 #else
464 ZVAL_ZVAL(*result, &parser_status->ret, 1, 1);
465 #endif
466 }
467 }
468
469 efree(processed_comment);
470 efree(parser_status);
471
472 return status;
473 }
474