diff options
-rw-r--r-- | Zend/zend_globals.h | 3 | ||||
-rw-r--r-- | Zend/zend_ini.h | 14 | ||||
-rw-r--r-- | Zend/zend_ini_parser.y | 209 | ||||
-rw-r--r-- | Zend/zend_ini_scanner.h | 9 | ||||
-rw-r--r-- | Zend/zend_ini_scanner.l | 480 | ||||
-rw-r--r-- | ext/standard/basic_functions.c | 168 | ||||
-rw-r--r-- | ext/standard/basic_functions.h | 3 | ||||
-rw-r--r-- | ext/standard/browscap.c | 42 | ||||
-rw-r--r-- | ext/standard/tests/file/parse_ini_file.phpt | 400 | ||||
-rw-r--r-- | ext/standard/tests/general_functions/parse_ini_basic.data | 106 | ||||
-rw-r--r-- | ext/standard/tests/general_functions/parse_ini_basic.phpt | 239 | ||||
-rw-r--r-- | ext/standard/tests/general_functions/parse_ini_booleans.data | 27 | ||||
-rw-r--r-- | ext/standard/tests/general_functions/parse_ini_booleans.phpt | 69 | ||||
-rw-r--r-- | ext/standard/tests/general_functions/parse_ini_file.phpt | 28 | ||||
-rw-r--r-- | main/main.c | 9 | ||||
-rw-r--r-- | main/php_globals.h | 3 | ||||
-rw-r--r-- | main/php_ini.c | 300 | ||||
-rw-r--r-- | main/php_ini.h | 7 | ||||
-rw-r--r-- | php.ini-dist | 11 | ||||
-rw-r--r-- | php.ini-recommended | 12 | ||||
-rw-r--r-- | sapi/cgi/cgi_main.c | 116 |
21 files changed, 1706 insertions, 549 deletions
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index d2a7909d53..9351239499 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -265,6 +265,9 @@ struct _zend_scanner_globals { int yy_start_stack_depth; int *yy_start_stack; + /* For ini scanner. Modes are: ZEND_INI_SCANNER_NORMAL, ZEND_INI_SCANNER_RAW */ + int scanner_mode; + #ifdef ZEND_MULTIBYTE /* original (unfiltered) script */ char *script_org; diff --git a/Zend/zend_ini.h b/Zend/zend_ini.h index 2b7ac53e44..03c62a410e 100644 --- a/Zend/zend_ini.h +++ b/Zend/zend_ini.h @@ -164,7 +164,6 @@ END_EXTERN_C() #define INI_ORIG_STR(name) zend_ini_string((name), sizeof(name), 1) #define INI_ORIG_BOOL(name) ((zend_bool) INI_ORIG_INT(name)) - #define REGISTER_INI_ENTRIES() zend_register_ini_entries(ini_entries, module_number TSRMLS_CC) #define UNREGISTER_INI_ENTRIES() zend_unregister_ini_entries(module_number TSRMLS_CC) #define DISPLAY_INI_ENTRIES() display_ini_entries(zend_module) @@ -193,15 +192,16 @@ END_EXTERN_C() #define ZEND_INI_STAGE_HTACCESS (1<<5) /* INI parsing engine */ -typedef void (*zend_ini_parser_cb_t)(zval *arg1, zval *arg2, int callback_type, void *arg); +typedef void (*zend_ini_parser_cb_t)(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC); BEGIN_EXTERN_C() -ZEND_API int zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffered_errors, zend_ini_parser_cb_t ini_parser_cb, void *arg); -ZEND_API int zend_parse_ini_string(char *str, zend_bool unbuffered_errors, zend_ini_parser_cb_t ini_parser_cb, void *arg); +ZEND_API int zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg TSRMLS_DC); +ZEND_API int zend_parse_ini_string(char *str, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg TSRMLS_DC); END_EXTERN_C() -#define ZEND_INI_PARSER_ENTRY 1 -#define ZEND_INI_PARSER_SECTION 2 -#define ZEND_INI_PARSER_POP_ENTRY 3 +/* INI entries */ +#define ZEND_INI_PARSER_ENTRY 1 /* Normal entry: foo = bar */ +#define ZEND_INI_PARSER_SECTION 2 /* Section: [foobar] */ +#define ZEND_INI_PARSER_POP_ENTRY 3 /* Offset entry: foo[] = bar */ typedef struct _zend_ini_parser_param { zend_ini_parser_cb_t ini_parser_cb; diff --git a/Zend/zend_ini_parser.y b/Zend/zend_ini_parser.y index d392c41e5a..cadb5cfd69 100644 --- a/Zend/zend_ini_parser.y +++ b/Zend/zend_ini_parser.y @@ -13,13 +13,15 @@ | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Zeev Suraski <zeev@zend.com> | + | Authors: Zeev Suraski <zeev@zend.com> | + | Jani Taskinen <jani@php.net> | +----------------------------------------------------------------------+ */ /* $Id$ */ #define DEBUG_CFG_PARSER 0 + #include "zend.h" #include "zend_API.h" #include "zend_ini.h" @@ -27,32 +29,23 @@ #include "zend_ini_scanner.h" #include "zend_extensions.h" +#define YYERROR_VERBOSE #define YYSTYPE zval #ifdef ZTS #define YYPARSE_PARAM tsrm_ls #define YYLEX_PARAM tsrm_ls -#endif - -#define ZEND_INI_PARSER_CB (CG(ini_parser_param))->ini_parser_cb -#define ZEND_INI_PARSER_ARG (CG(ini_parser_param))->arg - -int ini_lex(zval *ini_lval TSRMLS_DC); -#ifdef ZTS int ini_parse(void *arg); #else int ini_parse(void); #endif -zval yylval; - -#ifndef ZTS -extern int ini_lex(zval *ini_lval TSRMLS_DC); -extern FILE *ini_in; -extern void init_cfg_scanner(void); -#endif +#define ZEND_INI_PARSER_CB (CG(ini_parser_param))->ini_parser_cb +#define ZEND_INI_PARSER_ARG (CG(ini_parser_param))->arg -void zend_ini_do_op(char type, zval *result, zval *op1, zval *op2) +/* {{{ zend_ini_do_op() +*/ +static void zend_ini_do_op(char type, zval *result, zval *op1, zval *op2) { int i_result; int i_op1, i_op2; @@ -91,16 +84,22 @@ void zend_ini_do_op(char type, zval *result, zval *op1, zval *op2) Z_STRVAL_P(result)[Z_STRLEN_P(result)] = 0; Z_TYPE_P(result) = IS_STRING; } +/* }}} */ -void zend_ini_init_string(zval *result) +/* {{{ zend_ini_init_string() +*/ +static void zend_ini_init_string(zval *result) { Z_STRVAL_P(result) = malloc(1); Z_STRVAL_P(result)[0] = 0; Z_STRLEN_P(result) = 0; Z_TYPE_P(result) = IS_STRING; } +/* }}} */ -void zend_ini_add_string(zval *result, zval *op1, zval *op2) +/* {{{ zend_ini_add_string() +*/ +static void zend_ini_add_string(zval *result, zval *op1, zval *op2) { int length = Z_STRLEN_P(op1) + Z_STRLEN_P(op2); @@ -110,12 +109,15 @@ void zend_ini_add_string(zval *result, zval *op1, zval *op2) Z_STRLEN_P(result) = length; Z_TYPE_P(result) = IS_STRING; } +/* }}} */ -void zend_ini_get_constant(zval *result, zval *name) +/* {{{ zend_ini_get_constant() +*/ +static void zend_ini_get_constant(zval *result, zval *name TSRMLS_DC) { zval z_constant; - TSRMLS_FETCH(); + /* If name contains ':' it is not a constant. Bug #26893. */ if (!memchr(Z_STRVAL_P(name), ':', Z_STRLEN_P(name)) && zend_get_constant(Z_STRVAL_P(name), Z_STRLEN_P(name), &z_constant TSRMLS_CC)) { /* z_constant is emalloc()'d */ @@ -129,16 +131,20 @@ void zend_ini_get_constant(zval *result, zval *name) *result = *name; } } +/* }}} */ -void zend_ini_get_var(zval *result, zval *name) +/* {{{ zend_ini_get_var() +*/ +static void zend_ini_get_var(zval *result, zval *name TSRMLS_DC) { zval curval; char *envvar; - TSRMLS_FETCH(); + /* Fetch configuration option value */ if (zend_get_configuration_directive(Z_STRVAL_P(name), Z_STRLEN_P(name)+1, &curval) == SUCCESS) { Z_STRVAL_P(result) = zend_strndup(Z_STRVAL(curval), Z_STRLEN(curval)); Z_STRLEN_P(result) = Z_STRLEN(curval); + /* ..or if not found, try ENV */ } else if ((envvar = zend_getenv(Z_STRVAL_P(name), Z_STRLEN_P(name) TSRMLS_CC)) != NULL || (envvar = getenv(Z_STRVAL_P(name))) != NULL) { Z_STRVAL_P(result) = strdup(envvar); @@ -147,9 +153,11 @@ void zend_ini_get_var(zval *result, zval *name) zend_ini_init_string(result); } } +/* }}} */ - -static void ini_error(char *str) +/* {{{ ini_error() +*/ +static void ini_error(char *msg) { char *error_buf; int error_buf_len; @@ -158,10 +166,10 @@ static void ini_error(char *str) currently_parsed_filename = zend_ini_scanner_get_filename(TSRMLS_C); if (currently_parsed_filename) { - error_buf_len = 128+strlen(currently_parsed_filename); /* should be more than enough */ + 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, "Error parsing %s on line %d\n", currently_parsed_filename, zend_ini_scanner_get_lineno(TSRMLS_C)); + 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"); } @@ -177,71 +185,87 @@ static void ini_error(char *str) } efree(error_buf); } +/* }}} */ - -ZEND_API int zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffered_errors, zend_ini_parser_cb_t ini_parser_cb, void *arg) +/* {{{ zend_parse_ini_file() +*/ +ZEND_API int zend_parse_ini_file(zend_file_handle *fh, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg TSRMLS_DC) { int retval; zend_ini_parser_param ini_parser_param; - TSRMLS_FETCH(); ini_parser_param.ini_parser_cb = ini_parser_cb; ini_parser_param.arg = arg; - CG(ini_parser_param) = &ini_parser_param; - if (zend_ini_open_file_for_scanning(fh TSRMLS_CC)==FAILURE) { + + if (zend_ini_open_file_for_scanning(fh, scanner_mode TSRMLS_CC) == FAILURE) { return FAILURE; } CG(ini_parser_unbuffered_errors) = unbuffered_errors; retval = ini_parse(TSRMLS_C); - zend_ini_close_file(fh TSRMLS_CC); - if (retval==0) { + shutdown_ini_scanner(TSRMLS_C); + + if (retval == 0) { return SUCCESS; } else { return FAILURE; } } +/* }}} */ - -ZEND_API int zend_parse_ini_string(char *str, zend_bool unbuffered_errors, zend_ini_parser_cb_t ini_parser_cb, void *arg) +/* {{{ zend_parse_ini_string() +*/ +ZEND_API int zend_parse_ini_string(char *str, zend_bool unbuffered_errors, int scanner_mode, zend_ini_parser_cb_t ini_parser_cb, void *arg TSRMLS_DC) { + int retval; zend_ini_parser_param ini_parser_param; - TSRMLS_FETCH(); ini_parser_param.ini_parser_cb = ini_parser_cb; ini_parser_param.arg = arg; - CG(ini_parser_param) = &ini_parser_param; - if (zend_ini_prepare_string_for_scanning(str TSRMLS_CC)==FAILURE) { + + if (zend_ini_prepare_string_for_scanning(str, scanner_mode TSRMLS_CC) == FAILURE) { return FAILURE; } CG(ini_parser_unbuffered_errors) = unbuffered_errors; + retval = ini_parse(TSRMLS_C); + + shutdown_ini_scanner(TSRMLS_C); - if (ini_parse(TSRMLS_C)) { + if (retval == 0) { return SUCCESS; } else { return FAILURE; } } - +/* }}} */ %} +%expect 1 %pure_parser + +%token TC_SECTION +%token TC_RAW +%token TC_NUMBER %token TC_STRING -%token TC_ENCAPSULATED_STRING -%token BRACK -%token SECTION -%token CFG_TRUE -%token CFG_FALSE +%token TC_OFFSET %token TC_DOLLAR_CURLY +%token TC_VARNAME +%token TC_QUOTED_STRING +%token BOOL_TRUE +%token BOOL_FALSE +%token END_OF_LINE +%token '=' ':' ',' '.' '"' '\'' '^' '+' '-' '/' '*' '%' '$' '~' '<' '>' '?' '@' %left '|' '&' %right '~' '!' +%destructor { free(Z_STRVAL($$)); } TC_RAW TC_NUMBER TC_STRING TC_OFFSET TC_VARNAME TC_QUOTED_STRING BOOL_TRUE BOOL_FALSE + %% statement_list: @@ -250,61 +274,94 @@ statement_list: ; statement: - TC_STRING '=' string_or_value { + TC_SECTION section_string_or_value ']' { #if DEBUG_CFG_PARSER - printf("'%s' = '%s'\n", Z_STRVAL($1), Z_STRVAL($3)); + printf("SECTION: [%s]\n", Z_STRVAL($2)); #endif - ZEND_INI_PARSER_CB(&$1, &$3, ZEND_INI_PARSER_ENTRY, ZEND_INI_PARSER_ARG); + ZEND_INI_PARSER_CB(&$2, NULL, NULL, ZEND_INI_PARSER_SECTION, ZEND_INI_PARSER_ARG TSRMLS_CC); + free(Z_STRVAL($2)); + } + | TC_STRING '=' string_or_value { +#if DEBUG_CFG_PARSER + printf("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_STRING BRACK '=' string_or_value { + | TC_OFFSET option_offset ']' '=' string_or_value { #if DEBUG_CFG_PARSER - printf("'%s'[ ] = '%s'\n", Z_STRVAL($1), Z_STRVAL($4)); + printf("OFFSET: '%s'[%s] = '%s'\n", Z_STRVAL($1), Z_STRVAL($2), Z_STRVAL($5)); #endif - ZEND_INI_PARSER_CB(&$1, &$4, ZEND_INI_PARSER_POP_ENTRY, ZEND_INI_PARSER_ARG); + 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($4)); + free(Z_STRVAL($2)); + free(Z_STRVAL($5)); } - | TC_STRING { ZEND_INI_PARSER_CB(&$1, NULL, ZEND_INI_PARSER_ENTRY, ZEND_INI_PARSER_ARG); free(Z_STRVAL($1)); } - | SECTION { ZEND_INI_PARSER_CB(&$1, NULL, ZEND_INI_PARSER_SECTION, ZEND_INI_PARSER_ARG); free(Z_STRVAL($1)); } - | '\n' + | TC_STRING { ZEND_INI_PARSER_CB(&$1, NULL, NULL, ZEND_INI_PARSER_ENTRY, ZEND_INI_PARSER_ARG TSRMLS_CC); free(Z_STRVAL($1)); } + | END_OF_LINE ; +section_string_or_value: + TC_RAW { $$ = $1; } + | section_var_list { $$ = $1; } + | '"' encapsed_list '"' { $$ = $2; } + | /* empty */ { zend_ini_init_string(&$$); } +; string_or_value: - expr { $$ = $1; } - | CFG_TRUE { $$ = $1; } - | CFG_FALSE { $$ = $1; } - | '\n' { zend_ini_init_string(&$$); } - | /* empty */ { zend_ini_init_string(&$$); } + expr { $$ = $1; } + | TC_RAW { $$ = $1; } + | TC_NUMBER { $$ = $1; } + | BOOL_TRUE { $$ = $1; } + | BOOL_FALSE { $$ = $1; } + | '"' encapsed_list '"' { $$ = $2; } + | END_OF_LINE { zend_ini_init_string(&$$); } ; +option_offset: + TC_NUMBER { $$ = $1; } + | TC_RAW { $$ = $1; } + | var_string_list { $$ = $1; } + | '"' encapsed_list '"' { $$ = $2; } + | /* empty */ { zend_ini_init_string(&$$); } +; -var_string_list: - cfg_var_ref { $$ = $1; } - | TC_ENCAPSULATED_STRING { $$ = $1; } - | constant_string { $$ = $1; } - | var_string_list cfg_var_ref { zend_ini_add_string(&$$, &$1, &$2); free($2.value.str.val); } - | var_string_list TC_ENCAPSULATED_STRING { zend_ini_add_string(&$$, &$1, &$2); free(Z_STRVAL($2)); } - | var_string_list constant_string { zend_ini_add_string(&$$, &$1, &$2); free($2.value.str.val); } +encapsed_list: + encapsed_list cfg_var_ref { zend_ini_add_string(&$$, &$1, &$2); free(Z_STRVAL($2)); } + | encapsed_list TC_QUOTED_STRING { zend_ini_add_string(&$$, &$1, &$2); free(Z_STRVAL($2)); } + | /* empty */ { zend_ini_init_string(&$$); } ; -cfg_var_ref: - TC_DOLLAR_CURLY TC_STRING '}' { zend_ini_get_var(&$$, &$2); free($2.value.str.val); } +section_var_list: + cfg_var_ref { $$ = $1; } + | TC_STRING { $$ = $1; } + | section_var_list cfg_var_ref { zend_ini_add_string(&$$, &$1, &$2); free(Z_STRVAL($2)); } + | section_var_list TC_STRING { zend_ini_add_string(&$$, &$1, &$2); free(Z_STRVAL($2)); } +; + +var_string_list: + cfg_var_ref { $$ = $1; } + | constant_string { $$ = $1; } + | var_string_list cfg_var_ref { zend_ini_add_string(&$$, &$1, &$2); free(Z_STRVAL($2)); } + | var_string_list constant_string { zend_ini_add_string(&$$, &$1, &$2); free(Z_STRVAL($2)); } ; expr: - var_string_list { $$ = $1; } - | expr '|' expr { zend_ini_do_op('|', &$$, &$1, &$3); } - | expr '&' expr { zend_ini_do_op('&', &$$, &$1, &$3); } - | '~' expr { zend_ini_do_op('~', &$$, &$2, NULL); } - | '!' expr { zend_ini_do_op('!', &$$, &$2, NULL); } - | '(' expr ')' { $$ = $2; } + var_string_list { $$ = $1; } + | expr '|' expr { zend_ini_do_op('|', &$$, &$1, &$3); } + | expr '&' expr { zend_ini_do_op('&', &$$, &$1, &$3); } + | '~' expr { zend_ini_do_op('~', &$$, &$2, NULL); } + | '!' expr { zend_ini_do_op('!', &$$, &$2, NULL); } + | '(' expr ')' { $$ = $2; } +; + +cfg_var_ref: + TC_DOLLAR_CURLY TC_VARNAME '}' { zend_ini_get_var(&$$, &$2 TSRMLS_CC); free(Z_STRVAL($2)); } ; constant_string: - TC_STRING { zend_ini_get_constant(&$$, &$1); } + TC_STRING { zend_ini_get_constant(&$$, &$1 TSRMLS_CC); } ; /* diff --git a/Zend/zend_ini_scanner.h b/Zend/zend_ini_scanner.h index 76ca771058..3ba6e120f5 100644 --- a/Zend/zend_ini_scanner.h +++ b/Zend/zend_ini_scanner.h @@ -22,13 +22,18 @@ #ifndef _ZEND_INI_SCANNER_H #define _ZEND_INI_SCANNER_H +/* Scanner modes */ +#define ZEND_INI_SCANNER_NORMAL 0 /* Normal mode. [DEFAULT] */ +#define ZEND_INI_SCANNER_RAW 1 /* Raw mode. Option values are not parsed */ + BEGIN_EXTERN_C() int zend_ini_scanner_get_lineno(TSRMLS_D); char *zend_ini_scanner_get_filename(TSRMLS_D); -int zend_ini_open_file_for_scanning(zend_file_handle *fh TSRMLS_DC); -int zend_ini_prepare_string_for_scanning(char *str TSRMLS_DC); +int zend_ini_open_file_for_scanning(zend_file_handle *fh, int scanner_mode TSRMLS_DC); +int zend_ini_prepare_string_for_scanning(char *str, int scanner_mode TSRMLS_DC); void zend_ini_close_file(zend_file_handle *fh TSRMLS_DC); int ini_lex(zval *ini_lval TSRMLS_DC); +void shutdown_ini_scanner(TSRMLS_D); END_EXTERN_C() #endif /* _ZEND_INI_SCANNER_H */ diff --git a/Zend/zend_ini_scanner.l b/Zend/zend_ini_scanner.l index 3f91bb64a7..17d0f01c1c 100644 --- a/Zend/zend_ini_scanner.l +++ b/Zend/zend_ini_scanner.l @@ -13,21 +13,71 @@ | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Zeev Suraski <zeev@zend.com> | + | Authors: Zeev Suraski <zeev@zend.com> | + | Jani Taskinen <jani@php.net> | +----------------------------------------------------------------------+ */ /* $Id$ */ +#define DEBUG_CFG_SCANNER 0 + #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) + +/* How it works (for the core ini directives): + * =========================================== + * + * 1. Scanner scans file for tokens and passes them to parser. + * 2. Parser parses the tokens and passes the name/value pairs to the callback + * function which stores them in the configuration hash table. + * 3. Later REGISTER_INI_ENTRIES() is called which triggers the actual + * registering of ini entries and uses zend_get_configuration_directive() + * to fetch the previously stored name/value pair from configuration hash table + * and registers the static ini entries which match the name to the value + * into EG(ini_directives) hash table. + * 4. PATH section entries are used per-request from down to top, each overriding + * previous if one exists. zend_alter_ini_entry() is called for each entry. + * Settings in PATH section are ZEND_INI_SYSTEM accessible and thus mimics the + * php_admin_* directives used within Apache httpd.conf when PHP is compiled as + * module for Apache. + * 5. User defined ini files (like .htaccess for apache) are parsed for each request and + * stored in separate hash defined by SAPI. + */ + +/* TODO: (ordered by importance :-) + * =============================================================================== + * + * - Separate constant lookup totally from plain strings (using CONSTANT pattern) + * - Add #if .. #else .. #endif and ==, !=, <, > , <=, >= operators + * - Add #include "some.ini" + * - Allow variables to refer to options also when using parse_ini_file() + * + */ + +/* These are not needed when yymore() is not used */ +/* #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_DOUBLE_QUOTES +%x ST_OFFSET +%x ST_RAW +%x ST_SECTION_RAW +%x ST_SECTION_VALUE +%x ST_VALUE +%x ST_VARNAME +%option stack + +%{ #include <errno.h> #include "zend.h" @@ -35,9 +85,6 @@ #include <zend_ini_parser.h> #include "zend_ini_scanner.h" -#undef YYSTYPE -#define YYSTYPE zval - #define YY_DECL int ini_lex(zval *ini_lval TSRMLS_DC) /* Globals Macros */ @@ -48,204 +95,417 @@ ZEND_API ts_rsrc_id ini_scanner_globals_id; ZEND_API zend_scanner_globals ini_scanner_globals; #endif -# define YY_INPUT(buf, result, max_size) \ - if ( ((result = zend_stream_read(yyin, buf, max_size TSRMLS_CC)) == 0) \ - && zend_stream_ferror( yyin TSRMLS_CC) ) \ - YY_FATAL_ERROR( "input in flex scanner failed" ); +/* Eat trailing whitespace + extra char */ +#define EAT_TRAILING_WHITESPACE_EX(ch) \ + while (yyleng > 0 && ( \ + (ch != 'X' && yytext[yyleng - 1] == ch) || \ + yytext[yyleng - 1] == '\n' || \ + yytext[yyleng - 1] == '\r' || \ + yytext[yyleng - 1] == '\t' || \ + yytext[yyleng - 1] == ' ') \ + ) { \ + yyleng--; \ + yytext[yyleng]=0; \ + } + +/* Eat trailing whitespace */ +#define EAT_TRAILING_WHITESPACE() EAT_TRAILING_WHITESPACE_EX('X') + +#define zend_ini_copy_value(retval, str, len) { \ + Z_STRVAL_P(retval) = zend_strndup(str, len); \ + Z_STRLEN_P(retval) = len; \ + Z_TYPE_P(retval) = IS_STRING; \ +} + +#define RETURN_TOKEN(type, str, len) { \ + zend_ini_copy_value(ini_lval, str, len); \ + return type; \ +} static char *ini_filename; -void init_ini_scanner(TSRMLS_D) +/* {{{ init_ini_scanner() +*/ +static void init_ini_scanner(TSRMLS_D) +{ + SCNG(lineno) = 1; + SCNG(scanner_mode) = ZEND_INI_SCANNER_NORMAL; + SCNG(yy_start_stack_ptr) = 0; + SCNG(yy_start_stack_depth) = 0; + SCNG(current_buffer) = NULL; +} +/* }}} */ + +/* {{{ shutdown_ini_scanner() +*/ +void shutdown_ini_scanner(TSRMLS_D) { - SCNG(lineno)=1; + if (SCNG(yy_start_stack)) { + yy_flex_free(SCNG(yy_start_stack)); + SCNG(yy_start_stack) = NULL; + } + yy_delete_buffer(SCNG(current_buffer) TSRMLS_CC); + if (ini_filename) { + free(ini_filename); + } } +/* }}} */ +/* {{{ zend_ini_scanner_get_lineno() +*/ int zend_ini_scanner_get_lineno(TSRMLS_D) { return SCNG(lineno); } +/* }}} */ +/* {{{ zend_ini_scanner_get_filename() +*/ char *zend_ini_scanner_get_filename(TSRMLS_D) { return ini_filename; } +/* }}} */ -int zend_ini_open_file_for_scanning(zend_file_handle *fh TSRMLS_DC) +/* {{{ zend_ini_open_file_for_scanning() +*/ +int zend_ini_open_file_for_scanning(zend_file_handle *fh, int scanner_mode TSRMLS_DC) { if (FAILURE == zend_stream_fixup(fh TSRMLS_CC)) { return FAILURE; } init_ini_scanner(TSRMLS_C); + SCNG(scanner_mode) = scanner_mode; yyin = fh; yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE TSRMLS_CC) TSRMLS_CC); - ini_filename = fh->filename; + ini_filename = zend_strndup(fh->filename, strlen(fh->filename)); return SUCCESS; } +/* }}} */ -int zend_ini_prepare_string_for_scanning(char *str TSRMLS_DC) +/* {{{ zend_ini_prepare_string_for_scanning() +*/ +int zend_ini_prepare_string_for_scanning(char *str, int scanner_mode TSRMLS_DC) { int len = strlen(str); + init_ini_scanner(TSRMLS_C); + SCNG(scanner_mode) = scanner_mode; yyin = NULL; yy_scan_buffer(str, len + 2 TSRMLS_CC); ini_filename = NULL; return SUCCESS; } +/* }}} */ +/* {{{ zend_ini_close_file() +*/ void zend_ini_close_file(zend_file_handle *fh TSRMLS_DC) { zend_stream_close(fh); } +/* }}} */ + +/* {{{ zend_ini_escape_string() + */ +static void zend_ini_escape_string(zval *lval, char *str, int len, char quote_type TSRMLS_DC) +{ + register char *s, *t; + char *end; + + zend_ini_copy_value(lval, str, len); + + /* convert escape sequences */ + s = t = Z_STRVAL_P(lval); + end = s + Z_STRLEN_P(lval); + + while (s < end) { + if (*s == '\\') { + s++; + if (s >= end) { + continue; + } + switch (*s) { + case 'n': + *t++ = '\n'; + Z_STRLEN_P(lval)--; + break; + case 'r': + *t++ = '\r'; + Z_STRLEN_P(lval)--; + break; + case 't': + *t++ = '\t'; + Z_STRLEN_P(lval)--; + break; + case '"': + if (*s != quote_type) { + *t++ = '\\'; + *t++ = *s; + break; + } + case '\\': + case '$': + *t++ = *s; + Z_STRLEN_P(lval)--; + break; + default: + *t++ = '\\'; + *t++ = *s; + break; + } + } else { + *t++ = *s; + } + + if (*s == '\n' || (*s == '\r' && (*(s+1) != '\n'))) { + SCNG(lineno)++; + } + s++; + } + *t = 0; +} +/* }}} */ %} -NEWLINE ("\r"|"\n"|"\r\n") +LNUM [0-9]+ +DNUM ([0-9]*[\.][0-9]+)|([0-9]+[\.][0-9]*) +NUMBER {LNUM}|{DNUM} +ANY_CHAR (.|[\n]) +NEWLINE ("\r"|"\n"|"\r\n") +TABS_AND_SPACES [ \t] +CONSTANT [a-zA-Z][a-zA-Z0-9_]* +LABEL [a-zA-Z0-9][a-zA-Z0-9._]* +TOKENS [:,.\[\]"'()|^&+-/*=%$!~<>?@] +OPERATORS [&|~()!] + +LITERAL_DOLLAR ("$"+([^a-zA-Z0-9$"'\\{]|("\\"{ANY_CHAR}))) +VALUE_CHARS ("{"*([^=\n\r;&|~()!$"'\\{]|("\\"{ANY_CHAR}))|{LITERAL_DOLLAR}) +RAW_VALUE_CHARS [^=\n\r;] +SINGLE_QUOTED_CHARS [^'] + +SECTION_VALUE_CHARS ("{"*([^\n\r;$"'\\{\]]|("\\"{ANY_CHAR}))|{LITERAL_DOLLAR}) +SECTION_RAW_CHARS [^\]\n\r] +/* Allow using ${foobar} inside quoted strings */ +DOUBLE_QUOTES_CHARS ("{"*([^$"\\{]|("\\"{ANY_CHAR}))|{LITERAL_DOLLAR}) + +/* " */ + +%option nounput %option noyywrap +%option noyylineno +%option noyy_top_state %option never-interactive %% -<INITIAL>[ ]*[\[][ ]*[\]][ ]* { - return BRACK; +<INITIAL>"[" { /* Section start */ + /* Enter section data lookup state */ + if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) { + yy_push_state(ST_SECTION_RAW TSRMLS_CC); + } else { + yy_push_state(ST_SECTION_VALUE TSRMLS_CC); + } + return TC_SECTION; +} + +<ST_VALUE,ST_SECTION_VALUE,ST_OFFSET>"'"{SINGLE_QUOTED_CHARS}+"'" { /* Raw string */ + /* Eat leading and trailing single quotes */ + if (yytext[0] == '\'' && yytext[yyleng - 1] == '\'') { + yytext++; + yyleng = yyleng - 2; + yytext[yyleng] = 0; + } + RETURN_TOKEN(TC_RAW, yytext, yyleng); } -<INITIAL>[ ]*("true"|"on"|"yes")[ ]* { - Z_STRVAL_P(ini_lval) = zend_strndup("1", 1); - Z_STRLEN_P(ini_lval) = 1; - Z_TYPE_P(ini_lval) = IS_STRING; - return CFG_TRUE; +<ST_SECTION_RAW,ST_SECTION_VALUE>"]"{TABS_AND_SPACES}*{NEWLINE}? { /* End of section */ + BEGIN(INITIAL); + SCNG(lineno)++; + return ']'; } +<INITIAL>{LABEL}"["{TABS_AND_SPACES}* { /* Start of option with offset */ + /* Eat trailing whitespace and [ */ + EAT_TRAILING_WHITESPACE_EX('['); -<INITIAL>[ ]*("false"|"off"|"no"|"none"|"null")[ ]* { - Z_STRVAL_P(ini_lval) = zend_strndup("", 0); - Z_STRLEN_P(ini_lval) = 0; - Z_TYPE_P(ini_lval) = IS_STRING; - return CFG_FALSE; + /* Enter offset lookup state */ + yy_push_state(ST_OFFSET TSRMLS_CC); + + RETURN_TOKEN(TC_OFFSET, yytext, yyleng); } -<INITIAL>[[][^\]\n]+[\]][ ]*{NEWLINE}? { - /* SECTION */ +<ST_OFFSET>{TABS_AND_SPACES}*"]" { /* End of section or an option offset */ + BEGIN(INITIAL); + return ']'; +} - /* eat trailing ] and spaces */ - while (yyleng>0 && (yytext[yyleng-1]=='\n' || yytext[yyleng-1]=='\r' || yytext[yyleng-1]==']' || yytext[yyleng-1]==' ')) { - yyleng--; - yytext[yyleng]=0; - } +<ST_DOUBLE_QUOTES,ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>"${" { /* Variable start */ + yy_push_state(ST_VARNAME TSRMLS_CC); + return TC_DOLLAR_CURLY; +} - SCNG(lineno)++; +<ST_VARNAME>{LABEL} { /* Variable name */ + RETURN_TOKEN(TC_VARNAME, yytext, yyleng); +} - /* eat leading [ */ - 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!) */ + RETURN_TOKEN(BOOL_TRUE, "1", 1); +} - Z_STRVAL_P(ini_lval) = zend_strndup(yytext, yyleng); - Z_STRLEN_P(ini_lval) = yyleng; - Z_TYPE_P(ini_lval) = IS_STRING; - return SECTION; +<INITIAL,ST_VALUE>("false"|"off"|"no"|"none"|"null"){TABS_AND_SPACES}* { /* FALSE value (when used outside option value/offset this causes parse error!)*/ + RETURN_TOKEN(BOOL_FALSE, "", 0); } -<INITIAL>["][^"]*["] { - char *p = yytext; +<INITIAL,ST_OFFSET>{LABEL} { /* Get option name or option offset value */ + RETURN_TOKEN(TC_STRING, yytext, yyleng); +} - /* ENCAPSULATED TC_STRING */ +<INITIAL>{TABS_AND_SPACES}*[=]{TABS_AND_SPACES}* { /* Start option value */ + if (SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW) { + yy_push_state(ST_RAW TSRMLS_CC); + } else { + yy_push_state(ST_VALUE TSRMLS_CC); + } + return '='; +} - while ((p = strpbrk(p, "\r\n"))) { - if (*p == '\r' && *(p + 1) == '\n') { - p++; - } - SCNG(lineno)++; - p++; +<ST_RAW>{RAW_VALUE_CHARS}+ { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */ + /* Eat leading and trailing double quotes */ + if (yytext[0] == '"' && yytext[yyleng - 1] == '"') { + yytext++; + yyleng = yyleng - 2; + yytext[yyleng] = 0; } + RETURN_TOKEN(TC_RAW, yytext, yyleng); +} - /* eat trailing " */ - yytext[yyleng-1]=0; +<ST_SECTION_RAW>{SECTION_RAW_CHARS}+ { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */ + RETURN_TOKEN(TC_RAW, yytext, yyleng); +} - /* eat leading " */ - yytext++; +<ST_VALUE,ST_RAW>{NEWLINE} { /* End of option value */ + BEGIN(INITIAL); + SCNG(lineno)++; + return END_OF_LINE; +} - Z_STRVAL_P(ini_lval) = zend_strndup(yytext, yyleng - 2); - Z_STRLEN_P(ini_lval) = yyleng - 2; - Z_TYPE_P(ini_lval) = IS_STRING; - return TC_ENCAPSULATED_STRING; +<ST_VALUE,ST_OFFSET>{CONSTANT} { /* Get constant option value */ + RETURN_TOKEN(TC_STRING, yytext, yyleng); } -<INITIAL>"${" { - return TC_DOLLAR_CURLY; +<ST_VALUE,ST_OFFSET>{NUMBER} { /* Get number option value as string */ + RETURN_TOKEN(TC_NUMBER, yytext, yyleng); } -<INITIAL>"}" { - Z_LVAL_P(ini_lval) = (long) yytext[0]; +<INITIAL>{TOKENS} { /* Disallow these chars outside option values */ return yytext[0]; } -<INITIAL>[&|~$(){}!] { +<ST_VALUE>{OPERATORS}{TABS_AND_SPACES}* { /* Boolean operators */ return yytext[0]; } -<INITIAL>[^=\n\r\t;|&$~(){}!"\[]+ { - /* STRING */ - register int i; +<ST_VALUE>[=] { /* Make = used in option value to trigger error */ + yyless(yyleng - 1); + BEGIN(INITIAL); + return END_OF_LINE; +} - /* eat trailing whitespace */ - for (i=yyleng-1; i>=0; i--) { - if (yytext[i]==' ' || yytext[i]=='\t') { - yytext[i]=0; - yyleng--; - } else { - break; - } - } - /* eat leading whitespace */ - while (yytext[0]) { - if (yytext[0]==' ' || yytext[0]=='\t') { - yytext++; - yyleng--; - } else { - break; - } - } - if (yyleng!=0) { - Z_STRVAL_P(ini_lval) = zend_strndup(yytext, yyleng); - Z_STRLEN_P(ini_lval) = yyleng; - Z_TYPE_P(ini_lval) = IS_STRING; - return TC_STRING; - } else { - /* whitespace */ - } +<ST_VALUE>{VALUE_CHARS}+ { /* Get everything else as option/offset value */ + /* Eat trailing tabs and spaces */ + EAT_TRAILING_WHITESPACE(); + RETURN_TOKEN(TC_STRING, yytext, yyleng); } -<INITIAL>[=\n] { - if (yytext[0] == '\n') { - SCNG(lineno)++; - } - return yytext[0]; +<ST_SECTION_VALUE,ST_OFFSET>{SECTION_VALUE_CHARS}+ { /* Get rest as section/offset value */ + RETURN_TOKEN(TC_STRING, yytext, yyleng); } -<INITIAL>{NEWLINE} { - SCNG(lineno)++; - return '\n'; +<ST_SECTION_VALUE,ST_VALUE,ST_OFFSET>["] { /* Double quoted '"' string start */ + yy_push_state(ST_DOUBLE_QUOTES TSRMLS_CC); + return '"'; } -<INITIAL>[;][^\r\n]*{NEWLINE}? { - /* comment */ - SCNG(lineno)++; - return '\n'; +<ST_DOUBLE_QUOTES>{DOUBLE_QUOTES_CHARS}+ { /* Escape double quoted string contents */ + zend_ini_escape_string(ini_lval, yytext, yyleng, '"' TSRMLS_CC); + return TC_QUOTED_STRING; +} + +<ST_DOUBLE_QUOTES>["] { /* Double quoted '"' string ends */ + yy_pop_state(TSRMLS_C); + return '"'; } -<INITIAL>[ \t] { +<INITIAL,ST_VALUE,ST_RAW,ST_OFFSET>{TABS_AND_SPACES} { /* eat whitespace */ } -<INITIAL>. { -#if DEBUG - php_error(E_NOTICE,"Unexpected character on line %d: '%s' (ASCII %d)\n", yylineno, yytext, yytext[0]); -#endif +<INITIAL>{NEWLINE} { + SCNG(lineno)++; + return END_OF_LINE; +} + +<INITIAL,ST_VALUE,ST_RAW>[;][^\r\n]*{NEWLINE} { /* Comment */ + BEGIN(INITIAL); + SCNG(lineno)++; + return END_OF_LINE; +} + +<ST_VALUE,ST_RAW><<EOF>> { /* End of option value (if EOF is reached before EOL */ + BEGIN(INITIAL); + return END_OF_LINE; } <<EOF>> { - yy_delete_buffer(YY_CURRENT_BUFFER TSRMLS_CC); +#if DEBUG_CFG_SCANNER + while (YYSTATE != INITIAL) { + switch (YYSTATE) { + case INITIAL: + break; + + case ST_DOUBLE_QUOTES: + fprintf(stderr, "ERROR: Unterminated ini option value double quotes\n"); + break; + + case ST_OFFSET: + fprintf(stderr, "ERROR: Unterminated ini option offset\n"); + break; + + case ST_RAW: + fprintf(stderr, "ERROR: Unterminated raw ini option value\n"); + break; + + case ST_SECTION_RAW: + fprintf(stderr, "ERROR: Unterminated raw ini section value\n"); + break; + + case ST_SECTION_VALUE: + fprintf(stderr, "ERROR: Unterminated ini section value\n"); + break; + + case ST_VALUE: + fprintf(stderr, "ERROR: Unterminated ini option value\n"); + break; + + case ST_VARNAME: + fprintf(stderr, "ERROR: Unterminated ini variable\n"); + break; + + default: + fprintf(stderr, "BUG: Unknown state (%d)\n", YYSTATE); + break; + } + yy_pop_state(TSRMLS_C); + } +#endif yyterminate(); } diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index ce5c866a4c..358904919a 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -41,6 +41,7 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE; #include "zend.h" +#include "zend_ini_scanner.h" #include "zend_language_scanner.h" #include <zend_language_parser.h> @@ -947,8 +948,15 @@ static ZEND_BEGIN_ARG_INFO_EX(arginfo_parse_ini_file, 0, 0, 1) ZEND_ARG_INFO(0, filename) ZEND_ARG_INFO(0, process_sections) + ZEND_ARG_INFO(0, scanner_mode) ZEND_END_ARG_INFO() +#if ZEND_DEBUG +static +ZEND_BEGIN_ARG_INFO(arginfo_dump_config_hash, 0) +ZEND_END_ARG_INFO() +#endif + static ZEND_BEGIN_ARG_INFO_EX(arginfo_import_request_variables, 0, 0, 1) ZEND_ARG_INFO(0, types) @@ -1405,6 +1413,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_fnmatch, 0, 0, 2) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() #endif + static ZEND_BEGIN_ARG_INFO(arginfo_sys_get_temp_dir, 0) ZEND_END_ARG_INFO() @@ -3384,7 +3393,7 @@ const zend_function_entry basic_functions[] = { PHP_FE(set_magic_quotes_runtime, NULL) PHP_FE(get_magic_quotes_gpc, NULL) PHP_FE(get_magic_quotes_runtime, NULL) - + PHP_FE(import_request_variables, arginfo_import_request_variables) PHP_FE(error_log, arginfo_error_log) PHP_FE(error_get_last, arginfo_error_get_last) @@ -3430,6 +3439,9 @@ const zend_function_entry basic_functions[] = { PHP_FE(connection_status, arginfo_connection_status) PHP_FE(ignore_user_abort, arginfo_ignore_user_abort) PHP_FE(parse_ini_file, arginfo_parse_ini_file) +#if ZEND_DEBUG + PHP_FE(dump_config_hash, arginfo_dump_config_hash) +#endif PHP_FE(is_uploaded_file, arginfo_is_uploaded_file) PHP_FE(move_uploaded_file, arginfo_move_uploaded_file) @@ -3988,6 +4000,9 @@ PHP_MINIT_FUNCTION(basic) REGISTER_LONG_CONSTANT("INI_SYSTEM", ZEND_INI_SYSTEM, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("INI_ALL", ZEND_INI_ALL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("INI_SCANNER_NORMAL", ZEND_INI_SCANNER_NORMAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("INI_SCANNER_RAW", ZEND_INI_SCANNER_RAW, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("PHP_URL_SCHEME", PHP_URL_SCHEME, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PHP_URL_HOST", PHP_URL_HOST, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PHP_URL_PORT", PHP_URL_PORT, CONST_CS | CONST_PERSISTENT); @@ -4847,12 +4862,34 @@ PHP_FUNCTION(get_current_user) } /* }}} */ -/* {{{ proto string get_cfg_var(string option_name) +/* {{{ add_config_entry_cb + */ +static int add_config_entry_cb(zval *entry, int num_args, va_list args, zend_hash_key *hash_key TSRMLS_DC) +{ + zval *retval = (zval *) va_arg(args, int); + zval *tmp; + + if (Z_TYPE_P(entry) == IS_STRING) { + if (hash_key->nKeyLength > 0) { + add_assoc_stringl_ex(retval, hash_key->arKey, hash_key->nKeyLength, Z_STRVAL_P(entry), Z_STRLEN_P(entry), 1); + } else { + add_index_stringl(retval, hash_key->h, Z_STRVAL_P(entry), Z_STRLEN_P(entry), 1); + } + } else if (Z_TYPE_P(entry) == IS_ARRAY) { + MAKE_STD_ZVAL(tmp); + array_init(tmp); + zend_hash_apply_with_arguments(Z_ARRVAL_P(entry), (apply_func_args_t) add_config_entry_cb, 1, tmp TSRMLS_CC); + add_assoc_zval_ex(retval, hash_key->arKey, hash_key->nKeyLength, tmp); + } + return 0; +} +/* }}} */ + +/* {{{ proto mixed get_cfg_var(string option_name) Get the value of a PHP configuration option */ PHP_FUNCTION(get_cfg_var) { - zval **varname; - char *value; + zval **varname, *retval; if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &varname) == FAILURE) { WRONG_PARAM_COUNT; @@ -4860,10 +4897,19 @@ PHP_FUNCTION(get_cfg_var) convert_to_string_ex(varname); - if (cfg_get_string(Z_STRVAL_PP(varname), &value) == FAILURE) { + retval = cfg_get_entry(Z_STRVAL_PP(varname), Z_STRLEN_PP(varname) + 1); + + if (retval) { + if (Z_TYPE_P(retval) == IS_ARRAY) { + array_init(return_value); + zend_hash_apply_with_arguments(Z_ARRVAL_P(retval), (apply_func_args_t) add_config_entry_cb, 1, return_value TSRMLS_CC); + return; + } else { + RETURN_STRING(Z_STRVAL_P(retval), 1); + } + } else { RETURN_FALSE; } - RETURN_STRING(value, 1); } /* }}} */ @@ -5576,7 +5622,7 @@ PHP_FUNCTION(ini_get) convert_to_string_ex(varname); - str = zend_ini_string(Z_STRVAL_PP(varname), Z_STRLEN_PP(varname)+1, 0); + str = zend_ini_string(Z_STRVAL_PP(varname), Z_STRLEN_PP(varname) + 1, 0); if (!str) { RETURN_FALSE; @@ -5713,8 +5759,7 @@ PHP_FUNCTION(ini_set) } } - if (zend_alter_ini_entry(Z_STRVAL_PP(varname), Z_STRLEN_PP(varname)+1, Z_STRVAL_PP(new_value), Z_STRLEN_PP(new_value), - PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) { + if (zend_alter_ini_entry_ex(Z_STRVAL_PP(varname), Z_STRLEN_PP(varname) + 1, Z_STRVAL_PP(new_value), Z_STRLEN_PP(new_value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == FAILURE) { zval_dtor(return_value); RETURN_FALSE; } @@ -5739,7 +5784,6 @@ PHP_FUNCTION(ini_restore) /* {{{ proto string set_include_path(string new_include_path) Sets the include_path configuration option */ - PHP_FUNCTION(set_include_path) { zval **new_value; @@ -5756,19 +5800,15 @@ PHP_FUNCTION(set_include_path) } else { RETVAL_FALSE; } - if (zend_alter_ini_entry("include_path", sizeof("include_path"), - Z_STRVAL_PP(new_value), Z_STRLEN_PP(new_value), - PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) { + if (zend_alter_ini_entry_ex("include_path", sizeof("include_path"), Z_STRVAL_PP(new_value), Z_STRLEN_PP(new_value), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == FAILURE) { zval_dtor(return_value); RETURN_FALSE; } } - /* }}} */ /* {{{ proto string get_include_path() Get the current include_path configuration option */ - PHP_FUNCTION(get_include_path) { char *str; @@ -5781,22 +5821,18 @@ PHP_FUNCTION(get_include_path) } RETURN_STRING(str, 1); } - /* }}} */ /* {{{ proto void restore_include_path() Restore the value of the include_path configuration option */ - PHP_FUNCTION(restore_include_path) { if (ZEND_NUM_ARGS() != 0) { WRONG_PARAM_COUNT; } - zend_restore_ini_entry("include_path", sizeof("include_path"), - PHP_INI_STAGE_RUNTIME); + zend_restore_ini_entry("include_path", sizeof("include_path"), PHP_INI_STAGE_RUNTIME); } - /* }}} */ /* {{{ proto mixed print_r(mixed var [, bool return]) @@ -5861,7 +5897,7 @@ PHP_FUNCTION(ignore_user_abort) RETURN_FALSE; } convert_to_string_ex(arg); - zend_alter_ini_entry("ignore_user_abort", sizeof("ignore_user_abort"), Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), PHP_INI_USER, PHP_INI_STAGE_RUNTIME); + zend_alter_ini_entry_ex("ignore_user_abort", sizeof("ignore_user_abort"), Z_STRVAL_PP(arg), Z_STRLEN_PP(arg), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC); break; default: @@ -6116,7 +6152,7 @@ PHP_FUNCTION(move_uploaded_file) /* {{{ php_simple_ini_parser_cb */ -static void php_simple_ini_parser_cb(zval *arg1, zval *arg2, int callback_type, zval *arr) +static void php_simple_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, zval *arr TSRMLS_DC) { zval *element; @@ -6146,11 +6182,11 @@ static void php_simple_ini_parser_cb(zval *arg1, zval *arg2, int callback_type, if (!(Z_STRLEN_P(arg1) > 1 && Z_STRVAL_P(arg1)[0] == '0') && is_numeric_string(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1), NULL, NULL, 0) == IS_LONG) { ulong key = (ulong) zend_atoi(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)); if (zend_hash_index_find(Z_ARRVAL_P(arr), key, (void **) &find_hash) == FAILURE) { - ALLOC_ZVAL(hash); - INIT_PZVAL(hash); - array_init(hash); + ALLOC_ZVAL(hash); + INIT_PZVAL(hash); + array_init(hash); - zend_hash_index_update(Z_ARRVAL_P(arr), key, &hash, sizeof(zval *), NULL); + zend_hash_index_update(Z_ARRVAL_P(arr), key, &hash, sizeof(zval *), NULL); } else { hash = *find_hash; } @@ -6176,7 +6212,12 @@ static void php_simple_ini_parser_cb(zval *arg1, zval *arg2, int callback_type, *element = *arg2; zval_copy_ctor(element); INIT_PZVAL(element); - add_next_index_zval(hash, element); + + if (arg3 && Z_STRLEN_P(arg3) > 0) { + add_assoc_zval_ex(hash, Z_STRVAL_P(arg3), Z_STRLEN_P(arg3) + 1, element); + } else { + add_next_index_zval(hash, element); + } } break; @@ -6188,10 +6229,8 @@ static void php_simple_ini_parser_cb(zval *arg1, zval *arg2, int callback_type, /* {{{ php_ini_parser_cb_with_sections */ -static void php_ini_parser_cb_with_sections(zval *arg1, zval *arg2, int callback_type, zval *arr) +static void php_ini_parser_cb_with_sections(zval *arg1, zval *arg2, zval *arg3, int callback_type, zval *arr TSRMLS_DC) { - TSRMLS_FETCH(); - if (callback_type == ZEND_INI_PARSER_SECTION) { MAKE_STD_ZVAL(BG(active_ini_file_section)); array_init(BG(active_ini_file_section)); @@ -6205,59 +6244,62 @@ static void php_ini_parser_cb_with_sections(zval *arg1, zval *arg2, int callback active_arr = arr; } - php_simple_ini_parser_cb(arg1, arg2, callback_type, active_arr); + php_simple_ini_parser_cb(arg1, arg2, arg3, callback_type, active_arr TSRMLS_CC); } } /* }}} */ -/* {{{ proto array parse_ini_file(string filename [, bool process_sections]) +/* {{{ proto array parse_ini_file(string filename [, bool process_sections [, int scanner_mode]]) Parse configuration file */ PHP_FUNCTION(parse_ini_file) { - zval **filename, **process_sections; + char *filename = NULL; + int filename_len = 0; + zend_bool process_sections = 0; + long scanner_mode = ZEND_INI_SCANNER_NORMAL; zend_file_handle fh; zend_ini_parser_cb_t ini_parser_cb; - switch (ZEND_NUM_ARGS()) { - - case 1: - if (zend_get_parameters_ex(1, &filename) == FAILURE) { - RETURN_FALSE; - } - ini_parser_cb = (zend_ini_parser_cb_t) php_simple_ini_parser_cb; - break; - - case 2: - if (zend_get_parameters_ex(2, &filename, &process_sections) == FAILURE) { - RETURN_FALSE; - } - - convert_to_boolean_ex(process_sections); - - if (Z_BVAL_PP(process_sections)) { - BG(active_ini_file_section) = NULL; - ini_parser_cb = (zend_ini_parser_cb_t) php_ini_parser_cb_with_sections; - } else { - ini_parser_cb = (zend_ini_parser_cb_t) php_simple_ini_parser_cb; - } - break; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &filename, &filename_len, &process_sections, &scanner_mode) == FAILURE) { + RETURN_FALSE; + } - default: - ZEND_WRONG_PARAM_COUNT(); - break; + if (filename_len == 0) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filename can not be empty!"); + RETURN_FALSE; } - convert_to_string_ex(filename); + /* Set callback function */ + if (process_sections) { + BG(active_ini_file_section) = NULL; + ini_parser_cb = (zend_ini_parser_cb_t) php_ini_parser_cb_with_sections; + } else { + ini_parser_cb = (zend_ini_parser_cb_t) php_simple_ini_parser_cb; + } + /* Setup filehandle */ memset(&fh, 0, sizeof(fh)); - fh.filename = Z_STRVAL_PP(filename); - Z_TYPE(fh) = ZEND_HANDLE_FILENAME; + fh.filename = filename; + fh.type = ZEND_HANDLE_FILENAME; array_init(return_value); - zend_parse_ini_file(&fh, 0, ini_parser_cb, return_value); + zend_parse_ini_file(&fh, 0, scanner_mode, ini_parser_cb, return_value TSRMLS_CC); } /* }}} */ +#if ZEND_DEBUG +/* {{{ proto void dump_config_hash(void) + */ +PHP_FUNCTION(dump_config_hash) +{ + HashTable hash = get_configuration_hash(); + + array_init(return_value); + zend_hash_apply_with_arguments(&hash, (apply_func_args_t) add_config_entry_cb, 1, return_value TSRMLS_CC); +} +/* }}} */ +#endif + static int copy_request_variable(void *pDest, int num_args, va_list args, zend_hash_key *hash_key) { char *prefix, *new_key; diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h index 549e3acdf1..bda93e3c67 100644 --- a/ext/standard/basic_functions.h +++ b/ext/standard/basic_functions.h @@ -125,6 +125,9 @@ PHP_FUNCTION(move_uploaded_file); /* From the INI parser */ PHP_FUNCTION(parse_ini_file); +#if ZEND_DEBUG +PHP_FUNCTION(dump_config_hash); +#endif PHP_FUNCTION(str_rot13); PHP_FUNCTION(stream_get_filters); diff --git a/ext/standard/browscap.c b/ext/standard/browscap.c index 1c5bd70ba4..1d68ad141d 100644 --- a/ext/standard/browscap.c +++ b/ext/standard/browscap.c @@ -23,11 +23,12 @@ #include "php_browscap.h" #include "php_ini.h" #include "php_string.h" - +#include "zend_ini_scanner.h" #include "zend_globals.h" static HashTable browser_hash; static zval *current_section; +static char *current_section_name; #define DEFAULT_SECTION_NAME "Default Browser Capability Settings" @@ -88,7 +89,7 @@ static void convert_browscap_pattern(zval *pattern) /* {{{ php_browscap_parser_cb */ -static void php_browscap_parser_cb(zval *arg1, zval *arg2, int callback_type, void *arg) +static void php_browscap_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC) { if (!arg1) { return; @@ -100,12 +101,37 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, int callback_type, vo zval *new_property; char *new_key; + /* parent entry can not be same as current section -> causes infinite loop! */ + if (!strcasecmp(Z_STRVAL_P(arg1), "parent") && + !strcasecmp(current_section_name, Z_STRVAL_P(arg2)) + ) { + zend_error(E_CORE_ERROR, "Invalid browscap ini file: 'Parent' value can not be same as the section name: %s (in file %s)", current_section_name, INI_STR("browscap")); + return; + } + new_property = (zval *) pemalloc(sizeof(zval), 1); INIT_PZVAL(new_property); - Z_STRVAL_P(new_property) = zend_strndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2)); - Z_STRLEN_P(new_property) = Z_STRLEN_P(arg2); Z_TYPE_P(new_property) = IS_STRING; + /* Set proper value for true/false settings */ + if ((Z_STRLEN_P(arg2) == 2 && !strncasecmp(Z_STRVAL_P(arg2), "on", sizeof("on") - 1)) || + (Z_STRLEN_P(arg2) == 3 && !strncasecmp(Z_STRVAL_P(arg2), "yes", sizeof("yes") - 1)) || + (Z_STRLEN_P(arg2) == 4 && !strncasecmp(Z_STRVAL_P(arg2), "true", sizeof("true") - 1)) + ) { + Z_STRVAL_P(new_property) = zend_strndup("1", 1); + Z_STRLEN_P(new_property) = 1; + } else if ( + (Z_STRLEN_P(arg2) == 2 && !strncasecmp(Z_STRVAL_P(arg2), "no", sizeof("no") - 1)) || + (Z_STRLEN_P(arg2) == 3 && !strncasecmp(Z_STRVAL_P(arg2), "off", sizeof("off") - 1)) || + (Z_STRLEN_P(arg2) == 4 && !strncasecmp(Z_STRVAL_P(arg2), "none", sizeof("none") - 1)) || + (Z_STRLEN_P(arg2) == 5 && !strncasecmp(Z_STRVAL_P(arg2), "false", sizeof("false") - 1)) + ) { + Z_STRVAL_P(new_property) = zend_strndup("", 0); + Z_STRLEN_P(new_property) = 0; + } else { /* Other than true/false setting */ + Z_STRVAL_P(new_property) = zend_strndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2)); + Z_STRLEN_P(new_property) = Z_STRLEN_P(arg2); + } new_key = zend_strndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)); zend_str_tolower(new_key, Z_STRLEN_P(arg1)); zend_hash_update(Z_ARRVAL_P(current_section), new_key, Z_STRLEN_P(arg1)+1, &new_property, sizeof(zval *), NULL); @@ -127,8 +153,10 @@ static void php_browscap_parser_cb(zval *arg1, zval *arg2, int callback_type, vo section_properties = (HashTable *) pemalloc(sizeof(HashTable), 1); zend_hash_init(section_properties, 0, NULL, (dtor_func_t) browscap_entry_dtor, 1); - current_section->value.ht = section_properties; - current_section->type = IS_ARRAY; + Z_ARRVAL_P(current_section) = section_properties; + Z_TYPE_P(current_section) = IS_ARRAY; + current_section_name = zend_strndup(Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)); + zend_hash_update(&browser_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1)+1, (void *) ¤t_section, sizeof(zval *), NULL); Z_STRVAL_P(processed) = Z_STRVAL_P(arg1); @@ -171,7 +199,7 @@ PHP_MINIT_FUNCTION(browscap) } fh.filename = browscap; Z_TYPE(fh) = ZEND_HANDLE_FP; - zend_parse_ini_file(&fh, 1, (zend_ini_parser_cb_t) php_browscap_parser_cb, &browser_hash); + zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_RAW, (zend_ini_parser_cb_t) php_browscap_parser_cb, &browser_hash TSRMLS_CC); } return SUCCESS; diff --git a/ext/standard/tests/file/parse_ini_file.phpt b/ext/standard/tests/file/parse_ini_file.phpt index 741b03af8d..dab07e56e7 100644 --- a/ext/standard/tests/file/parse_ini_file.phpt +++ b/ext/standard/tests/file/parse_ini_file.phpt @@ -50,98 +50,98 @@ Hex_Value2 = 0x103 [Non-alphanumerics_as_values] ;Non-alpha numeric chars without quotes -Non-alpha1 = ; -Non-alpha2 = + -Non-alpha3 = * -Non-alpha4 = % -Non-alpha5 = <> -Non-alpha6 = @ -Non-alpha7 = # -Non-alpha8 = ^ -non-alpha9 = - -Non-alpha10 = : -Non-alpha11 = ? -Non-alpha12 = / -Non-alpha13 = \ +Non_alpha1 = ; +Non_alpha2 = + +Non_alpha3 = * +Non_alpha4 = % +Non_alpha5 = <> +Non_alpha6 = @ +Non_alpha7 = # +Non_alpha8 = ^ +Non_alpha9 = - +Non_alpha10 = : +Non_alpha11 = ? +Non_alpha12 = / +Non_alpha13 = \ ;These chars have a special meaning when used in the value, ; hence parser throws an error -;Non-alpha14 = & -;Non-alpha15 = {} -;Non-alpha16 = | -;Non-alpha17 = ~ -;Non-alpha18 = ! -;Non-alpha19 = $ -;Non-alpha20 = () - -Non-alpha1_quotes = ";" -Non-alpha2_quotes = "+" -Non-alpha3_quotes = "*" -Non-alpha4_quotes = "%" -Non-alpha5_quotes = "<>" -Non-alpha6_quotes = "@" -Non-alpha7_quotes = "#" -Non-alpha8_quotes = "^" -Non-alpha9_quotes = "-" -Non-alpha10_quotes = "=" -Non-alpha11_quotes = ":" -Non-alpha12_quotes = "?" -Non-alpha13_quotes = "/" -Non-alpha14_quotes = "\" -Non-alpha15_quotes = "&" -Non-alpha16_quotes = "{}" -Non-alpha17_quotes = "|" -Non-alpha18_quotes = "~" -Non-alpha19_quotes = "!" -non-alpha20_quotes = "$" -non-alpha21_quotes = "()" +;Non_alpha14 = & +;Non_alpha15 = {} +;Non_alpha16 = | +;Non_alpha17 = ~ +;Non_alpha18 = ! +;Non_alpha19 = $ +;Non_alpha20 = () + +Non_alpha1_quotes = ";" +Non_alpha2_quotes = "+" +Non_alpha3_quotes = "*" +Non_alpha4_quotes = "%" +Non_alpha5_quotes = "<>" +Non_alpha6_quotes = "@" +Non_alpha7_quotes = "#" +Non_alpha8_quotes = "^" +Non_alpha9_quotes = "-" +Non_alpha10_quotes = "=" +Non_alpha11_quotes = ":" +Non_alpha12_quotes = "?" +Non_alpha13_quotes = "/" +;Non_alpha14_quotes = "\" +Non_alpha15_quotes = "&" +Non_alpha16_quotes = "{}" +Non_alpha17_quotes = "|" +Non_alpha18_quotes = "~" +Non_alpha19_quotes = "!" +;Non_alpha20_quotes = "$" +Non_alpha21_quotes = "()" [Non-alpha numerics in strings] ;expected error, as the non-alphanumeric chars not enclosed in double quotes("") -Non-alpha_string1 = Hello@world -;Non-alpha_string2 = Hello!world -;Non-alpha_string3 = Hello#world -;Non-alpha_string4 = Hello%world -;Non-alpha_string5 = Hello&world -;Non-alpha_string6 = Hello*world -;Non-alpha_string7 = Hello+world -;Non-alpha_string8 = Hello-world -;Non-alpha_string9 = Hello'world -;Non-alpha_string10 = Hello:world -;Non-alpha_string11 = Hello;world -;Non-alpha_string12 = Hello<world -;Non-alpha_string13 = Hello>world -;Non-alpha_string14 = Hello>world -;Non-alpha_string15 = Hello?world -;Non-alpha_string16 = Hello\world -;Non-alpha_string17 = Hello^world -;Non-alpha_string18 = Hello_world -;Non-alpha_string19 = Hello|world -;Non-alpha_string20 = Hello~world -;Non-alpha_string21 = Hello`world -;Non-alpha_string22 = Hello(world) +Non_alpha_string1 = Hello@world +;Non_alpha_string2 = Hello!world +;Non_alpha_string3 = Hello#world +;Non_alpha_string4 = Hello%world +;Non_alpha_string5 = Hello&world +;Non_alpha_string6 = Hello*world +;Non_alpha_string7 = Hello+world +;Non_alpha_string8 = Hello-world +;Non_alpha_string9 = Hello'world +;Non_alpha_string10 = Hello:world +;Non_alpha_string11 = Hello;world +;Non_alpha_string12 = Hello<world +;Non_alpha_string13 = Hello>world +;Non_alpha_string14 = Hello>world +;Non_alpha_string15 = Hello?world +;Non_alpha_string16 = Hello\world +;Non_alpha_string17 = Hello^world +;Non_alpha_string18 = Hello_world +;Non_alpha_string19 = Hello|world +;Non_alpha_string20 = Hello~world +;Non_alpha_string21 = Hello`world +;Non_alpha_string22 = Hello(world) [Non-alpha numerics in strings -with quotes] -Non-alpha_string1_quotes = "Hello@world" -Non-alpha_string2_quotes = "Hello!world" -Non-alpha_string3_quotes = "Hello#world" -Non-alpha_string4_quotes = "Hello&world" -Non-alpha_string5_quotes = "Hello*world" -Non-alpha_string6_quotes = "Hello+world" -Non-alpha_string7_quotes = "Hello-world" -Non-alpha_string8_quotes = "Hello'world" -Non-alpha_string9_quotes = "Hello:world" -Non-alpha_string10_quotes = "Hello;world" -Non-alpha_string11_quotes = "Hello<world" -Non-alpha_string12_quotes = "Hello>world" -Non-alpha_string13_quotes = "Hello>world" -Non-alpha_string14_quotes = "Hello?world" -Non-alpha_string15_quotes = "Hello\world" -Non-alpha_string16_quotes = "Hello^world" -Non-alpha_string17_quotes = "Hello_world" -Non-alpha_string18_quotes = "Hello|world" -Non-alpha_string19_quotes = "Hello~world" -Non-alpha_string20_quotes = "Hello`world" -Non-alpha_string21_quotes = "Hello(world)" +Non_alpha_string1_quotes = "Hello@world" +Non_alpha_string2_quotes = "Hello!world" +Non_alpha_string3_quotes = "Hello#world" +Non_alpha_string4_quotes = "Hello&world" +Non_alpha_string5_quotes = "Hello*world" +Non_alpha_string6_quotes = "Hello+world" +Non_alpha_string7_quotes = "Hello-world" +Non_alpha_string8_quotes = "Hello'world" +Non_alpha_string9_quotes = "Hello:world" +Non_alpha_string10_quotes = "Hello;world" +Non_alpha_string11_quotes = "Hello<world" +Non_alpha_string12_quotes = "Hello>world" +Non_alpha_string13_quotes = "Hello>world" +Non_alpha_string14_quotes = "Hello?world" +Non_alpha_string15_quotes = "Hello\world" +Non_alpha_string16_quotes = "Hello^world" +Non_alpha_string17_quotes = "Hello_world" +Non_alpha_string18_quotes = "Hello|world" +Non_alpha_string19_quotes = "Hello~world" +Non_alpha_string20_quotes = "Hello`world" +Non_alpha_string21_quotes = "Hello(world)" [Newlines_in_Values] String1 = "Hello, world\nGood Morning" @@ -150,7 +150,7 @@ String2 = "\nHello, world String3 = 'Hello, world\tGood Morning' String4 = "\n" String5 = "\n\n" -String3 = Hello, world\tGood Morning +String6 = Hello, world\tGood Morning [ReservedKeys_as_Values] Key1 = YES @@ -242,74 +242,73 @@ Array [Octal_value] => 0100 [Hex_value1] => 0x101 [Hex_Value2] => 0x103 - [Non-alpha1] => - [Non-alpha2] => + - [Non-alpha3] => * - [Non-alpha4] => % - [Non-alpha5] => <> - [Non-alpha6] => @ - [Non-alpha7] => # - [Non-alpha8] => ^ - [non-alpha9] => - - [Non-alpha10] => : - [Non-alpha11] => ? - [Non-alpha12] => / - [Non-alpha13] => \ - [Non-alpha1_quotes] => ; - [Non-alpha2_quotes] => + - [Non-alpha3_quotes] => * - [Non-alpha4_quotes] => % - [Non-alpha5_quotes] => <> - [Non-alpha6_quotes] => @ - [Non-alpha7_quotes] => # - [Non-alpha8_quotes] => ^ - [Non-alpha9_quotes] => - - [Non-alpha10_quotes] => = - [Non-alpha11_quotes] => : - [Non-alpha12_quotes] => ? - [Non-alpha13_quotes] => / - [Non-alpha14_quotes] => \ - [Non-alpha15_quotes] => & - [Non-alpha16_quotes] => {} - [Non-alpha17_quotes] => | - [Non-alpha18_quotes] => ~ - [Non-alpha19_quotes] => ! - [non-alpha20_quotes] => $ - [non-alpha21_quotes] => () - [Non-alpha_string1] => Hello@world - [Non-alpha_string1_quotes] => Hello@world - [Non-alpha_string2_quotes] => Hello!world - [Non-alpha_string3_quotes] => Hello#world - [Non-alpha_string4_quotes] => Hello&world - [Non-alpha_string5_quotes] => Hello*world - [Non-alpha_string6_quotes] => Hello+world - [Non-alpha_string7_quotes] => Hello-world - [Non-alpha_string8_quotes] => Hello'world - [Non-alpha_string9_quotes] => Hello:world - [Non-alpha_string10_quotes] => Hello;world - [Non-alpha_string11_quotes] => Hello<world - [Non-alpha_string12_quotes] => Hello>world - [Non-alpha_string13_quotes] => Hello>world - [Non-alpha_string14_quotes] => Hello?world - [Non-alpha_string15_quotes] => Hello\world - [Non-alpha_string16_quotes] => Hello^world - [Non-alpha_string17_quotes] => Hello_world - [Non-alpha_string18_quotes] => Hello|world - [Non-alpha_string19_quotes] => Hello~world - [Non-alpha_string20_quotes] => Hello`world - [Non-alpha_string21_quotes] => Hello(world) + [Non_alpha1] => + [Non_alpha2] => + + [Non_alpha3] => * + [Non_alpha4] => % + [Non_alpha5] => <> + [Non_alpha6] => @ + [Non_alpha7] => # + [Non_alpha8] => ^ + [Non_alpha9] => - + [Non_alpha10] => : + [Non_alpha11] => ? + [Non_alpha12] => / + [Non_alpha13] => \ + [Non_alpha1_quotes] => ; + [Non_alpha2_quotes] => + + [Non_alpha3_quotes] => * + [Non_alpha4_quotes] => % + [Non_alpha5_quotes] => <> + [Non_alpha6_quotes] => @ + [Non_alpha7_quotes] => # + [Non_alpha8_quotes] => ^ + [Non_alpha9_quotes] => - + [Non_alpha10_quotes] => = + [Non_alpha11_quotes] => : + [Non_alpha12_quotes] => ? + [Non_alpha13_quotes] => / + [Non_alpha15_quotes] => & + [Non_alpha16_quotes] => {} + [Non_alpha17_quotes] => | + [Non_alpha18_quotes] => ~ + [Non_alpha19_quotes] => ! + [Non_alpha21_quotes] => () + [Non_alpha_string1] => Hello@world + [Non_alpha_string1_quotes] => Hello@world + [Non_alpha_string2_quotes] => Hello!world + [Non_alpha_string3_quotes] => Hello#world + [Non_alpha_string4_quotes] => Hello&world + [Non_alpha_string5_quotes] => Hello*world + [Non_alpha_string6_quotes] => Hello+world + [Non_alpha_string7_quotes] => Hello-world + [Non_alpha_string8_quotes] => Hello'world + [Non_alpha_string9_quotes] => Hello:world + [Non_alpha_string10_quotes] => Hello;world + [Non_alpha_string11_quotes] => Hello<world + [Non_alpha_string12_quotes] => Hello>world + [Non_alpha_string13_quotes] => Hello>world + [Non_alpha_string14_quotes] => Hello?world + [Non_alpha_string15_quotes] => Hello\world + [Non_alpha_string16_quotes] => Hello^world + [Non_alpha_string17_quotes] => Hello_world + [Non_alpha_string18_quotes] => Hello|world + [Non_alpha_string19_quotes] => Hello~world + [Non_alpha_string20_quotes] => Hello`world + [Non_alpha_string21_quotes] => Hello(world) [String1] => Hello, world Good Morning [String2] => Hello, world Good Morning - [String3] => Hello, worldGood Morning + [String3] => Hello, world Good Morning [String4] => [String5] => + [String6] => Hello, world Good Morning [Key1] => 1 [Key2] => 1 [Key3] => 1 @@ -379,70 +378,68 @@ Array [Non-alphanumerics_as_values] => Array ( - [Non-alpha1] => - [Non-alpha2] => + - [Non-alpha3] => * - [Non-alpha4] => % - [Non-alpha5] => <> - [Non-alpha6] => @ - [Non-alpha7] => # - [Non-alpha8] => ^ - [non-alpha9] => - - [Non-alpha10] => : - [Non-alpha11] => ? - [Non-alpha12] => / - [Non-alpha13] => \ - [Non-alpha1_quotes] => ; - [Non-alpha2_quotes] => + - [Non-alpha3_quotes] => * - [Non-alpha4_quotes] => % - [Non-alpha5_quotes] => <> - [Non-alpha6_quotes] => @ - [Non-alpha7_quotes] => # - [Non-alpha8_quotes] => ^ - [Non-alpha9_quotes] => - - [Non-alpha10_quotes] => = - [Non-alpha11_quotes] => : - [Non-alpha12_quotes] => ? - [Non-alpha13_quotes] => / - [Non-alpha14_quotes] => \ - [Non-alpha15_quotes] => & - [Non-alpha16_quotes] => {} - [Non-alpha17_quotes] => | - [Non-alpha18_quotes] => ~ - [Non-alpha19_quotes] => ! - [non-alpha20_quotes] => $ - [non-alpha21_quotes] => () + [Non_alpha1] => + [Non_alpha2] => + + [Non_alpha3] => * + [Non_alpha4] => % + [Non_alpha5] => <> + [Non_alpha6] => @ + [Non_alpha7] => # + [Non_alpha8] => ^ + [Non_alpha9] => - + [Non_alpha10] => : + [Non_alpha11] => ? + [Non_alpha12] => / + [Non_alpha13] => \ + [Non_alpha1_quotes] => ; + [Non_alpha2_quotes] => + + [Non_alpha3_quotes] => * + [Non_alpha4_quotes] => % + [Non_alpha5_quotes] => <> + [Non_alpha6_quotes] => @ + [Non_alpha7_quotes] => # + [Non_alpha8_quotes] => ^ + [Non_alpha9_quotes] => - + [Non_alpha10_quotes] => = + [Non_alpha11_quotes] => : + [Non_alpha12_quotes] => ? + [Non_alpha13_quotes] => / + [Non_alpha15_quotes] => & + [Non_alpha16_quotes] => {} + [Non_alpha17_quotes] => | + [Non_alpha18_quotes] => ~ + [Non_alpha19_quotes] => ! + [Non_alpha21_quotes] => () ) [Non-alpha numerics in strings] => Array ( - [Non-alpha_string1] => Hello@world + [Non_alpha_string1] => Hello@world ) [Non-alpha numerics in strings -with quotes] => Array ( - [Non-alpha_string1_quotes] => Hello@world - [Non-alpha_string2_quotes] => Hello!world - [Non-alpha_string3_quotes] => Hello#world - [Non-alpha_string4_quotes] => Hello&world - [Non-alpha_string5_quotes] => Hello*world - [Non-alpha_string6_quotes] => Hello+world - [Non-alpha_string7_quotes] => Hello-world - [Non-alpha_string8_quotes] => Hello'world - [Non-alpha_string9_quotes] => Hello:world - [Non-alpha_string10_quotes] => Hello;world - [Non-alpha_string11_quotes] => Hello<world - [Non-alpha_string12_quotes] => Hello>world - [Non-alpha_string13_quotes] => Hello>world - [Non-alpha_string14_quotes] => Hello?world - [Non-alpha_string15_quotes] => Hello\world - [Non-alpha_string16_quotes] => Hello^world - [Non-alpha_string17_quotes] => Hello_world - [Non-alpha_string18_quotes] => Hello|world - [Non-alpha_string19_quotes] => Hello~world - [Non-alpha_string20_quotes] => Hello`world - [Non-alpha_string21_quotes] => Hello(world) + [Non_alpha_string1_quotes] => Hello@world + [Non_alpha_string2_quotes] => Hello!world + [Non_alpha_string3_quotes] => Hello#world + [Non_alpha_string4_quotes] => Hello&world + [Non_alpha_string5_quotes] => Hello*world + [Non_alpha_string6_quotes] => Hello+world + [Non_alpha_string7_quotes] => Hello-world + [Non_alpha_string8_quotes] => Hello'world + [Non_alpha_string9_quotes] => Hello:world + [Non_alpha_string10_quotes] => Hello;world + [Non_alpha_string11_quotes] => Hello<world + [Non_alpha_string12_quotes] => Hello>world + [Non_alpha_string13_quotes] => Hello>world + [Non_alpha_string14_quotes] => Hello?world + [Non_alpha_string15_quotes] => Hello\world + [Non_alpha_string16_quotes] => Hello^world + [Non_alpha_string17_quotes] => Hello_world + [Non_alpha_string18_quotes] => Hello|world + [Non_alpha_string19_quotes] => Hello~world + [Non_alpha_string20_quotes] => Hello`world + [Non_alpha_string21_quotes] => Hello(world) ) [Newlines_in_Values] => Array @@ -453,12 +450,13 @@ Good Morning Hello, world Good Morning - [String3] => Hello, worldGood Morning + [String3] => Hello, world Good Morning [String4] => [String5] => + [String6] => Hello, world Good Morning ) [ReservedKeys_as_Values] => Array @@ -488,4 +486,4 @@ Hello, world ) ) -*** Done **
\ No newline at end of file +*** Done ** diff --git a/ext/standard/tests/general_functions/parse_ini_basic.data b/ext/standard/tests/general_functions/parse_ini_basic.data new file mode 100644 index 0000000000..cba8f9d70f --- /dev/null +++ b/ext/standard/tests/general_functions/parse_ini_basic.data @@ -0,0 +1,106 @@ +[basic] +basicval = bar +longval = 12345 +with.dot = fooobar +boolon = on +booltrue = true +boolyes = yes +booloff = off +boolfalse = false +boolnone = none +boolno = no +string = asdadfsdjkslkj ¡@£$$ { }[ ]/%#¤ +sqstring = 'adsasdadasdasd' +dqstring = "asdadfsdjkslkj ¡@£$$ { } !^~|¥¥{[()/)&/% ¤ # #" +php_constant = E_ALL + +[basic with whitespace] +basicval = bar +longval = 12345 +with.dot = fooobar +boolon = on +booltrue = true +boolyes = yes +booloff = off +boolfalse = false +boolnone = none +boolno = no +sqstring = 'adsasdadasdasd' +dqstring = "asdadfsdjkslkj ¡@£$$€¥¥{[()/)&/%#¤" +php_constant = E_ALL + +[comments] +; some comment + ; some comment with whitespace +somecomment = comment follows;aaa@bbb ; comment here +; + +[variables] +var1 = ${basicval} +var2 = ${basicval}/foo +var3 = foo/${basicval} +var4 = foo/${basicval}/foo +quoted_var1 = "${basicqval}" +quoted_var2 = "${basicqval}/foo" +quoted_var3 = "foo/${basicqval}" +quoted_var4 = "foo/${basicqval}/foo" + +[offset values] +foo1[] = "basic offset 1" +foo1[ ] = "basic offset 2" +foo2[123] = "long offset" +foo3[abc] = "string offset" +foo4[""] = "quoted offset 1" +foo4[" "] = "quoted offset 2" +foo4["sqfoobar"] = "quoted string offset" +foo4['dqfoobar'] = "single quoted offset" +foo6[${basicval}] = "variable" +foo6[${basicval}/foo] = "variable with string 1" +foo6[foo/${basicval}] = "variable with string 2" +foo6[foo/${basicval}/foo] = "variable with string 3" +foo7["${basicqval}"] = "quoted variable 1" +foo7["${basicqval}/foo"] = "quoted variable 2" +foo7["foo/${basicqval}"] = "quoted variable 3" +foo7[ "foo/${basicqval}/foo" ] = "quoted variable 4" + +[non value] +novalue_option1 = +novalue_option2= +novalue_option3 = +novalue_option4= +novalue_option4[] = +novalue_option4[]= +novalue_option4[]= + +["Quoted strings and variables in sections"] + +[${basicval}] +[${basicval}/foo] +[foo/${basicval}] +[foo/${basicval}/foo] + +["${basicqval}"] +["${basicqval}/foo"] +["foo/${basicqval}"] +["foo/${basicqval}/foo"] + +[PATH=${basicval}/no/quotes] +; Invalid! +;[PATH="${basicval}/path/quoted"] +["PATH=${basicval}/all/quoted"] + +; The rest is from bug #29306 +[01] +e=e +f=f +[02] +g=g +h=h +[1] +a=a +b=b +[2] +c=c +d=d +[0815] +bla=bla diff --git a/ext/standard/tests/general_functions/parse_ini_basic.phpt b/ext/standard/tests/general_functions/parse_ini_basic.phpt new file mode 100644 index 0000000000..7ab95d7dc0 --- /dev/null +++ b/ext/standard/tests/general_functions/parse_ini_basic.phpt @@ -0,0 +1,239 @@ +--TEST-- +parse_ini_file() tests +--ENV-- +basicval=FUBAR_VARIABLE +basicqval=FUBAR_QUOTES_VARIABLE +--FILE-- +<?php + +$ini_file = dirname(__FILE__)."/parse_ini_basic.data"; + +var_dump(parse_ini_file($ini_file, 1)); + +echo "Done.\n"; +?> +--EXPECTF-- +array(22) { + ["basic"]=> + array(14) { + ["basicval"]=> + string(3) "bar" + ["longval"]=> + string(5) "12345" + ["with.dot"]=> + string(7) "fooobar" + ["boolon"]=> + string(1) "1" + ["booltrue"]=> + string(1) "1" + ["boolyes"]=> + string(1) "1" + ["booloff"]=> + string(0) "" + ["boolfalse"]=> + string(0) "" + ["boolnone"]=> + string(0) "" + ["boolno"]=> + string(0) "" + ["string"]=> + string(34) "asdadfsdjkslkj ¡@£$$ { }[ ]/%#¤" + ["sqstring"]=> + string(14) "adsasdadasdasd" + ["dqstring"]=> + string(51) "asdadfsdjkslkj ¡@£$$ { } !^~|¥¥{[()/)&/% ¤ # #" + ["php_constant"]=> + string(4) "6143" + } + ["basic with whitespace"]=> + array(13) { + ["basicval"]=> + string(3) "bar" + ["longval"]=> + string(5) "12345" + ["with.dot"]=> + string(7) "fooobar" + ["boolon"]=> + string(1) "1" + ["booltrue"]=> + string(1) "1" + ["boolyes"]=> + string(1) "1" + ["booloff"]=> + string(0) "" + ["boolfalse"]=> + string(0) "" + ["boolnone"]=> + string(0) "" + ["boolno"]=> + string(0) "" + ["sqstring"]=> + string(14) "adsasdadasdasd" + ["dqstring"]=> + string(41) "asdadfsdjkslkj ¡@£$$€¥¥{[()/)&/%#¤" + ["php_constant"]=> + string(4) "6143" + } + ["comments"]=> + array(1) { + ["somecomment"]=> + string(15) "comment follows" + } + ["variables"]=> + array(8) { + ["var1"]=> + string(14) "FUBAR_VARIABLE" + ["var2"]=> + string(18) "FUBAR_VARIABLE/foo" + ["var3"]=> + string(18) "foo/FUBAR_VARIABLE" + ["var4"]=> + string(22) "foo/FUBAR_VARIABLE/foo" + ["quoted_var1"]=> + string(21) "FUBAR_QUOTES_VARIABLE" + ["quoted_var2"]=> + string(25) "FUBAR_QUOTES_VARIABLE/foo" + ["quoted_var3"]=> + string(25) "foo/FUBAR_QUOTES_VARIABLE" + ["quoted_var4"]=> + string(29) "foo/FUBAR_QUOTES_VARIABLE/foo" + } + ["offset values"]=> + array(6) { + ["foo1"]=> + array(2) { + [0]=> + string(14) "basic offset 1" + [1]=> + string(14) "basic offset 2" + } + ["foo2"]=> + array(1) { + [123]=> + string(11) "long offset" + } + ["foo3"]=> + array(1) { + ["abc"]=> + string(13) "string offset" + } + ["foo4"]=> + array(4) { + [0]=> + string(15) "quoted offset 1" + [" "]=> + string(15) "quoted offset 2" + ["sqfoobar"]=> + string(20) "quoted string offset" + ["dqfoobar"]=> + string(20) "single quoted offset" + } + ["foo6"]=> + array(4) { + ["FUBAR_VARIABLE"]=> + string(8) "variable" + ["FUBAR_VARIABLE/foo"]=> + string(22) "variable with string 1" + ["foo/FUBAR_VARIABLE"]=> + string(22) "variable with string 2" + ["foo/FUBAR_VARIABLE/foo"]=> + string(22) "variable with string 3" + } + ["foo7"]=> + array(4) { + ["FUBAR_QUOTES_VARIABLE"]=> + string(17) "quoted variable 1" + ["FUBAR_QUOTES_VARIABLE/foo"]=> + string(17) "quoted variable 2" + ["foo/FUBAR_QUOTES_VARIABLE"]=> + string(17) "quoted variable 3" + ["foo/FUBAR_QUOTES_VARIABLE/foo"]=> + string(17) "quoted variable 4" + } + } + ["non value"]=> + array(4) { + ["novalue_option1"]=> + string(0) "" + ["novalue_option2"]=> + string(0) "" + ["novalue_option3"]=> + string(0) "" + ["novalue_option4"]=> + array(3) { + [0]=> + string(0) "" + [1]=> + string(0) "" + [2]=> + string(0) "" + } + } + ["Quoted strings and variables in sections"]=> + array(0) { + } + ["FUBAR_VARIABLE"]=> + array(0) { + } + ["FUBAR_VARIABLE/foo"]=> + array(0) { + } + ["foo/FUBAR_VARIABLE"]=> + array(0) { + } + ["foo/FUBAR_VARIABLE/foo"]=> + array(0) { + } + ["FUBAR_QUOTES_VARIABLE"]=> + array(0) { + } + ["FUBAR_QUOTES_VARIABLE/foo"]=> + array(0) { + } + ["foo/FUBAR_QUOTES_VARIABLE"]=> + array(0) { + } + ["foo/FUBAR_QUOTES_VARIABLE/foo"]=> + array(0) { + } + ["PATH=FUBAR_VARIABLE/no/quotes"]=> + array(0) { + } + ["PATH=FUBAR_VARIABLE/all/quoted"]=> + array(0) { + } + ["01"]=> + array(2) { + ["e"]=> + string(1) "e" + ["f"]=> + string(1) "f" + } + ["02"]=> + array(2) { + ["g"]=> + string(1) "g" + ["h"]=> + string(1) "h" + } + [1]=> + array(2) { + ["a"]=> + string(1) "a" + ["b"]=> + string(1) "b" + } + [2]=> + array(2) { + ["c"]=> + string(1) "c" + ["d"]=> + string(1) "d" + } + ["0815"]=> + array(1) { + ["bla"]=> + string(3) "bla" + } +} +Done. diff --git a/ext/standard/tests/general_functions/parse_ini_booleans.data b/ext/standard/tests/general_functions/parse_ini_booleans.data new file mode 100644 index 0000000000..2f1c2af214 --- /dev/null +++ b/ext/standard/tests/general_functions/parse_ini_booleans.data @@ -0,0 +1,27 @@ +[error_reporting values] +foo = E_ALL E_NOTICE +error_reporting = E_ALL +error_reporting1 = E_COMPILE_ERROR|E_RECOVERABLE_ERROR |E_ERROR|E_CORE_ERROR +error_reporting2 = E_ALL&~E_NOTICE +error_reporting3 = E_ALL & ~E_NOTICE +error_reporting4 = E_ALL & ~E_NOTICE | E_STRICT + +[true or false] +bool_true = true +bool_yes = yes +bool_on = on +bool_false=false +bool_off =Off +bool_no=No +bool_none= NoNe +bool_null = NULl + +[strings] +string_true = "true" +string_yes = " yes" +string_on = " on " +string_false="false" +string_off ="Off " +string_no="No " +string_none=" NoNe" +string_null = "NULl" diff --git a/ext/standard/tests/general_functions/parse_ini_booleans.phpt b/ext/standard/tests/general_functions/parse_ini_booleans.phpt new file mode 100644 index 0000000000..401e587ff7 --- /dev/null +++ b/ext/standard/tests/general_functions/parse_ini_booleans.phpt @@ -0,0 +1,69 @@ +--TEST-- +parse_ini_file() boolean operators +--FILE-- +<?php + +$ini_file = dirname(__FILE__)."/parse_ini_booleans.data"; + +var_dump(parse_ini_file($ini_file, 1)); + +echo "Done.\n"; + +?> +--EXPECTF-- +array(3) { + ["error_reporting values"]=> + array(6) { + ["foo"]=> + string(14) "E_ALL E_NOTICE" + ["error_reporting"]=> + string(4) "6143" + ["error_reporting1"]=> + string(4) "4177" + ["error_reporting2"]=> + string(4) "6135" + ["error_reporting3"]=> + string(4) "6135" + ["error_reporting4"]=> + string(4) "8183" + } + ["true or false"]=> + array(8) { + ["bool_true"]=> + string(1) "1" + ["bool_yes"]=> + string(1) "1" + ["bool_on"]=> + string(1) "1" + ["bool_false"]=> + string(0) "" + ["bool_off"]=> + string(0) "" + ["bool_no"]=> + string(0) "" + ["bool_none"]=> + string(0) "" + ["bool_null"]=> + string(0) "" + } + ["strings"]=> + array(8) { + ["string_true"]=> + string(4) "true" + ["string_yes"]=> + string(4) " yes" + ["string_on"]=> + string(5) " on " + ["string_false"]=> + string(5) "false" + ["string_off"]=> + string(4) "Off " + ["string_no"]=> + string(4) "No " + ["string_none"]=> + string(5) " NoNe" + ["string_null"]=> + string(4) "NULl" + } +} +Done. diff --git a/ext/standard/tests/general_functions/parse_ini_file.phpt b/ext/standard/tests/general_functions/parse_ini_file.phpt index 399a224db9..62ed5c79ce 100644 --- a/ext/standard/tests/general_functions/parse_ini_file.phpt +++ b/ext/standard/tests/general_functions/parse_ini_file.phpt @@ -1,12 +1,13 @@ --TEST-- -parse_ini_file() tests +parse_ini_file() multiple calls --FILE-- <?php $filename = dirname(__FILE__)."/parse_ini_file.dat"; +@unlink($filename); /* Make sure the file really does not exist! */ var_dump(parse_ini_file()); -var_dump(parse_ini_file(1,1,1)); +var_dump(parse_ini_file(1,1,1,1)); var_dump(parse_ini_file($filename)); var_dump(parse_ini_file($filename, true)); @@ -15,7 +16,6 @@ test = "; file_put_contents($filename, $ini); var_dump(parse_ini_file($filename)); - $ini = " test== "; @@ -81,7 +81,6 @@ $ini = " "; file_put_contents($filename, $ini); var_dump(parse_ini_file($filename, true)); - $ini = " test=test2 test=test3 @@ -90,22 +89,21 @@ test=test4 file_put_contents($filename, $ini); var_dump(parse_ini_file($filename, true)); - @unlink($filename); echo "Done\n"; ?> --EXPECTF-- -Warning: Wrong parameter count for parse_ini_file() in %s on line %d -NULL +Warning: parse_ini_file() expects at least 1 parameter, 0 given in %sparse_ini_file.php on line 6 +bool(false) -Warning: Wrong parameter count for parse_ini_file() in %s on line %d -NULL +Warning: parse_ini_file() expects at most 3 parameters, 4 given in %sparse_ini_file.php on line 7 +bool(false) -Warning: parse_ini_file(%sparse_ini_file.dat): failed to open stream: No such file or directory in %s on line %d +Warning: parse_ini_file(%sparse_ini_file.dat): failed to open stream: No such file or directory in %sparse_ini_file.php on line 8 array(0) { } -Warning: parse_ini_file(%sparse_ini_file.dat): failed to open stream: No such file or directory in %s on line %d +Warning: parse_ini_file(%sparse_ini_file.dat): failed to open stream: No such file or directory in %sparse_ini_file.php on line 9 array(0) { } array(1) { @@ -113,15 +111,15 @@ array(1) { string(0) "" } -Warning: Error parsing %sparse_ini_file.dat on line 2 - in %s on line %d +Warning: syntax error, unexpected '=' in %sparse_ini_file.dat on line 2 + in %sparse_ini_file.php on line 20 array(1) { ["test"]=> string(0) "" } -Warning: Error parsing %sparse_ini_file.dat on line 2 - in %s on line %d +Warning: syntax error, unexpected '=' in %sparse_ini_file.dat on line 2 + in %sparse_ini_file.php on line 26 array(1) { ["test"]=> string(4) "test" diff --git a/main/main.c b/main/main.c index bb63051181..b08fb65634 100644 --- a/main/main.c +++ b/main/main.c @@ -447,12 +447,15 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL) PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL) - STD_PHP_INI_BOOLEAN("allow_url_fopen", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_url_fopen, php_core_globals, core_globals) - STD_PHP_INI_BOOLEAN("allow_url_include", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_url_include, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("allow_url_fopen", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_url_fopen, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("allow_url_include", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_url_include, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("always_populate_raw_post_data", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, always_populate_raw_post_data, php_core_globals, core_globals) STD_PHP_INI_ENTRY("realpath_cache_size", "16K", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_size_limit, virtual_cwd_globals, cwd_globals) STD_PHP_INI_ENTRY("realpath_cache_ttl", "120", PHP_INI_SYSTEM, OnUpdateLong, realpath_cache_ttl, virtual_cwd_globals, cwd_globals) + + STD_PHP_INI_ENTRY("user_ini.filename", ".user.ini", PHP_INI_SYSTEM, OnUpdateString, user_ini_filename, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("user_ini.cache_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, user_ini_cache_ttl, php_core_globals, core_globals) PHP_INI_END() /* }}} */ @@ -1025,7 +1028,7 @@ PHP_FUNCTION(set_time_limit) } convert_to_string_ex(new_timeout); - if (zend_alter_ini_entry("max_execution_time", sizeof("max_execution_time"), Z_STRVAL_PP(new_timeout), Z_STRLEN_PP(new_timeout), PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == SUCCESS) { + if (zend_alter_ini_entry_ex("max_execution_time", sizeof("max_execution_time"), Z_STRVAL_PP(new_timeout), Z_STRLEN_PP(new_timeout), PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == SUCCESS) { RETURN_TRUE; } else { RETURN_FALSE; diff --git a/main/php_globals.h b/main/php_globals.h index 6c796d2c76..889072bc11 100644 --- a/main/php_globals.h +++ b/main/php_globals.h @@ -161,6 +161,9 @@ struct _php_core_globals { #endif long max_input_nesting_level; zend_bool in_user_include; + + char *user_ini_filename; + long user_ini_cache_ttl; }; diff --git a/main/php_ini.c b/main/php_ini.c index 390883da45..fd0e560de1 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -21,6 +21,7 @@ #include "php.h" #include "ext/standard/info.h" #include "zend_ini.h" +#include "zend_ini_scanner.h" #include "php_ini.h" #include "ext/standard/dl.h" #include "zend_extensions.h" @@ -45,8 +46,8 @@ typedef struct _php_extension_lists { zend_llist functions; } php_extension_lists; - /* True globals */ +static HashTable *active_ini_hash; static HashTable configuration_hash; PHPAPI char *php_ini_opened_path=NULL; static php_extension_lists extension_lists; @@ -54,14 +55,13 @@ PHPAPI char *php_ini_scanned_files=NULL; /* {{{ php_ini_displayer_cb */ -static void php_ini_displayer_cb(zend_ini_entry *ini_entry, int type) +static void php_ini_displayer_cb(zend_ini_entry *ini_entry, int type TSRMLS_DC) { if (ini_entry->displayer) { ini_entry->displayer(ini_entry, type); } else { char *display_string; uint display_string_length, esc_html=0; - TSRMLS_FETCH(); if (type == ZEND_INI_DISPLAY_ORIG && ini_entry->modified) { if (ini_entry->orig_value && ini_entry->orig_value[0]) { @@ -112,16 +112,16 @@ static int php_ini_displayer(zend_ini_entry *ini_entry, int module_number TSRMLS PUTS("<td class=\"e\">"); PHPWRITE(ini_entry->name, ini_entry->name_length - 1); PUTS("</td><td class=\"v\">"); - php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE); + php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE TSRMLS_CC); PUTS("</td><td class=\"v\">"); - php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG); + php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG TSRMLS_CC); PUTS("</td></tr>\n"); } else { PHPWRITE(ini_entry->name, ini_entry->name_length - 1); PUTS(" => "); - php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE); + php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ACTIVE TSRMLS_CC); PUTS(" => "); - php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG); + php_ini_displayer_cb(ini_entry, ZEND_INI_DISPLAY_ORIG TSRMLS_CC); PUTS("\n"); } return 0; @@ -148,7 +148,6 @@ PHPAPI void display_ini_entries(zend_module_entry *module) /* }}} */ /* php.ini support */ - #ifdef ZTS # if (ZEND_DEBUG) # define ZEND_EXTENSION_TOKEN "zend_extension_debug_ts" @@ -163,17 +162,46 @@ PHPAPI void display_ini_entries(zend_module_entry *module) # endif #endif -/* {{{ php_config_ini_parser_cb +/* {{{ config_zval_dtor */ -static void php_config_ini_parser_cb(zval *arg1, zval *arg2, int callback_type, void *arg) +void config_zval_dtor(zval *zvalue) { + if (Z_TYPE_P(zvalue) == IS_ARRAY) { + zend_hash_destroy(Z_ARRVAL_P(zvalue)); + free(Z_ARRVAL_P(zvalue)); + } else if (Z_TYPE_P(zvalue) == IS_STRING) { + free(Z_STRVAL_P(zvalue)); + } +} +/* Reset / free active_ini_sectin global */ +#define RESET_ACTIVE_INI_HASH() do { \ + active_ini_hash = NULL; \ +} while (0) +/* }}} */ + +/* {{{ php_ini_parser_cb + */ +static void php_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, HashTable *target_hash) +{ + zval *entry; + HashTable *active_hash; + + if (active_ini_hash) { + active_hash = active_ini_hash; + } else { + active_hash = target_hash; + } + switch (callback_type) { case ZEND_INI_PARSER_ENTRY: { - zval *entry; - if (!arg2) { + /* bare string - nothing to do */ break; } + +/* FIXME: Should the extension loading be disabled for PATH sections? */ + + /* PHP and Zend extensions are not added into configuration hash! */ if (!strcasecmp(Z_STRVAL_P(arg1), "extension")) { /* load function module */ zval copy; @@ -185,41 +213,101 @@ static void php_config_ini_parser_cb(zval *arg1, zval *arg2, int callback_type, char *extension_name = estrndup(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2)); zend_llist_add_element(&extension_lists.engine, &extension_name); + + /* All other entries are added into either configuration_hash or active ini section array */ } else { - zend_hash_update(&configuration_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, arg2, sizeof(zval), (void **) &entry); + /* Store in active hash */ + zend_hash_update(active_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, arg2, sizeof(zval), (void **) &entry); Z_STRVAL_P(entry) = zend_strndup(Z_STRVAL_P(entry), Z_STRLEN_P(entry)); } } break; case ZEND_INI_PARSER_POP_ENTRY: { - zval *hash; - zval **find_hash; - zval *element; + zval *option_arr; + zval *find_arr; if (!arg2) { /* bare string - nothing to do */ break; } - if (zend_hash_find(&configuration_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, (void **) &find_hash) == FAILURE) { - ALLOC_ZVAL(hash); - array_init(hash); +/* fprintf(stdout, "ZEND_INI_PARSER_POP_ENTRY: %s[%s] = %s\n",Z_STRVAL_P(arg1), Z_STRVAL_P(arg3), Z_STRVAL_P(arg2)); */ + + /* If option not found in hash or is not an array -> create array, otherwise add to existing array */ + if (zend_hash_find(active_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, (void **) &find_arr) == FAILURE || Z_TYPE_P(find_arr) != IS_ARRAY) { + option_arr = (zval *) pemalloc(sizeof(zval), 1); + INIT_PZVAL(option_arr); + Z_TYPE_P(option_arr) = IS_ARRAY; + Z_ARRVAL_P(option_arr) = (HashTable *) pemalloc(sizeof(HashTable), 1); + zend_hash_init(Z_ARRVAL_P(option_arr), 0, NULL, (dtor_func_t) config_zval_dtor, 1); + zend_hash_update(active_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, option_arr, sizeof(zval), (void **) &find_arr); + free(option_arr); + } - zend_hash_update(&configuration_hash, Z_STRVAL_P(arg1), Z_STRLEN_P(arg1) + 1, &hash, sizeof(zval *), NULL); + /* arg3 is possible option offset name */ + if (arg3 && Z_STRLEN_P(arg3) > 0) { + zend_symtable_update(Z_ARRVAL_P(find_arr), Z_STRVAL_P(arg3), Z_STRLEN_P(arg3) + 1, arg2, sizeof(zval), (void **) &entry); } else { - hash = *find_hash; + zend_hash_next_index_insert(Z_ARRVAL_P(find_arr), arg2, sizeof(zval), (void **) &entry); } - - ALLOC_ZVAL(element); - *element = *arg2; - zval_copy_ctor(element); - INIT_PZVAL(element); - add_next_index_zval(hash, element); + Z_STRVAL_P(entry) = zend_strndup(Z_STRVAL_P(entry), Z_STRLEN_P(entry)); } break; - case ZEND_INI_PARSER_SECTION: + case ZEND_INI_PARSER_SECTION: { /* Create an array of entries of each section */ + +/* fprintf(stdout, "ZEND_INI_PARSER_SECTION: %s\n",Z_STRVAL_P(arg1)); */ + + char *key = NULL; + uint key_len; + + /* Only PATH sections are handled here! */ + if (!strncasecmp(Z_STRVAL_P(arg1), "PATH", sizeof("PATH") - 1)) { + key = Z_STRVAL_P(arg1); + key = key + sizeof("PATH") - 1; + key_len = Z_STRLEN_P(arg1) - sizeof("PATH") + 1; + +#if 0 /* Disable HOST sections for now. If someone can come up with some good usage case, then I can reconsider :) */ + } else if (!strncasecmp(Z_STRVAL_P(arg1), "HOST", sizeof("HOST") - 1)) { + key = Z_STRVAL_P(arg1); + key = key + sizeof("HOST") - 1; + key_len = Z_STRLEN_P(arg1) - sizeof("HOST") + 1; +#endif + } + + if (key && key_len > 0) { + /* Strip any trailing slashes */ + while (key_len > 0 && (key[key_len - 1] == '/' || key[key_len - 1] == '\\')) { + key_len--; + key[key_len] = 0; + } + + /* Strip any leading whitespace and '=' */ + while (*key && ( + *key == '=' || + *key == ' ' || + *key == '\t' + )) { + key++; + key_len--; + } + + /* Search for existing entry and if it does not exist create one */ + if (zend_hash_find(target_hash, key, key_len + 1, (void **) &entry) == FAILURE) { + zval *section_arr; + + section_arr = (zval *) pemalloc(sizeof(zval), 1); + INIT_PZVAL(section_arr); + Z_TYPE_P(section_arr) = IS_ARRAY; + Z_ARRVAL_P(section_arr) = (HashTable *) pemalloc(sizeof(HashTable), 1); + zend_hash_init(Z_ARRVAL_P(section_arr), 0, NULL, (dtor_func_t) config_zval_dtor, 1); + zend_hash_update(target_hash, key, key_len + 1, section_arr, sizeof(zval), (void **) &entry); + free(section_arr); + } + active_ini_hash = Z_ARRVAL_P(entry); + } + } break; } } @@ -244,16 +332,6 @@ static void php_load_zend_extension_cb(void *arg TSRMLS_DC) } /* }}} */ -/* {{{ pvalue_config_destructor - */ -static void pvalue_config_destructor(zval *pvalue) -{ - if (Z_TYPE_P(pvalue) == IS_STRING) { - free(Z_STRVAL_P(pvalue)); - } -} -/* }}} */ - /* {{{ php_init_config */ int php_init_config(TSRMLS_D) @@ -264,14 +342,8 @@ int php_init_config(TSRMLS_D) char *open_basedir; int free_ini_search_path = 0; zend_file_handle fh; - struct stat sb; - char ini_file[MAXPATHLEN]; - char *p; - zend_llist scanned_ini_list; - int l, total_l=0; - zend_llist_element *element; - if (zend_hash_init(&configuration_hash, 0, NULL, (dtor_func_t) pvalue_config_destructor, 1) == FAILURE) { + if (zend_hash_init(&configuration_hash, 0, NULL, (dtor_func_t) config_zval_dtor, 1) == FAILURE) { return FAILURE; } @@ -281,7 +353,6 @@ int php_init_config(TSRMLS_D) zend_llist_init(&extension_lists.engine, sizeof(char *), (llist_dtor_func_t) free_estring, 1); zend_llist_init(&extension_lists.functions, sizeof(zval), (llist_dtor_func_t) ZVAL_DESTRUCTOR, 1); - zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1); safe_mode_state = PG(safe_mode); open_basedir = PG(open_basedir); @@ -492,8 +563,9 @@ int php_init_config(TSRMLS_D) if (fh.handle.fp) { fh.type = ZEND_HANDLE_FP; + RESET_ACTIVE_INI_HASH(); - zend_parse_ini_file(&fh, 1, php_config_ini_parser_cb, &extension_lists); + 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; @@ -501,6 +573,8 @@ int php_init_config(TSRMLS_D) Z_STRLEN(tmp) = strlen(fh.filename); Z_STRVAL(tmp) = zend_strndup(fh.filename, Z_STRLEN(tmp)); Z_TYPE(tmp) = IS_STRING; + tmp.refcount = 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); @@ -514,10 +588,24 @@ int php_init_config(TSRMLS_D) if (!sapi_module.php_ini_ignore && strlen(PHP_CONFIG_FILE_SCAN_DIR)) { struct dirent **namelist; int ndir, i; + struct stat sb; + char ini_file[MAXPATHLEN]; + char *p; + zend_file_handle fh; + zend_llist scanned_ini_list; + zend_llist_element *element; + int l, total_l = 0; + + /* Reset active ini section */ + RESET_ACTIVE_INI_HASH(); if ((ndir = php_scandir(PHP_CONFIG_FILE_SCAN_DIR, &namelist, 0, php_alphasort)) > 0) { + zend_llist_init(&scanned_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1); + memset(&fh, 0, sizeof(fh)); + for (i = 0; i < ndir; i++) { - /* check for a .ini extension */ + + /* check for any file with .ini extension */ if (!(p = strrchr(namelist[i]->d_name, '.')) || (p && strcmp(p, ".ini"))) { free(namelist[i]); continue; @@ -528,12 +616,14 @@ int php_init_config(TSRMLS_D) if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) { fh.filename = ini_file; fh.type = ZEND_HANDLE_FP; - zend_parse_ini_file(&fh, 1, php_config_ini_parser_cb, &extension_lists); - /* Here, add it to the list of ini files read */ - l = strlen(ini_file); - total_l += l + 2; - p = estrndup(ini_file, l); - zend_llist_add_element(&scanned_ini_list, &p); + + if (zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC) == SUCCESS) { + /* Here, add it to the list of ini files read */ + l = strlen(ini_file); + total_l += l + 2; + p = estrndup(ini_file, l); + zend_llist_add_element(&scanned_ini_list, &p); + } } } } @@ -541,14 +631,17 @@ int php_init_config(TSRMLS_D) } free(namelist); - /* - * Don't need an extra byte for the \0 in this malloc as the last - * element will not get a trailing , which gives us the byte for the \0 - */ if (total_l) { - php_ini_scanned_files = (char *) malloc(total_l); - *php_ini_scanned_files = '\0'; + int php_ini_scanned_files_len = (php_ini_scanned_files) ? strlen(php_ini_scanned_files) + 1 : 0; + php_ini_scanned_files = (char *) realloc(php_ini_scanned_files, php_ini_scanned_files_len + total_l + 1); + if (!php_ini_scanned_files_len) { + *php_ini_scanned_files = '\0'; + } + total_l += php_ini_scanned_files_len; for (element = scanned_ini_list.head; element; element = element->next) { + if (php_ini_scanned_files_len) { + strlcat(php_ini_scanned_files, ",\n", total_l); + } strlcat(php_ini_scanned_files, *(char **)element->data, total_l); strlcat(php_ini_scanned_files, element->next ? ",\n" : "\n", total_l); } @@ -558,7 +651,9 @@ int php_init_config(TSRMLS_D) } if (sapi_module.ini_entries) { - zend_parse_ini_string(sapi_module.ini_entries, 1, php_config_ini_parser_cb, &extension_lists); + /* Reset active ini section */ + RESET_ACTIVE_INI_HASH(); + zend_parse_ini_string(sapi_module.ini_entries, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash TSRMLS_CC); } return SUCCESS; @@ -594,6 +689,81 @@ void php_ini_register_extensions(TSRMLS_D) } /* }}} */ +/* {{{ php_parse_user_ini_file + */ +PHPAPI int php_parse_user_ini_file(char *dirname, char *ini_filename, HashTable *target_hash TSRMLS_DC) +{ + struct stat sb; + char ini_file[MAXPATHLEN]; + zend_file_handle fh; + + snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, ini_filename); + + if (VCWD_STAT(ini_file, &sb) == 0) { + if (S_ISREG(sb.st_mode)) { + memset(&fh, 0, sizeof(fh)); + if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) { + fh.filename = ini_file; + fh.type = ZEND_HANDLE_FP; + + /* Reset active ini section */ + RESET_ACTIVE_INI_HASH(); + + if (zend_parse_ini_file(&fh, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, target_hash TSRMLS_CC) == SUCCESS) { + /* FIXME: Add parsed file to the list of user files read? */ + return SUCCESS; + } + return FAILURE; + } + } + } + return FAILURE; +} +/* }}} */ + +/* {{{ php_ini_activate_config + */ +PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int stage TSRMLS_DC) +{ + char *str; + zval *data; + uint str_len; + ulong num_index; + + /* Walk through config hash and alter matching ini entries using the values found in the hash */ + for (zend_hash_internal_pointer_reset(source_hash); + zend_hash_get_current_key_ex(source_hash, &str, &str_len, &num_index, 0, NULL) == HASH_KEY_IS_STRING; + zend_hash_move_forward(source_hash) + ) { + zend_hash_get_current_data(source_hash, (void **) &data); + zend_alter_ini_entry_ex(str, str_len, Z_STRVAL_P(data), Z_STRLEN_P(data), modify_type, stage, 0 TSRMLS_CC); + } +} +/* }}} */ + +/* {{{ php_ini_activate_per_dir_config + */ +PHPAPI void php_ini_activate_per_dir_config(char *path, uint path_len TSRMLS_DC) +{ + zval *tmp; + char *ptr; + + /* Walk through each directory in path and apply any found per-dir-system-configuration from configuration_hash */ + if (path && path_len) { + ptr = path + 1; + while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) { + *ptr = 0; + /* Search for source array matching the path from configuration_hash */ + if (zend_hash_find(&configuration_hash, path, path_len, (void **) &tmp) == SUCCESS) { + php_ini_activate_config(Z_ARRVAL_P(tmp), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE TSRMLS_CC); + } + *ptr = '/'; + ptr++; + } + } +} +/* }}} */ + /* {{{ cfg_get_entry */ PHPAPI zval *cfg_get_entry(char *name, uint name_length) @@ -659,6 +829,14 @@ PHPAPI int cfg_get_string(char *varname, char **result) } /* }}} */ +#if ZEND_DEBUG +#include "php_ini.h" +PHPAPI HashTable get_configuration_hash(void) +{ + return configuration_hash; +} +#endif + /* * Local variables: * tab-width: 4 diff --git a/main/php_ini.h b/main/php_ini.h index a2b3125c3a..9034a9cce2 100644 --- a/main/php_ini.h +++ b/main/php_ini.h @@ -24,6 +24,7 @@ #include "zend_ini.h" BEGIN_EXTERN_C() +void config_zval_dtor(zval *zvalue); int php_init_config(TSRMLS_D); int php_shutdown_config(void); void php_ini_register_extensions(TSRMLS_D); @@ -31,6 +32,12 @@ PHPAPI zval *cfg_get_entry(char *name, uint name_length); PHPAPI int cfg_get_long(char *varname, long *result); PHPAPI int cfg_get_double(char *varname, double *result); PHPAPI int cfg_get_string(char *varname, char **result); +PHPAPI int php_parse_user_ini_file(char *dirname, char *ini_filename, HashTable *target_hash TSRMLS_DC); +PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int stage TSRMLS_DC); +PHPAPI void php_ini_activate_per_dir_config(char *path, uint path_len TSRMLS_DC); +#if ZEND_DEBUG +PHPAPI HashTable get_configuration_hash(void); +#endif END_EXTERN_C() #define PHP_INI_USER ZEND_INI_USER diff --git a/php.ini-dist b/php.ini-dist index 006ea59495..019352cdad 100644 --- a/php.ini-dist +++ b/php.ini-dist @@ -63,6 +63,17 @@ ; defaults (that is, if no php.ini is used, or if you delete these lines, ; the builtin defaults will be identical). +;;;;;;;;;;;;;;;;;;;; +; php.ini Options ; +;;;;;;;;;;;;;;;;;;;; +; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini" +;user_ini.filename = ".user.ini" + +; To disable this feature set this option to empty value +;user_ini.filename = + +; TTL for user-defined php.ini files (time-to-live) in seconds. Default is 300 seconds (5 minutes) +;user_ini.cache_ttl = 300 ;;;;;;;;;;;;;;;;;;;; ; Language Options ; diff --git a/php.ini-recommended b/php.ini-recommended index 41d29569c9..e05eeeb145 100644 --- a/php.ini-recommended +++ b/php.ini-recommended @@ -113,6 +113,18 @@ ; since short tags may not be supported on the target server. ;;;;;;;;;;;;;;;;;;;; +; php.ini Options ; +;;;;;;;;;;;;;;;;;;;; +; Name for user-defined php.ini (.htaccess) files. Default is ".user.ini" +;user_ini.filename = ".user.ini" + +; To disable this feature set this option to empty value +;user_ini.filename = + +; TTL for user-defined php.ini files (time-to-live) in seconds. Default is 300 seconds (5 minutes) +;user_ini.cache_ttl = 300 + +;;;;;;;;;;;;;;;;;;;; ; Language Options ; ;;;;;;;;;;;;;;;;;;;; diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c index 46ac15cfc4..f8c1954bbe 100644 --- a/sapi/cgi/cgi_main.c +++ b/sapi/cgi/cgi_main.c @@ -152,8 +152,30 @@ typedef struct _php_cgi_globals_struct { #ifdef PHP_WIN32 zend_bool impersonate; #endif + HashTable user_config_cache; } php_cgi_globals_struct; +/* {{{ user_config_cache + * + * Key for each cache entry is dirname(PATH_TRANSLATED). + * + * NOTE: Each cache entry config_hash contains the combination from all user ini files found in + * the path starting from doc_root throught to dirname(PATH_TRANSLATED). There is no point + * storing per-file entries as it would not be possible to detect added / deleted entries + * between separate files. + */ +typedef struct _user_config_cache_entry { + time_t expires; + HashTable *user_config; +} user_config_cache_entry; + +static void user_config_cache_entry_dtor(user_config_cache_entry *entry) /* {{{ */ +{ + zend_hash_destroy(entry->user_config); + free(entry->user_config); +} +/* }}} */ + #ifdef ZTS static int php_cgi_globals_id; #define CGIG(v) TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v) @@ -566,12 +588,96 @@ static void sapi_cgi_log_message(char *message) } } +/* {{{ php_cgi_ini_activate_user_config + */ +static void php_cgi_ini_activate_user_config(char *path, int path_len, int start TSRMLS_DC) +{ + char *ptr; + user_config_cache_entry *new_entry, *entry; + time_t request_time = sapi_get_request_time(TSRMLS_C); + + /* Find cached config entry: If not found, create one */ + if (zend_hash_find(&CGIG(user_config_cache), path, path_len + 1, (void **) &entry) == FAILURE) { + new_entry = pemalloc(sizeof(user_config_cache_entry), 1); + new_entry->expires = 0; + new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1); + zend_hash_init(new_entry->user_config, 0, NULL, (dtor_func_t) config_zval_dtor, 1); + zend_hash_update(&CGIG(user_config_cache), path, path_len + 1, new_entry, sizeof(user_config_cache_entry), (void **) &entry); + free(new_entry); + } + + /* Check whether cache entry has expired and rescan if it is */ + if (request_time > entry->expires) { + + /* Clear the expired config */ + zend_hash_clean(entry->user_config); + + /* Walk through each directory and apply entries to user_config hash */ + ptr = path + start; /* start is the point where doc_root ends! */ + while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) { + *ptr = 0; + php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC); + *ptr = '/'; + ptr++; + } + entry->expires = request_time + PG(user_ini_cache_ttl); + } + + /* Activate ini entries with values from the user config hash */ + php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS TSRMLS_CC); +} +/* }}} */ + +static int sapi_cgi_activate(TSRMLS_D) +{ + char *path, *doc_root; + uint path_len, doc_root_len; + + /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */ + if (!SG(request_info).path_translated) { + return FAILURE; + } + + doc_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC); + + /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */ + if (!doc_root) { + return FAILURE; + } + doc_root_len = strlen(doc_root); + if (doc_root[doc_root_len - 1] == '/') { + --doc_root_len; + } + + /* Prepare search path */ + path_len = strlen(SG(request_info).path_translated); + path = zend_strndup(SG(request_info).path_translated, path_len); + php_dirname(path, path_len); + path_len = strlen(path); + + /* Make sure we have trailing slash! */ + if (!IS_SLASH(path[path_len])) { + path[path_len++] = DEFAULT_SLASH; + } + path[path_len] = 0; + + /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */ + php_ini_activate_per_dir_config(path, path_len TSRMLS_CC); /* Note: for global settings sake we check from root to path */ + + /* Load and activate user ini files in path starting from DOCUMENT_ROOT */ + if (strlen(PG(user_ini_filename))) { + php_cgi_ini_activate_user_config(path, path_len, doc_root_len - 1 TSRMLS_CC); + } + + free(path); + return SUCCESS; +} + static int sapi_cgi_deactivate(TSRMLS_D) { /* flush only when SAPI was started. The reasons are: 1. SAPI Deactivate is called from two places: module init and request shutdown - 2. When the first call occurs and the request is not set up, flush fails on - FastCGI. + 2. When the first call occurs and the request is not set up, flush fails on FastCGI. */ if (SG(sapi_started)) { sapi_cgibin_flush(SG(server_context)); @@ -587,7 +693,6 @@ static int php_cgi_startup(sapi_module_struct *sapi_module) return SUCCESS; } - /* {{{ sapi_module_struct cgi_sapi_module */ static sapi_module_struct cgi_sapi_module = { @@ -597,7 +702,7 @@ static sapi_module_struct cgi_sapi_module = { php_cgi_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ - NULL, /* activate */ + sapi_cgi_activate, /* activate */ sapi_cgi_deactivate, /* deactivate */ sapi_cgibin_ub_write, /* unbuffered write */ @@ -1072,6 +1177,7 @@ static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals TSRMLS_ #ifdef PHP_WIN32 php_cgi_globals->impersonate = 0; #endif + zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, (dtor_func_t) user_config_cache_entry_dtor, 1); } /* }}} */ @@ -1093,6 +1199,8 @@ static PHP_MINIT_FUNCTION(cgi) */ static PHP_MSHUTDOWN_FUNCTION(cgi) { + zend_hash_destroy(&CGIG(user_config_cache)); + UNREGISTER_INI_ENTRIES(); return SUCCESS; } |