diff --git a/README.md b/README.md index 5fdf092..b84fcb7 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Yaconf is a configurations container, it parses ini files, and store the result - Zero-copy while accesses configurations - Support sections, sections inheritance - Configurations reload automatically after changed +- Support multi-level directory ### Install @@ -42,6 +43,11 @@ $ make && make install In which interval Yaconf will detect ini file's change(by directory's mtime), if it is set to zero, you have to restart php to reloading configurations. ``` +- yaconf.check_type +``` + if it is set to zero (default) will detect ini file's change by directory's mtime , + otherwise will detect each file's mtime. +``` ### APIs @@ -80,6 +86,18 @@ children="NULL" [children:base] children="set" ```` + +and test/foo.ini( fullpath is : /tmp/yaconf/test/foo.ini) +````ini +name="test/yaconf" +year=2015 +features[]="fast" +features.1="light" +features.plus="zero-copy" +features.constant=PHP_VERSION +features.env=${HOME} +```` + #### Run lets access the configurations @@ -145,5 +163,29 @@ array(2) { } */ ```` - Children section has inherited values in base sections, and children were able to override the values they want. + + +##### test/foo.ini +Now let's see the ini in the directories : +````php +php7 -r 'var_dump(Yaconf::get("test/foo"));' +/* +array(2) { + ["base"]=> + array(2) { + ["parent"]=> + string(6) "test/yaconf" + ["children"]=> + string(4) "NULL" + } + ["children"]=> + array(2) { + ["parent"]=> + string(6) "yaconf" + ["children"]=> + string(3) "set" + } +} +*/ +```` diff --git a/php_yaconf.h b/php_yaconf.h index 305a2a4..700b503 100644 --- a/php_yaconf.h +++ b/php_yaconf.h @@ -45,10 +45,14 @@ extern zend_module_entry yaconf_module_entry; #define YACONF_DEBUG(m) #endif + +#define MAX_SEARCH_DIR_SIZE 10240 + ZEND_BEGIN_MODULE_GLOBALS(yaconf) char *directory; int parse_err; #ifndef ZTS + long check_type; long check_delay; time_t last_check; time_t directory_mtime; diff --git a/tests/001.phpt b/tests/001.phpt index d6ed78a..734dca8 100644 --- a/tests/001.phpt +++ b/tests/001.phpt @@ -2,6 +2,8 @@ Check for Yaconf presence --SKIPIF-- +--INI-- +yaconf.directory={PWD}/inis/data --FILE-- --INI-- -yaconf.directory={PWD}/inis/ +yaconf.directory={PWD}/inis/data --FILE-- --EXPECTF-- Array @@ -92,3 +93,24 @@ Array ) ) +Array +( + [common] => Array + ( + [domain] => Array + ( + [allow] => Array + ( + [0] => 127.0.0.1 + ) + + [deny] => Array + ( + [0] => 192.168.1.0 + ) + + ) + + ) + +) diff --git a/tests/003.phpt b/tests/003.phpt index ac15122..e886f45 100644 --- a/tests/003.phpt +++ b/tests/003.phpt @@ -3,7 +3,7 @@ Check for Yaconf --SKIPIF-- --INI-- -yaconf.directory={PWD}/inis/ +yaconf.directory={PWD}/inis/data --FILE-- --INI-- -yaconf.directory={PWD}/inis/ +yaconf.directory={PWD}/inis/data --FILE-- --EXPECTF-- PHP Warning: Nesting too deep? Only less than 16 level inheritance is allowed in Unknown on line 0 Warning: Nesting too deep? Only less than 16 level inheritance is allowed in Unknown on line 0 bool(false) +array(2) { + ["name"]=> + string(1) "1" + ["student"]=> + array(1) { + ["test"]=> + string(1) "2" + } +} \ No newline at end of file diff --git a/tests/inis/a.ini b/tests/inis/data/a.ini similarity index 100% rename from tests/inis/a.ini rename to tests/inis/data/a.ini diff --git a/tests/inis/a.ini.OLD b/tests/inis/data/a.ini.OLD similarity index 100% rename from tests/inis/a.ini.OLD rename to tests/inis/data/a.ini.OLD diff --git a/tests/inis/b.ini b/tests/inis/data/b.ini similarity index 100% rename from tests/inis/b.ini rename to tests/inis/data/b.ini diff --git a/tests/inis/data/common/a.ini b/tests/inis/data/common/a.ini new file mode 100644 index 0000000..7911840 --- /dev/null +++ b/tests/inis/data/common/a.ini @@ -0,0 +1,4 @@ +[common] +domain = 'com' +domain.allow[] = '127.0.0.1' +domain.deny[] = '192.168.1.0' \ No newline at end of file diff --git a/tests/inis/d.ini b/tests/inis/data/d.ini similarity index 100% rename from tests/inis/d.ini rename to tests/inis/data/d.ini diff --git a/tests/inis/e.ini b/tests/inis/data/e.ini similarity index 100% rename from tests/inis/e.ini rename to tests/inis/data/e.ini diff --git a/tests/inis/err/section/b.ini b/tests/inis/err/section/b.ini new file mode 100644 index 0000000..84293cf --- /dev/null +++ b/tests/inis/err/section/b.ini @@ -0,0 +1,3 @@ +name=1 +[student:name] +test=2 \ No newline at end of file diff --git a/tests/issue19.phpt b/tests/issue19.phpt index d742a3e..471ccfb 100644 --- a/tests/issue19.phpt +++ b/tests/issue19.phpt @@ -3,7 +3,7 @@ ISSUE #19 Memory leak on foreach and reference --SKIPIF-- --INI-- -yaconf.directory={PWD}/inis/ +yaconf.directory={PWD}/inis/data --FILE-- 0) { - uint32_t i; - unsigned char c; - struct zend_stat sb; - zend_file_handle fh = {0}; + dirname_len = strlen(dirname); + search_dir_names[search_dir_size++] = estrdup(dirname); - PALLOC_HASHTABLE(ini_containers); - zend_hash_init(ini_containers, ndir, NULL, NULL, 1); + PALLOC_HASHTABLE(ini_containers); + zend_hash_init(ini_containers, 32, NULL, NULL, 1); - PALLOC_HASHTABLE(parsed_ini_files); - zend_hash_init(parsed_ini_files, ndir, NULL, NULL, 1); + PALLOC_HASHTABLE(parsed_ini_files); + zend_hash_init(parsed_ini_files, 32, NULL, NULL, 1); - for (i = 0; i < ndir; i++) { - if (!(p = strrchr(namelist[i]->d_name, '.')) || strcmp(p, ".ini")) { - free(namelist[i]); - continue; - } + while( search_dir_size > 0 ){ + cur_dir_name = search_dir_names[--search_dir_size]; - snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, namelist[i]->d_name); - - if (VCWD_STAT(ini_file, &sb) == 0) { - if (S_ISREG(sb.st_mode)) { - yaconf_filenode node; - if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) { - fh.filename = ini_file; - fh.type = ZEND_HANDLE_FP; - ZVAL_UNDEF(&active_ini_file_section); - YACONF_G(parse_err) = 0; - php_yaconf_hash_init(&result, 128); - if (zend_parse_ini_file(&fh, 1, 0 /* ZEND_INI_SCANNER_NORMAL */, - php_yaconf_ini_parser_cb, (void *)&result) == FAILURE || YACONF_G(parse_err)) { - YACONF_G(parse_err) = 0; - php_yaconf_hash_destroy(Z_ARRVAL(result)); + if ((ndir = php_scandir(cur_dir_name, &namelist, 0, php_alphasort)) > 0) { + uint32_t i; + unsigned char c; + struct zend_stat sb; + zend_file_handle fh = {0}; + for (i = 0; i < ndir; i++) { + if (strcmp(namelist[i]->d_name, ".") == 0 || strcmp(namelist[i]->d_name, "..") == 0) { + free(namelist[i]); + continue; + } + + ini_file_len = snprintf(ini_file, MAXPATHLEN, "%s%c%s", cur_dir_name, DEFAULT_SLASH, namelist[i]->d_name); + + if (ini_file_len < 0 || VCWD_STAT(ini_file, &sb) == 0) { + if (S_ISREG(sb.st_mode)) { + if (!(p = strrchr(namelist[i]->d_name, '.')) || strcmp(p, ".ini")) { free(namelist[i]); continue; } - } - - php_yaconf_symtable_update(ini_containers, - php_yaconf_str_persistent(namelist[i]->d_name, p - namelist[i]->d_name), &result); - node.filename = zend_string_init(namelist[i]->d_name, strlen(namelist[i]->d_name), 1); - node.mtime = sb.st_mtime; - zend_hash_update_mem(parsed_ini_files, node.filename, &node, sizeof(yaconf_filenode)); + yaconf_filenode node; + if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) { + fh.filename = ini_file; + fh.type = ZEND_HANDLE_FP; + ZVAL_UNDEF(&active_ini_file_section); + YACONF_G(parse_err) = 0; + php_yaconf_hash_init(&result, 128); + if (zend_parse_ini_file(&fh, 1, 0 /* ZEND_INI_SCANNER_NORMAL */, + php_yaconf_ini_parser_cb, (void *)&result) == FAILURE || YACONF_G(parse_err)) { + YACONF_G(parse_err) = 0; + php_yaconf_hash_destroy(Z_ARRVAL(result)); + free(namelist[i]); + continue; + } + } + + php_yaconf_symtable_update(ini_containers, + php_yaconf_str_persistent(ini_file+dirname_len+1, ini_file_len - dirname_len - 1 - strlen(p) ), &result); + + node.filename = zend_string_init(ini_file+dirname_len+1, ini_file_len - dirname_len - 1 , 1); + node.mtime = sb.st_mtime; + zend_hash_update_mem(parsed_ini_files, node.filename, &node, sizeof(yaconf_filenode)); + }else if(S_ISDIR(sb.st_mode) ){ + if(search_dir_size >= MAX_SEARCH_DIR_SIZE){ + php_error(E_WARNING, "max dir size '%d'", search_dir_size); + }else{ + search_dir_names[search_dir_size++] = estrdup(ini_file); + } + } + } else { + php_error(E_ERROR, "Could not stat '%s'", ini_file); } - } else { - php_error(E_ERROR, "Could not stat '%s'", ini_file); + free(namelist[i]); } - free(namelist[i]); + free(namelist); + } else { + php_error(E_ERROR, "Couldn't opendir '%s'", dirname); } -#ifndef ZTS - YACONF_G(last_check) = time(NULL); -#endif - free(namelist); - } else { - php_error(E_ERROR, "Couldn't opendir '%s'", dirname); + + efree(cur_dir_name); } + #ifndef ZTS + YACONF_G(last_check) = time(NULL); + #endif } return SUCCESS; @@ -571,84 +600,109 @@ PHP_RINIT_FUNCTION(yaconf) return SUCCESS; } else { char *dirname; + int dirname_len =0; struct zend_stat dir_sb = {0}; YACONF_G(last_check) = time(NULL); if ((dirname = YACONF_G(directory)) && !VCWD_STAT(dirname, &dir_sb) && S_ISDIR(dir_sb.st_mode)) { - if (dir_sb.st_mtime == YACONF_G(directory_mtime)) { + if (YACONF_G(check_type) == 0 && dir_sb.st_mtime == YACONF_G(directory_mtime) ) { YACONF_DEBUG("config directory is not modefied"); return SUCCESS; } else { zval result; int i, ndir; struct dirent **namelist; - char *p, ini_file[MAXPATHLEN]; + char *p, ini_file[MAXPATHLEN],ini_file_len=0; + + char *search_dir_names[MAX_SEARCH_DIR_SIZE]; + int search_dir_size=0; + char *cur_dir_name = NULL; YACONF_G(directory_mtime) = dir_sb.st_mtime; - if ((ndir = php_scandir(dirname, &namelist, 0, php_alphasort)) > 0) { - zend_string *file_key; - struct zend_stat sb; - zend_file_handle fh = {0}; - yaconf_filenode *node = NULL; + dirname_len = strlen(dirname); + search_dir_names[search_dir_size++] = estrdup(dirname); + while( search_dir_size > 0 ){ + cur_dir_name = search_dir_names[--search_dir_size]; - for (i = 0; i < ndir; i++) { - zval *orig_ht = NULL; - if (!(p = strrchr(namelist[i]->d_name, '.')) || strcmp(p, ".ini")) { - free(namelist[i]); - continue; - } + if ((ndir = php_scandir(cur_dir_name, &namelist, 0, php_alphasort)) > 0) { + zend_string *file_key; + struct zend_stat sb; + zend_file_handle fh = {0}; + yaconf_filenode *node = NULL; - snprintf(ini_file, MAXPATHLEN, "%s%c%s", dirname, DEFAULT_SLASH, namelist[i]->d_name); - if (VCWD_STAT(ini_file, &sb) || !S_ISREG(sb.st_mode)) { - free(namelist[i]); - continue; - } + for (i = 0; i < ndir; i++) { + if (strcmp(namelist[i]->d_name, ".") == 0 || strcmp(namelist[i]->d_name, "..") == 0) { + free(namelist[i]); + continue; + } + + zval *orig_ht = NULL; + + ini_file_len = snprintf(ini_file, MAXPATHLEN, "%s%c%s", cur_dir_name, DEFAULT_SLASH, namelist[i]->d_name); + if (ini_file_len < 0 || VCWD_STAT(ini_file, &sb) || !S_ISREG(sb.st_mode)) { + if(S_ISDIR(sb.st_mode) ){ + if(search_dir_size >= MAX_SEARCH_DIR_SIZE){ + php_error(E_WARNING, "max dir size '%d'", search_dir_size); + }else{ + search_dir_names[search_dir_size++] = estrdup(ini_file); + } + } + free(namelist[i]); + continue; + } - if ((node = (yaconf_filenode*)zend_hash_str_find_ptr(parsed_ini_files, namelist[i]->d_name, strlen(namelist[i]->d_name))) == NULL) { - YACONF_DEBUG("new configure file found"); - } else if (node->mtime == sb.st_mtime) { - free(namelist[i]); - continue; - } + if (!(p = strrchr(namelist[i]->d_name, '.')) || strcmp(p, ".ini")) { + free(namelist[i]); + continue; + } - if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) { - fh.filename = ini_file; - fh.type = ZEND_HANDLE_FP; - ZVAL_UNDEF(&active_ini_file_section); - YACONF_G(parse_err) = 0; - php_yaconf_hash_init(&result, 128); - if (zend_parse_ini_file(&fh, 1, 0 /* ZEND_INI_SCANNER_NORMAL */, - php_yaconf_ini_parser_cb, (void *)&result) == FAILURE || YACONF_G(parse_err)) { - YACONF_G(parse_err) = 0; - php_yaconf_hash_destroy(Z_ARRVAL(result)); + if ((node = (yaconf_filenode*)zend_hash_str_find_ptr(parsed_ini_files, ini_file+dirname_len+1, ini_file_len - dirname_len - 1)) == NULL) { + YACONF_DEBUG("new configure file found"); + } else if (node->mtime == sb.st_mtime) { free(namelist[i]); continue; } - } + if ((fh.handle.fp = VCWD_FOPEN(ini_file, "r"))) { + fh.filename = ini_file; + fh.type = ZEND_HANDLE_FP; + ZVAL_UNDEF(&active_ini_file_section); + YACONF_G(parse_err) = 0; + php_yaconf_hash_init(&result, 128); + if (zend_parse_ini_file(&fh, 1, 0 /* ZEND_INI_SCANNER_NORMAL */, + php_yaconf_ini_parser_cb, (void *)&result) == FAILURE || YACONF_G(parse_err)) { + YACONF_G(parse_err) = 0; + php_yaconf_hash_destroy(Z_ARRVAL(result)); + free(namelist[i]); + continue; + } + } - file_key = php_yaconf_str_persistent(namelist[i]->d_name, p - namelist[i]->d_name); - if ((orig_ht = zend_symtable_find(ini_containers, file_key)) != NULL) { - php_yaconf_hash_destroy(Z_ARRVAL_P(orig_ht)); - ZVAL_COPY_VALUE(orig_ht, &result); - free(file_key); - } else { - php_yaconf_symtable_update(ini_containers, file_key, &result); - } - if (node) { - node->mtime = sb.st_mtime; - } else { - yaconf_filenode n = {0}; - n.filename = zend_string_init(namelist[i]->d_name, strlen(namelist[i]->d_name), 1); - n.mtime = sb.st_mtime; - zend_hash_update_mem(parsed_ini_files, n.filename, &n, sizeof(yaconf_filenode)); + file_key = php_yaconf_str_persistent(ini_file+dirname_len+1, ini_file_len - dirname_len - 1 - strlen(p)); + if ((orig_ht = zend_symtable_find(ini_containers, file_key)) != NULL) { + php_yaconf_hash_destroy(Z_ARRVAL_P(orig_ht)); + ZVAL_COPY_VALUE(orig_ht, &result); + free(file_key); + } else { + php_yaconf_symtable_update(ini_containers, file_key, &result); + } + + if (node) { + node->mtime = sb.st_mtime; + } else { + yaconf_filenode n = {0}; + n.filename = zend_string_init(ini_file+dirname_len+1, ini_file_len - dirname_len - 1, 1); + n.mtime = sb.st_mtime; + zend_hash_update_mem(parsed_ini_files, n.filename, &n, sizeof(yaconf_filenode)); + } + free(namelist[i]); } - free(namelist[i]); + free(namelist); } - free(namelist); + efree(cur_dir_name); } return SUCCESS; }