Hi list, Apologies if I'm sending this to the wrong list; I couldn't see another which was more appropriate on the PHP Mailing Lists page.
I'm developing a PHP extension for which part of the functionality can be described in a nutshell as: * at request start-up time, build a map of identifiers to path-names, read from a configuration file; * whilst a user script is being processed, a function provided by the extension can be called to add, remove or modify items in the mapping; * a user script can call a function, passing it an identifier in the map, and the extension should simulate require_once being called with the corresponding pathname (with some transformation applied). For example, if the configuration file specified that 'foo' mapped to /www/common/foo, calling the above function with a parameter of 'foo' might simulate require_once('/www/common/foo/script.php') (where the transformation applied in this case is appending 'script.php' to the given pathname). A prototype implementation written in PHP itself works well enough, but obviously there are scoping issues with such an implementation (i.e., any scripts included are included within the scope of the function, not the caller) which I want to avoid through the use of an extension. Obviously, much of this is pretty trivial and straightforward. My problem is the actual simulation of require_once itself. As it's a language intrinsic, there's no simply-exposed API for performing the same action. Digging through the PHP sources, I've come across zend_execute_scripts(), which seems to fit the bill, although there's no documentation and very few examples of it being used outside of the PHP engine itself. >From skimming as many bits of the PHP sources that actually use zend_execute_scripts() that I could find, the code I've come up with isn't hugely dissimilar to this: static int do_required(const char *filename TSRMLS_DC) { int r; zend_file_handle zh; if(SUCCESS != (r = zend_stream_open(filename, &zh TSRMLS_CC))) { return r; } if(NULL == zh.opened_path) { zh.opened_path = estrdup(filename); } if(zend_hash_add_empty_element(&EG(included_files), zh.opened_path, strlen(zh.opened_path) + 1) == SUCCESS) { r = zend_execute_scripts(ZEND_REQUIRE_ONCE TSRMLS_CC, NULL, 1, &zh); } zend_stream_close(TSRMLS_CC); return r; } Simple enough, right? Wrong. I'm hoping at this point that somebody who knows the Zend internals pretty well will immediately spot which things I'm not initialising, saving/restoring, or happen to be double-freeing at this point, because I'm at a loss. My symptoms are this: * If calls to this function are nested, and an inner call results in zend_stream_open() failing, I get faults in zend_get_executed_lineno(), suggesting corruption somewhere. * If I save, reset and restore return_value_ptr_ptr, active_op_array, opline_ptr before doing anything, things seem better, but the Warning message reported when the file can't be opened gives the error location as [no active file] on line 0, which is less than ideal. * If I only save/reset/restore around the call to zend_get_executed_lineno() itself, things seem to work until I get as far as installing the extension for my Apache 2.2 module build of PHP: at which point, as soon as there's some nesting things start to go bad (errors reported or not). Removing the final zend_stream_close() call stops Apache from dying, but I strongly suspect that I'm just masking the problem rather than fixing it. So, my questions are: what am I doing wrong, and is there a better way to accomplish the same thing? I considered evaluating a script instead of trying to simulate require_once itself, but that seemed incredibly kludgy. Any help appreciated. Thanks, Mo. -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php