PHP INI Includes patch (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License Provides ability to do INI includes. To use: Apply the patch with the following commands: cd php-X.X.X patch -p1 -i ini_includes.phpXXX.patch Rebuild/install php and you'll have the ability to add the following to your PHP ini files: include "common.ini" or include "/path/*" Feedback and suggestions: diff --git a/Zend/zend_ini.h b/Zend/zend_ini.h index db9df19..2bb78b9 100644 --- a/Zend/zend_ini.h +++ b/Zend/zend_ini.h @@ -202,6 +202,7 @@ END_EXTERN_C() #define ZEND_INI_PARSER_ENTRY 1 #define ZEND_INI_PARSER_SECTION 2 #define ZEND_INI_PARSER_POP_ENTRY 3 +#define ZEND_INI_PARSER_INCLUDE 4 /**** PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ typedef struct _zend_ini_parser_param { zend_ini_parser_cb_t ini_parser_cb; diff --git a/Zend/zend_ini_parser.y b/Zend/zend_ini_parser.y index 60b9258..df38a3f 100644 --- a/Zend/zend_ini_parser.y +++ b/Zend/zend_ini_parser.y @@ -52,6 +52,17 @@ extern FILE *ini_in; extern void init_cfg_scanner(void); #endif +/**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ +#ifdef ZTS +#define INI_SCNG_SAVE(buf) memcpy( buf, (((zend_scanner_globals*) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(ini_scanner_globals_id)])), sizeof(zend_scanner_globals)) +#define INI_SCNG_RESTORE(buf) memcpy( (((zend_scanner_globals*) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(ini_scanner_globals_id)])), buf, sizeof(zend_scanner_globals)) +#else +#define INI_SCNG_SAVE(buf) memcpy( (void*)buf, (void*)&ini_scanner_globals, sizeof(zend_scanner_globals)) +#define INI_SCNG_RESTORE(buf) memcpy( (void*)&ini_scanner_globals, (void*)buf, sizeof(zend_scanner_globals)) +#endif +/**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + + void zend_ini_do_op(char type, zval *result, zval *op1, zval *op2) { int i_result; @@ -234,6 +245,7 @@ ZEND_API int zend_parse_ini_string(char *str, zend_bool unbuffered_errors, zend_ %pure_parser %token TC_STRING %token TC_ENCAPSULATED_STRING +%token INCLUDE /**** PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ %token BRACK %token SECTION %token CFG_TRUE @@ -266,6 +278,27 @@ statement: free(Z_STRVAL($1)); free(Z_STRVAL($4)); } + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + | INCLUDE string_or_value { +#if DEBUG_CFG_PARSER + printf("include '%s'\n", $2.value.str.val); +#endif + zval *arg2; + zend_scanner_globals orig_scng; + char *orig_ini_filename; + TSRMLS_FETCH(); + MAKE_STD_ZVAL(arg2); + + orig_ini_filename = zend_ini_scanner_get_filename(TSRMLS_C); + ZVAL_STRING(arg2, orig_ini_filename, 1); + INI_SCNG_SAVE(&orig_scng); + ZEND_INI_PARSER_CB(&$2, arg2, ZEND_INI_PARSER_INCLUDE, ZEND_INI_PARSER_ARG); + zval_ptr_dtor(&arg2); + free($2.value.str.val); + INI_SCNG_RESTORE(&orig_scng); + zend_ini_scanner_set_filename(orig_ini_filename); + } + /**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ | 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' diff --git a/Zend/zend_ini_scanner.l b/Zend/zend_ini_scanner.l index 3f91bb6..3c84a2c 100644 --- a/Zend/zend_ini_scanner.l +++ b/Zend/zend_ini_scanner.l @@ -70,6 +70,13 @@ char *zend_ini_scanner_get_filename(TSRMLS_D) return ini_filename; } +/**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ +void zend_ini_scanner_set_filename(char *filename TSRMLS_DC) +{ + ini_filename = filename; +} +/**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + int zend_ini_open_file_for_scanning(zend_file_handle *fh TSRMLS_DC) { if (FAILURE == zend_stream_fixup(fh TSRMLS_CC)) { @@ -107,6 +114,12 @@ NEWLINE ("\r"|"\n"|"\r\n") %% + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ +[ ]*"include"[ ]* { + return INCLUDE; +} + /**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + [ ]*[\[][ ]*[\]][ ]* { return BRACK; } diff --git a/ext/standard/info.c b/ext/standard/info.c index 0c3118a..c6beec8 100644 --- a/ext/standard/info.c +++ b/ext/standard/info.c @@ -58,6 +58,7 @@ ZEND_EXTERN_MODULE_GLOBALS(iconv) PHPAPI extern char *php_ini_opened_path; PHPAPI extern char *php_ini_scanned_files; +PHPAPI extern char *php_ini_scanned_dirs; /**** PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ static int php_info_write_wrapper(const char *str, uint str_length) { @@ -502,8 +503,14 @@ PHPAPI void php_print_info(int flag TSRMLS_DC) php_info_print_table_row(2, "Configuration File (php.ini) Path", PHP_CONFIG_FILE_PATH); php_info_print_table_row(2, "Loaded Configuration File", php_ini_opened_path ? php_ini_opened_path : "(none)"); + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ +#if 0 if (strlen(PHP_CONFIG_FILE_SCAN_DIR)) { php_info_print_table_row(2, "Scan this dir for additional .ini files", PHP_CONFIG_FILE_SCAN_DIR); +#endif + if (php_ini_scanned_dirs) { + php_info_print_table_row(2, "Scan dirs for additional .ini files", php_ini_scanned_dirs); + /**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ if (php_ini_scanned_files) { php_info_print_table_row(2, "additional .ini files parsed", php_ini_scanned_files); } diff --git a/main/php_ini.c b/main/php_ini.c index bb52975..4900ba2 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -52,6 +52,15 @@ PHPAPI char *php_ini_opened_path=NULL; static php_extension_lists extension_lists; PHPAPI char *php_ini_scanned_files=NULL; +/**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ +PHPAPI char *php_ini_scanned_dirs=NULL; /* added for scanned ini file dirs */ +static char *php_ini_search_path=NULL; /* moved from php_init_config for ini includes ***/ +static zend_llist scanned_ini_list; /* moved from php_init_config for ini includes ***/ +static zend_llist opened_ini_list; /* added for opened ini files list */ +static zend_llist scanned_dir_list; /* added for scanned ini file dirs list */ +/**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + + /* {{{ php_ini_displayer_cb */ static void php_ini_displayer_cb(zend_ini_entry *ini_entry, int type) @@ -167,6 +176,19 @@ PHPAPI void display_ini_entries(zend_module_entry *module) */ static void php_config_ini_parser_cb(zval *arg1, zval *arg2, int callback_type, void *arg) { + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + zend_ini_parser_param *orig_ini_parser_param; + zend_file_handle fh = {0}; + struct stat finfo; + char ini_file[MAXPATHLEN]; + char sfilepath[MAXPATHLEN]; + char fullpath[MAXPATHLEN]; + struct dirent **fname_list; + int ndir, i; + char *tmp, *opened_path; + TSRMLS_FETCH(); + /**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + switch (callback_type) { case ZEND_INI_PARSER_ENTRY: { zval *entry; @@ -221,6 +243,74 @@ static void php_config_ini_parser_cb(zval *arg1, zval *arg2, int callback_type, case ZEND_INI_PARSER_SECTION: break; + + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + case ZEND_INI_PARSER_INCLUDE: + /* arg1=, arg2= */ + + /* save current state to be restored later */ + orig_ini_parser_param = CG(ini_parser_param); + + /* Look for include file/dir relative to including script, otherwise try the usual places */ + snprintf(sfilepath, MAXPATHLEN, "%s", Z_STRVAL_P(arg2)); /* directory of source filename */ + if ((tmp = strrchr(sfilepath, DEFAULT_SLASH)) != NULL) { + *(tmp+1) = 0; + } + snprintf(ini_file, MAXPATHLEN, "%s%s", sfilepath, Z_STRVAL_P(arg1)); + + fh.handle.fp = php_fopen_with_path(ini_file, "r", NULL, &opened_path TSRMLS_CC); + if (!fh.handle.fp) { + if ((fh.handle.fp = php_fopen_with_path(Z_STRVAL_P(arg1), "r", php_ini_search_path, &opened_path TSRMLS_CC)) != NULL ) { + snprintf(ini_file, MAXPATHLEN, "%s", opened_path); + } else { + zend_error(E_CORE_ERROR, "Unable to open included ini file or directory: '%s'", Z_STRVAL_P(arg1)); + } + } + + VCWD_STAT(ini_file, &finfo); + + if (S_ISDIR(finfo.st_mode)) { + zend_stream_close(&fh); + if (ini_file[strlen(ini_file)-1] != DEFAULT_SLASH) { + snprintf(ini_file, MAXPATHLEN, "%s%c", ini_file, DEFAULT_SLASH); /* opened_path will never have the trailing slash */ + } + tmp = estrndup(ini_file, strlen(ini_file)-1); + zend_llist_add_element(&scanned_dir_list, &tmp); + + if ((ndir = php_scandir(ini_file, &fname_list, 0, php_alphasort)) > 0) { + for (i=0; i < ndir; i++) { + if (!(tmp = strrchr(fname_list[i]->d_name, '.')) || (tmp && strcmp(tmp, ".ini"))) { + free(fname_list[i]); + continue; + } + snprintf(fullpath, MAXPATHLEN, "%s%s", ini_file, fname_list[i]->d_name); + if (VCWD_STAT(fullpath, &finfo) == 0) { + if (S_ISREG(finfo.st_mode) || S_ISLNK(finfo.st_mode)) { + if ((fh.handle.fp = VCWD_FOPEN(fullpath, "r"))) { + fh.filename = fullpath; + fh.type = ZEND_HANDLE_FP; + tmp = estrndup(fullpath, strlen(fullpath)); + zend_llist_add_element(&scanned_ini_list, &tmp); + zend_parse_ini_file(&fh, 1, php_config_ini_parser_cb, &extension_lists); + } + } + } + free(fname_list[i]); + } + free(fname_list); + } + } else if (S_ISREG(finfo.st_mode) || S_ISLNK(finfo.st_mode)) { + fh.type = ZEND_HANDLE_FP; + fh.filename = opened_path; + tmp = estrndup(opened_path, strlen(opened_path)); + zend_llist_add_element(&opened_ini_list, &tmp); + zend_parse_ini_file(&fh, 1, php_config_ini_parser_cb, &extension_lists); + } + + CG(ini_parser_param) = orig_ini_parser_param; + + break; + /**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ } } /* }}} */ @@ -259,7 +349,7 @@ static void pvalue_config_destructor(zval *pvalue) int php_init_config(TSRMLS_D) { char *php_ini_file_name = NULL; - char *php_ini_search_path = NULL; + /* char *php_ini_search_path = NULL; */ /**** PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ int safe_mode_state; char *open_basedir; int free_ini_search_path = 0; @@ -267,7 +357,7 @@ int php_init_config(TSRMLS_D) struct stat sb; char ini_file[MAXPATHLEN]; char *p; - zend_llist scanned_ini_list; + /* zend_llist scanned_ini_list; */ /**** PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ int l, total_l=0; zend_llist_element *element; @@ -283,6 +373,13 @@ int php_init_config(TSRMLS_D) 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); + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + zend_llist_init(&opened_ini_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1); + zend_llist_init(&scanned_dir_list, sizeof(char *), (llist_dtor_func_t) free_estring, 1); + p = estrndup(PHP_CONFIG_FILE_SCAN_DIR, strlen(PHP_CONFIG_FILE_SCAN_DIR)); + zend_llist_add_element(&scanned_dir_list, &p); + /**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + safe_mode_state = PG(safe_mode); open_basedir = PG(open_basedir); @@ -483,9 +580,13 @@ int php_init_config(TSRMLS_D) } } + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ +#if 0 if (free_ini_search_path) { efree(php_ini_search_path); } +#endif + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ PG(safe_mode) = safe_mode_state; PG(open_basedir) = open_basedir; @@ -531,7 +632,7 @@ int php_init_config(TSRMLS_D) 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; + /* total_l += l + 2; */ /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ p = estrndup(ini_file, l); zend_llist_add_element(&scanned_ini_list, &p); } @@ -545,7 +646,13 @@ int php_init_config(TSRMLS_D) * 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) { + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + /* if (total_l) { */ + if (scanned_ini_list.count > 0) { + for (total_l = 0, element = scanned_ini_list.head; element; element = element->next) { + total_l += strlen(element->data) + 2; + } + /**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ php_ini_scanned_files = (char *) malloc(total_l); *php_ini_scanned_files = '\0'; for (element = scanned_ini_list.head; element; element = element->next) { @@ -561,6 +668,38 @@ int php_init_config(TSRMLS_D) zend_parse_ini_string(sapi_module.ini_entries, 1, php_config_ini_parser_cb, &extension_lists); } + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + if (opened_ini_list.count > 0) { + for (total_l = strlen(php_ini_opened_path)+1, element = opened_ini_list.head; element; element = element->next) { + total_l += strlen(*(char**)element->data) + 2; + } + php_ini_opened_path = (char *) realloc(php_ini_opened_path, total_l); + strlcat(php_ini_opened_path, ",\n", total_l); + for (element = opened_ini_list.head; element; element = element->next) { + strlcat(php_ini_opened_path, *(char **)element->data, total_l); + strlcat(php_ini_opened_path, element->next ? ",\n" : "\n", total_l); + } + } + zend_llist_destroy(&opened_ini_list); + + if (scanned_dir_list.count > 0) { + for (total_l = 0, element = scanned_dir_list.head; element; element = element->next) { + total_l += strlen(*(char**)element->data) + 2; + } + php_ini_scanned_dirs = (char *) malloc(total_l); + *php_ini_scanned_dirs = '\0'; + for (element = scanned_dir_list.head; element; element = element->next) { + strlcat(php_ini_scanned_dirs, *(char **)element->data, total_l); + strlcat(php_ini_scanned_dirs, element->next ? ",\n" : "\n", total_l); + } + } + zend_llist_destroy(&scanned_dir_list); + + if (free_ini_search_path) { + efree(php_ini_search_path); + } + /**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + return SUCCESS; } /* }}} */ @@ -578,6 +717,12 @@ int php_shutdown_config(void) free(php_ini_scanned_files); php_ini_scanned_files = NULL; } + /**** BEGIN PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ + if (php_ini_scanned_dirs) { + free(php_ini_scanned_dirs); + php_ini_scanned_dirs = NULL; + } + /**** END PATCH: "php_ini_includes", (c)2007-2008 Brian Shire & Facebook Inc, Licensed under MIT License ***/ return SUCCESS; } /* }}} */