Hi,
I have separated fixes and optimizations from the delayed early binding itself and added comments about options. Now the patch must be more clear.
The order of actual class declaration in PHP scripts doesn't follow to syntax order. Some classes might be declared at compile-time (early binding - the classes which don't implement interfaces and doesn't have parent or have parent class that is known during compilation) others only in run-time. Many applications depend on this order and will be broken in case if we disable early binding. On the other hand early binding provides a big problem for opcode caches which are not able to emulate it properly. The early binding itself is impossible because caches cannot create cross-script dependencies or dependencies from internal classes. So most of them try to emulate early binding during restoring script from cache, but in fact they may declare classes that were never declared at compile time (for example class declarations wrapped by тАЬifтАЭ statementтАЭ).
The path for PHP_5_3 is going to provide a general solution to control some aspect of PHP compilation. The patch introduces CG(compiler_options) that is a bit-set of compiler flags. ZE provides two default sets of the options. One for script compilation and another for code evaluation. Some extensions like opcode caches and debuggers might set (or reset) individual flags in the CG(compiler_options).
The patch defines the following options:
ZEND_COMPILE_EXTENDED_INFO is a replacement of CG(extended_info)
ZEND_COMPILE_HANDLE_OP_ARRAY is a replacement CG(handle_op_arrays)
ZEND_COMPILE_IGNORE_INTERNAL_FUNCTIONS disables generation of direct calls to internal functions (ZEND_DO_FCALL) and use indirect calls (ZEND_INIT_FCALL_BY_NAME + ZEND_DO_FCALL_BY_NAME). This flag may be useful if script compiled by one PHP binary but executed by another, which may miss some internal functions.
ZEND_COMPILE_IGNORE_INTERNAL_CLASSES disables usage of internal classes during compilation. It disables early binding of classes with parent internal classes. Also all non-compound names in namespaces might be resolved to internal classes at runtime.
ZEND_COMPILE_DELAYED_BINDING changes ZEND_DECLARE_INHERITED_CLASS opcode into ZEND_DECLARE_INHERITED_CLASS_DELAYD for all opcodes that were failed to perform early binding because of inexistence of parent class. This flag is especial useful with caches which have to perform early binding during script-loading-time instead of compile-time. In case if parent class doesn't exists at script-loading-time too, actual class declaration will be finally delayed to run-time.
So to use delayed early binding, opcode cache should set proper flags and then call the original compiler.
cache_compile_file() {
...
orig_compiler_options = CG(compiler_options);
CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES |
ZEND_COMPILE_DELAYED_BINDING;
ret = orig_compile_file();
CG(compiler_options) = orig_compiler_options;
...
retur ret;
}
And then call to zend_do_delayed_early_binding() for op_array of each restored script.
Thanks. Dmitry.
Hi Dmitry,
I have separated fixes and optimizations from the delayed early binding
itself and added comments about options. Now the patch must be more clear.
Thanks, that's really interesting.
As mentioned on http://turl.ca/ynny, I'm wondering if there are
already tests in CVS which illustrate the original issue when using
opcode caches. If not, it seems it would be great to add such tests
along with the patch!
Regards,
Robin