@@ -328,6 +328,7 @@ typedef struct _zend_fcall_info_cache {
328328 zend_class_backed_enum_table(ce)
329329
330330#define ZEND_FCI_INITIALIZED (fci ) ((fci).size != 0)
331+ #define ZEND_FCC_INITIALIZED (fcc ) ((fcc).function_handler != NULL)
331332
332333ZEND_API int zend_next_free_module (void );
333334
@@ -729,6 +730,57 @@ ZEND_API void zend_fcall_info_argn(zend_fcall_info *fci, uint32_t argc, ...);
729730 */
730731ZEND_API zend_result zend_fcall_info_call (zend_fcall_info * fci , zend_fcall_info_cache * fcc , zval * retval , zval * args );
731732
733+ /* Zend FCC API to store and handle PHP userland functions */
734+ static zend_always_inline bool zend_fcc_equals (const zend_fcall_info_cache * a , const zend_fcall_info_cache * b )
735+ {
736+ return a -> function_handler == b -> function_handler
737+ && a -> object == b -> object
738+ && a -> calling_scope == b -> calling_scope
739+ && a -> closure == b -> closure
740+ ;
741+ }
742+
743+ static zend_always_inline void zend_fcc_addref (zend_fcall_info_cache * fcc )
744+ {
745+ if (fcc -> object ) {
746+ GC_ADDREF (fcc -> object );
747+ }
748+ if (fcc -> closure ) {
749+ GC_ADDREF (fcc -> closure );
750+ }
751+ }
752+
753+ static zend_always_inline void zend_fcc_dup (/* restrict */ zend_fcall_info_cache * dest , const zend_fcall_info_cache * src )
754+ {
755+ memcpy (dest , src , sizeof (zend_fcall_info_cache ));
756+ zend_fcc_addref (dest );
757+ }
758+
759+ static zend_always_inline void zend_fcc_dtor (zend_fcall_info_cache * fcc )
760+ {
761+ if (fcc -> object ) {
762+ OBJ_RELEASE (fcc -> object );
763+ }
764+ if (fcc -> closure ) {
765+ OBJ_RELEASE (fcc -> closure );
766+ }
767+ memcpy (fcc , & empty_fcall_info_cache , sizeof (zend_fcall_info_cache ));
768+ }
769+
770+ ZEND_API void zend_get_callable_zval_from_fcc (const zend_fcall_info_cache * fcc , zval * callable );
771+
772+ /* Moved out of zend_gc.h because zend_fcall_info_cache is an unknown type in that header */
773+ static zend_always_inline void zend_get_gc_buffer_add_fcc (zend_get_gc_buffer * gc_buffer , zend_fcall_info_cache * fcc )
774+ {
775+ ZEND_ASSERT (ZEND_FCC_INITIALIZED (* fcc ));
776+ if (fcc -> object ) {
777+ zend_get_gc_buffer_add_obj (gc_buffer , fcc -> object );
778+ }
779+ if (fcc -> closure ) {
780+ zend_get_gc_buffer_add_obj (gc_buffer , fcc -> closure );
781+ }
782+ }
783+
732784/* Can only return FAILURE if EG(active) is false during late engine shutdown.
733785 * If the call or call setup throws, EG(exception) will be set and the retval
734786 * will be UNDEF. Otherwise, the retval will be a non-UNDEF value. */
@@ -751,6 +803,12 @@ ZEND_API void zend_call_known_function(
751803 zend_function * fn , zend_object * object , zend_class_entry * called_scope , zval * retval_ptr ,
752804 uint32_t param_count , zval * params , HashTable * named_params );
753805
806+ static zend_always_inline void zend_call_known_fcc (
807+ zend_fcall_info_cache * fcc , zval * retval_ptr , uint32_t param_count , zval * params , HashTable * named_params )
808+ {
809+ zend_call_known_function (fcc -> function_handler , fcc -> object , fcc -> called_scope , retval_ptr , param_count , params , named_params );
810+ }
811+
754812/* Call the provided zend_function instance method on an object. */
755813static zend_always_inline void zend_call_known_instance_method (
756814 zend_function * fn , zend_object * object , zval * retval_ptr ,
0 commit comments