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?
As I was advised at PHP internals (internals@lists.php.net), 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.