]> granicus.if.org Git - php/commitdiff
- Added ".htaccess" style user-defined php.ini files support for
authorJani Taskinen <jani@php.net>
Fri, 28 Sep 2007 02:04:28 +0000 (02:04 +0000)
committerJani Taskinen <jani@php.net>
Fri, 28 Sep 2007 02:04:28 +0000 (02:04 +0000)
  CGI/FastCGI.
- Added support for special [PATH=/opt/httpd/www.example.com/] sections
  in php.ini. All directives set in these sections will not be able to be
  overridden in user-defined ini-files or during runtime in the specified
  path.

- Improved php.ini handling:
  . Added better error reporting for syntax errors in php.ini files
  . Allowed "ini-variables" to be used almost everywhere ini php.ini files
  . Allowed using alphanumeric/variable indexes in "array" ini options
  . Fixed get_cfg_var() to be able to return "array" ini options

- Fixed bug #27372 (parse error loading browscap.ini at apache startup)
- Fixed bug #42069 (parse_ini_file() allows using some non-alpha numeric
  characters)

@DOC Pierre promised to handle the manual part of this :D

21 files changed:
Zend/zend_globals.h
Zend/zend_ini.h
Zend/zend_ini_parser.y
Zend/zend_ini_scanner.h
Zend/zend_ini_scanner.l
ext/standard/basic_functions.c
ext/standard/basic_functions.h
ext/standard/browscap.c
ext/standard/tests/file/parse_ini_file.phpt
ext/standard/tests/general_functions/parse_ini_basic.data [new file with mode: 0644]
ext/standard/tests/general_functions/parse_ini_basic.phpt [new file with mode: 0644]
ext/standard/tests/general_functions/parse_ini_booleans.data [new file with mode: 0644]
ext/standard/tests/general_functions/parse_ini_booleans.phpt [new file with mode: 0644]
ext/standard/tests/general_functions/parse_ini_file.phpt
main/main.c
main/php_globals.h
main/php_ini.c
main/php_ini.h
php.ini-dist
php.ini-recommended
sapi/cgi/cgi_main.c

index 89e2527a3129d1ea1a3b0c39814a8e740e311102..a543fe63735631c219cf1b12c636d2fb691807fc 100644 (file)
@@ -268,6 +268,9 @@ struct _zend_scanner_globals {
        zend_bool encoding_checked;
        char* rest_str;
        int rest_len;
+
+       /* For ini scanner. Modes are: ZEND_INI_SCANNER_NORMAL, ZEND_INI_SCANNER_RAW */
+       int scanner_mode;
 };
 
 struct _zend_unicode_globals {
index 5fa0fdf059d3d821460af310755bbfc63a433d43..e382c57400b1226be8f7170498c3f5b0624c9ce8 100644 (file)
@@ -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)
@@ -194,15 +193,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;
index d392c41e5a04a703836ee6b24b1f33ae6028d5c1..cadb5cfd6927723feb0a4dc61547f44b832d95bf 100644 (file)
    | 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"
 #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); }
 ;
 
 /*
index 76ca77105832fced83b96f790e21a0a5369320be..3ba6e120f5dd99fe183a733bc8dd1e9d50a945b3 100644 (file)
 #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 */
index 3f91bb64a7407ebb6693d0e192f098ac509c4de9..a73d948a1308511f95d22dadd70c31dbe9f97488 100644 (file)
    | 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"
 #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)
 
+#define YY_INPUT(buf, result, max_size)                                    \
+if ( ((result = zend_unicode_yyinput(yyin, buf, max_size TSRMLS_CC)) == 0) \
+       && zend_stream_ferror( yyin TSRMLS_CC) )                               \
+       YY_FATAL_ERROR( "input in flex scanner failed" );
+
 /* Globals Macros */
 #define SCNG   INI_SCNG
 #ifdef ZTS
@@ -48,204 +100,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 '}';
+}
 
-       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>("true"|"on"|"yes"){TABS_AND_SPACES}* { /* TRUE value (when used outside option value/offset this causes parse error!) */
+       RETURN_TOKEN(BOOL_TRUE, "1", 1);
 }
 
-<INITIAL>["][^"]*["] {
-       char *p = yytext;
+<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);
+}
 
-       /* ENCAPSULATED TC_STRING */
+<INITIAL,ST_OFFSET>{LABEL} { /* Get option name or option offset value */
+       RETURN_TOKEN(TC_STRING, yytext, yyleng);
+}
 
