Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 4d32c6f

Browse files
authored
Allow user to supply a callback function (#8)
* Failing tests for providing a callable as a callback * Use call_user_function_ex to execute the callback function. Collect and pass the same arguments to the callback function (+1 if there is a current $this object). * Add tests for specifying callback as a string * Handle all callables * Update documentation on being able to supply any callback. * Segfault on wrong return for executing a callable * Handle closure and callables the same way * Fix test name
1 parent 2736840 commit 4d32c6f

9 files changed

Lines changed: 342 additions & 44 deletions

ext/README.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ opencensus_trace_method('Foobar', '__construct');
9696
$foobar = new Foobar();
9797
```
9898

99-
The `$handler` parameter can be either an array or a closure callback.
99+
The `$handler` parameter can be either an array or a callable.
100100

101101
If an array is provided, it should be an associative array with the following optional keys:
102102

@@ -105,8 +105,8 @@ If an array is provided, it should be an associative array with the following op
105105
* `endTime` - float - the end time of the span. **Defaults to** the time that the method invocation completed.
106106
* `labels` - array - an associative array of string => string tags for this span.
107107

108-
If a closure is provided, it will be passed the instance of the class (scope) and a copy of each parameter
109-
provided to the watched method. The closure should return an array with the above options. If the closure does
108+
If a callback is provided, it will be passed the instance of the class (scope) and a copy of each parameter
109+
provided to the watched method. The callback should return an array with the above options. If the callback does
110110
not return an array, an `E_USER_WARNING` error is thrown.
111111

112112
```php
@@ -126,6 +126,17 @@ opencensus_trace_method('Foobar', '__construct', function ($scope, $constructArg
126126
]
127127
];
128128
});
129+
130+
// Example: supply a callback
131+
function my_callback($scope, $constructArg1, $constructArg2)
132+
{
133+
return [
134+
'labels' => [
135+
'arg1' => $constructArg1
136+
]
137+
];
138+
}
139+
opencensus_trace_method('Foobar', '__construct', 'my_callback');
129140
```
130141

131142
To trace a function, use the `opencensus_trace_function`:
@@ -135,7 +146,7 @@ To trace a function, use the `opencensus_trace_function`:
135146
* Trace each invocation of the specified function
136147
*
137148
* @param string $functionName
138-
* @param array|Closure $handler
149+
* @param array|callback $handler
139150
* @return bool
140151
*/
141152
function opencensus_trace_function($functionName, $handler = []);
@@ -145,8 +156,8 @@ opencensus_trace_function('var_dump');
145156
var_dump(123);
146157
```
147158

148-
Just like tracing a method, you can provide a `$handler` option which can be an array or a closure. The behavior
149-
is the same as the method tracing, except that the closure will not be passed the scope parameter as there is
159+
Just like tracing a method, you can provide a `$handler` option which can be an array or a callback. The behavior
160+
is the same as the method tracing, except that the callback will not be passed the scope parameter as there is
150161
not object scope available.
151162

