Re: [RFC] Conditional INI support

From: Date: Wed, 13 Feb 2008 16:26:31 +0000
Subject: Re: [RFC] Conditional INI support
References: 1 2  Groups: php.internals 
Request: Send a blank email to internals+get-35451@lists.php.net to get a copy of this message
Hello Andi, Pierre, Monday, February 11, 2008, 7:49:04 PM, you wrote: > Hi Marcus, > In general I think conditional INI support can benefit many of our > users. We just need to make sure that we cover the most common needs and > also that we keep it very basic and simple so that we don't boil the > ocean and maintain two languages. So it's a balance we need to meet. Right > It would be very helpful that as part of the feedback loop you write > something a bit more detailed about this feature and what is and isn't > supported. I imagine most people on this list would have a hard time to > figure that out from the patch. This includes: > - What operators are supported in conditionals <, <=, >, >=, ==, !=, &&, || all use atoi [${left} < ${right}] -> atoi(${left}) < atoi(${right}) === and !== are string comparisons [${left} === ${right}] -> !strcmp(${lect}, ${right}) Instead of === and !== I could have introduced functions like strcmp but I wanted to keep it as simple as possible for the lexer as well as the user. The only thing I might want to add is !. But that would be unary and increase complexity. > - Are there any "functions" like empty() or is the ternary operator the > proposed solution for that? None, though instead of empty() you can do: ${var}==="" > - Is it possible to get to environment variables from this? If so how is > it namespaced? How about registry on Windows? Anything the ini allows today is possible, no changes, values are evaluated just as in assignments of the ini file. > - What happens when you use a variable which is not set? > - Do you propose a solution for concatenation of two values or is this > outside the scope? afaik th eini allows that already > - Can each [IF] include multiple conditionals i.e. $(value) > 1 AND > $(value) < 5 or just one condition with the expectation that people will > use nesting. Nesting is supported as well as braces. So the following works: [(${a} < ${b}) && (${c} < ${d})] > A few specific feedbacks: > - Ternary operator's syntax is a bit weird. My preference would be not > to have such an operator and require using [if ..] so that it's very > clear what's pre-processed even if it requires a bit more works. We > don't want the INI files to become too cryptic. Also the syntax would be > better formed as ${value}?1:2 but again I think it's better not to have > it. That seems the common response her. I'll drop it. > - I would prefer to see [ELSEIF] instead of [ELIF] to be consistent with > PHP. It would look weird for the same solution to have two approaches to > this. Both of you guys say the same for the same reasons., so I changed that as well. Also I reduced the number of added values to 'php.zts' and 'php.debug'. Maybe we can add those two as consts (Pierre?). regards marcus >> -----Original Message----- >> From: Marcus Boerger [mailto:helly@php.net] >> Sent: Saturday, February 09, 2008 6:33 AM >> To: PHP Internals List >> Subject: [PHP-DEV] [RFC] Conditional INI support >> >> Hello PHPlers, >> >> attached is a patch against 5.3 that brings three feature additions >> to INI parsing. >> >> 1) Ternary support for values >> >> setting = ${value?1:2} >> >> If ${value} evaluates to true then setting becomes 1 otherwise 2. >> This cannot be nested and only works for values, not for setting > names. >> >> 2) if-elif-else-endif support >> >> [IF ${value} == 1] >> setting = 1 >> [ELIF ${value} == 2] >> setting = 2 >> [ELSE] >> setting = 3 >> [ENDIF] >> >> This can be nested. Alternatively we could use apache style syntax > that >> looks more like XML. The reason I used square brackets is that this is >> the smallest change to normal INI files. >> >> 3) Add more values to INI parsing, namely: >> >> ${php.version} = 50300 >> ${php.debug} = 0 >> ${php.zts} = 0 >> ${php.sapi} = CLI >> >> Any comments? >> >> Best regards, >> Marcus Best regards, Marcus