-       while ((p = strpbrk(p, "\r\n"))) {
-               if (*p == '\r' && *(p + 1) == '\n') {
-                       p++;
-               }
-               SCNG(lineno)++;
-               p++;
+<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 '=';
+}
 
-       /* eat trailing " */
-       yytext[yyleng-1]=0;
+<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);
+}
+
+<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();
 }
index 31bbee8d410d20eea9ec2d1d48e58827d056c078..1455b0d9a67445a570e8dc50077b1f555d8c1fb8 100644 (file)
@@ -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>
 
@@ -940,8 +941,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)
@@ -3375,7 +3383,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */
        PHP_FE(get_current_user,                                                                                                arginfo_get_current_user)
        PHP_FE(set_time_limit,                                                                                                  arginfo_set_time_limit)
        PHP_FE(get_cfg_var,                                                                                                             arginfo_get_cfg_var)
-       
+
        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)
@@ -3422,6 +3430,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)
 
@@ -3940,6 +3951,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);
@@ -4768,22 +4782,55 @@ PHP_FUNCTION(get_current_user)
 }
 /* }}} */
 
-/* {{{ proto string get_cfg_var(string option_name) U
+/* {{{ 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.s, 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.s, hash_key->nKeyLength, tmp);
+       }
+       return 0;
+}
+/* }}} */
+
+/* {{{ proto mixed get_cfg_var(string option_name) U
    Get the value of a PHP configuration option */
 PHP_FUNCTION(get_cfg_var)
 {
        char *varname;
        int varname_len;
        char *value;
-
+       zval *retval;
+       
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s&", &varname, &varname_len, UG(utf8_conv)) == FAILURE) {
                return;
        }
 
-       if (cfg_get_string(varname, &value) == FAILURE) {
+       retval = cfg_get_entry(varname, varname_len + 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_UTF8_STRING(Z_STRVAL_P(retval), ZSTR_DUPLICATE);
+               }
+       } else {
                RETURN_FALSE;
        }
-       RETURN_UTF8_STRING(value, ZSTR_DUPLICATE);
 }
 /* }}} */
 
@@ -5458,7 +5505,7 @@ PHP_FUNCTION(ini_get)
                return;
        }
 
-       str = zend_ini_string(varname, varname_len+1, 0);
+       str = zend_ini_string(varname, varname_len + 1, 0);
 
        if (!str) {
                RETURN_FALSE;
@@ -5615,8 +5662,7 @@ PHP_FUNCTION(ini_set)
                new_value_len = temp_len;
        }
 
