Skip to content

Commit cb95d10

Browse files
committed
Clarify the handling of callables more
Describe how to store a callable properly, mention trampolines and what they are Point to better APIs and pitfalls of some of the existing FCI APIs
1 parent 07024bd commit cb95d10

File tree

1 file changed

+38
-8
lines changed

1 file changed

+38
-8
lines changed

Book/php7/internal_types/functions/callables.rst

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Let detail the various FCI fields:
4141
Mandatory field, the actual callable, do not be fooled by the name of this field as this is a leftover when
4242
PHP didn't have objects and class methods. It must be a string zval or an array following the same rules as
4343
callables in PHP, namely the first index is a class or instance object, and the second one is the method name.
44-
It can also be undefined if, and only if, a non empty FCC is provided.
44+
It can also be undefined if, and only if, an initialized FCC is provided.
4545
``retval``:
4646
Mandatory field, which will contain the result of the PHP function
4747
``param_count``:
@@ -83,21 +83,51 @@ Let detail the various FCC fields:
8383
``calling_scope``:
8484
The scope in which this call is made, only used by the VM.
8585

86-
.. note:: To release a FCC you should use the ``void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc)``
87-
function.
88-
8986
.. warning:: Prior to PHP 7.3.0 there existed an ``initialized`` field. Now an FCC is considered initialized when
9087
``function_handler`` is set to a non-null pointer.
9188

89+
The *only* case where an FCC will be uninitialized is if the function is a trampoline, i.e. when the method
90+
of a class does not exist but is handled by the magic methods ``__call()``/``__callStatic()``.
91+
This is because a trampoline is freed by ZPP as it is a newly allocated ``zend_function`` struct with the
92+
op array copied, and is freed when called. To retrieve it manually use ``zend_is_callable_ex()``.
93+
94+
.. warning:: It is not sufficient to just store the FCC to be able to call a user function at a later stage.
95+
If the callable zval from the FCI is an object (because it has an ``__invoke`` method, is a ``Closure``,
96+
or a trampoline) then a reference to the ``zend_object`` must also be stored, the refcount incremented,
97+
and released as needed. Moreover, if the callable is a trampoline the ``function_handler`` must be copied
98+
to be persisted between calls (see how SPL implements the storage of autoloading functions).
99+
100+
.. note:: To release a FCC you should use the ``void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc)``
101+
function if maintain copies of a potential trampoline, as this will release the trampoline properly.
102+
Moreover, this needs to be called *prior* to freeing the closure, as the trampoline will partially refer to a
103+
``zend_function *`` entry in the closure CE.
104+
105+
..
106+
This API is still being worked on and won't be usable for a year
107+
note:: As of PHP 8.3.0, the FCC holds a ``closure`` field and a dedicated API to handle storing userland callables.
108+
92109
Zend Engine API for callables
93110
-----------------------------
94111

95-
The API can be found in the ``Zend_API.h`` header file.
112+
The API is located at various locations in the ``Zend_API.h`` header file.
113+
We will describe the various APIs needed to deal with callables in PHP.
114+
115+
First of all, to check if an FCI is initialized use the ``ZEND_FCI_INITIALIZED(fci)`` macro.
116+
117+
.. And, as of PHP 8.3.0, the ``ZEND_FCC_INITIALIZED(fcc)`` macro to check if an FCC is initialized.
96118
97-
If you have a FCI/FCC pair for a callable you can call it directly by using the
119+
If you have a correctly initialized and set up FCI/FCC pair for a callable you can call it directly by using the
98120
``zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache)`` function.
99-
If you just need to change, or provide the arguments and return value you can use the
100-
``zend_fcall_info_call(zend_fcall_info *fci, zend_fcall_info_cache *fcc, zval *retval, zval *args)`` function.
121+
122+
.. warning:: The ``zend_fcall_info_arg*()`` and ``zend_fcall_info_call()`` APIs should not be used.
123+
The reasons for this is because the ``zval *args`` parameter does *not* set the ``params`` field of the FCI,
124+
but is expected to be a PHP array containing positional arguments. If this is the case the ``named_params``
125+
field should be set instead. Moreover, those functions reallocate the parameters on the heap when generally
126+
the arguments are stack allocated because the call is only done once with predetermined arguments.
127+
128+
..
129+
note:: As of PHP 8.3.0, the ``zend_call_function_with_return_value(*fci, *fcc, zval *retval)`` function has
130+
been added to replace the usage of ``zend_fcall_info_call(fci, fcc, retval, NULL)``.
101131
102132
In the more likely case where you just have a callable zval, you have the choice of a couple different options
103133
depending on the use case.

0 commit comments

Comments
 (0)