php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
tokenizer.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Andrei Zmievski <andrei@php.net> |
14 +----------------------------------------------------------------------+
15*/
16
17#ifdef HAVE_CONFIG_H
18#include <config.h>
19#endif
20
21#include "php.h"
22#include "ext/standard/info.h"
23#include "php_tokenizer.h"
24
25#include "zend.h"
26#include "zend_exceptions.h"
28#include "zend_language_scanner_defs.h"
29#include <zend_language_parser.h>
30#include "zend_interfaces.h"
31
33#include "tokenizer_arginfo.h"
34
35#define zendtext LANG_SCNG(yy_text)
36#define zendleng LANG_SCNG(yy_leng)
37#define zendcursor LANG_SCNG(yy_cursor)
38#define zendlimit LANG_SCNG(yy_limit)
39
40static zend_class_entry *php_token_ce;
41
42/* {{{ tokenizer_module_entry */
45 "tokenizer",
46 ext_functions,
47 PHP_MINIT(tokenizer),
48 NULL,
49 NULL,
50 NULL,
51 PHP_MINFO(tokenizer),
54};
55/* }}} */
56
57#ifdef COMPILE_DL_TOKENIZER
58ZEND_GET_MODULE(tokenizer)
59#endif
60
61static zval *php_token_get_id(zval *obj) {
62 zval *id = OBJ_PROP_NUM(Z_OBJ_P(obj), 0);
63 if (Z_ISUNDEF_P(id)) {
65 "Typed property PhpToken::$id must not be accessed before initialization");
66 return NULL;
67 }
68
69 ZVAL_DEREF(id);
71 return id;
72}
73
74static zend_string *php_token_get_text(zval *obj) {
75 zval *text_zval = OBJ_PROP_NUM(Z_OBJ_P(obj), 1);
76 if (Z_ISUNDEF_P(text_zval)) {
78 "Typed property PhpToken::$text must not be accessed before initialization");
79 return NULL;
80 }
81
82 ZVAL_DEREF(text_zval);
83 ZEND_ASSERT(Z_TYPE_P(text_zval) == IS_STRING);
84 return Z_STR_P(text_zval);
85}
86
87static bool tokenize_common(
89
91{
92 zend_string *source;
93 zend_long flags = 0;
94 zend_class_entry *token_class;
95
97 Z_PARAM_STR(source)
101
102 token_class = zend_get_called_scope(execute_data);
103
104 /* Check construction preconditions in advance, so these are not repeated for each token. */
105 if (token_class->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) {
106 zend_throw_error(NULL, "Cannot instantiate abstract class %s", ZSTR_VAL(token_class->name));
108 }
109 if (zend_update_class_constants(token_class) == FAILURE) {
111 }
112
113 if (!tokenize_common(return_value, source, flags, token_class)) {
115 }
116}
117
118PHP_METHOD(PhpToken, __construct)
119{
120 zend_long id;
122 zend_long line = -1;
123 zend_long pos = -1;
125
127 Z_PARAM_LONG(id)
133
134 ZVAL_LONG(OBJ_PROP_NUM(obj, 0), id);
137 ZVAL_LONG(OBJ_PROP_NUM(obj, 2), line);
138 ZVAL_LONG(OBJ_PROP_NUM(obj, 3), pos);
139}
140
142{
143 zval *kind;
144
148
149 if (Z_TYPE_P(kind) == IS_LONG) {
150 zval *id_zval = php_token_get_id(ZEND_THIS);
151 if (!id_zval) {
153 }
154
155 RETURN_BOOL(Z_LVAL_P(id_zval) == Z_LVAL_P(kind));
156 } else if (Z_TYPE_P(kind) == IS_STRING) {
157 zend_string *text = php_token_get_text(ZEND_THIS);
158 if (!text) {
160 }
161
162 RETURN_BOOL(zend_string_equals(text, Z_STR_P(kind)));
163 } else if (Z_TYPE_P(kind) == IS_ARRAY) {
164 zval *id_zval = NULL, *entry;
167 ZVAL_DEREF(entry);
168 if (Z_TYPE_P(entry) == IS_LONG) {
169 if (!id_zval) {
170 id_zval = php_token_get_id(ZEND_THIS);
171 if (!id_zval) {
173 }
174 }
175 if (Z_LVAL_P(id_zval) == Z_LVAL_P(entry)) {
177 }
178 } else if (Z_TYPE_P(entry) == IS_STRING) {
179 if (!text) {
180 text = php_token_get_text(ZEND_THIS);
181 if (!text) {
183 }
184 }
185 if (zend_string_equals(text, Z_STR_P(entry))) {
187 }
188 } else {
189 zend_argument_type_error(1, "must only have elements of type string|int, %s given", zend_zval_value_name(entry));
191 }
194 } else {
195 zend_argument_type_error(1, "must be of type string|int|array, %s given", zend_zval_value_name(kind));
197 }
198}
199
200PHP_METHOD(PhpToken, isIgnorable)
201{
203
204 zval *id_zval = php_token_get_id(ZEND_THIS);
205 if (!id_zval) {
207 }
208
209 zend_long id = Z_LVAL_P(id_zval);
210 RETURN_BOOL(id == T_WHITESPACE || id == T_COMMENT || id == T_DOC_COMMENT || id == T_OPEN_TAG);
211}
212
213PHP_METHOD(PhpToken, getTokenName)
214{
216
217 zval *id_zval = php_token_get_id(ZEND_THIS);
218 if (!id_zval) {
220 }
221
222 if (Z_LVAL_P(id_zval) < 256) {
223 RETURN_CHAR(Z_LVAL_P(id_zval));
224 } else {
225 const char *token_name = get_token_type_name(Z_LVAL_P(id_zval));
226 if (!token_name) {
227 RETURN_NULL();
228 }
229
231 }
232}
233
235{
237
238 zend_string *text = php_token_get_text(ZEND_THIS);
239 if (!text) {
241 }
242
244}
245
246/* {{{ PHP_MINIT_FUNCTION */
248{
249 register_tokenizer_data_symbols(module_number);
250 register_tokenizer_symbols(module_number);
251 php_token_ce = register_class_PhpToken(zend_ce_stringable);
252
253 return SUCCESS;
254}
255/* }}} */
256
257/* {{{ PHP_MINFO_FUNCTION */
259{
261 php_info_print_table_row(2, "Tokenizer Support", "enabled");
263}
264/* }}} */
265
266static zend_string *make_str(unsigned char *text, size_t leng, HashTable *interned_strings) {
267 if (leng == 1) {
268 return ZSTR_CHAR(text[0]);
269 } else if (interned_strings) {
270 zend_string *interned_str = zend_hash_str_find_ptr(interned_strings, (char *) text, leng);
271 if (interned_str) {
272 return zend_string_copy(interned_str);
273 }
274 interned_str = zend_string_init((char *) text, leng, 0);
275 zend_hash_add_new_ptr(interned_strings, interned_str, interned_str);
276 return interned_str;
277 } else {
278 return zend_string_init((char *) text, leng, 0);
279 }
280}
281
282static void add_token(
283 zval *return_value, int token_type, unsigned char *text, size_t leng, int lineno,
284 zend_class_entry *token_class, HashTable *interned_strings) {
285 zval token;
286 if (token_class) {
287 zend_object *obj = zend_objects_new(token_class);
288 ZVAL_OBJ(&token, obj);
289 ZVAL_LONG(OBJ_PROP_NUM(obj, 0), token_type);
290 ZVAL_STR(OBJ_PROP_NUM(obj, 1), make_str(text, leng, interned_strings));
291 ZVAL_LONG(OBJ_PROP_NUM(obj, 2), lineno);
292 ZVAL_LONG(OBJ_PROP_NUM(obj, 3), text - LANG_SCNG(yy_start));
293
294 /* If the class is extended with additional properties, initialized them as well. */
295 if (UNEXPECTED(token_class->default_properties_count > 4)) {
296 zval *dst = OBJ_PROP_NUM(obj, 4);
297 zval *src = &token_class->default_properties_table[4];
298 zval *end = token_class->default_properties_table
299 + token_class->default_properties_count;
300 for (; src < end; src++, dst++) {
301 ZVAL_COPY_PROP(dst, src);
302 }
303 }
304 } else if (token_type >= 256) {
305 array_init_size(&token, 3);
308 ZEND_HASH_FILL_SET_LONG(token_type);
310 ZEND_HASH_FILL_SET_STR(make_str(text, leng, interned_strings));
315 } else {
316 ZVAL_STR(&token, make_str(text, leng, interned_strings));
317 }
319}
320
321static bool tokenize(zval *return_value, zend_string *source, zend_class_entry *token_class)
322{
323 zval source_zval;
324 zend_lex_state original_lex_state;
325 zval token;
326 int token_type;
327 int token_line = 1;
328 int need_tokens = -1; /* for __halt_compiler lexing. -1 = disabled */
329 HashTable interned_strings;
330
331 ZVAL_STR_COPY(&source_zval, source);
332 zend_save_lexical_state(&original_lex_state);
333
335
336 LANG_SCNG(yy_state) = yycINITIAL;
337 zend_hash_init(&interned_strings, 0, NULL, NULL, 0);
339
340 while ((token_type = lex_scan(&token, NULL))) {
341 ZEND_ASSERT(token_type != T_ERROR);
342
343 add_token(
344 return_value, token_type, zendtext, zendleng, token_line,
345 token_class, &interned_strings);
346
347 if (Z_TYPE(token) != IS_UNDEF) {
348 zval_ptr_dtor_nogc(&token);
349 ZVAL_UNDEF(&token);
350 }
351
352 /* after T_HALT_COMPILER collect the next three non-dropped tokens */
353 if (need_tokens != -1) {
354 if (token_type != T_WHITESPACE && token_type != T_OPEN_TAG
355 && token_type != T_COMMENT && token_type != T_DOC_COMMENT
356 && --need_tokens == 0
357 ) {
358 /* fetch the rest into a T_INLINE_HTML */
359 if (zendcursor < zendlimit) {
360 add_token(
362 token_line, token_class, &interned_strings);
363 }
364 break;
365 }
366 } else if (token_type == T_HALT_COMPILER) {
367 need_tokens = 3;
368 }
369
370 if (CG(increment_lineno)) {
371 CG(zend_lineno)++;
372 CG(increment_lineno) = 0;
373 }
374
375 token_line = CG(zend_lineno);
376 }
377
378 zval_ptr_dtor_str(&source_zval);
379 zend_restore_lexical_state(&original_lex_state);
380 zend_hash_destroy(&interned_strings);
381
382 return 1;
383}
384
389
390static zval *extract_token_id_to_replace(zval *token_zv, const char *text, size_t length) {
391 zval *id_zv, *text_zv;
392 ZEND_ASSERT(token_zv);
393 if (Z_TYPE_P(token_zv) == IS_ARRAY) {
394 id_zv = zend_hash_index_find(Z_ARRVAL_P(token_zv), 0);
395 text_zv = zend_hash_index_find(Z_ARRVAL_P(token_zv), 1);
396 } else if (Z_TYPE_P(token_zv) == IS_OBJECT) {
397 id_zv = OBJ_PROP_NUM(Z_OBJ_P(token_zv), 0);
398 text_zv = OBJ_PROP_NUM(Z_OBJ_P(token_zv), 1);
399 } else {
400 return NULL;
401 }
402
403 /* There are multiple candidate tokens to which this feedback may apply,
404 * check text to make sure this is the right one. */
405 ZEND_ASSERT(Z_TYPE_P(text_zv) == IS_STRING);
406 if (Z_STRLEN_P(text_zv) == length && !memcmp(Z_STRVAL_P(text_zv), text, length)) {
407 return id_zv;
408 }
409 return NULL;
410}
411
412static void on_event(
413 zend_php_scanner_event event, int token, int line,
414 const char *text, size_t length, void *context)
415{
416 struct event_context *ctx = context;
417
418 switch (event) {
419 case ON_TOKEN:
420 if (token == END) break;
421 /* Special cases */
422 if (token == ';' && LANG_SCNG(yy_leng) > 1) { /* ?> or ?>\n or ?>\r\n */
423 token = T_CLOSE_TAG;
424 } else if (token == T_ECHO && LANG_SCNG(yy_leng) == sizeof("<?=") - 1) {
425 token = T_OPEN_TAG_WITH_ECHO;
426 }
427 add_token(
428 ctx->tokens, token, (unsigned char *) text, length, line, ctx->token_class, NULL);
429 break;
430 case ON_FEEDBACK: {
431 HashTable *tokens_ht = Z_ARRVAL_P(ctx->tokens);
432 zval *token_zv, *id_zv = NULL;
433 ZEND_HASH_REVERSE_FOREACH_VAL(tokens_ht, token_zv) {
434 id_zv = extract_token_id_to_replace(token_zv, text, length);
435 if (id_zv) {
436 break;
437 }
439 ZEND_ASSERT(id_zv);
440 ZVAL_LONG(id_zv, token);
441 break;
442 }
443 case ON_STOP:
444 if (LANG_SCNG(yy_cursor) != LANG_SCNG(yy_limit)) {
445 add_token(ctx->tokens, T_INLINE_HTML, LANG_SCNG(yy_cursor),
446 LANG_SCNG(yy_limit) - LANG_SCNG(yy_cursor), CG(zend_lineno),
447 ctx->token_class, NULL);
448 }
449 break;
450 }
451}
452
453static bool tokenize_parse(
455{
456 zval source_zval;
457 struct event_context ctx;
458 zval token_stream;
459 zend_lex_state original_lex_state;
460 bool original_in_compilation;
461 bool success;
462
463 ZVAL_STR_COPY(&source_zval, source);
464
465 original_in_compilation = CG(in_compilation);
466 CG(in_compilation) = 1;
467 zend_save_lexical_state(&original_lex_state);
468
470 array_init(&token_stream);
471
472 ctx.tokens = &token_stream;
474
475 CG(ast) = NULL;
476 CG(ast_arena) = zend_arena_create(1024 * 32);
477 LANG_SCNG(yy_state) = yycINITIAL;
478 LANG_SCNG(on_event) = on_event;
479 LANG_SCNG(on_event_context) = &ctx;
480
481 if((success = (zendparse() == SUCCESS))) {
482 ZVAL_COPY_VALUE(return_value, &token_stream);
483 } else {
484 zval_ptr_dtor(&token_stream);
485 }
486
487 zend_ast_destroy(CG(ast));
488 zend_arena_destroy(CG(ast_arena));
489
490 /* restore compiler and scanner global states */
491 zend_restore_lexical_state(&original_lex_state);
492 CG(in_compilation) = original_in_compilation;
493
494 zval_ptr_dtor_str(&source_zval);
495
496 return success;
497}
498
499static bool tokenize_common(
501{
502 if (flags & TOKEN_PARSE) {
503 return tokenize_parse(return_value, source, token_class);
504 } else {
505 int success = tokenize(return_value, source, token_class);
506 /* Normal token_get_all() should not throw. */
508 return success;
509 }
510}
511
512/* }}} */
513
514/* {{{ */
516{
517 zend_string *source;
518 zend_long flags = 0;
519
521 Z_PARAM_STR(source)
525
526 if (!tokenize_common(return_value, source, flags, /* token_class */ NULL)) {
528 }
529}
530/* }}} */
531
532/* {{{ */
534{
536
540
541 const char *token_name = get_token_type_name(type);
542 if (!token_name) {
543 token_name = "UNKNOWN";
544 }
546}
547/* }}} */
zend_ffi_type * type
Definition ffi.c:3812
new_type kind
Definition ffi.c:4363
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
php_info_print_table_start()
Definition info.c:1064
php_info_print_table_row(2, "PDO Driver for Firebird", "enabled")
php_info_print_table_end()
Definition info.c:1074
#define PHP_FUNCTION
Definition php.h:364
#define PHP_MINFO
Definition php.h:396
#define PHP_MINIT_FUNCTION
Definition php.h:400
#define PHP_MINFO_FUNCTION
Definition php.h:404
#define PHP_MINIT
Definition php.h:392
#define PHP_METHOD
Definition php.h:365
int line
Definition php_ffi.h:54
unsigned const char * end
Definition php_ffi.h:51
unsigned const char * pos
Definition php_ffi.h:52
unsigned const char * text
Definition php_ffi.h:53
#define PHP_TOKENIZER_VERSION
char * get_token_type_name(int token_type)
zend_module_entry tokenizer_module_entry
Definition tokenizer.c:43
#define TOKEN_PARSE
zend_string * name
Definition zend.h:149
uint32_t ce_flags
Definition zend.h:156
int default_properties_count
Definition zend.h:158
zval * default_properties_table
Definition zend.h:160
Definition dce.c:49
zend_class_entry * token_class
Definition tokenizer.c:387
zval * tokens
Definition tokenizer.c:386
#define zendleng
Definition tokenizer.c:36
#define zendlimit
Definition tokenizer.c:38
#define zendcursor
Definition tokenizer.c:37
#define zendtext
Definition tokenizer.c:35
token_name(int $id)
token_get_all(string $code, int $flags=0)
const T_COMMENT
const T_OPEN_TAG
const T_ECHO
const T_INLINE_HTML
const T_OPEN_TAG_WITH_ECHO
const T_DOC_COMMENT
const T_HALT_COMPILER
const T_WHITESPACE
const T_CLOSE_TAG
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format,...)
Definition zend.c:1772
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type)
Definition zend_API.c:1518
ZEND_API const char * zend_zval_value_name(const zval *arg)
Definition zend_API.c:148
ZEND_API ZEND_COLD void zend_argument_type_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:423
#define RETURN_CHAR(c)
Definition zend_API.h:1048
#define RETURN_STRING(s)
Definition zend_API.h:1043
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define ZEND_PARSE_PARAMETERS_NONE()
Definition zend_API.h:1623
#define RETURN_NULL()
Definition zend_API.h:1036
#define array_init_size(arg, size)
Definition zend_API.h:538
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define ZEND_GET_MODULE(name)
Definition zend_API.h:241
#define Z_PARAM_STR(dest)
Definition zend_API.h:2086
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define Z_PARAM_LONG(dest)
Definition zend_API.h:1896
#define RETURN_BOOL(b)
Definition zend_API.h:1035
#define RETURN_THROWS()
Definition zend_API.h:1060
#define ZEND_THIS
Definition zend_API.h:523
#define Z_PARAM_ZVAL(dest)
Definition zend_API.h:2100
#define RETURN_TRUE
Definition zend_API.h:1059
#define RETURN_STR_COPY(s)
Definition zend_API.h:1042
#define array_init(arg)
Definition zend_API.h:537
ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
Definition zend_ast.c:1163
struct _zval_struct zval
#define OBJ_PROP_NUM(obj, num)
#define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
ZEND_API int ZEND_FASTCALL lex_scan(zval *zendlval, zend_parser_stack_elem *elem)
ZEND_API void zend_clear_exception(void)
ZEND_API zend_class_entry * zend_get_called_scope(zend_execute_data *ex)
zend_php_scanner_event
@ ON_TOKEN
@ ON_STOP
@ ON_FEEDBACK
#define CG(v)
ZEND_API int zendparse(void)
#define LANG_SCNG(v)
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
Definition zend_hash.c:1727
ZEND_API void ZEND_FASTCALL zend_hash_real_init_packed(HashTable *ht)
Definition zend_hash.c:330
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert_new(HashTable *ht, zval *pData)
Definition zend_hash.c:1229
ZEND_API zval *ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h)
Definition zend_hash.c:2701
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
Definition zend_hash.h:108
#define ZEND_HASH_REVERSE_FOREACH_VAL(ht, _val)
Definition zend_hash.h:1106
#define ZEND_HASH_FILL_PACKED(ht)
Definition zend_hash.h:1528
#define ZEND_HASH_FILL_END()
Definition zend_hash.h:1582
#define ZEND_HASH_FILL_SET_STR(_val)
Definition zend_hash.h:1556
#define ZEND_HASH_FILL_SET_LONG(_val)
Definition zend_hash.h:1550
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
#define ZEND_HASH_FILL_NEXT()
Definition zend_hash.h:1565
#define ZEND_HASH_FOREACH_VAL(ht, _val)
Definition zend_hash.h:1102
ZEND_API zend_class_entry * zend_ce_stringable
ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state)
struct _zend_lex_state zend_lex_state
ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state)
ZEND_API void zend_prepare_string_for_scanning(zval *str, zend_string *filename)
int32_t zend_long
Definition zend_long.h:42
struct _zend_string zend_string
#define STANDARD_MODULE_HEADER
struct _zend_module_entry zend_module_entry
#define STANDARD_MODULE_PROPERTIES
ZEND_API zend_object *ZEND_FASTCALL zend_objects_new(zend_class_entry *ce)
#define ZEND_ASSERT(c)
#define UNEXPECTED(condition)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_EMPTY_ALLOC()
#define ZSTR_CHAR(c)
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define ZVAL_STR(z, s)
#define ZVAL_UNDEF(z)
#define Z_STRVAL_P(zval_p)
Definition zend_types.h:975
#define IS_UNDEF
Definition zend_types.h:600
#define Z_ISUNDEF_P(zval_p)
Definition zend_types.h:957
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define ZVAL_DEREF(z)
#define ZVAL_LONG(z, l)
#define IS_STRING
Definition zend_types.h:606
#define ZVAL_STR_COPY(z, s)
struct _zend_array HashTable
Definition zend_types.h:386
#define Z_OBJ_P(zval_p)
Definition zend_types.h:990
#define IS_ARRAY
Definition zend_types.h:607
#define Z_STR_P(zval_p)
Definition zend_types.h:972
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
#define ZVAL_OBJ(z, o)
@ FAILURE
Definition zend_types.h:61
#define IS_OBJECT
Definition zend_types.h:608
#define IS_LONG
Definition zend_types.h:604
#define ZVAL_COPY_PROP(z, v)
#define Z_TYPE(zval)
Definition zend_types.h:659
#define Z_ARRVAL(zval)
Definition zend_types.h:986
#define Z_LVAL_P(zval_p)
Definition zend_types.h:966
#define ZVAL_COPY_VALUE(z, v)
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval * return_value
execute_data