-       if (zend_alter_ini_entry(varname, varname_len+1, new_value.s, new_value_len,
-                                                               PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
+       if (zend_alter_ini_entry_ex(varname, varname_len+1, new_value.s, new_value_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == FAILURE) {
                zval_dtor(return_value);
                RETVAL_FALSE;
        }
@@ -5643,7 +5689,6 @@ PHP_FUNCTION(ini_restore)
 
 /* {{{ proto string set_include_path(string new_include_path) U
    Sets the include_path configuration option */
-
 PHP_FUNCTION(set_include_path)
 {
        zstr new_value;
@@ -5694,8 +5739,7 @@ PHP_FUNCTION(set_include_path)
                }
        }
 
-       if (zend_alter_ini_entry("include_path", sizeof("include_path"),
-                                                        new_value.s, new_value_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME) == FAILURE) {
+       if (zend_alter_ini_entry_ex("include_path", sizeof("include_path"), new_value.s, new_value_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == FAILURE) {
                zval_dtor(return_value);
                RETURN_FALSE;
        }
@@ -5704,12 +5748,10 @@ PHP_FUNCTION(set_include_path)
                efree(new_value.s);
        }
 }
-
 /* }}} */
 
 /* {{{ proto string get_include_path() U
    Get the current include_path configuration option */
-
 PHP_FUNCTION(get_include_path)
 {
     char *str;
@@ -5723,21 +5765,17 @@ PHP_FUNCTION(get_include_path)
        }
        RETURN_UTF8_STRING(str, ZSTR_DUPLICATE);
 }
-
 /* }}} */
 
 /* {{{ proto void restore_include_path() U
    Restore the value of the include_path configuration option */
-
 PHP_FUNCTION(restore_include_path)
 {
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") == FAILURE) {
                return;
        }
-       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]) U
@@ -5797,7 +5835,7 @@ PHP_FUNCTION(ignore_user_abort)
        }
 
        if (arg) {
-               zend_alter_ini_entry("ignore_user_abort", sizeof("ignore_user_abort"), arg, arg_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+               zend_alter_ini_entry_ex("ignore_user_abort", sizeof("ignore_user_abort"), arg, arg_len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC);
        }
 
        RETURN_LONG(old_setting);
@@ -6061,10 +6099,9 @@ 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, name;
-       TSRMLS_FETCH();
 
        switch (callback_type) {
 
@@ -6102,11 +6139,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;
                                }
@@ -6148,10 +6185,16 @@ static void php_simple_ini_parser_cb(zval *arg1, zval *arg2, int callback_type,
                        *element = *arg2;
                        zval_copy_ctor(element);
                        INIT_PZVAL(element);
+
                        if (UG(unicode)) {
                                convert_to_unicode_with_converter(element, UG(utf8_conv));
                        }
-                       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;
 
@@ -6163,10 +6206,9 @@ 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)
 {
        zval name;
-       TSRMLS_FETCH();
 
        if (callback_type == ZEND_INI_PARSER_SECTION) {
                MAKE_STD_ZVAL(BG(active_ini_file_section));
@@ -6189,51 +6231,64 @@ 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]) U
+/* {{{ proto array parse_ini_file(string filename [, bool process_sections [, int scanner_mode]]) U
    Parse configuration file */
 PHP_FUNCTION(parse_ini_file)
 {
        zval **pp_filename;
-       zend_bool process_sections;
        char *filename;
        int filename_len;
+       zend_bool process_sections;
+       long scanner_mode = ZEND_INI_SCANNER_NORMAL;
        zend_file_handle fh;
        zend_ini_parser_cb_t ini_parser_cb;
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|b", &pp_filename, &process_sections) == FAILURE ||
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|bl", &pp_filename, &process_sections, &scanner_mode) == FAILURE ||
                php_stream_path_param_encode(pp_filename, &filename, &filename_len, REPORT_ERRORS, FG(default_context)) == FAILURE) {
-               return;
+               RETURN_FALSE;
        }
 
-       switch (ZEND_NUM_ARGS()) {
-               case 1:
-                       ini_parser_cb = (zend_ini_parser_cb_t) php_simple_ini_parser_cb;
-                       break;
-
-               case 2:
-                       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;
-                       }
-                       break;
+       if (filename_len == 0) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filename can not be empty!");
+               RETURN_FALSE;
        }
 
+       /* 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 = 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) /* {{{ */
 {
        zval *prefix, new_key;
index 7879caf29b05205a49c090c89f4ef169bffe3f3c..5c49889032359c555b9c9dae222bf0d9b65eb48c 100644 (file)
@@ -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);
index f77af3efe6b8090f6464a7e3623a73354a020c10..8cea8be1579da75539e1d6ee52ca37b9369531b4 100644 (file)
 #include "php_string.h"
 #include "ext/ereg/php_regex.h"
 
+#include "zend_ini_scanner.h"
 #include "zend_globals.h"
 
 static HashTable browser_hash;
 static zval *current_section;
+static zval *current_section_name;
 
 #define DEFAULT_SECTION_NAME "Default Browser Capability Settings"
 
@@ -85,7 +87,7 @@ static void convert_browscap_pattern(zval *pattern) /* {{{ */
 }
 /* }}} */
 
-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;
@@ -97,12 +99,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);
@@ -124,8 +151,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 *) &current_section, sizeof(zval *), NULL);
 
                                Z_STRVAL_P(processed) = Z_STRVAL_P(arg1);
@@ -166,7 +195,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;
index 9a5dfb9d27c07a67006ad81a8ad7ae71666c333d..dab07e56e70b8075ce59fbee2986d885ead47863 100644 (file)
@@ -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,285 +450,13 @@ Good Morning
 Hello, world
              Good Morning
 
