Search code examples
phpchashtablephp-7php-internals

Passing HashTable data from php.ini to an extension method: corrupted values


I am trying to make a PHP (7.4) extension use in an extension method a setting from php.ini (like myext.map=key1=val1,key2=val2;) parsed as an associative array. I want the parsing to happen in PHP_MINIT_FUNCTION(myext).

So, I declared a php setting, uncommented REGISTER_INI_ENTRIES(); in PHP_MINIT_FUNCTION(myext), declared:

ZEND_BEGIN_MODULE_GLOBALS(myext)
    HashTable map;
ZEND_END_MODULE_GLOBALS(myext)

and filled MYEXT_G(map) from the php.ini settingJR:

PHP_INI_MH(myext_update_map) {
// The part that parses new_value into (key => val) pairs is skipped. It works.
//...
// key = val assignments have been counted.
zend_hash_init(&MYEXT_G(map), count, NULL, NULL, 1); // 1 for "persistent".
// ...
// Iterating (key => value) pairs.
    // key, key_len, val, val_len have been successfully extracted from php.ini.
    zend_string * key_z = zend_string_init( key, key_len, 1 );
    zval val_z;
    ZVAL_STRINGL( &val_z, val, val_len );
    zend_hash_add_new( &MYEXT_G(map), key_z, &val_z );

Then, in a PHP method, I try to read data from &MYEXT_G(map):

PHP_METHOD(MyExt, getMap) {
// ...
zend_string * key_z;
zval * val_z;
ZEND_HASH_FOREACH_STR_KEY_VAL(&MYEXT_G(map), key_z, val_z)
    php_error_docref( NULL TSRMLS_CC, E_WARNING, "ZSTR_VAL(key_z) = %s\n", ZSTR_VAL(key_z) );
    php_error_docref( NULL TSRMLS_CC, E_WARNING, "Z_STRVAL_P(val_z) = %s\n", Z_STRVAL_P(val_z) );
    php_error_docref( NULL TSRMLS_CC, E_WARNING, "Z_STRLEN_P(val_z) = %lu\n", Z_STRLEN_P(val_z) );
ZEND_HASH_FOREACH_END();

If the PHP script containing MyExt::getLibraries() is called from the command line, the PHP warnings print keys and values as they were set in php.ini.

However, if the script is called by HTTP request, in the warning messages, ZSTR_VAL(key_z) is correct but both Z_STRVAL_P(val_z) and Z_STRLEN_P(val_z) contain garbage.

All involved functions are either defined as PHP macros or receive TSRMLS_DC.

It seems that the values (but not keys) in a global hashtable get deallocated between module initialisation and serving HTTP request.

I have made sure that scalar values in MYEXT_G survive from PHP_MINIT_FUNCTION(myExt) into PHP method called from HTTP request.

So, how can I make sure that HashTable values are really persistent?


Solution

  • As I was advised at PHP internals ([email protected]), ZVAL_STRINGL allocates a per-request string; and I needed ZVAL_STR: ZVAL_STR(&val_z, zend_string_init(val, val_len, 1));.

    Then I added val_z to the global hash table: zend_hash_str_add_new( &MYEXT_G(map), key_z, key_len, &val_z );. The hashtable &MYEXT_G(map) was readable (both keys and values) while serving HTTP requests.