In some instances, it may be necessary to call back into the user script to perform some functionality. For example event-based programming, array walking and searching programs may require callbacks to user code. The function available for performing callbacks is defined below.
1 int call_user_function_ex(HashTable *function_table, zval **object_pp, zval
*function_name, zval **retval_ptr_ptr, int param_count, zval **params[], int no_separation, HashTable *symbol_table TSRMLS_DC)
The parameters of this function are described below.
HashTable *function_table The global function table,
EG(function_table), is typically used as this argument. However, if you are going to pass an object as the second argument, this can be NULL.
zval **object_pp User object pointer. When this argument is non-null, the actual function table used is pulled from this object, so the previous
argument can be NULL.
zval *function_name Function or method name to call. The internal type of this zval argument can be a string (representing a function if the global function table is used, or a method if an object pointer is provided) or it can be an array containing an object and a string which is standard PHP notation meaning call the method of the object called string.
zval **retval_ptr_ptr Pointer to a zval* to store the return value from the user function. The memory for this does not need to be allocated, it will be done automatically – however you must destroy the container using zval_dtor() (or a variant thereof).
int param_count The number of arguments being sent to the user function.
zval **params[] The arguments being sent to the user function.
int no_separation Should the parameters be separated? This should be set to 0 generally. It may be slightly faster to avoid argument separation, but the function will fail if any arguments need separation.
HashTable *symbol_table An alternate symbol table to be passed into the user function. Normally this is NULL which causes a default symbol table to be passed along to the user function.
TSRMLS_DC Use the TSRMLS_CC macro to provide the
value for this argument.
Table 22: Arguments to the call_user_function_ex() function.
When writing functions that call the call_user_function_ex() function, it is useful to know whether the function_name argument is a valid function or method. The zend_is_callable() function is available to check this. The definition of this function is below:
1 zend_bool zend_is_callable(zval *callable, zend_bool syntax_only, char
**callable_name)
The first parameter is the function_name argument. The second is a flag to indicate whether to simply check syntax or check syntax and existence of the function_name argument. Syntactically, most strings would pass the syntax check, so this argument will usually be set to zero (false) indicating that you want to check syntax and the existence of the user function. The last parameter is a location in which to return the fully qualified function or method name
represented by the first argument. This is useful in presenting error messages back to the user in case of failure. You do not need to allocate the storage for this argument, but you do need to free it.
The example for exercising the call_user_function_ex() function is more complex than many of the examples previously, but it is worth the effort to
understand this concept. The example function accepts two parameters, the first is a PHP array, and the second is the callback function. The example simply calls back into user space passing the array key and value. The user function should return a string. The final result of the sample function is a new array consisting of the strings returned by the user function. Following is the code for the
example.
1 PHP_FUNCTION(callback_test) 2 {
3 zval* input_array;
4 zval* callback_func;
5 char* func_name;
12 if ( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "zz", &input_array,
&callback_func ) == FAILURE ) 13 {
14 return;
15 }
1617 if ( Z_TYPE_P( input_array ) != IS_ARRAY ) 18 {
19 zend_error( E_WARNING, "First argument must be an array" );
20 return;
21 } 22
23 if ( !zend_is_callable( callback_func, 0, &func_name ) ) 24 {
25 zend_error( E_WARNING, "Second argument must be a callback function or method" );
26 efree( func_name );
27 return;
28 }
29 efree( func_name );
3031 array_init( return_value );
3233 // get number of elements in the array
34 count = zend_hash_num_elements( Z_ARRVAL_P( input_array ) );
35
36 // move to the begining of the array
37 zend_hash_internal_pointer_reset( Z_ARRVAL_P( input_array ) );
38 for ( i = 0; i < count; i ++ ) 45 zend_hash_get_current_data( Z_ARRVAL_P( input_array ), (void**) &item );
4647 MAKE_STD_ZVAL( *zkey );
48 // get the key (note this function returns key type)
49 if ( zend_hash_get_current_key( Z_ARRVAL_P( input_array ), &key, &ind, 0 )
== HASH_KEY_IS_STRING )
56 }
5758 args[0] = zkey;
59 args[1] = item;
6061 if ( call_user_function_ex( EG( function_table ), NULL, callback_func,
&retval, 2, args, 0, NULL TSRMLS_CC ) == SUCCESS ) 62 {
63 convert_to_string_ex( &retval );
64 add_next_index_string( return_value, Z_STRVAL_P( retval ), 1 );
65 zval_dtor( retval );
66 } 67 else 68 {
69 zend_error( E_ERROR, "An error occurred while attempting to call the user callback" );
70 return;
71 }
7273 zend_hash_move_forward( Z_ARRVAL_P( input_array ) );
74 } 75 }
Code Fragment 49: Example using call_user_function_ex().
Lines 1 through 31 set up local variables, check the input arguments and set the return value to be of type array. Following that, the function walks through the input array similar to the example in Code Fragment 28. The key and value are obtained using the zend_hash_get_current_key and
zend_hash_get_current_data functions, respectively. Lines 49 through 56 convert the returned key data to a zval. Lines 58 and 59 assign the zval
arguments to the argument array that will be handed off to the user function and line 61 calls into the user function. If the call to call_user_function_ex is
successful, then its return value (the retval argument) is coerced into a string and assigned to the result of this function and freed (line 63 through 65).
A sample PHP script showing the usage of this new function is below. Note that the PHP script calls this function using two standard methods for specifying a callback function. The first call simply names the function to be called using a string; the second uses the standard notation of array( obj, string ) to denote a callback method.
1 <?php
2 class TestClass 3 {
4 function DoArrayWork( $key, $value ) 5 {
6 return 'TestClass::DoArrayWork ('. $key . ' => ' . $value . ')';
7 } 8 } 9
10 function DoWork( $key, $value ) 11 {
12 return 'DoWork (' . $key . ' => ' . $value . ')';
13 }
1415 $array1 = array( 5, 4, 3, 2, 1 );
16 $array2 = array( 'one' => 1, 'two' => 2, 'three' => 3, 'four' => 4, 'five'
=>
17 5 );
1819 $res_array = callback_test( $array1, 'DoWork' );
20 print( "Results (first call):\n" . implode( "\n", $res_array ) . "\n\n" );
2122 $obj = new TestClass();
23 $res_array = callback_test( $array2, array( $obj, 'DoArrayWork' ) );
24 print( "Results (second call):\n" . implode( "\n", $res_array ) . "\n\n" );
25
26 callback_test( $array1, "BogusFunc" );
27 ?>
Code Fragment 50: PHP script demonstrating the use of callback functions Each instance of the callback simply returns a string consisting of the name of the function (or method) and the key and the value. The third call is to illustrate the output when attempting to use a non-existent function name as the callback function. The output is shown below.
1 Results (first call):
2 DoWork (0 => 5) 3 DoWork (1 => 4) 4 DoWork (2 => 3) 5 DoWork (3 => 2) 6 DoWork (4 => 1) 7
8 Results (second call):
9 TestClass::DoArrayWork (one => 1) 10 TestClass::DoArrayWork (two => 2) 11 TestClass::DoArrayWork (three => 3) 12 TestClass::DoArrayWork (four => 4) 13 TestClass::DoArrayWork (five => 5) 14
15
16 Warning: Second argument must be a callback function or method in /home/blake/source/php-4.3.2/callback_test.php on line 25
Results/Output : Results of the callback function tests.