-            [String3] => Hello, worldGood Morning
-            [String4] => 
-
-            [String5] => 
-
-
-        )
-
-    [ReservedKeys_as_Values] => Array
-        (
-            [Key1] => 1
-            [Key2] => 1
-            [Key3] => 1
-            [Key4] => 
-            [Key5] => 
-            [Key6] => 
-            [Key7] => 1
-            [Key8] => 1
-            [Key9] => 1
-            [Key10] => 1
-            [Key11] => 
-            [Key12] => 
-            [Key13] => 
-            [Key14] => 
-            [Key15] => 
-            [Key16] => 
-            [Key17] => 
-            [Key18] => 
-        )
-
-    [ReservedKeys_as_Keys] => Array
-        (
-        )
-
-)
-*** Done **
---UEXPECTF--
-Notice: fwrite(): 4121 character unicode buffer downcoded for binary stream runtime_encoding in %s on line %d
-*** Test parse_ini_file() function:  with various keys and values given in parse.ini file ***
--- ini file without process_sections optional arg --
-Array
-(
-    [one] => 1
-    [five] => 5
-    [animal] => Humming bird
-    [Language] => PHP
-    [PHP_CONSTANT] => 1.2345678
-    [10] => Ten
-    [HELLO] => HELLO
-    [date] => 
-    [time] => 
-    [path] => /usr/local/bin
-    [URL] => http://www.php.net
-    [Decimal_value1] => 100
-    [Decimal_value2] => -100
-    [Decimal_value3] => -2147483647
-    [Decimal_value4] => 2147483647
-    [Decimal_value5] => -2147483648
-    [Decimal_value6] => 2147483648
-    [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)
-    [String1] => Hello, world
-Good Morning
-    [String2] => 
-Hello, world
-             Good Morning
-
-    [String3] => Hello, worldGood Morning
-    [String4] => 
-
-    [String5] => 
-
-
-    [Key1] => 1
-    [Key2] => 1
-    [Key3] => 1
-    [Key4] => 
-    [Key5] => 
-    [Key6] => 
-    [Key7] => 1
-    [Key8] => 1
-    [Key9] => 1
-    [Key10] => 1
-    [Key11] => 
-    [Key12] => 
-    [Key13] => 
-    [Key14] => 
-    [Key15] => 
-    [Key16] => 
-    [Key17] => 
-    [Key18] => 
-)
-
--- ini file with process_sections as TRUE --
-Array
-(
-    [Constans] => Array
-        (
-            [one] => 1
-            [five] => 5
-            [animal] => Humming bird
-            [Language] => PHP
-            [PHP_CONSTANT] => 1.2345678
-            [10] => Ten
-            [HELLO] => HELLO
-        )
-
-    [date] => Array
-        (
-            [date] => 
-            [time] => 
-        )
-
-    [paths] => Array
-        (
-            [path] => /usr/local/bin
-            [URL] => http://www.php.net
-        )
-
-    [Decimal] => Array
-        (
-            [Decimal_value1] => 100
-            [Decimal_value2] => -100
-            [Decimal_value3] => -2147483647
-            [Decimal_value4] => 2147483647
-            [Decimal_value5] => -2147483648
-            [Decimal_value6] => 2147483648
-        )
-
-    [Octal] => Array
-        (
-            [Octal_value] => 0100
-        )
-
-    [Hex] => Array
-        (
-            [Hex_value1] => 0x101
-            [Hex_Value2] => 0x103
-        )
-
-    [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-alpha numerics in strings] => Array
-        (
-            [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)
-        )
-
-    [Newlines_in_Values] => Array
-        (
-            [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
         )
 
     [ReservedKeys_as_Values] => Array
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 (file)
index 0000000..cba8f9d
--- /dev/null
@@ -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 (file)
index 0000000..fa7481b
--- /dev/null
@@ -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) "8191"
+  }
+  ["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) "8191"
+  }
+  ["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 (file)
index 0000000..2f1c2af
--- /dev/null
@@ -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 (file)
index 0000000..ca75745
--- /dev/null
@@ -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) "8191"
+    ["error_reporting1"]=>
+    string(4) "4177"
+    ["error_reporting2"]=>
+    string(4) "8183"
+    ["error_reporting3"]=>
+    string(4) "8183"
+    ["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.
index 6308edda7311393f3a3ab4b3bcfa4973d04c5fca..62ed5c79cebe754b5fe4150c76088f7fdcef10da 100644 (file)
@@ -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: parse_ini_file() expects at least 1 parameter, 0 given 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: parse_ini_file() expects at most 2 parameters, 3 given 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"
@@ -173,83 +171,3 @@ array(1) {
   string(5) "test4"
 }
 Done
---UEXPECTF--
-Warning: parse_ini_file() expects at least 1 parameter, 0 given in %s on line %d
-NULL
-
-Warning: parse_ini_file() expects at most 2 parameters, 3 given in %s on line %d
-NULL
-
-Warning: parse_ini_file(%sparse_ini_file.dat): failed to open stream: No such file or directory in %s on line %d
-array(0) {
-}
-
-Warning: parse_ini_file(%sparse_ini_file.dat): failed to open stream: No such file or directory in %s on line %d
-array(0) {
-}
-array(1) {
-  [u"test"]=>
-  unicode(0) ""
-}
-
-Warning: Error parsing %sparse_ini_file.dat on line 2
- in %s on line %d
-array(1) {
-  [u"test"]=>
-  unicode(0) ""
-}
-
-Warning: Error parsing %sparse_ini_file.dat on line 2
- in %s on line %d
-array(1) {
-  [u"test"]=>
-  unicode(4) "test"
-}
-array(1) {
-  [u"test"]=>
-  unicode(8) "new
-line"
-}
-array(1) {
-  [u"test"]=>
-  unicode(16) "test const value"
-}
-array(1) {
-  [u"section"]=>
-  array(1) {
-    [u"test"]=>
-    unicode(5) "hello"
-  }
-}
-array(1) {
-  [u"test"]=>
-  unicode(5) "hello"
-}
-array(1) {
-  [u"section.test"]=>
-  unicode(5) "hello"
-}
-array(1) {
-  [u"section"]=>
-  array(1) {
-    [u"section.test"]=>
-    unicode(5) "hello"
-  }
-}
-array(1) {
-  [u"section"]=>
-  array(1) {
-    [1]=>
-    unicode(1) "2"
-  }
-}
-array(1) {
-  [1]=>
-  unicode(1) "2"
-}
-array(1) {
-  [u"test"]=>
-  unicode(5) "test4"
-}
-Done
-
index a69109b930f45fd1a6b7cf4a963ed1ab2913f61e..85b5113a0d62aaad0e0a2acbbafa58c88cb7281d 100644 (file)
@@ -548,6 +548,9 @@ PHP_INI_BEGIN()
 
        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()
 /* }}} */
 
@@ -1114,7 +1117,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;
index 7b8eb80e9ab6d180c3a26a503070ca0bc10342db..337ab44044d2161e748948de7de5a3640b22493d 100644 (file)
@@ -147,6 +147,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;
 };
 
 
