[prev in list] [next in list] [prev in thread] [next in thread] 

List:       php-internals
Subject:    [PHP-DEV] RE : [PHP-DEV] Simulating require_once within an extension
From:       LAUPRETRE_François_(P) <francois.laupretre () ratp ! fr>
Date:       2007-02-26 10:26:54
Message-ID: C2F85D46E420FD4DB2065FE6901C6D0B410F19 () EXCHANGE04 ! info ! ratp
[Download RAW message or body]

Sorry, I can't help with your C extension but I can give you a solution I implemented in PHP 
for the same needs. I did using two mechanisms :

	- A stream wrapper to allow require_once to reference a virtual path. Not required if you 
require 'real' files. In PHK, I need it because I implement a virtual file system.

	- A 'tunneling' function like yours, used through an eval(). I agree it is not very 
elegant but, unfortunately, there is no 'uplevel' feature in PHP (could be an interesting 
addition, along with a who_called_me()). This function generally returns a 
'require_once <stream-wrapped path>' string,  but as we eval()uate the result, it can return 
any code, even a 'throw Exception', to be thrown from the calling scope.

If you want to see more about this example, download the PHK_Creator building kit from 
http://www.tekwire.net/joomla/download/phk, and search for the 'web_tunnel' function in 
file PHK.php.

Regards

François

> -----Original Message-----
> From: Mo McRoberts [mailto:mo@nevali.net] 
> Sent: Saturday, February 24, 2007 4:18 AM
> To: internals@lists.php.net
> Subject: [PHP-DEV] Simulating require_once within an extension
> 
> 
> 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
> 
> 

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic