@@ -41,7 +41,7 @@ Let detail the various FCI fields:
41
41
Mandatory field, the actual callable, do not be fooled by the name of this field as this is a leftover when
42
42
PHP didn't have objects and class methods. It must be a string zval or an array following the same rules as
43
43
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.
45
45
``retval ``:
46
46
Mandatory field, which will contain the result of the PHP function
47
47
``param_count ``:
@@ -83,21 +83,59 @@ Let detail the various FCC fields:
83
83
``calling_scope ``:
84
84
The scope in which this call is made, only used by the VM.
85
85
86
- .. note :: To release a FCC you should use the ``void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc)``
87
- function.
88
-
89
86
.. warning :: Prior to PHP 7.3.0 there existed an ``initialized`` field. Now an FCC is considered initialized when
90
87
``function_handler `` is set to a non-null pointer.
91
88
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 determine that two user functions are equal comparing the ``function_handler``, ``object``,
101
+ ``called_scope ``, ``calling_scope ``, and the pointer to the ``zend_object `` for closures is generally sufficient.
102
+ Except when the user function is a trampoline, this is because the ``function_handler `` is reallocated for every
103
+ call, in that case one needs to compared the ``function_handler->common.function_name `` field using
104
+ ``zend_string_equals() `` instead of comparing the pointers of the function handler directly.
105
+
106
+ .. note :: In most cases an FCC does not need to be released, the exception is if the FCC may hold a trampoline
107
+ in which case the ``void zend_release_fcall_info_cache(zend_fcall_info_cache *fcc) `` should be used to release it.
108
+ Moreover, if a reference to the closure is kept, this must be called *prior * to freeing the closure,
109
+ as the trampoline will partially refer to a ``zend_function * `` entry in the closure CE.
110
+
111
+ ..
112
+ This API is still being worked on and won't be usable for a year
113
+ note:: As of PHP 8.3.0, the FCC holds a ``closure`` field and a dedicated API to handle storing userland callables.
114
+
92
115
Zend Engine API for callables
93
116
-----------------------------
94
117
95
- The API can be found in the ``Zend_API.h `` header file.
118
+ The API is located at various locations in the ``Zend_API.h `` header file.
119
+ We will describe the various APIs needed to deal with callables in PHP.
120
+
121
+ First of all, to check if an FCI is initialized use the ``ZEND_FCI_INITIALIZED(fci) `` macro.
96
122
97
- If you have a FCI/FCC pair for a callable you can call it directly by using the
123
+ .. And, as of PHP 8.3.0, the ``ZEND_FCC_INITIALIZED(fcc)`` macro to check if an FCC is initialized.
124
+
125
+ If you have a correctly initialized and set up FCI/FCC pair for a callable you can call it directly by using the
98
126
``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.
127
+
128
+ .. warning :: The ``zend_fcall_info_arg*()`` and ``zend_fcall_info_call()`` APIs should not be used.
129
+ The ``zval *args `` parameter does *not * set the ``params `` field of the FCI directly.
130
+ Instead it expect it to be a PHP array (IS_ARRAY zval) containing positional arguments, which will be reallocated
131
+ into a new C array. As the ``named_params `` field accepts positional arguments, it is generally better to simply
132
+ assign the HashTable pointer of this argument to this field.
133
+ Moreover, as arguments to a userland call are predetermined and stack allocated it is better to assign the
134
+ ``params `` and ``param_count `` fields directly.
135
+
136
+ ..
137
+ note:: As of PHP 8.3.0, the ``zend_call_function_with_return_value(*fci, *fcc, zval *retval)`` function has
138
+ been added to replace the usage of ``zend_fcall_info_call(fci, fcc, retval, NULL)``.
101
139
102
140
In the more likely case where you just have a callable zval, you have the choice of a couple different options
103
141
depending on the use case.
0 commit comments