index 9108d790a87d7e7557ba6a84f4330a22c5abede2..2e889ddb7e3b146057e995748643f52c777911cf 100644 (file)
@@ -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)
@@ -263,14 +341,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;
        }
 
@@ -280,7 +352,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);
 
        open_basedir = PG(open_basedir);
 
@@ -488,8 +559,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;
@@ -497,6 +569,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);
@@ -510,10 +584,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;
@@ -524,12 +612,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);
+                                                       }
                                                }
                                        }
                                }
@@ -537,14 +627,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);
                                }
@@ -554,7 +647,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;
@@ -590,6 +685,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)
@@ -655,6 +825,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
index a2b3125c3ab4b64e457245e391b4e1d828f44404..9034a9cce219e665517ed25730c258bdf8b67c33 100644 (file)
@@ -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
index 48dd4e14e5f95f5c223d1509eb91602da5f14c2e..78415155d516c395936ae8a20573adb49ce00c2b 100644 (file)
 ; 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 ;
index 1a293231e1d22149e9415cd3b71012301afba578..4532d8eeac2266e2d6a9fe8aa10b746887206482 100644 (file)
 ;     Using short tags is discouraged when developing code meant for redistribution
 ;     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 ;
 ;;;;;;;;;;;;;;;;;;;;
index 8dff949fc0b2e595bd6d10015f2477b0e56a8a67..2e3b2a8d9c0a99b6eef624be8e0f7c3b437dbd9e 100644 (file)
@@ -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)
@@ -563,12 +585,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));
@@ -584,7 +690,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 = {
@@ -594,7 +699,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 */
@@ -1081,6 +1186,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);
 }
 /* }}} */
 
@@ -1102,6 +1208,8 @@ static PHP_MINIT_FUNCTION(cgi)
  */
 static PHP_MSHUTDOWN_FUNCTION(cgi)
 {
+       zend_hash_destroy(&CGIG(user_config_cache));
+
        UNREGISTER_INI_ENTRIES();
        return SUCCESS;
 }