Newbie question: zend_hash_find

php.internals

Arnold Daniels

19 years ago
Hi, I'm new at PHP internals and have read the 'Extension Writing' tutorial (part 1-3) of devzone.zend.com. I'm writing a set of parsers, to parse different kind of configuration formats. The first is an .ini parser. The code almost works correctly. The only problem I have is that sections get overwritten if appear multiple times in the .ini content. [abc] test = 1 [abc] xyz = 2 Should result in: array('abc'=>array('test'=>1, 'xyz'=>2)) but instead returns: array('abc'=>array('xyz'=>2)) The logical conclusion is that my zend_hash_find call doesn't work correctly and is always returning FAILURE. Though I've got no clue what's wrong. Perhaps someone could have a quick look at the code. Thanks a lot, Arnold -------------------- /** * Parse .ini content into a zend array */ PHP_FUNCTION(qconf_ini_parse) { char *content; int len; char *c; char *ln_start; char key[ASCIILINESZ+1]; char val[ASCIILINESZ+1]; zval *cur_out; zval **fnd_sec; char t; // boolean: trim value yes/no if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &content, &len) == FAILURE) { RETURN_NULL(); } array_init(return_value); cur_out = return_value; ln_start = content; for (c=content; c <= content+len; c++) { if (*c == '\n' || *c == '\0' || c == content+len) { *c = '\0'; t = 0; ln_start = strskp(ln_start); /* Skip leading spaces */ if (*ln_start!=';' && *ln_start!='#' && *ln_start!=0) { /* Skip comment lines */ if (sscanf(ln_start, "[%[^]]", key)==1) { /* Valid section name */ if (zend_hash_find(Z_ARRVAL_P(return_value), key, strlen(key), (void**)&fnd_sec) == FAILURE) { ALLOC_INIT_ZVAL(cur_out); array_init(cur_out); add_assoc_zval(return_value, key, cur_out); } else { cur_out = *fnd_sec; } } else if (sscanf (ln_start, "%[^=] = \"%[^\"]\"", key, val) == 2 || sscanf (ln_start, "%[^=] = '%[^\']'", key, val) == 2 || (t=1 && sscanf (ln_start, "%[^=] = %[^;#]", key, val) == 2)) { strcpy(key, strlwc(strcrop(key))); /* * sscanf cannot handle "" or '' as empty value, * this is done here */ if (!strcmp(val, "\"\"") || !strcmp(val, "''")) { val[0] = (char)0; } else if (t) { strcpy(val, strcrop(val)); } add_assoc_string(cur_out, key, val, 1); } } ln_start = c+1; } } }

M. Sokolewicz

19 years ago
hi, Have you tried looking at how the parse_ini_file function works? that should answer your questions on this. - tul Arnold Daniels wrote:

Arnold Daniels

19 years ago
Hi again, Well I first though of copying and changing parse_ini_file. But the implementation of the function is located in the zend core, spread out of 3 files and does all kind of strange things with global variables. My C skills are ok but not great and I've got next to no experience in writing/editing PHP extensions. I've managed to write an extension by following the (great) tutorial, but understanding parts of the zend core is simply out of my reach at this moment. I know newbie questions are always a pain in the butt, but I hope you see that I have read the manual and besides I have to start somewhere. Perhaps you can have a look at the code still. Off course, I can send you all the file of the extension if you like. Thank you, Arnold -----Original Message----- From: M.Sokolewicz [mailto:tularis@php.net] Sent: woensdag 8 november 2006 19:40 To: Arnold Daniels Cc: internals@lists.php.net Subject: [PHP-DEV] Re: Newbie question: zend_hash_find hi, Have you tried looking at how the parse_ini_file function works? that should answer your questions on this. - tul Arnold Daniels wrote:
> Hi, > > I'm new at PHP internals and have read the 'Extension Writing' tutorial > (part 1-3) of devzone.zend.com. I'm writing a set of parsers, to parse > different kind of configuration formats. The first is an .ini parser. > The code almost works correctly. The only problem I have is that > sections get overwritten if appear multiple times in the .ini content. > > [abc] > test = 1 > [abc] > xyz = 2 > > Should result in: array('abc'=>array('test'=>1, 'xyz'=>2)) > but instead returns: array('abc'=>array('xyz'=>2)) > > The logical conclusion is that my zend_hash_find call doesn't work > correctly and is always returning FAILURE. Though I've got no clue > what's wrong. Perhaps someone could have a quick look at the code. > > Thanks a lot, > Arnold > > > -------------------- > > /** > * Parse .ini content into a zend array > */ > PHP_FUNCTION(qconf_ini_parse) > { > char *content; > int len; > > char *c; > char *ln_start; > char key[ASCIILINESZ+1]; > char val[ASCIILINESZ+1]; > zval *cur_out; > zval **fnd_sec; > > char t; // boolean: trim value yes/no > > if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &content, > &len) == FAILURE) { > RETURN_NULL(); > } > > array_init(return_value); > > cur_out = return_value; > ln_start = content; > > for (c=content; c <= content+len; c++) { > if (*c == '\n' || *c == '\0' || c == content+len) { > *c = '\0'; > > t = 0; > ln_start = strskp(ln_start); /* Skip leading spaces */ > if (*ln_start!=';' && *ln_start!='#' && *ln_start!=0) { /* > Skip comment lines */ > if (sscanf(ln_start, "[%[^]]", key)==1) { > /* Valid section name */ > if (zend_hash_find(Z_ARRVAL_P(return_value), key, > strlen(key), (void**)&fnd_sec) == FAILURE) { > ALLOC_INIT_ZVAL(cur_out); > array_init(cur_out); > add_assoc_zval(return_value, key, cur_out); > } else { > cur_out = *fnd_sec; > } > } else if (sscanf (ln_start, "%[^=] = \"%[^\"]\"", key, > val) == 2 > || sscanf (ln_start, "%[^=] = '%[^\']'", key, > val) == 2 > || (t=1 && sscanf (ln_start, "%[^=] = %[^;#]", > key, val) == 2)) { > strcpy(key, strlwc(strcrop(key))); > /* > * sscanf cannot handle "" or '' as empty value, > * this is done here > */ > if (!strcmp(val, "\"\"") || !strcmp(val, "''")) { > val[0] = (char)0; > } else if (t) { > strcpy(val, strcrop(val)); > } > add_assoc_string(cur_out, key, val, 1); > } > } > > ln_start = c+1; > } > } > } >
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php

Matt W

19 years ago
Hi Arnold, zend_hash_find() is always returning FAILURE because its "key_length" parameter *includes* the terminating '\0'. :-) So you need strlen(key) + 1. BTW, are any of your keys going to have numeric values ([123] sections etc.)? Then you'll also have a problem with hash_find() as it doesn't handle numeric string keys since PHP 5 (converting them to a true number index like when you use $arr['123']), but the add_assoc_* functions do. If that's an issue, you have to use zend_symtable_find() since PHP 5, which works exactly like hash_find() in PHP 4. Hope that helps. Matt ----- Original Message ----- From: "Arnold Daniels" Sent: Wednesday, November 08, 2006