152163
```php

ext/opencensus_trace.c

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,12 @@ static double opencensus_now()
174174
}
175175

176176
/**
177-
* Call the provided Closure with the provided parameters to the traced
178-
* function. The Closure must return an array or an E_WARNING is raised.
177+
* Call the provided callback with the provided parameters to the traced
178+
* function. The callback must return an array or an E_WARNING is raised.
179179
*/
180-
static int opencensus_trace_zend_fcall_closure(zend_execute_data *execute_data, opencensus_trace_span_t *span, zval *closure, zval *closure_result TSRMLS_DC)
180+
static int opencensus_trace_call_user_function_callback(zend_execute_data *execute_data, opencensus_trace_span_t *span, zval *callback, zval *callback_result TSRMLS_DC)
181181
{
182182
int i, num_args = EX_NUM_ARGS(), has_scope = 0;
183-
zend_fcall_info fci;
184-
zend_fcall_info_cache fcc;
185183
zval *args = emalloc((num_args + 1) * sizeof(zval));
186184

187185
if (getThis() == NULL) {
@@ -195,28 +193,7 @@ static int opencensus_trace_zend_fcall_closure(zend_execute_data *execute_data,
195193
ZVAL_ZVAL(&args[i + has_scope], EX_VAR_NUM(i), 0, 1);
196194
}
197195

198-
if (zend_fcall_info_init(
199-
closure,
200-
0,
201-
&fci,
202-
&fcc,
203-
NULL,
204-
NULL
205-
TSRMLS_CC
206-
) != SUCCESS) {
207-
efree(args);
208-
return FAILURE;
209-
};
210-
211-
ZVAL_NULL(closure_result);
212-
213-
fci.retval = closure_result;
214-
fci.params = &args[0];
215-
fci.param_count = num_args + has_scope;
216-
217-
fcc.initialized = 1;
218-
219-
if (zend_call_function(&fci, &fcc TSRMLS_CC) != SUCCESS) {
196+
if (call_user_function_ex(EG(function_table), NULL, callback, callback_result, num_args + has_scope, args, 0, NULL) != SUCCESS) {
220197
efree(args);
221198
return FAILURE;
222199
}
@@ -226,7 +203,7 @@ static int opencensus_trace_zend_fcall_closure(zend_execute_data *execute_data,
226203
return FAILURE;
227204
}
228205

229-
if (Z_TYPE_P(closure_result) != IS_ARRAY) {
206+
if (Z_TYPE_P(callback_result) != IS_ARRAY) {
230207
/* only raise the warning if the closure succeeded */
231208
php_error_docref(NULL, E_WARNING, "Trace callback should return array");
232209
return FAILURE;
@@ -237,21 +214,23 @@ static int opencensus_trace_zend_fcall_closure(zend_execute_data *execute_data,
237214

238215
/**
239216
* Handle the callback for the traced method depending on the type
240-
* - if the zval is an array, then assume it's the trace span initialization
217+
* - if the zval is an associative array, then assume it's the trace span initialization
241218
* options
219+
* - if the zval is an array that looks like a callable, then assume it's a callable
242220
* - if the zval is a Closure, then execute the closure and take the results as
243221
* the trace span initialization options
244222
*/
245223
static void opencensus_trace_execute_callback(opencensus_trace_span_t *span, zend_execute_data *execute_data, zval *span_options TSRMLS_DC)
246224
{
247-
if (Z_TYPE_P(span_options) == IS_ARRAY) {
248-
opencensus_trace_span_apply_span_options(span, span_options);
249-
} else if ( (Z_TYPE_P(span_options) == IS_OBJECT) &&
250-
(Z_OBJCE_P(span_options) == zend_ce_closure)) {
251-
zval closure_result;
252-
if (opencensus_trace_zend_fcall_closure(execute_data, span, span_options, &closure_result TSRMLS_CC) == SUCCESS) {
253-
opencensus_trace_span_apply_span_options(span, &closure_result);
225+
zend_string *callback_name;
226+
if (zend_is_callable(span_options, 0, &callback_name)) {
227+
zval callback_result;
228+
if (opencensus_trace_call_user_function_callback(execute_data, span, span_options, &callback_result TSRMLS_CC) == SUCCESS) {
229+
opencensus_trace_span_apply_span_options(span, &callback_result);
254230
}
231+
zend_string_release(callback_name);
232+
} else if (Z_TYPE_P(span_options) == IS_ARRAY) {
233+
opencensus_trace_span_apply_span_options(span, span_options);
255234
}
256235
}
257236

@@ -534,7 +513,7 @@ void opencensus_trace_execute_internal(INTERNAL_FUNCTION_PARAMETERS)
534513
* Register the provided function for tracing.
535514
*
536515
* @param string $functionName
537-
* @param array|Closure $handler
516+
* @param array|callable $handler
538517
* @return bool
539518
*/
540519
PHP_FUNCTION(opencensus_trace_function)
@@ -565,7 +544,7 @@ PHP_FUNCTION(opencensus_trace_function)
565544
*
566545
* @param string $className
567546
* @param string $methodName
568-
* @param array|Closure $handler
547+
* @param array|callable $handler
569548
* @return bool
570549
*/
571550
PHP_FUNCTION(opencensus_trace_method)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
OpenCensus Trace: Customize the trace span options for a function with a callback array
3+
--FILE--
4+
<?php
5+
6+
require_once(__DIR__ . '/common.php');
7+
8+
class CallbackTest
9+
{
10+
public static function handle()
11+
{
12+
return ['name' => 'foo', 'startTime' => 0.1, 'labels' => ['asdf' => 'qwer', 'zxcv' => 'jkl;']];
13+
}
14+
}
15+
16+
// 1: Sanity test a simple profile run
17+
opencensus_trace_function("bar", ['CallbackTest', 'handle']);
18+
bar();
19+
$traces = opencensus_trace_list();
20+
echo "Number of traces: " . count($traces) . "\n";
21+
$span = $traces[0];
22+
23+
$test = gettype($span->spanId());
24+
echo "Span id is a $test\n";
25+
26+
echo "Span name is: '{$span->name()}'\n";
27+
28+
$test = gettype($span->startTime()) == 'double';
29+
echo "Span startTime is a double: $test\n";
30+
31+
echo "Span startTime is: '{$span->startTime()}'\n";
32+
33+
$test = gettype($span->endTime()) == 'double';
34+
echo "Span endTime is a double: $test\n";
35+
36+
print_r($span->labels());
37+
?>
38+
--EXPECT--
39+
Number of traces: 1
40+
Span id is a integer
41+
Span name is: 'foo'
42+
Span startTime is a double: 1
43+
Span startTime is: '0.1'
44+
Span endTime is a double: 1
45+
Array
46+
(
47+
[asdf] => qwer
48+
[zxcv] => jkl;
49+
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
OpenCensus Trace: Callback returning a non-array response should yield a warning.
3+
--FILE--
4+
<?php
5+
6+
require_once(__DIR__ . '/common.php');
7+
8+
function wrong_return($x)
9+
{
10+
return $x;
11+
}
12+
13+
opencensus_trace_function('foo', 'wrong_return');
14+
foo(3);
15+
?>
16+
--EXPECTF--
17+
Warning: main(): Trace callback should return array in %s on line %d
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
OpenCensus Trace: Customize the trace span options for a function with a callback string with static method
3+
--FILE--
4+
<?php
5+
6+
require_once(__DIR__ . '/common.php');
7+
8+
class MyCallback
9+
{
10+
public static function callbackHandle()
11+
{
12+
return ['name' => 'foo', 'startTime' => 0.1, 'labels' => ['asdf' => 'qwer', 'zxcv' => 'jkl;']];
13+
}
14+
}
15+
16+
// 1: Sanity test a simple profile run
17+
opencensus_trace_function("bar", 'MyCallback::callbackHandle');
18+
bar();
19+
$traces = opencensus_trace_list();
20+
echo "Number of traces: " . count($traces) . "\n";
21+
$span = $traces[0];
22+
23+
$test = gettype($span->spanId());
24+
echo "Span id is a $test\n";
25+
26+
echo "Span name is: '{$span->name()}'\n";
27+
28+
$test = gettype($span->startTime()) == 'double';
29+
echo "Span startTime is a double: $test\n";
30+
31+
echo "Span startTime is: '{$span->startTime()}'\n";
32+
33+
$test = gettype($span->endTime()) == 'double';
34+
echo "Span endTime is a double: $test\n";
35+
36+
print_r($span->labels());
37+
?>
38+
--EXPECT--
39+
Number of traces: 1
40+
Span id is a integer
41+
Span name is: 'foo'
42+
Span startTime is a double: 1
43+
Span startTime is: '0.1'
44+
Span endTime is a double: 1
45+
Array
46+
(
47+
[asdf] => qwer
48+
[zxcv] => jkl;
49+
)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
OpenCensus Trace: Customize the trace span options for a function with a callback string
3+
--FILE--
4+
<?php
5+
6+
require_once(__DIR__ . '/common.php');
7+
8+
function callbackHandle()
9+
{
10+
return ['name' => 'foo', 'startTime' => 0.1, 'labels' => ['asdf' => 'qwer', 'zxcv' => 'jkl;']];
11+
}
12+
13+
// 1: Sanity test a simple profile run
14+
opencensus_trace_function("bar", 'callbackHandle');
15+
bar();
16+
$traces = opencensus_trace_list();
17+
echo "Number of traces: " . count($traces) . "\n";
18+
$span = $traces[0];
19+
20+
$test = gettype($span->spanId());
21+
echo "Span id is a $test\n";
22+
23+
echo "Span name is: '{$span->name()}'\n";
24+
25+
$test = gettype($span->startTime()) == 'double';
26+
echo "Span startTime is a double: $test\n";
27+
28+
echo "Span startTime is: '{$span->startTime()}'\n";
29+
30+
$test = gettype($span->endTime()) == 'double';
31+
echo "Span endTime is a double: $test\n";
32+
33+
print_r($span->labels());
34+
?>
35+
--EXPECT--
36+
Number of traces: 1
37+
Span id is a integer
38+
Span name is: 'foo'
39+
Span startTime is a double: 1
40+
Span startTime is: '0.1'
41+
Span endTime is a double: 1
42+
Array
43+
(
44+
[asdf] => qwer
45+
[zxcv] => jkl;
46+
)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--TEST--
2+
OpenCensus Trace: Customize the trace span options for a function with a callback array
3+
--FILE--
4+
<?php
5+
6+
require_once(__DIR__ . '/common.php');
7+
8+
class CallbackTest
9+
{
10+
public static function handle()
11+
{
12+
return ['name' => 'foo', 'startTime' => 0.1, 'labels' => ['asdf' => 'qwer', 'zxcv' => 'jkl;']];
13+
}
14+
}
15+
16+
// 1: Sanity test a simple profile run
17+
opencensus_trace_method("Foo", "bar", ['CallbackTest', 'handle']);
18+
$f = new Foo();
19+
$f->bar();
20+
$traces = opencensus_trace_list();
21+
echo "Number of traces: " . count($traces) . "\n";
22+
$span = $traces[0];
23+
24+
$test = gettype($span->spanId());
25+
echo "Span id is a $test\n";
26+
27+
echo "Span name is: '{$span->name()}'\n";
28+
29+
$test = gettype($span->startTime()) == 'double';
30+
echo "Span startTime is a double: $test\n";
31+
32+
echo "Span startTime is: '{$span->startTime()}'\n";
33+
34+
$test = gettype($span->endTime()) == 'double';
35+
echo "Span endTime is a double: $test\n";
36+
37+
print_r($span->labels());
38+
?>
39+
--EXPECT--
40+
Number of traces: 1
41+
Span id is a integer
42+
Span name is: 'foo'
43+
Span startTime is a double: 1
44+
Span startTime is: '0.1'
45+
Span endTime is a double: 1
46+
Array
47+
(
48+
[asdf] => qwer
49+
[zxcv] => jkl;
50+
)

0 commit comments

Comments
 (0)