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

Reply via email to