Index: main/php_ini.c =================================================================== RCS file: /repository/php-src/main/php_ini.c,v retrieving revision 1.136.2.4.2.15.2.7 diff -u -p -d -r1.136.2.4.2.15.2.7 php_ini.c --- main/php_ini.c 3 Feb 2008 14:35:59 -0000 1.136.2.4.2.15.2.7 +++ main/php_ini.c 13 Feb 2008 16:20:26 -0000 @@ -20,6 +20,7 @@ #include "php.h" #include "ext/standard/info.h" +#include "ext/standard/basic_functions.h" #include "zend_ini.h" #include "zend_ini_scanner.h" #include "php_ini.h" @@ -328,6 +329,32 @@ static void php_load_zend_extension_cb(v } /* }}} */ +PHPAPI void php_ini_add_config_stringl(const char* name, int name_size, const char *value, int value_len TSRMLS_DC) /* {{{*/ +{ + zval tmp; + ZVAL_STRINGL(&tmp, zend_strndup(value, value_len), value_len, 0); + Z_SET_REFCOUNT(tmp, 0); + Z_UNSET_ISREF(tmp); + zend_hash_update(&configuration_hash, name, name_size, (void *) &tmp, sizeof(zval), NULL); +} /* }}} */ + +PHPAPI void php_ini_add_config_string(const char* name, int name_size, const char *value TSRMLS_DC) /* {{{*/ +{ + php_ini_add_config_stringl(name, name_size, value, strlen(value) TSRMLS_CC); +} /* }}} */ + +PHPAPI void php_ini_add_config_long(const char* name, int name_size, long value TSRMLS_DC) /* {{{*/ +{ + char str_val[32]; + int value_len = slprintf(str_val, sizeof(str_val), "%ld", value); + php_ini_add_config_stringl(name, name_size, str_val, value_len TSRMLS_CC); +} /* }}} */ + +PHPAPI void php_ini_add_config_bool(const char* name, int name_size, int value TSRMLS_DC) /* {{{*/ +{ + php_ini_add_config_stringl(name, name_size, value ? "1" : "0", 1 TSRMLS_CC); +} /* }}} */ + /* {{{ php_init_config */ int php_init_config(TSRMLS_D) @@ -347,6 +374,17 @@ int php_init_config(TSRMLS_D) sapi_module.ini_defaults(&configuration_hash); } +#if ZTS + php_ini_add_config_bool("php.zts", sizeof("php.zts"), 1 TSRMLS_CC); +#else + php_ini_add_config_bool("php.zts", sizeof("php.zts"), 0 TSRMLS_CC); +#endif +#if ZEND_DEBUG + php_ini_add_config_bool("php.debug", sizeof("php.debug"), 1 TSRMLS_CC); +#else + php_ini_add_config_bool("php.debug", sizeof("php.debug"), 0 TSRMLS_CC); +#endif + zend_llist_init(&extension_lists.engine, sizeof(char *), (llist_dtor_func_t) free_estring, 1); zend_llist_init(&extension_lists.functions, sizeof(char *), (llist_dtor_func_t) free_estring, 1); @@ -558,24 +596,19 @@ int php_init_config(TSRMLS_D) PG(open_basedir) = open_basedir; if (fh.handle.fp) { + int filename_len; + char *ftmp; fh.type = ZEND_HANDLE_FP; RESET_ACTIVE_INI_HASH(); zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC); - { - zval tmp; - - Z_STRLEN(tmp) = strlen(fh.filename); - Z_STRVAL(tmp) = zend_strndup(fh.filename, Z_STRLEN(tmp)); - Z_TYPE(tmp) = IS_STRING; - Z_SET_REFCOUNT(tmp, 0); - - zend_hash_update(&configuration_hash, "cfg_file_path", sizeof("cfg_file_path"), (void *) &tmp, sizeof(zval), NULL); - if (php_ini_opened_path) { - efree(php_ini_opened_path); - } - php_ini_opened_path = zend_strndup(Z_STRVAL(tmp), Z_STRLEN(tmp)); + filename_len = strlen(fh.filename); + php_ini_add_config_stringl("cfg_file_path", sizeof("cfg_file_path"), fh.filename, filename_len TSRMLS_CC); + ftmp = php_ini_opened_path; + php_ini_opened_path = zend_strndup(fh.filename, filename_len); + if (ftmp) { + efree(ftmp); } } Index: Zend/zend_globals.h =================================================================== RCS file: /repository/ZendEngine2/zend_globals.h,v retrieving revision 1.141.2.3.2.7.2.7 diff -u -p -d -r1.141.2.3.2.7.2.7 zend_globals.h --- Zend/zend_globals.h 24 Jan 2008 09:41:37 -0000 1.141.2.3.2.7.2.7 +++ Zend/zend_globals.h 13 Feb 2008 16:20:27 -0000 @@ -52,6 +52,8 @@ END_EXTERN_C() #endif #define SYMTABLE_CACHE_SIZE 32 +#define ZEND_DEBUG_CFG_PARSER 0 + #include "zend_compile.h" @@ -272,6 +274,7 @@ struct _zend_scanner_globals { /* For ini scanner. Modes are: ZEND_INI_SCANNER_NORMAL, ZEND_INI_SCANNER_RAW */ int scanner_mode; + int if_level; #ifdef ZEND_MULTIBYTE /* original (unfiltered) script */ @@ -290,6 +293,12 @@ struct _zend_scanner_globals { #endif /* ZEND_MULTIBYTE */ }; +typedef enum { + ZEND_INI_COND_READ, + ZEND_INI_COND_IGNORE, + ZEND_INI_COND_DONE, +} ZEND_INI_CONDITIONAL; + #endif /* ZEND_GLOBALS_H */ /* Index: Zend/zend_ini_parser.y =================================================================== RCS file: /repository/ZendEngine2/zend_ini_parser.y,v retrieving revision 1.41.2.2.2.2.2.4 diff -u -p -d -r1.41.2.2.2.2.2.4 zend_ini_parser.y --- Zend/zend_ini_parser.y 4 Feb 2008 20:45:20 -0000 1.41.2.2.2.2.2.4 +++ Zend/zend_ini_parser.y 13 Feb 2008 16:20:28 -0000 @@ -15,13 +15,12 @@ +----------------------------------------------------------------------+ | Authors: Zeev Suraski <zeev@zend.com> | | Jani Taskinen <jani@php.net> | + | Marcus Boerger <helly@php.net> | +----------------------------------------------------------------------+ */ /* $Id: zend_ini_parser.y,v 1.41.2.2.2.2.2.4 2008/02/04 20:45:20 jani Exp $ */ -#define DEBUG_CFG_PARSER 0 - #include "zend.h" #include "zend_API.h" #include "zend_ini.h" @@ -29,6 +28,9 @@ #include "zend_ini_scanner.h" #include "zend_extensions.h" +extern void zend_ini_set_ignore(ZEND_INI_CONDITIONAL ignore TSRMLS_DC); +extern void zend_ini_error(int severity TSRMLS_DC, char *error); + #define YYERROR_VERBOSE #define YYSTYPE zval @@ -86,6 +88,106 @@ static void zend_ini_do_op(char type, zv } /* }}} */ +static void zend_ini_compare(int type, zval *result, zval *op1, zval *op2) /* {{{ */ +{ + int i_op1, i_op2, ret; + char *str2 = ""; + + if (type == T_IS_NOT_IDENTICAL || type == T_IS_NOT_IDENTICAL) { + if (op2) { + ret = !strcmp(Z_STRVAL_P(op1), Z_STRVAL_P(op2)); + str2 = Z_STRVAL_P(op2); + } else { + ret = Z_STRLEN_P(op1) > 0; + } + if (type == T_IS_NOT_IDENTICAL) { +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare('%s' !== '%s')\n", Z_STRVAL_P(op1), str2); +#endif + ret = ret ? 0 : 1; + } else { +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare('%s' === '%s')\n", Z_STRVAL_P(op1), str2); +#endif + ret = ret ? 1 : 0; + } + } else { + i_op1 = atoi(Z_STRVAL_P(op1)); + if (op2) { + i_op2 = atoi(Z_STRVAL_P(op2)); + } else { + i_op2 = 0; + } + + switch (type) { + case T_IS_EQ: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare(%d == %d)\n", i_op1, i_op2); +#endif + ret = i_op1 == i_op2; + break; + case T_IS_NE: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare(%d != %d)\n", i_op1, i_op2); +#endif + ret = i_op1 != i_op2; + break; + case T_IS_GE: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare(%d >= %d)\n", i_op1, i_op2); +#endif + ret = i_op1 >= i_op2; + break; + case '>': +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare(%d > %d)\n", i_op1, i_op2); +#endif + ret = i_op1 > i_op2; + break; + case T_IS_LE: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare(%d <= %d)\n", i_op1, i_op2); +#endif + ret = i_op1 <= i_op2; + break; + case '<': +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare(%d < %d)\n", i_op1, i_op2); +#endif + ret = i_op1 < i_op2; + break; + case T_L_AND: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare(%d && %d)\n", i_op1, i_op2); +#endif + ret = i_op1 && i_op2; + break; + case T_L_OR: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare(%d || %d)\n", i_op1, i_op2); +#endif + ret = i_op1 && i_op2; + break; + default: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "INI compare(%d ? %d)\n", i_op1, i_op2); +#endif + ret = 0; + break; + } + } + free(Z_STRVAL_P(op1)); + if (op2) { + free(Z_STRVAL_P(op2)); + } + Z_STRVAL_P(result) = malloc(2); + Z_STRVAL_P(result)[0] = ret ? '1' : '0'; + Z_STRVAL_P(result)[1] = 0; + Z_STRLEN_P(result) = 1; + Z_TYPE_P(result) = IS_STRING; +} +/* }}} */ + /* {{{ zend_ini_init_string() */ static void zend_ini_init_string(zval *result) @@ -159,31 +261,7 @@ static void zend_ini_get_var(zval *resul */ static void ini_error(char *msg) { - char *error_buf; - int error_buf_len; - char *currently_parsed_filename; - TSRMLS_FETCH(); - - currently_parsed_filename = zend_ini_scanner_get_filename(TSRMLS_C); - if (currently_parsed_filename) { - error_buf_len = 128 + strlen(msg) + strlen(currently_parsed_filename); /* should be more than enough */ - error_buf = (char *) emalloc(error_buf_len); - - sprintf(error_buf, "%s in %s on line %d\n", msg, currently_parsed_filename, zend_ini_scanner_get_lineno(TSRMLS_C)); - } else { - error_buf = estrdup("Invalid configuration directive\n"); - } - - if (CG(ini_parser_unbuffered_errors)) { -#ifdef PHP_WIN32 - MessageBox(NULL, error_buf, "PHP Error", MB_OK|MB_TOPMOST|0x00200000L); -#else - fprintf(stderr, "PHP: %s", error_buf); -#endif - } else { - zend_error(E_WARNING, "%s", error_buf); - } - efree(error_buf); + zend_ini_error(E_WARNING TSRMLS_CC, msg); } /* }}} */ @@ -250,6 +328,7 @@ ZEND_API int zend_parse_ini_string(char %pure_parser %token TC_SECTION +%token TC_CONDITION %token TC_RAW %token TC_CONSTANT %token TC_NUMBER @@ -263,7 +342,11 @@ ZEND_API int zend_parse_ini_string(char %token BOOL_TRUE %token BOOL_FALSE %token END_OF_LINE -%token '=' ':' ',' '.' '"' '\'' '^' '+' '-' '/' '*' '%' '$' '~' '<' '>' '?' '@' '{' '}' +%left T_L_OR +%left T_L_AND +%left T_IS_EQ T_IS_NE T_IS_IDENTICAL T_IS_NOT_IDENTICAL +%left T_IS_LE T_IS_GE +%token '=' ':' ',' '.' '"' '\'' '^' '+' '-' '/' '*' '%' '$' '~' '<' '>' '?' '@' '{' '}' '(' ')' %left '|' '&' %right '~' '!' @@ -276,33 +359,71 @@ statement_list: statement: TC_SECTION section_string_or_value ']' { -#if DEBUG_CFG_PARSER - printf("SECTION: [%s]\n", Z_STRVAL($2)); +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "SECTION: [%s]\n", Z_STRVAL($2)); #endif ZEND_INI_PARSER_CB(&$2, NULL, NULL, ZEND_INI_PARSER_SECTION, ZEND_INI_PARSER_ARG TSRMLS_CC); free(Z_STRVAL($2)); } + | TC_CONDITION compare ']' { + int ignore = Z_STRVAL($2)[0] == '1' ? 0 : 1; + free(Z_STRVAL($2)); + zend_ini_set_ignore(ignore ? ZEND_INI_COND_IGNORE : ZEND_INI_COND_READ TSRMLS_CC); + } | TC_LABEL '=' string_or_value { -#if DEBUG_CFG_PARSER - printf("NORMAL: '%s' = '%s'\n", Z_STRVAL($1), Z_STRVAL($3)); +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "NORMAL: '%s' = '%s'\n", Z_STRVAL($1), Z_STRVAL($3)); #endif ZEND_INI_PARSER_CB(&$1, &$3, NULL, ZEND_INI_PARSER_ENTRY, ZEND_INI_PARSER_ARG TSRMLS_CC); free(Z_STRVAL($1)); free(Z_STRVAL($3)); } | TC_OFFSET option_offset ']' '=' string_or_value { -#if DEBUG_CFG_PARSER - printf("OFFSET: '%s'[%s] = '%s'\n", Z_STRVAL($1), Z_STRVAL($2), Z_STRVAL($5)); +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "OFFSET: '%s'[%s] = '%s'\n", Z_STRVAL($1), Z_STRVAL($2), Z_STRVAL($5)); #endif ZEND_INI_PARSER_CB(&$1, &$5, &$2, ZEND_INI_PARSER_POP_ENTRY, ZEND_INI_PARSER_ARG TSRMLS_CC); free(Z_STRVAL($1)); free(Z_STRVAL($2)); free(Z_STRVAL($5)); } - | TC_LABEL { ZEND_INI_PARSER_CB(&$1, NULL, NULL, ZEND_INI_PARSER_ENTRY, ZEND_INI_PARSER_ARG TSRMLS_CC); free(Z_STRVAL($1)); } + | TC_LABEL { + ZEND_INI_PARSER_CB(&$1, NULL, NULL, ZEND_INI_PARSER_ENTRY, ZEND_INI_PARSER_ARG TSRMLS_CC); + free(Z_STRVAL($1)); + } | END_OF_LINE ; +compare: + compare_right T_L_AND compare_right { zend_ini_compare(T_L_AND, &$$, &$1, &$3); } + | compare_right T_L_OR compare_right { zend_ini_compare(T_L_OR, &$$, &$1, &$3); } + | compare_right T_IS_GE compare_right { zend_ini_compare(T_IS_GE, &$$, &$1, &$3); } + | compare_right '>' compare_right { zend_ini_compare('>', &$$, &$1, &$3); } + | compare_right T_IS_LE compare_right { zend_ini_compare(T_IS_LE, &$$, &$1, &$3); } + | compare_right '<' compare_right { zend_ini_compare('<', &$$, &$1, &$3); } + | compare_right T_IS_EQ compare_right { zend_ini_compare(T_IS_EQ, &$$, &$1, &$3); } + | compare_right T_IS_NE compare_right { zend_ini_compare(T_IS_NE, &$$, &$1, &$3); } + | compare_right T_IS_IDENTICAL compare_right { zend_ini_compare(T_IS_IDENTICAL, &$$, &$1, &$3); } + | compare_right T_IS_NOT_IDENTICAL compare_right { zend_ini_compare(T_IS_NOT_IDENTICAL, &$$, &$1, &$3); } +; + +compare_right: + compare_value { $$ = $1; } + | '(' compare ')' { $$ = $2; } +; + +compare_value: + | TC_RAW { $$ = $1; } + | TC_NUMBER { $$ = $1; } + | TC_STRING { $$ = $1; } + | BOOL_TRUE { $$ = $1; } + | BOOL_FALSE { $$ = $1; } + | TC_CONSTANT { zend_ini_get_constant(&$$, &$1 TSRMLS_CC); } + | '"' encapsed_list '"' { $$ = $2; } + | cfg_var_ref { $$ = $1; } + | '(' expr ')' { $$ = $2; } +; + section_string_or_value: var_string_list { $$ = $1; } | /* empty */ { zend_ini_init_string(&$$); } @@ -345,15 +466,18 @@ expr: ; cfg_var_ref: - TC_DOLLAR_CURLY TC_VARNAME '}' { zend_ini_get_var(&$$, &$2 TSRMLS_CC); free(Z_STRVAL($2)); } + TC_DOLLAR_CURLY TC_VARNAME '}' { + zend_ini_get_var(&$$, &$2 TSRMLS_CC); + free(Z_STRVAL($2)); + } ; constant_string: TC_CONSTANT { zend_ini_get_constant(&$$, &$1 TSRMLS_CC); } - | TC_RAW { $$ = $1; /*printf("TC_RAW: '%s'\n", Z_STRVAL($1));*/ } - | TC_NUMBER { $$ = $1; /*printf("TC_NUMBER: '%s'\n", Z_STRVAL($1));*/ } - | TC_STRING { $$ = $1; /*printf("TC_STRING: '%s'\n", Z_STRVAL($1));*/ } - | TC_WHITESPACE { $$ = $1; /*printf("TC_WHITESPACE: '%s'\n", Z_STRVAL($1));*/ } + | TC_RAW { $$ = $1; /*fprintf(stderr, "TC_RAW: '%s'\n", Z_STRVAL($1));*/ } + | TC_NUMBER { $$ = $1; /*fprintf(stderr, "TC_NUMBER: '%s'\n", Z_STRVAL($1));*/ } + | TC_STRING { $$ = $1; /*fprintf(stderr, "TC_STRING: '%s'\n", Z_STRVAL($1));*/ } + | TC_WHITESPACE { $$ = $1; /*fprintf(stderr, "TC_WHITESPACE: '%s'\n", Z_STRVAL($1));*/ } ; /* Index: Zend/zend_ini_scanner.l =================================================================== RCS file: /repository/ZendEngine2/zend_ini_scanner.l,v retrieving revision 1.41.2.2.2.2.2.2 diff -u -p -d -r1.41.2.2.2.2.2.2 zend_ini_scanner.l --- Zend/zend_ini_scanner.l 4 Feb 2008 20:45:20 -0000 1.41.2.2.2.2.2.2 +++ Zend/zend_ini_scanner.l 13 Feb 2008 16:20:28 -0000 @@ -15,6 +15,7 @@ +----------------------------------------------------------------------+ | Authors: Zeev Suraski <zeev@zend.com> | | Jani Taskinen <jani@php.net> | + | Marcus Boerger <helly@php.net> | +----------------------------------------------------------------------+ */ @@ -68,11 +69,14 @@ %} +%x IGNORE +%x IFDONE %x ST_DOUBLE_QUOTES %x ST_OFFSET %x ST_RAW %x ST_SECTION_RAW %x ST_SECTION_VALUE +%x ST_CONDITION_VALUE %x ST_VALUE %x ST_VARNAME %option stack @@ -122,6 +126,8 @@ ZEND_API zend_scanner_globals ini_scanne return type; \ } +void zend_ini_set_ignore(ZEND_INI_CONDITIONAL state TSRMLS_DC); + static char *ini_filename; /* {{{ init_ini_scanner() @@ -129,6 +135,7 @@ static char *ini_filename; static void init_ini_scanner(TSRMLS_D) { SCNG(lineno) = 1; + SCNG(if_level) = 0; SCNG(scanner_mode) = ZEND_INI_SCANNER_NORMAL; SCNG(yy_start_stack_ptr) = 0; SCNG(yy_start_stack_depth) = 0; @@ -199,6 +206,35 @@ int zend_ini_prepare_string_for_scanning } /* }}} */ +void zend_ini_error(int severity TSRMLS_DC, char *error) /* {{{ */ +{ + if (!error) { + error = "Error"; + } + if (CG(ini_parser_unbuffered_errors)) { +#ifdef PHP_WIN32 + if (ini_filename) { + zend_spprintf(&error, 0, "%s at line %d of ini file %s.", error, SCNG(lineno), ini_filename); + } else { + zend_spprintf(&error, 0, "%s in invalid configuration directive.", error); + } + MessageBox(NULL, error, "PHP Error", MB_OK|MB_TOPMOST|0x00200000L); + efree(error); +#else + if (ini_filename) { + fprintf(stderr, "PHP: %s at line %d of ini file %s.\n", error, SCNG(lineno), ini_filename); + } else { + fprintf(stderr, "PHP: %s in invalid configuration directive.\n", error); + } +#endif + } else if (ini_filename) { + zend_error(severity, "%s at line %d of ini file %s", error, SCNG(lineno), ini_filename); + } else { + zend_error(severity, "%s in invalid configuration directive", error); + } +} +/* }}} */ + /* {{{ zend_ini_close_file() */ void zend_ini_close_file(zend_file_handle *fh TSRMLS_DC) @@ -290,6 +326,7 @@ RAW_VALUE_CHARS [^=\n\r;] LITERAL_DOLLAR ("$"([^a-zA-Z0-9{]|("\\"{ANY_CHAR}))) VALUE_CHARS ([^$= \t\n\r;&|~()!"']|{LITERAL_DOLLAR}) SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR}) +CONDITION_VALUE_CHARS ([^$ \t\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR}) DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR}) /* " */ @@ -312,7 +349,81 @@ DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_C return TC_SECTION; } -<ST_VALUE,ST_SECTION_VALUE,ST_OFFSET>"'"{SINGLE_QUOTED_CHARS}+"'" { /* Raw string */ +<IGNORE>"[ELSE]"{TABS_AND_SPACES}*{NEWLINE} { /* End of condition */ + zend_ini_set_ignore(ZEND_INI_COND_READ TSRMLS_CC); + BEGIN(INITIAL); + SCNG(lineno)++; + return END_OF_LINE; +} + +<IFDONE,INITIAL>"[ELSE]"{TABS_AND_SPACES}*{NEWLINE} { /* End of condition */ + if (SCNG(scanner_mode) != ZEND_INI_SCANNER_RAW) { + if (!SCNG(if_level)) { + zend_ini_error(E_WARNING TSRMLS_CC, "[ELSE] without [IF]"); + yyterminate(); + } + zend_ini_set_ignore(ZEND_INI_COND_IGNORE TSRMLS_CC); + BEGIN(IFDONE); + } else { + BEGIN(INITIAL); + } + SCNG(lineno)++; + return END_OF_LINE; +} + +<INITIAL,IGNORE,IFDONE>"[ENDIF]"{TABS_AND_SPACES}*{NEWLINE} { /* End of condition */ + if (SCNG(scanner_mode) != ZEND_INI_SCANNER_RAW) { + if (!SCNG(if_level)--) { + zend_ini_error(E_WARNING TSRMLS_CC, "[ENDIF] without [IF]"); + yyterminate(); + } + yy_pop_state(TSRMLS_C); +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "ENDIF\n"); +#endif + } + SCNG(lineno)++; + return END_OF_LINE; +} + +<INITIAL,IGNORE>"[ELSEIF"{TABS_AND_SPACES}+ { /* Condition elseif */ + if (SCNG(scanner_mode) != ZEND_INI_SCANNER_RAW) { + if (!SCNG(if_level)) { + zend_error(E_WARNING, "[ELSEIF] without [IF]"); + yyterminate(); + } + BEGIN(ST_CONDITION_VALUE); + return TC_CONDITION; + } else { + BEGIN(IFDONE); + return END_OF_LINE; + } +} + +<IFDONE>"[ELSEIF"{TABS_AND_SPACES}+ { /* Condition elseif */ + return END_OF_LINE; +} + +<INITIAL>"[IF"{TABS_AND_SPACES}+ { /* Condition if */ + if (SCNG(scanner_mode) != ZEND_INI_SCANNER_RAW) { + SCNG(if_level)++; + yy_push_state(ST_CONDITION_VALUE TSRMLS_CC); + return TC_CONDITION; + } else { + BEGIN(IFDONE); + return END_OF_LINE; + } +} + +<IGNORE,IFDONE>[^\[]{NEWLINE} { + SCNG(lineno)++; + if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) { + BEGIN(INITIAL); + } + return END_OF_LINE; +} + +<ST_VALUE,ST_SECTION_VALUE,ST_CONDITION_VALUE,ST_OFFSET>"'"{SINGLE_QUOTED_CHARS}+"'" { /* Raw string */ /* Eat leading and trailing single quotes */ if (yytext[0] == '\'' && yytext[yyleng - 1] == '\'') { yytext++; @@ -328,6 +439,52 @@ DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_C return ']'; } +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*"]"{TABS_AND_SPACES}*{NEWLINE}? { /* End of condition */ + BEGIN(INITIAL); + SCNG(lineno)++; + return ']'; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*"||"{TABS_AND_SPACES}* { + return T_L_OR; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*"&&"{TABS_AND_SPACES}* { + return T_L_AND; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*"=="{TABS_AND_SPACES}* { + return T_IS_EQ; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*"!="{TABS_AND_SPACES}* { + return T_IS_NE; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*"==="{TABS_AND_SPACES}* { + return T_IS_IDENTICAL; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*"!=="{TABS_AND_SPACES}* { + return T_IS_NOT_IDENTICAL; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*"<="{TABS_AND_SPACES}* { + return T_IS_LE; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*">="{TABS_AND_SPACES}* { + return T_IS_GE; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*"("{TABS_AND_SPACES}* { + return '('; +} + +<ST_CONDITION_VALUE>{TABS_AND_SPACES}*")"{TABS_AND_SPACES}* { + return ')'; +} + <INITIAL>{LABEL}"["{TABS_AND_SPACES}* { /* Start of option with offset */ /* Eat trailing whitespace and [ */ EAT_TRAILING_WHITESPACE_EX('['); @@ -343,7 +500,7 @@ DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_C return ']'; } -<ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{DOLLAR_CURLY} { /* Variable start */ +<ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_CONDITION_VALUE,ST_VALUE,ST_OFFSET>{DOLLAR_CURLY} { /* Variable start */ yy_push_state(ST_VARNAME TSRMLS_CC); return TC_DOLLAR_CURLY; } @@ -352,12 +509,7 @@ DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_C RETURN_TOKEN(TC_VARNAME, yytext, yyleng); } -<ST_VARNAME>"}" { /* Variable end */ - yy_pop_state(TSRMLS_C); - return '}'; -} - -<INITIAL,ST_VALUE>("true"|"on"|"yes"){TABS_AND_SPACES}* { /* TRUE value (when used outside option value/offset this causes parse error!) */ +<INITIAL,ST_CONDITION_VALUE,ST_VALUE>("true"|"on"|"yes"){TABS_AND_SPACES}* { /* TRUE value (when used outside option value/offset this causes parse error!) */ RETURN_TOKEN(BOOL_TRUE, "1", 1); } @@ -365,6 +517,10 @@ DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_C RETURN_TOKEN(BOOL_FALSE, "", 0); } +<ST_CONDITION_VALUE>("false"|"off"|"no"|"none"|"null"){TABS_AND_SPACES}* { /* FALSE value (when used in conditions we want '0' rather than '')*/ + RETURN_TOKEN(BOOL_FALSE, "0", 1); +} + <INITIAL>{LABEL} { /* Get option name */ RETURN_TOKEN(TC_LABEL, yytext, yyleng); } @@ -398,11 +554,11 @@ DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_C return END_OF_LINE; } -<ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */ +<ST_SECTION_VALUE,ST_CONDITION_VALUE,ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */ RETURN_TOKEN(TC_CONSTANT, yytext, yyleng); } -<ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */ +<ST_SECTION_VALUE,ST_CONDITION_VALUE,ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */ RETURN_TOKEN(TC_NUMBER, yytext, yyleng); } @@ -428,7 +584,7 @@ DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_C RETURN_TOKEN(TC_STRING, yytext, yyleng); } -<ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */ +<ST_SECTION_VALUE,ST_CONDITION_VALUE,ST_VALUE,ST_OFFSET>{TABS_AND_SPACES}*["] { /* Double quoted '"' string start */ yy_push_state(ST_DOUBLE_QUOTES TSRMLS_CC); return '"'; } @@ -473,37 +629,46 @@ DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_C switch (YYSTATE) { case INITIAL: break; + + case IGNORE: + case IFDONE: + zend_ini_error(E_ERROR TSRMLS_CC, "Unterminated [IF] in ini"); + break; case ST_DOUBLE_QUOTES: - fprintf(stderr, "ERROR: Unterminated ini option value double quotes\n"); + zend_ini_error(E_ERROR TSRMLS_CC, "Unterminated ini option value double quotes"); break; case ST_OFFSET: - fprintf(stderr, "ERROR: Unterminated ini option offset\n"); + zend_ini_error(E_ERROR TSRMLS_CC, "Unterminated ini option offset"); break; case ST_RAW: - fprintf(stderr, "ERROR: Unterminated raw ini option value\n"); + zend_ini_error(E_ERROR TSRMLS_CC, "Unterminated raw ini option value"); break; case ST_SECTION_RAW: - fprintf(stderr, "ERROR: Unterminated raw ini section value\n"); + zend_ini_error(E_ERROR TSRMLS_CC, "Unterminated raw ini section value"); break; case ST_SECTION_VALUE: - fprintf(stderr, "ERROR: Unterminated ini section value\n"); + zend_ini_error(E_ERROR TSRMLS_CC, "Unterminated ini section value"); + break; + + case ST_CONDITION_VALUE: + zend_ini_error(E_ERROR TSRMLS_CC, "Unterminated ini condition value"); break; case ST_VALUE: - fprintf(stderr, "ERROR: Unterminated ini option value\n"); + zend_ini_error(E_ERROR TSRMLS_CC, "Unterminated ini option value"); break; case ST_VARNAME: - fprintf(stderr, "ERROR: Unterminated ini variable\n"); + zend_ini_error(E_ERROR TSRMLS_CC, "Unterminated ini variable"); break; default: - fprintf(stderr, "BUG: Unknown state (%d)\n", YYSTATE); + zend_ini_error(E_ERROR TSRMLS_CC, "Unknown state"); /* (%d)\n", YYSTATE); */ break; } yy_pop_state(TSRMLS_C); @@ -511,3 +676,30 @@ DOUBLE_QUOTES_CHARS ([^$"\\]|("\\"{ANY_C #endif yyterminate(); } + +%% + +void zend_ini_set_ignore(ZEND_INI_CONDITIONAL state TSRMLS_DC) /* {{{ */ +{ + switch(state) { + case ZEND_INI_COND_READ: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "READ\n"); +#endif + BEGIN(INITIAL); + break; + case ZEND_INI_COND_IGNORE: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "IGNORE\n"); +#endif + BEGIN(IGNORE); + break; + case ZEND_INI_COND_DONE: +#if ZEND_DEBUG_CFG_PARSER + fprintf(stderr, "IF DONE\n"); +#endif + BEGIN(IFDONE); + break; + } +} +/* }}} */
« previous php.internals (#35451) next »