This RFC proposes a way to opcode-cache the PHP code generated via a stream wrapper.
It is an extension of the 'is_cacheable' concept developed in a previous RFC.
When stream wrappers were introduced in PHP, their relationship with opcode caches was not a problem, as they were mostly used to access remote data. The need for a better interaction arose with package systems, like phar and phk, as these sytems use stream wrappers to expose virtual trees of PHP code. If the scripts distributed by such systems cannot be opcode-cached, they loose a great part of their value.
Historically, the issue was addressed using different workarounds :
So, I consider that we need a more generic system, which must allow any PHP code handled via a stream wrapper ('core' or 'userspace') to be opcode-cached.
My previous RFC proposed the implementation of an 'is_cacheable' operation, returning a boolean value. This implied that the cache key to use would always be the URL itself. Some users expressed concerns that the stream wrapper should also have the power to determine the cache key. So, this is what I am now proposing :
Using a (zend_string *) for input and output allows the mechanism to be very fast because, in most cases, when an URL is cacheable, the key to use will be the path itself. Look at the phar example :
/** * Called by the opcode cache to get the key to use when caching this URL */ static zend_string *phar_wrapper_cache_key(php_stream_wrapper *wrapper , zend_string *url, int options, php_stream_context *context) /* {{{ */ { /* Phar URLs are always cacheable */ zend_string_addref(url); return url; } /* }}} */
In order to ensure key unicity, the returned string must start with the same '<scheme>://' prefix as the input URL. For performance reasons, this is checked in debug mode only on C-level wrappers.
The stream wrapper has the responsibility to ensure that the data associated with a given key will always be the same. If this is not the case (e.g. if a 'non-cacheable' URL is declared as 'cacheable'), PHP won't fail but, from a user's point of view, the behavior will be unpredictable.
None
7.2
None
Phar implements a new cache_key() operation, always returning the input path.
The same for the plain files wrapper.
No other core extension needs to be modified.
When 3rd-party extensions decide to have the benefit of the new feature, they will just implement an additional 'cache_key' operation. This is not mandatory and only stream wrappers distributing PHP source code have a reason to do that. In order to remain compatible with previous PHP versions, the declaration for this operation must be enclosed in an appropriate '#if PHP_API_VERSION >= 20160731' block.
Userspace stream wrappers may just define a new 'cache_key()' method. On previous PHP versions, this method will never be called, but won't harm.
Opcode cache must implement the following logic :
These changes are NOT included yet in the PR below.
None
Stream wrappers not using the feature are not modified in any way.
Required majority: 50%+1
Pull Request: https://github.com/php/php-src/pull/1711
This PR includes every modification described in this RFC, except changes to the opcache code. It includes phar and plain files cache_key() handlers and contains a set of tests.
PHP documentation additions (file_cache_key(), streamWrapper::cache_key()) not written yet.