%{ /* +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ | Copyright (c) 1998-2006 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.zend.com/license/2_00.txt. | | If you did not receive a copy of the Zend license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andi Gutmans | | Zeev Suraski | +----------------------------------------------------------------------+ */ /* $Id: zend_language_scanner.l,v 1.131.2.11.2.13 2007/09/09 16:33:34 iliaa Exp $ */ #define yyleng SCNG(yy_leng) #define yytext SCNG(yy_text) #define yytext_ptr SCNG(yy_text) #define yyin SCNG(yy_in) #define yyout SCNG(yy_out) #define yy_last_accepting_state SCNG(_yy_last_accepting_state) #define yy_last_accepting_cpos SCNG(_yy_last_accepting_cpos) #define yy_more_flag SCNG(_yy_more_flag) #define yy_more_len SCNG(_yy_more_len) %} %x ST_IN_SCRIPTING %x ST_DOUBLE_QUOTES %x ST_BACKQUOTE %x ST_HEREDOC %x ST_START_HEREDOC %x ST_END_HEREDOC %x ST_LOOKING_FOR_PROPERTY %x ST_LOOKING_FOR_VARNAME %x ST_VAR_OFFSET %x ST_COMMENT %x ST_DOC_COMMENT %x ST_ONE_LINE_COMMENT %option stack %{ #include #include "zend.h" #include "zend_alloc.h" #include #include "zend_compile.h" #include "zend_language_scanner.h" #include "zend_highlight.h" #include "zend_constants.h" #include "zend_variables.h" #include "zend_operators.h" #include "zend_API.h" #include "zend_strtod.h" #include "zend_exceptions.h" #ifdef HAVE_STDARG_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #define YY_DECL int lex_scan(zval *zendlval TSRMLS_DC) #define ECHO { ZEND_WRITE( yytext, yyleng ); } #ifdef ZTS # define MY_INPUT yyinput #else # define MY_INPUT input #endif /* Globals Macros */ #define SCNG LANG_SCNG #ifdef ZTS ZEND_API ts_rsrc_id language_scanner_globals_id; #else ZEND_API zend_scanner_globals language_scanner_globals; #endif #define YY_FATAL_ERROR zend_fatal_scanner_error #define HANDLE_NEWLINES(s, l) \ do { \ char *p = (s), *boundary = p+(l); \ \ while (p='0' && (c)<='7') #define ZEND_IS_HEX(c) (((c)>='0' && (c)<='9') || ((c)>='a' && (c)<='f') || ((c)>='A' && (c)<='F')) void zend_fatal_scanner_error(char *message) { zend_error(E_COMPILE_ERROR, "%s", message); } BEGIN_EXTERN_C() void startup_scanner(TSRMLS_D) { CG(heredoc) = NULL; CG(heredoc_len) = 0; CG(doc_comment) = NULL; CG(doc_comment_len) = 0; SCNG(yy_start_stack_ptr) = 0; SCNG(yy_start_stack_depth) = 0; SCNG(current_buffer) = NULL; #ifdef ZEND_MULTIBYTE SCNG(script_org) = NULL; SCNG(script_org_size) = 0; SCNG(script_filtered) = NULL; SCNG(script_filtered_size) = 0; SCNG(input_filter) = NULL; SCNG(output_filter) = NULL; SCNG(script_encoding) = NULL; SCNG(internal_encoding) = NULL; #endif /* ZEND_MULTIBYTE */ } void shutdown_scanner(TSRMLS_D) { if (CG(heredoc)) { efree(CG(heredoc)); CG(heredoc_len)=0; } if (SCNG(yy_start_stack)) { yy_flex_free(SCNG(yy_start_stack)); SCNG(yy_start_stack) = NULL; } RESET_DOC_COMMENT(); #ifdef ZEND_MULTIBYTE if (SCNG(script_org)) { efree(SCNG(script_org)); SCNG(script_org) = NULL; } if (SCNG(script_filtered)) { efree(SCNG(script_filtered)); SCNG(script_filtered) = NULL; } SCNG(script_org_size) = 0; SCNG(script_filtered_size) = 0; SCNG(input_filter) = NULL; SCNG(output_filter) = NULL; SCNG(script_encoding) = NULL; SCNG(internal_encoding) = NULL; #endif /* ZEND_MULTIBYTE */ } END_EXTERN_C() ZEND_API void zend_save_lexical_state(zend_lex_state *lex_state TSRMLS_DC) { memcpy(&lex_state->buffer_state, &YY_CURRENT_BUFFER, sizeof(YY_BUFFER_STATE)); lex_state->in = SCNG(yy_in); lex_state->state = YYSTATE; lex_state->filename = zend_get_compiled_filename(TSRMLS_C); lex_state->lineno = CG(zend_lineno); #ifdef ZEND_MULTIBYTE lex_state->script_org = SCNG(script_org); lex_state->script_org_size = SCNG(script_org_size); lex_state->script_filtered = SCNG(script_filtered); lex_state->script_filtered_size = SCNG(script_filtered_size); lex_state->input_filter = SCNG(input_filter); lex_state->output_filter = SCNG(output_filter); lex_state->script_encoding = SCNG(script_encoding); lex_state->internal_encoding = SCNG(internal_encoding); #endif /* ZEND_MULTIBYTE */ } ZEND_API void zend_restore_lexical_state(zend_lex_state *lex_state TSRMLS_DC) { YY_BUFFER_STATE original_buffer_state = YY_CURRENT_BUFFER; if (lex_state->buffer_state) { yy_switch_to_buffer(lex_state->buffer_state TSRMLS_CC); } else { YY_CURRENT_BUFFER = NULL; } yy_delete_buffer(original_buffer_state TSRMLS_CC); SCNG(yy_in) = lex_state->in; BEGIN(lex_state->state); CG(zend_lineno) = lex_state->lineno; zend_restore_compiled_filename(lex_state->filename TSRMLS_CC); #ifdef ZEND_MULTIBYTE if (SCNG(script_org)) { efree(SCNG(script_org)); SCNG(script_org) = NULL; } if (SCNG(script_filtered)) { efree(SCNG(script_filtered)); SCNG(script_filtered) = NULL; } SCNG(script_org) = lex_state->script_org; SCNG(script_org_size) = lex_state->script_org_size; SCNG(script_filtered) = lex_state->script_filtered; SCNG(script_filtered_size) = lex_state->script_filtered_size; SCNG(input_filter) = lex_state->input_filter; SCNG(output_filter) = lex_state->output_filter; SCNG(script_encoding) = lex_state->script_encoding; SCNG(internal_encoding) = lex_state->internal_encoding; #endif /* ZEND_MULTIBYTE */ } BEGIN_EXTERN_C() ZEND_API void zend_file_handle_dtor(zend_file_handle *fh) { TSRMLS_FETCH(); switch (fh->type) { case ZEND_HANDLE_FP: fclose(fh->handle.fp); break; case ZEND_HANDLE_STREAM: if (fh->handle.stream.closer) { fh->handle.stream.closer(fh->handle.stream.handle TSRMLS_CC); } break; case ZEND_HANDLE_FILENAME: /* We're only supposed to get here when destructing the used_files hash, * which doesn't really contain open files, but references to their names/paths */ break; } if (fh->opened_path) { efree(fh->opened_path); fh->opened_path = NULL; } if (fh->free_filename && fh->filename) { efree(fh->filename); fh->filename = NULL; } } int zend_compare_file_handles(zend_file_handle *fh1, zend_file_handle *fh2) { if (fh1->type != fh2->type) { return 0; } switch (fh1->type) { case ZEND_HANDLE_FP: return fh1->handle.fp==fh2->handle.fp; break; case ZEND_HANDLE_STREAM: return fh1->handle.stream.handle == fh2->handle.stream.handle; break; } return 0; } ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle TSRMLS_DC) { zend_llist_del_element(&CG(open_files), file_handle, (int (*)(void *, void *)) zend_compare_file_handles); /* zend_file_handle_dtor() operates on the copy, so we have to NULLify the original here */ file_handle->opened_path = NULL; if (file_handle->free_filename) { file_handle->filename = NULL; } } ZEND_API int open_file_for_scanning(zend_file_handle *file_handle TSRMLS_DC) { char *file_path=NULL; if (FAILURE == zend_stream_fixup(file_handle TSRMLS_CC)) { return FAILURE; } zend_llist_add_element(&CG(open_files), file_handle); /* Reset the scanner for scanning the new file */ SCNG(yy_in) = file_handle; #ifdef ZEND_MULTIBYTE if (file_handle->handle.stream.interactive == 0) { if (zend_multibyte_read_script(TSRMLS_C) != 0) { return FAILURE; } /* force flex to use buffer only */ SCNG(yy_in) = NULL; SCNG(init) = 0; SCNG(start) = 1; zend_multibyte_set_filter(NULL TSRMLS_CC); if (!SCNG(input_filter)) { SCNG(script_filtered) = (char*)emalloc(SCNG(script_org_size)+1); memcpy(SCNG(script_filtered), SCNG(script_org), SCNG(script_org_size)+1); SCNG(script_filtered_size) = SCNG(script_org_size); } else { SCNG(input_filter)(&SCNG(script_filtered), &SCNG(script_filtered_size), SCNG(script_org), SCNG(script_org_size) TSRMLS_CC); } /* flex requires doubled null */ SCNG(script_filtered) = (char*)erealloc(SCNG(script_filtered), SCNG(script_filtered_size)+2); *(SCNG(script_filtered)+SCNG(script_filtered_size)) = (char)NULL; *(SCNG(script_filtered)+SCNG(script_filtered_size)+1) = (char)NULL; yy_scan_buffer(SCNG(script_filtered), SCNG(script_filtered_size)+2 TSRMLS_CC); } else { yy_switch_to_buffer(yy_create_buffer(SCNG(yy_in), YY_BUF_SIZE TSRMLS_CC) TSRMLS_CC); } #else /* !ZEND_MULTIBYTE */ yy_switch_to_buffer(yy_create_buffer(SCNG(yy_in), YY_BUF_SIZE TSRMLS_CC) TSRMLS_CC); #endif /* ZEND_MULTIBYTE */ BEGIN(INITIAL); if (file_handle->opened_path) { file_path = file_handle->opened_path; } else { file_path = file_handle->filename; } zend_set_compiled_filename(file_path TSRMLS_CC); if (CG(start_lineno)) { CG(zend_lineno) = CG(start_lineno); CG(start_lineno) = 0; } else { CG(zend_lineno) = 1; } CG(increment_lineno) = 0; return SUCCESS; } END_EXTERN_C() ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) { zend_lex_state original_lex_state; zend_op_array *op_array = (zend_op_array *) emalloc(sizeof(zend_op_array)); zend_op_array *original_active_op_array = CG(active_op_array); zend_op_array *retval=NULL; int compiler_result; zend_bool compilation_successful=0; znode retval_znode; zend_bool original_in_compilation = CG(in_compilation); retval_znode.op_type = IS_CONST; retval_znode.u.constant.type = IS_LONG; retval_znode.u.constant.value.lval = 1; retval_znode.u.constant.is_ref = 0; retval_znode.u.constant.refcount = 1; zend_save_lexical_state(&original_lex_state TSRMLS_CC); retval = op_array; /* success oriented */ if (open_file_for_scanning(file_handle TSRMLS_CC)==FAILURE) { if (type==ZEND_REQUIRE) { zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); zend_bailout(); } else { zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); } compilation_successful=0; } else { init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC); CG(in_compilation) = 1; CG(active_op_array) = op_array; compiler_result = zendparse(TSRMLS_C); zend_do_return(&retval_znode, 0 TSRMLS_CC); zend_do_handle_exception(TSRMLS_C); CG(in_compilation) = original_in_compilation; if (compiler_result==1) { /* parser error */ zend_bailout(); } compilation_successful=1; } if (retval) { CG(active_op_array) = original_active_op_array; if (compilation_successful) { pass_two(op_array TSRMLS_CC); } else { efree(op_array); retval = NULL; } } if (compilation_successful) { zend_restore_lexical_state(&original_lex_state TSRMLS_CC); } return retval; } zend_op_array *compile_filename(int type, zval *filename TSRMLS_DC) { zend_file_handle file_handle; zval tmp; zend_op_array *retval; char *opened_path = NULL; if (filename->type != IS_STRING) { tmp = *filename; zval_copy_ctor(&tmp); convert_to_string(&tmp); filename = &tmp; } file_handle.filename = filename->value.str.val; file_handle.free_filename = 0; file_handle.type = ZEND_HANDLE_FILENAME; file_handle.opened_path = NULL; file_handle.handle.fp = NULL; retval = zend_compile_file(&file_handle, type TSRMLS_CC); if (retval && file_handle.handle.stream.handle) { int dummy = 1; if (!file_handle.opened_path) { file_handle.opened_path = opened_path = estrndup(filename->value.str.val, filename->value.str.len); } zend_hash_add(&EG(included_files), file_handle.opened_path, strlen(file_handle.opened_path)+1, (void *)&dummy, sizeof(int), NULL); if (opened_path) { efree(opened_path); } } zend_destroy_file_handle(&file_handle TSRMLS_CC); if (filename==&tmp) { zval_dtor(&tmp); } return retval; } ZEND_API int zend_prepare_string_for_scanning(zval *str, char *filename TSRMLS_DC) { /* enforce two trailing NULLs for flex... */ STR_REALLOC(str->value.str.val, str->value.str.len+2); str->value.str.val[str->value.str.len+1]=0; SCNG(yy_in)=NULL; #ifdef ZEND_MULTIBYTE SCNG(script_org) = estrdup(str->value.str.val); SCNG(script_org_size) = str->value.str.len; zend_multibyte_set_filter(CG(internal_encoding) TSRMLS_CC); if (!SCNG(input_filter)) { SCNG(script_filtered) = (char*)emalloc(SCNG(script_org_size)+1); memcpy(SCNG(script_filtered), SCNG(script_org), SCNG(script_org_size)+1); SCNG(script_filtered_size) = SCNG(script_org_size); } else { SCNG(input_filter)(&SCNG(script_filtered), &SCNG(script_filtered_size), SCNG(script_org), SCNG(script_org_size) TSRMLS_CC); } /* flex requires doubled null */ SCNG(script_filtered) = (char*)erealloc(SCNG(script_filtered), SCNG(script_filtered_size)+2); *(SCNG(script_filtered)+SCNG(script_filtered_size)) = (char)NULL; *(SCNG(script_filtered)+SCNG(script_filtered_size)+1) = (char)NULL; yy_scan_buffer(SCNG(script_filtered), SCNG(script_filtered_size)+2 TSRMLS_CC); #else /* !ZEND_MULTIBYTE */ yy_scan_buffer(str->value.str.val, str->value.str.len+2 TSRMLS_CC); #endif /* ZEND_MULTIBYTE */ zend_set_compiled_filename(filename TSRMLS_CC); CG(zend_lineno) = 1; CG(increment_lineno) = 0; return SUCCESS; } ZEND_API int zend_get_scanned_file_offset(TSRMLS_D) { if (yyin) { int offset_in_buffer = (yy_c_buf_p - (YY_CURRENT_BUFFER)->yy_ch_buf); int read_bytes = SCNG(yy_n_chars); int offset_from_the_end = read_bytes - offset_in_buffer; return zend_stream_ftell(yyin TSRMLS_CC) - offset_from_the_end; } else { /* The entire file is in the buffer; probably zend multibyte is enabled */ return (yy_c_buf_p - (YY_CURRENT_BUFFER)->yy_ch_buf); } } zend_op_array *compile_string(zval *source_string, char *filename TSRMLS_DC) { zend_lex_state original_lex_state; zend_op_array *op_array = (zend_op_array *) emalloc(sizeof(zend_op_array)); zend_op_array *original_active_op_array = CG(active_op_array); zend_op_array *retval; zval tmp; int compiler_result; zend_bool original_in_compilation = CG(in_compilation); if (source_string->value.str.len==0) { efree(op_array); return NULL; } CG(in_compilation) = 1; tmp = *source_string; zval_copy_ctor(&tmp); convert_to_string(&tmp); source_string = &tmp; zend_save_lexical_state(&original_lex_state TSRMLS_CC); if (zend_prepare_string_for_scanning(source_string, filename TSRMLS_CC)==FAILURE) { efree(op_array); retval = NULL; } else { zend_bool orig_interactive = CG(interactive); CG(interactive) = 0; init_op_array(op_array, ZEND_EVAL_CODE, INITIAL_OP_ARRAY_SIZE TSRMLS_CC); CG(interactive) = orig_interactive; CG(active_op_array) = op_array; BEGIN(ST_IN_SCRIPTING); compiler_result = zendparse(TSRMLS_C); #ifdef ZEND_MULTIBYTE if (SCNG(script_org)) { efree(SCNG(script_org)); SCNG(script_org) = NULL; } if (SCNG(script_filtered)) { efree(SCNG(script_filtered)); SCNG(script_filtered) = NULL; } #endif /* ZEND_MULTIBYTE */ if (compiler_result==1) { CG(active_op_array) = original_active_op_array; CG(unclean_shutdown)=1; retval = NULL; } else { zend_do_return(NULL, 0 TSRMLS_CC); zend_do_handle_exception(TSRMLS_C); CG(active_op_array) = original_active_op_array; pass_two(op_array TSRMLS_CC); retval = op_array; } zend_restore_lexical_state(&original_lex_state TSRMLS_CC); } zval_dtor(&tmp); CG(in_compilation) = original_in_compilation; return retval; } BEGIN_EXTERN_C() int highlight_file(char *filename, zend_syntax_highlighter_ini *syntax_highlighter_ini TSRMLS_DC) { zend_lex_state original_lex_state; zend_file_handle file_handle; file_handle.type = ZEND_HANDLE_FILENAME; file_handle.filename = filename; file_handle.free_filename = 0; file_handle.opened_path = NULL; zend_save_lexical_state(&original_lex_state TSRMLS_CC); if (open_file_for_scanning(&file_handle TSRMLS_CC)==FAILURE) { zend_message_dispatcher(ZMSG_FAILED_HIGHLIGHT_FOPEN, filename); return FAILURE; } zend_highlight(syntax_highlighter_ini TSRMLS_CC); #ifdef ZEND_MULTIBYTE if (SCNG(script_org)) { efree(SCNG(script_org)); SCNG(script_org) = NULL; } if (SCNG(script_filtered)) { efree(SCNG(script_filtered)); SCNG(script_filtered) = NULL; } #endif /* ZEND_MULTIBYTE */ zend_destroy_file_handle(&file_handle TSRMLS_CC); zend_restore_lexical_state(&original_lex_state TSRMLS_CC); return SUCCESS; } int highlight_string(zval *str, zend_syntax_highlighter_ini *syntax_highlighter_ini, char *str_name TSRMLS_DC) { zend_lex_state original_lex_state; zval tmp = *str; str = &tmp; zval_copy_ctor(str); zend_save_lexical_state(&original_lex_state TSRMLS_CC); if (zend_prepare_string_for_scanning(str, str_name TSRMLS_CC)==FAILURE) { return FAILURE; } BEGIN(INITIAL); zend_highlight(syntax_highlighter_ini TSRMLS_CC); #ifdef ZEND_MULTIBYTE if (SCNG(script_org)) { efree(SCNG(script_org)); SCNG(script_org) = NULL; } if (SCNG(script_filtered)) { efree(SCNG(script_filtered)); SCNG(script_filtered) = NULL; } #endif /* ZEND_MULTIBYTE */ zend_restore_lexical_state(&original_lex_state TSRMLS_CC); zval_dtor(str); return SUCCESS; } END_EXTERN_C() #ifdef ZEND_MULTIBYTE BEGIN_EXTERN_C() ZEND_API void zend_multibyte_yyinput_again(zend_encoding_filter old_input_filter, zend_encoding *old_encoding TSRMLS_DC) { YY_BUFFER_STATE b = YY_CURRENT_BUFFER; int offset, original_offset, length, free_flag; char *p; zend_encoding *new_encoding; /* calculate current position */ offset = original_offset = yy_c_buf_p - b->yy_ch_buf; if (old_input_filter && original_offset > 0) { new_encoding = SCNG(script_encoding); SCNG(script_encoding) = old_encoding; do { (old_input_filter)(&p, &length, SCNG(script_org), offset TSRMLS_CC); if (!p) { SCNG(script_encoding) = new_encoding; return; } efree(p); if (length > original_offset) { offset--; } else if (length < original_offset) { offset++; } } while (original_offset != length); SCNG(script_encoding) = new_encoding; } /* convert and set */ if (!SCNG(input_filter)) { length = SCNG(script_org_size)-offset-1; p = SCNG(script_org)+offset+1; free_flag = 0; } else { SCNG(input_filter)(&p, &length, SCNG(script_org)+offset+1, SCNG(script_org_size)-offset-1 TSRMLS_CC); free_flag = 1; } if (original_offset+length+1 > (int)b->yy_buf_size) { b->yy_buf_size = original_offset+length+1; b->yy_ch_buf = (char*)erealloc(b->yy_ch_buf, b->yy_buf_size+2); SCNG(script_filtered) = b->yy_ch_buf; SCNG(script_filtered_size) = b->yy_buf_size; } yy_c_buf_p = b->yy_ch_buf + original_offset; strncpy(yy_c_buf_p+1, p, length); b->yy_n_chars = original_offset + length + 1; SCNG(yy_n_chars) = b->yy_n_chars; b->yy_ch_buf[SCNG(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[SCNG(yy_n_chars)+1] = YY_END_OF_BUFFER_CHAR; if (free_flag) { efree(p); } } ZEND_API int zend_multibyte_yyinput(zend_file_handle *file_handle, char *buf, size_t len TSRMLS_DC) { int c = '*', n; if (file_handle->handle.stream.interactive == 0) { return zend_stream_read(file_handle, buf, len TSRMLS_CC); } /* interactive */ if (SCNG(script_org)) { efree(SCNG(script_org)); } if (SCNG(script_filtered)) { efree(SCNG(script_filtered)); } SCNG(script_org) = NULL; SCNG(script_org_size) = 0; /* TODO: support widechars */ for (n = 0; n < sizeof(buf) && (c = zend_stream_getc(yyin TSRMLS_CC)) != EOF && c != '\n'; ++n) { buf[n] = (char)c; } if (c == '\n') { buf[n++] = (char) c; } SCNG(script_org_size) = n; SCNG(script_org) = (char*)emalloc(SCNG(script_org_size)+1); memcpy(SCNG(script_org)+SCNG(script_org_size)-n, buf, n); return n; } ZEND_API int zend_multibyte_read_script(TSRMLS_D) { char buf[8192]; int n; if (SCNG(script_org)) { efree(SCNG(script_org)); } SCNG(script_org) = NULL; SCNG(script_org_size) = 0; for (;;) { n = zend_stream_read(yyin, buf, sizeof(buf) TSRMLS_CC); if (n <= 0) { break; } SCNG(script_org_size) += n; if (SCNG(script_org)) { SCNG(script_org) = (char*)erealloc(SCNG(script_org), SCNG(script_org_size)+1); } else { SCNG(script_org) = (char*)emalloc(SCNG(script_org_size)+1); } memcpy(SCNG(script_org)+SCNG(script_org_size)-n, buf, n); } if (n < 0) { return -1; } if (!SCNG(script_org)) { SCNG(script_org) = emalloc(SCNG(script_org_size)+1); } *(SCNG(script_org)+SCNG(script_org_size)) = (char)NULL; return 0; } # define zend_copy_value(zendlval, yytext, yyleng) \ if (SCNG(output_filter)) { \ SCNG(output_filter)(&(zendlval->value.str.val), &(zendlval->value.str.len), yytext, yyleng TSRMLS_CC); \ } else { \ zendlval->value.str.val = (char *) estrndup(yytext, yyleng); \ zendlval->value.str.len = yyleng; \ } #else /* ZEND_MULTIBYTE */ # define zend_copy_value(zendlval, yytext, yyleng) \ zendlval->value.str.val = (char *)estrndup(yytext, yyleng); \ zendlval->value.str.len = yyleng; #endif /* ZEND_MULTIBYTE */ static void zend_scan_escape_string(zval *zendlval, char *str, int len, char quote_type TSRMLS_DC) { register char *s, *t; char *end; ZVAL_STRINGL(zendlval, str, len, 1); /* convert escape sequences */ s = t = zendlval->value.str.val; end = s+zendlval->value.str.len; while (s=end) { continue; } switch(*s) { case 'n': *t++ = '\n'; zendlval->value.str.len--; break; case 'r': *t++ = '\r'; zendlval->value.str.len--; break; case 't': *t++ = '\t'; zendlval->value.str.len--; break; case 'f': *t++ = '\f'; zendlval->value.str.len--; break; case 'v': *t++ = '\v'; zendlval->value.str.len--; break; case '"': case '`': if (*s != quote_type) { *t++ = '\\'; *t++ = *s; break; } case '\\': case '$': *t++ = *s; zendlval->value.str.len--; break; case 'x': case 'X': if (ZEND_IS_HEX(*(s+1))) { char hex_buf[3] = { 0, 0, 0 }; zendlval->value.str.len--; /* for the 'x' */ hex_buf[0] = *(++s); zendlval->value.str.len--; if (ZEND_IS_HEX(*(s+1))) { hex_buf[1] = *(++s); zendlval->value.str.len--; } *t++ = (char) strtol(hex_buf, NULL, 16); } else { *t++ = '\\'; *t++ = *s; } break; default: /* check for an octal */ if (ZEND_IS_OCT(*s)) { char octal_buf[4] = { 0, 0, 0, 0 }; octal_buf[0] = *s; zendlval->value.str.len--; if (ZEND_IS_OCT(*(s+1))) { octal_buf[1] = *(++s); zendlval->value.str.len--; if (ZEND_IS_OCT(*(s+1))) { octal_buf[2] = *(++s); zendlval->value.str.len--; } } *t++ = (char) strtol(octal_buf, NULL, 8); } else { *t++ = '\\'; *t++ = *s; } break; } } else { *t++ = *s; } if (*s == '\n' || (*s == '\r' && (*(s+1) != '\n'))) { CG(zend_lineno)++; } s++; } *t = 0; #ifdef ZEND_MULTIBYTE if (SCNG(output_filter)) { s = zendlval->value.str.val; SCNG(output_filter)(&(zendlval->value.str.val), &(zendlval->value.str.len), s, zendlval->value.str.len TSRMLS_CC); efree(s); } #endif /* ZEND_MULTIBYTE */ } %} LNUM [0-9]+ DNUM ([0-9]*[\.][0-9]+)|([0-9]+[\.][0-9]*) EXPONENT_DNUM (({LNUM}|{DNUM})[eE][+-]?{LNUM}) HNUM "0x"[0-9a-fA-F]+ LABEL [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]* WHITESPACE [ \n\r\t]+ TABS_AND_SPACES [ \t]* TOKENS [;:,.\[\]()|^&+-/*=%!~$<>?@] ANY_CHAR (.|[\n]) NEWLINE ("\r"|"\n"|"\r\n") /* * LITERAL_DOLLAR matches unescaped $ that aren't followed by a label character * or a { and therefore will be taken literally. The case of literal $ before * a variable or "${" is handled in a rule for each string type */ DOUBLE_QUOTES_LITERAL_DOLLAR ("$"+([^a-zA-Z_\x7f-\xff$"\\{]|("\\"{ANY_CHAR}))) BACKQUOTE_LITERAL_DOLLAR ("$"+([^a-zA-Z_\x7f-\xff$`\\{]|("\\"{ANY_CHAR}))) HEREDOC_LITERAL_DOLLAR ("$"+([^a-zA-Z_\x7f-\xff$\n\r\\{]|("\\"[^\n\r]))) /* * Usually, HEREDOC_NEWLINE will just function like a simple NEWLINE, but some * special cases need to be handled. HEREDOC_CHARS doesn't allow a line to * match when { or $, and/or \ is at the end. (("{"*|"$"*)"\\"?) handles that, * along with cases where { or $, and/or \ is the ONLY thing on a line * * The other case is when a line contains a label, followed by ONLY * { or $, and/or \ Handled by ({LABEL}";"?((("{"+|"$"+)"\\"?)|"\\")) */ HEREDOC_NEWLINE ((({LABEL}";"?((("{"+|"$"+)"\\"?)|"\\"))|(("{"*|"$"*)"\\"?)){NEWLINE}) /* * This pattern is just used in the next 2 for matching { or literal $, and/or * \ escape sequence immediately at the beginning of a line or after a label */ HEREDOC_CURLY_OR_ESCAPE_OR_DOLLAR (("{"+[^$\n\r\\{])|("{"*"\\"[^\n\r])|{HEREDOC_LITERAL_DOLLAR}) /* * These 2 label-related patterns allow HEREDOC_CHARS to continue "regular" * matching after a newline that starts with either a non-label character or a * label that isn't followed by a newline. Like HEREDOC_CHARS, they won't match * a variable or "{$" Matching a newline, and possibly label, up TO a variable * or "{$", is handled in the heredoc rules * * The HEREDOC_LABEL_NO_NEWLINE pattern (";"[^$\n\r\\{]) handles cases where ; * follows a label. [^a-zA-Z0-9_\x7f-\xff;$\n\r\\{] is needed to prevent a label * character or ; from matching on a possible (real) ending label */ HEREDOC_NON_LABEL ([^a-zA-Z_\x7f-\xff$\n\r\\{]|{HEREDOC_CURLY_OR_ESCAPE_OR_DOLLAR}) HEREDOC_LABEL_NO_NEWLINE ({LABEL}([^a-zA-Z0-9_\x7f-\xff;$\n\r\\{]|(";"[^$\n\r\\{])|(";"?{HEREDOC_CURLY_OR_ESCAPE_OR_DOLLAR}))) /* * CHARS matches everything up to a variable or "{$" * {'s are matched as long as they aren't followed by a $ * The case of { before "{$" is handled in a rule for each string type * * For heredocs, matching continues across/after newlines if/when it's known * that the next line doesn't contain a possible ending label */ DOUBLE_QUOTES_CHARS ("{"*([^$"\\{]|("\\"{ANY_CHAR}))|{DOUBLE_QUOTES_LITERAL_DOLLAR}) BACKQUOTE_CHARS ("{"*([^$`\\{]|("\\"{ANY_CHAR}))|{BACKQUOTE_LITERAL_DOLLAR}) HEREDOC_CHARS ("{"*([^$\n\r\\{]|("\\"[^\n\r]))|{HEREDOC_LITERAL_DOLLAR}|({HEREDOC_NEWLINE}+({HEREDOC_NON_LABEL}|{HEREDOC_LABEL_NO_NEWLINE}))) %option noyylineno %option noyywrap %% "exit" { return T_EXIT; } "die" { return T_EXIT; } "function" { return T_FUNCTION; } "const" { return T_CONST; } "return" { return T_RETURN; } "try" { return T_TRY; } "catch" { return T_CATCH; } "throw" { return T_THROW; } "if" { return T_IF; } "elseif" { return T_ELSEIF; } "endif" { return T_ENDIF; } "else" { return T_ELSE; } "while" { return T_WHILE; } "endwhile" { return T_ENDWHILE; } "do" { return T_DO; } "for" { return T_FOR; } "endfor" { return T_ENDFOR; } "foreach" { return T_FOREACH; } "endforeach" { return T_ENDFOREACH; } "declare" { return T_DECLARE; } "enddeclare" { return T_ENDDECLARE; } "instanceof" { return T_INSTANCEOF; } "as" { return T_AS; } "switch" { return T_SWITCH; } "endswitch" { return T_ENDSWITCH; } "case" { return T_CASE; } "default" { return T_DEFAULT; } "break" { return T_BREAK; } "continue" { return T_CONTINUE; } "echo" { return T_ECHO; } "print" { return T_PRINT; } "class" { return T_CLASS; } "interface" { return T_INTERFACE; } "extends" { return T_EXTENDS; } "implements" { return T_IMPLEMENTS; } "->" { yy_push_state(ST_LOOKING_FOR_PROPERTY TSRMLS_CC); return T_OBJECT_OPERATOR; } "->" { return T_OBJECT_OPERATOR; } {LABEL} { yy_pop_state(TSRMLS_C); zend_copy_value(zendlval, yytext, yyleng); zendlval->type = IS_STRING; return T_STRING; } {ANY_CHAR} { yyless(0); yy_pop_state(TSRMLS_C); } "::" { return T_PAAMAYIM_NEKUDOTAYIM; } "new" { return T_NEW; } "clone" { return T_CLONE; } "var" { return T_VAR; } "("{TABS_AND_SPACES}("int"|"integer"){TABS_AND_SPACES}")" { return T_INT_CAST; } "("{TABS_AND_SPACES}("real"|"double"|"float"){TABS_AND_SPACES}")" { return T_DOUBLE_CAST; } "("{TABS_AND_SPACES}"string"{TABS_AND_SPACES}")" { return T_STRING_CAST; } "("{TABS_AND_SPACES}"binary"{TABS_AND_SPACES}")" { return T_STRING_CAST; } "("{TABS_AND_SPACES}"array"{TABS_AND_SPACES}")" { return T_ARRAY_CAST; } "("{TABS_AND_SPACES}"object"{TABS_AND_SPACES}")" { return T_OBJECT_CAST; } "("{TABS_AND_SPACES}("bool"|"boolean"){TABS_AND_SPACES}")" { return T_BOOL_CAST; } "("{TABS_AND_SPACES}("unset"){TABS_AND_SPACES}")" { return T_UNSET_CAST; } "eval" { return T_EVAL; } "include" { return T_INCLUDE; } "include_once" { return T_INCLUDE_ONCE; } "require" { return T_REQUIRE; } "require_once" { return T_REQUIRE_ONCE; } "use" { return T_USE; } "global" { return T_GLOBAL; } "isset" { return T_ISSET; } "empty" { return T_EMPTY; } "__halt_compiler" { return T_HALT_COMPILER; } "static" { return T_STATIC; } "abstract" { return T_ABSTRACT; } "final" { return T_FINAL; } "private" { return T_PRIVATE; } "protected" { return T_PROTECTED; } "public" { return T_PUBLIC; } "unset" { return T_UNSET; } "=>" { return T_DOUBLE_ARROW; } "list" { return T_LIST; } "array" { return T_ARRAY; } "++" { return T_INC; } "--" { return T_DEC; } "===" { return T_IS_IDENTICAL; } "!==" { return T_IS_NOT_IDENTICAL; } "==" { return T_IS_EQUAL; } "!="|"<>" { return T_IS_NOT_EQUAL; } "<=" { return T_IS_SMALLER_OR_EQUAL; } ">=" { return T_IS_GREATER_OR_EQUAL; } "+=" { return T_PLUS_EQUAL; } "-=" { return T_MINUS_EQUAL; } "*=" { return T_MUL_EQUAL; } "/=" { return T_DIV_EQUAL; } ".=" { return T_CONCAT_EQUAL; } "%=" { return T_MOD_EQUAL; } "<<=" { return T_SL_EQUAL; } ">>=" { return T_SR_EQUAL; } "&=" { return T_AND_EQUAL; } "|=" { return T_OR_EQUAL; } "^=" { return T_XOR_EQUAL; } "||" { return T_BOOLEAN_OR; } "&&" { return T_BOOLEAN_AND; } "OR" { return T_LOGICAL_OR; } "AND" { return T_LOGICAL_AND; } "XOR" { return T_LOGICAL_XOR; } "<<" { return T_SL; } ">>" { return T_SR; } {TOKENS} { return yytext[0]; } "{" { yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); return '{'; } "${" { yy_push_state(ST_LOOKING_FOR_VARNAME TSRMLS_CC); return T_DOLLAR_OPEN_CURLY_BRACES; } "}" { RESET_DOC_COMMENT(); /* This is a temporary fix which is dependant on flex and it's implementation */ if (yy_start_stack_ptr) { yy_pop_state(TSRMLS_C); } return '}'; } {LABEL} { zend_copy_value(zendlval, yytext, yyleng); zendlval->type = IS_STRING; yy_pop_state(TSRMLS_C); yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); return T_STRING_VARNAME; } {ANY_CHAR} { yyless(0); yy_pop_state(TSRMLS_C); yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); } {LNUM} { if (yyleng < MAX_LENGTH_OF_LONG - 1) { /* Won't overflow */ zendlval->value.lval = strtol(yytext, NULL, 0); } else { errno = 0; zendlval->value.lval = strtol(yytext, NULL, 0); if (errno == ERANGE) { /* Overflow */ if (yytext[0] == '0') { /* octal overflow */ zendlval->value.dval = zend_oct_strtod(yytext, NULL); } else { zendlval->value.dval = zend_strtod(yytext, NULL); } zendlval->type = IS_DOUBLE; return T_DNUMBER; } } zendlval->type = IS_LONG; return T_LNUMBER; } {HNUM} { char *hex = yytext + 2; /* Skip "0x" */ int len = yyleng - 2; /* Skip any leading 0s */ while (*hex == '0') { hex++; len--; } if (len < SIZEOF_LONG * 2 || (len == SIZEOF_LONG * 2 && *hex <= '7')) { zendlval->value.lval = strtol(hex, NULL, 16); zendlval->type = IS_LONG; return T_LNUMBER; } else { zendlval->value.dval = zend_hex_strtod(hex, NULL); zendlval->type = IS_DOUBLE; return T_DNUMBER; } } 0|([1-9][0-9]*) { /* Offset could be treated as a long */ if (yyleng < MAX_LENGTH_OF_LONG - 1 || (yyleng == MAX_LENGTH_OF_LONG - 1 && strcmp(yytext, long_min_digits) < 0)) { zendlval->value.lval = strtol(yytext, NULL, 10); zendlval->type = IS_LONG; } else { zendlval->value.str.val = (char *)estrndup(yytext, yyleng); zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; } return T_NUM_STRING; } {LNUM}|{HNUM} { /* Offset must be treated as a string */ zendlval->value.str.val = (char *)estrndup(yytext, yyleng); zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; return T_NUM_STRING; } {DNUM}|{EXPONENT_DNUM} { zendlval->value.dval = zend_strtod(yytext, NULL); zendlval->type = IS_DOUBLE; return T_DNUMBER; } "__CLASS__" { char *class_name = NULL; if (CG(active_class_entry)) { class_name = CG(active_class_entry)->name; } if (!class_name) { class_name = ""; } zendlval->value.str.len = strlen(class_name); zendlval->value.str.val = estrndup(class_name, zendlval->value.str.len); zendlval->type = IS_STRING; return T_CLASS_C; } "__FUNCTION__" { char *func_name = NULL; if (CG(active_op_array)) { func_name = CG(active_op_array)->function_name; } if (!func_name) { func_name = ""; } zendlval->value.str.len = strlen(func_name); zendlval->value.str.val = estrndup(func_name, zendlval->value.str.len); zendlval->type = IS_STRING; return T_FUNC_C; } "__METHOD__" { char *class_name = CG(active_class_entry) ? CG(active_class_entry)->name : NULL; char *func_name = CG(active_op_array)? CG(active_op_array)->function_name : NULL; size_t len = 0; if (class_name) { len += strlen(class_name) + 2; } if (func_name) { len += strlen(func_name); } zendlval->value.str.len = zend_spprintf(&zendlval->value.str.val, 0, "%s%s%s", class_name ? class_name : "", class_name && func_name ? "::" : "", func_name ? func_name : "" ); zendlval->type = IS_STRING; return T_METHOD_C; } "__LINE__" { zendlval->value.lval = CG(zend_lineno); zendlval->type = IS_LONG; return T_LINE; } "__FILE__" { char *filename = zend_get_compiled_filename(TSRMLS_C); if (!filename) { filename = ""; } zendlval->value.str.len = strlen(filename); zendlval->value.str.val = estrndup(filename, zendlval->value.str.len); zendlval->type = IS_STRING; return T_FILE; } (([^<]|"<"[^?%s<]){1,400})|"value.str.val), &(zendlval->value.str.len), yytext, yyleng TSRMLS_CC); if (readsize < yyleng) { yyless(readsize); } } else { zendlval->value.str.val = (char *) estrndup(yytext, yyleng); zendlval->value.str.len = yyleng; } #else /* !ZEND_MULTIBYTE */ zendlval->value.str.val = (char *) estrndup(yytext, yyleng); zendlval->value.str.len = yyleng; #endif /* ZEND_MULTIBYTE */ zendlval->type = IS_STRING; HANDLE_NEWLINES(yytext, yyleng); return T_INLINE_HTML; } "" { HANDLE_NEWLINES(yytext, yyleng); if (CG(short_tags) || yyleng>2) { /* yyleng>2 means it's not */ zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; BEGIN(ST_IN_SCRIPTING); return T_OPEN_TAG; } else { zendlval->value.str.val = (char *) estrndup(yytext, yyleng); zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; return T_INLINE_HTML; } } "<%="|"value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; BEGIN(ST_IN_SCRIPTING); return T_OPEN_TAG_WITH_ECHO; } else { zendlval->value.str.val = (char *) estrndup(yytext, yyleng); zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; return T_INLINE_HTML; } } "<%" { if (CG(asp_tags)) { zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; BEGIN(ST_IN_SCRIPTING); return T_OPEN_TAG; } else { zendlval->value.str.val = (char *) estrndup(yytext, yyleng); zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; return T_INLINE_HTML; } } "value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; HANDLE_NEWLINE(yytext[yyleng-1]); BEGIN(ST_IN_SCRIPTING); return T_OPEN_TAG; } "$"{LABEL} { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); zendlval->type = IS_STRING; return T_VARIABLE; } %{ /* Make sure a label character follows "->", otherwise there is no property * and "->" will be taken literally */ %} "$"{LABEL}"->"[a-zA-Z_\x7f-\xff] { yyless(yyleng - 3); yy_push_state(ST_LOOKING_FOR_PROPERTY TSRMLS_CC); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); zendlval->type = IS_STRING; return T_VARIABLE; } %{ /* A [ always designates a variable offset, regardless of what follows */ %} "$"{LABEL}"[" { yyless(yyleng - 1); yy_push_state(ST_VAR_OFFSET TSRMLS_CC); zend_copy_value(zendlval, (yytext+1), (yyleng-1)); zendlval->type = IS_STRING; return T_VARIABLE; } "]" { yy_pop_state(TSRMLS_C); return ']'; } {TOKENS}|[{}"`] { /* Only '[' can be valid, but returning other tokens will allow a more explicit parse error */ return yytext[0]; } [ \n\r\t\\'#] { /* Invalid rule to return a more explicit parse error with proper line number */ yyless(0); yy_pop_state(TSRMLS_C); ZVAL_EMPTY_STRING(zendlval); /* Empty since it won't be used */ return T_ENCAPSED_AND_WHITESPACE; } {LABEL} { zend_copy_value(zendlval, yytext, yyleng); zendlval->type = IS_STRING; return T_STRING; } {WHITESPACE} { zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; HANDLE_NEWLINES(yytext, yyleng); return T_WHITESPACE; } "#"|"//" { BEGIN(ST_ONE_LINE_COMMENT); yymore(); } "?"|"%"|">" { yymore(); } [^\n\r?%>]*{ANY_CHAR} { switch (yytext[yyleng-1]) { case '?': case '%': case '>': yyless(yyleng-1); yymore(); break; case '\n': CG(zend_lineno)++; /* intentional fall through */ default: zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; BEGIN(ST_IN_SCRIPTING); return T_COMMENT; } } {NEWLINE} { zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; BEGIN(ST_IN_SCRIPTING); CG(zend_lineno)++; return T_COMMENT; } "?>"|"%>" { if (CG(asp_tags) || yytext[yyleng-2] != '%') { /* asp comment? */ zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng-2; zendlval->type = IS_STRING; yyless(yyleng-2); BEGIN(ST_IN_SCRIPTING); return T_COMMENT; } else { yymore(); } } "/**"{WHITESPACE} { CG(comment_start_line) = CG(zend_lineno); RESET_DOC_COMMENT(); BEGIN(ST_DOC_COMMENT); yymore(); } "/*" { CG(comment_start_line) = CG(zend_lineno); BEGIN(ST_COMMENT); yymore(); } [^*]+ { yymore(); } "*/" { CG(doc_comment) = estrndup(yytext, yyleng); CG(doc_comment_len) = yyleng; HANDLE_NEWLINES(yytext, yyleng); BEGIN(ST_IN_SCRIPTING); return T_DOC_COMMENT; } "*/" { HANDLE_NEWLINES(yytext, yyleng); BEGIN(ST_IN_SCRIPTING); return T_COMMENT; } "*" { yymore(); } ("?>"|""){NEWLINE}? { zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; BEGIN(INITIAL); return T_CLOSE_TAG; /* implicit ';' at php-end tag */ } "%>"{NEWLINE}? { if (CG(asp_tags)) { BEGIN(INITIAL); zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; zendlval->value.str.val = yytext; /* no copying - intentional */ return T_CLOSE_TAG; /* implicit ';' at php-end tag */ } else { yyless(1); return yytext[0]; } } %{ /* ("{"*|"$"*) handles { or $ at the end of a string (or the entire contents) */ %} (b?["]{DOUBLE_QUOTES_CHARS}*("{"*|"$"*)["]) { int bprefix = (yytext[0] != '"') ? 1 : 0; zend_scan_escape_string(zendlval, yytext+bprefix+1, yyleng-bprefix-2, '"' TSRMLS_CC); return T_CONSTANT_ENCAPSED_STRING; } (b?[']([^'\\]|("\\"{ANY_CHAR}))*[']) { register char *s, *t; char *end; int bprefix = (yytext[0] != '\'') ? 1 : 0; zendlval->value.str.val = estrndup(yytext+bprefix+1, yyleng-bprefix-2); zendlval->value.str.len = yyleng-bprefix-2; zendlval->type = IS_STRING; /* convert escape sequences */ s = t = zendlval->value.str.val; end = s+zendlval->value.str.len; while (s=end) { continue; } switch(*s) { case '\\': case '\'': *t++ = *s; zendlval->value.str.len--; break; default: *t++ = '\\'; *t++ = *s; break; } } else { *t++ = *s; } if (*s == '\n' || (*s == '\r' && (*(s+1) != '\n'))) { CG(zend_lineno)++; } s++; } *t = 0; #ifdef ZEND_MULTIBYTE if (SCNG(output_filter)) { s = zendlval->value.str.val; SCNG(output_filter)(&(zendlval->value.str.val), &(zendlval->value.str.len), s, zendlval->value.str.len TSRMLS_CC); efree(s); } #endif /* ZEND_MULTIBYTE */ return T_CONSTANT_ENCAPSED_STRING; } b?["] { BEGIN(ST_DOUBLE_QUOTES); return '"'; } b?"<<<"{TABS_AND_SPACES}{LABEL}{NEWLINE} { char *s; int bprefix = (yytext[0] != '<') ? 1 : 0; CG(zend_lineno)++; CG(heredoc_len) = yyleng-bprefix-3-1-(yytext[yyleng-2]=='\r'?1:0); s = yytext+bprefix+3; while ((*s == ' ') || (*s == '\t')) { s++; CG(heredoc_len)--; } CG(heredoc) = estrndup(s, CG(heredoc_len)); BEGIN(ST_START_HEREDOC); return T_START_HEREDOC; } [`] { BEGIN(ST_BACKQUOTE); return '`'; } {ANY_CHAR} { yyless(0); BEGIN(ST_HEREDOC); } {LABEL}";"?[\n\r] { int label_len = yyleng - 1; if (yytext[label_len-1]==';') { label_len--; } yyless(label_len); if (label_len==CG(heredoc_len) && !memcmp(yytext, CG(heredoc), label_len)) { zendlval->value.str.val = CG(heredoc); zendlval->value.str.len = label_len; CG(heredoc)=NULL; CG(heredoc_len)=0; BEGIN(ST_IN_SCRIPTING); return T_END_HEREDOC; } else { yymore(); BEGIN(ST_HEREDOC); } } %{ /* Match everything up to and including a possible ending label, so if the label * doesn't match, it's kept with the rest of the string * * {HEREDOC_NEWLINE}+ handles the case of more than one newline sequence that * couldn't be matched with HEREDOC_CHARS, because of the following label */ %} {HEREDOC_CHARS}*{HEREDOC_NEWLINE}+{LABEL}";"?[\n\r] { char *end = yytext + yyleng - 1; if (end[-1] == ';') { end--; yyleng--; } if (yyleng > CG(heredoc_len) && !memcmp(end - CG(heredoc_len), CG(heredoc), CG(heredoc_len))) { int len = yyleng - CG(heredoc_len) - 2; /* 2 for newline before and after label */ if (len > 0 && yytext[len - 1] == '\r' && yytext[len] == '\n') { len--; } /* Go back before last label char, to match in ST_END_HEREDOC state */ yyless(yyleng - 2); /* Subtract the remaining label length. yyleng must include newline * before label, for zend_highlight/strip, tokenizer, etc. */ yyleng -= CG(heredoc_len) - 1; CG(increment_lineno) = 1; /* For newline before label */ BEGIN(ST_END_HEREDOC); zend_scan_escape_string(zendlval, yytext, len, 0 TSRMLS_CC); return T_ENCAPSED_AND_WHITESPACE; } else { /* Go back to end of label, so the next match works correctly in case of * a variable or another label at the beginning of the next line */ yyless(yyleng - 1); yymore(); } } {ANY_CHAR} { zendlval->value.str.val = CG(heredoc); zendlval->value.str.len = CG(heredoc_len); yytext = zendlval->value.str.val; yyleng = zendlval->value.str.len; CG(heredoc) = NULL; CG(heredoc_len) = 0; BEGIN(ST_IN_SCRIPTING); return T_END_HEREDOC; } "{$" { zendlval->value.lval = (long) '{'; yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); yyless(1); return T_CURLY_OPEN; } {DOUBLE_QUOTES_CHARS}+ { zend_scan_escape_string(zendlval, yytext, yyleng, '"' TSRMLS_CC); return T_ENCAPSED_AND_WHITESPACE; } %{ /* "{"{2,}|"$"{2,} handles { before "{$" or literal $ before a variable or "${" * (("{"+|"$"+)["]) handles { or $ at the end of a string * * Same for backquotes and heredocs, except the second case doesn't apply to * heredocs. yyless(yyleng - 1) is used to correct taking one character too many */ %} {DOUBLE_QUOTES_CHARS}*("{"{2,}|"$"{2,}|(("{"+|"$"+)["])) { yyless(yyleng - 1); zend_scan_escape_string(zendlval, yytext, yyleng, '"' TSRMLS_CC); return T_ENCAPSED_AND_WHITESPACE; } {BACKQUOTE_CHARS}+ { zend_scan_escape_string(zendlval, yytext, yyleng, '`' TSRMLS_CC); return T_ENCAPSED_AND_WHITESPACE; } {BACKQUOTE_CHARS}*("{"{2,}|"$"{2,}|(("{"+|"$"+)[`])) { yyless(yyleng - 1); zend_scan_escape_string(zendlval, yytext, yyleng, '`' TSRMLS_CC); return T_ENCAPSED_AND_WHITESPACE; } %{ /* ({HEREDOC_NEWLINE}+({LABEL}";"?)?)? handles the possible case of newline * sequences, possibly followed by a label, that couldn't be matched with * HEREDOC_CHARS because of a following variable or "{$" * * This doesn't affect real ending labels, as they are followed by a newline, * which will result in a longer match for the correct rule if present */ %} {HEREDOC_CHARS}*({HEREDOC_NEWLINE}+({LABEL}";"?)?)? { zend_scan_escape_string(zendlval, yytext, yyleng, 0 TSRMLS_CC); return T_ENCAPSED_AND_WHITESPACE; } {HEREDOC_CHARS}*({HEREDOC_NEWLINE}+({LABEL}";"?)?)?("{"{2,}|"$"{2,}) { yyless(yyleng - 1); zend_scan_escape_string(zendlval, yytext, yyleng, 0 TSRMLS_CC); return T_ENCAPSED_AND_WHITESPACE; } ["] { BEGIN(ST_IN_SCRIPTING); return '"'; } [`] { BEGIN(ST_IN_SCRIPTING); return '`'; } <> { zend_error(E_COMPILE_WARNING,"Unterminated comment starting line %d", CG(comment_start_line)); return 0; } {ANY_CHAR} { zend_error(E_COMPILE_WARNING,"Unexpected character in input: '%c' (ASCII=%d) state=%d", yytext[0], yytext[0], YYSTATE); }