Traditionnally, PHP extensions contain C code only. The main reason is obvious : this code needs to be fast and access external C APIs. But, in many cases, some parts of the extension code could be written in PHP.
Some use cases, extracted from the discussion referenced below :
- When data is retrieved from a persistent cache, the code executed on cache miss generally doesn't have to be extermely fast, as it is will executed only once to populate the cache.
- MongoDB connection code in C, userland OOP API in PHP.
- Low-Level Crypto code, simplified PHP functions (think ext/hash + ext/password)
- Database Vendor Extensions in C + common DB abstraction in PHP (PDO2).
- Low-Level Date handling, high level PHP code
What came out from the discussion is that different use cases require two different ways to relate extensions with PHP code :
- Some need to embed their PHP code in the extension code, making both impossible to split and desynchronize.
- Some prefer their PHP code to be distributed along with the extension as separate script files. This way, both can evolve in a more or less independant way. Among others, this allows to distribute new versions of the PHP code without having to recompile the C part.
The mechanism we propose here allows both options.
Here are the main issues we must solve :
- Script execution : when will the PHP script(s) be executed ? Should we execute them all at RINIT time, even if the request code does not use the defined functions/classes ? It seems clear that, when several extensions will start using this mechanism, the overhead will quickly become unacceptable. So, we need another way to make PHP-defined functions/classes available.
- Script path : when embedding PHP code in an extension, it is quite easy to execute it, but this code is not associated with a path. This has several implications, like the fact that this code cannot be cached by the opcode cache. This is an important performance issue. The fact that the PHP code has other side effects, like the fact that different scripts cannot reference and include one another. Some other use cases given in the discussion also require to reference their script using a path. This is quite complex to implement for an extension today because providing a path for scripts requires defining a stream wrapper.
I propose to implement an extension that would provide a service to other extensions. The interaction between this 'service' and 'client' extensions would be reduced to this : at MINIT time, each client extension registers the PHP code it wants to make available. This code may come from the extension code itself or from an external file that will be read and stored in persistent memory. File trees can be recursively registered too. The client extension also provides a virtual path which will be used to store and retrieve the corresponding PHP script.
After the MINIT phase, if the client extension needs to access or reference one of these PHP scripts, it will do it through this virtual path, using a common stream wrapper.
Each time the service extension receives a PHP script to register, it scans the PHP code to extracts the class names this code defines, and registers these names in an autoload map. Then, during a request, when one of these names is referenced, the corresponding script is autoloaded. By using the autoloader, we eliminate the need to execute the whole PHP code in every request. All these features (symbol extraction + autoloading) will use the Automap autoloader.
Today, using the autoloader allows to load classes only but, if this mechanism proves useful, autoloading can easily be extended to functions and constants. The only reason why it was not done earlier is that most people didn't see any valid use case. This may be one.
The opcache issue is slighly more complex because the current opcache code only allows the 'file:' and 'phar:' stream wrappers to be cached. This issue goes beyond this specific case. It will be solved by a future additional 'cache_key' stream operation, which will open opcode caching to every stream wrappers.
- http://www.serverphorums.com/read.php?7,1100873 : A long discussion on the php.internals mailing list about the way to include PHP code in extensions and auto-prepend this code at request start. At this time, using the autoloader was not suggested yet.
- http://www.serverphorums.com/read.php?7,1125125,1125125 : discusses a way to extend opcode caching to stream wrappers.