99 * It is a required component in Drupal 8, and strongly recommended by other
1010 * frameworks, including Symfony 2 and 3.
1111 *
12- * Our approach for Guzzle 6 is to register middleware on every client that
12+ * Our approach for Guzzle 6-7 is to register middleware on every client that
1313 * adds our headers to the request object, handles responses, and creates
1414 * metrics and trace nodes using the internal RequestHandler class declared
1515 * below.
1616 *
1717 * There is one issue with this approach, which is that the middleware is
1818 * called when the request is created, rather than when the request is sent. As
19- * Guzzle 6 removed the event system that allowed us to know exactly when the
19+ * Guzzle 6-7 removed the event system that allowed us to know exactly when the
2020 * request was sent, we are unable to get the time of the request being sent
2121 * without instrumenting much more deeply into Guzzle's handlers. We consider
2222 * this to be an obscure enough edge case that we are not doing this work at
6565 */
6666#if ZEND_MODULE_API_NO >= ZEND_5_5_X_API_NO
6767
68- /* {{{ newrelic\Guzzle6\RequestHandler class definition and methods */
69-
7068/*
7169 * True global for the RequestHandler class entry.
7270 */
7371zend_class_entry * nr_guzzle6_requesthandler_ce ;
72+ zval * config ;
73+ zend_class_entry * guzzle_client_ce ;
74+ zval * handler_stack ;
75+ zval * middleware = NULL ;
76+ zval * retval ;
7477
7578/*
7679 * Arginfo for the RequestHandler methods.
@@ -107,7 +110,7 @@ static void nr_guzzle6_requesthandler_handle_response(zval* handler,
107110 zval * response
108111 TSRMLS_DC ) {
109112 nr_segment_t * segment = NULL ;
110- nr_segment_external_params_t external_params = {.library = "Guzzle 6" };
113+ nr_segment_external_params_t external_params = {.library = "Guzzle 6-7 " };
111114 zval * request ;
112115 zval * method ;
113116 zval * status ;
@@ -140,7 +143,7 @@ static void nr_guzzle6_requesthandler_handle_response(zval* handler,
140143
141144 if (NRPRG (txn ) && NRTXN (special_flags .debug_cat )) {
142145 nrl_verbosedebug (
143- NRL_CAT , "CAT: outbound response: transport='Guzzle 6' %s=" NRP_FMT ,
146+ NRL_CAT , "CAT: outbound response: transport='Guzzle 6-7 ' %s=" NRP_FMT ,
144147 X_NEWRELIC_APP_DATA , NRP_CAT (external_params .encoded_response_header ));
145148 }
146149
@@ -206,14 +209,14 @@ static PHP_NAMED_FUNCTION(nr_guzzle6_requesthandler_construct) {
206209 zend_update_property (Z_OBJCE_P (this_obj ), ZVAL_OR_ZEND_OBJECT (this_obj ),
207210 NR_PSTR ("request" ), request TSRMLS_CC );
208211
209- nr_guzzle_obj_add (this_obj , "Guzzle 6" TSRMLS_CC );
212+ nr_guzzle_obj_add (this_obj , "Guzzle 6-7 " TSRMLS_CC );
210213}
211214
212215/*
213216 * Proto : void RequestHandler::onFulfilled(Psr\Http\Message\ResponseInterface
214217 * $response)
215218 *
216- * Purpose : Called when a Guzzle 6 request promise is fulfilled.
219+ * Purpose : Called when a Guzzle 6-7 request promise is fulfilled.
217220 *
218221 * Params : 1. The response object.
219222 */
@@ -257,7 +260,7 @@ static PHP_NAMED_FUNCTION(nr_guzzle6_requesthandler_onfulfilled) {
257260 * Proto : void
258261 * RequestHandler::onRejected(GuzzleHttp\Exception\TransferException $e)
259262 *
260- * Purpose : Called when a Guzzle 6 request promise failed.
263+ * Purpose : Called when a Guzzle 6-7 request promise failed.
261264 *
262265 * Params : 1. The exception object.
263266 */
@@ -340,40 +343,33 @@ const zend_function_entry nr_guzzle6_requesthandler_functions[]
340343 nr_guzzle6_requesthandler_onrejected_arginfo ,
341344 ZEND_ACC_PUBLIC ) PHP_FE_END };
342345
343- /* }}} */
346+ /*
347+ * Guzzle 7 requires PHP 7.2.0 or later, which is why we will not build Guzzle 7
348+ * support on older versions and will instead provide simple stubs for the two
349+ * exported functions to avoid linking errors.
350+ */
351+ #if ZEND_MODULE_API_NO >= ZEND_7_2_X_API_NO
344352
345- NR_PHP_WRAPPER_START (nr_guzzle6_client_construct ) {
346- zval * config ;
347- zend_class_entry * guzzle_client_ce ;
348- zval * handler_stack ;
349- zval * middleware = NULL ;
350- zval * retval ;
353+ NR_PHP_WRAPPER_START (nr_guzzle7_client_construct ){
351354 zval * this_var = nr_php_scope_get (NR_EXECUTE_ORIG_ARGS TSRMLS_CC );
352355
353356 (void )wraprec ;
354357 NR_UNUSED_SPECIALFN ;
355-
356- /* This is how we distinguish Guzzle 4/5. */
357- if (nr_guzzle_does_zval_implement_has_emitter (this_var TSRMLS_CC )) {
358- NR_PHP_WRAPPER_CALL ;
359- goto end ;
360- }
361-
362358 NR_PHP_WRAPPER_CALL ;
363359
364360 /*
365361 * Get our middleware callable (which is just a string), and make sure it's
366362 * actually callable before we invoke push(). (See also PHP-1184.)
367363 */
368364 middleware = nr_php_zval_alloc ();
369- nr_php_zval_str (middleware , "newrelic\\Guzzle6 \\middleware" );
365+ nr_php_zval_str (middleware , "newrelic\\Guzzle7 \\middleware" );
370366 if (!nr_php_is_zval_valid_callable (middleware TSRMLS_CC )) {
371367 nrl_verbosedebug (NRL_FRAMEWORK ,
372368 "%s: middleware string is not considered callable" ,
373369 __func__ );
374370
375371 nrm_force_add (NRTXN (unscoped_metrics ),
376- "Supportability/library/Guzzle 6 /MiddlewareNotCallable" , 0 );
372+ "Supportability/library/Guzzle 7 /MiddlewareNotCallable" , 0 );
377373
378374 goto end ;
379375 }
@@ -408,8 +404,8 @@ NR_PHP_WRAPPER_START(nr_guzzle6_client_construct) {
408404}
409405NR_PHP_WRAPPER_END
410406
411- void nr_guzzle6_enable (TSRMLS_D ) {
412- int retval ;
407+ void nr_guzzle7_enable (TSRMLS_D ) {
408+ int _retval ;
413409
414410 if (0 == NRINI (guzzle_enabled )) {
415411 return ;
@@ -429,20 +425,20 @@ void nr_guzzle6_enable(TSRMLS_D) {
429425 * as a standalone file, so we can use a normal namespace declaration to
430426 * avoid possible clashes.
431427 */
432- retval = zend_eval_string (
433- "namespace newrelic\\Guzzle6 ;"
428+ _retval = zend_eval_string (
429+ "namespace newrelic\\Guzzle7 ;"
434430
435431 "use Psr\\Http\\Message\\RequestInterface;"
436432
437- "if (!function_exists('newrelic\\Guzzle6 \\middleware')) {"
433+ "if (!function_exists('newrelic\\Guzzle7 \\middleware')) {"
438434 " function middleware(callable $handler) {"
439435 " return function (RequestInterface $request, array $options) use "
440436 "($handler) {"
441437
442438 /*
443439 * Start by adding the outbound CAT/DT/Synthetics headers to the request.
444440 */
445- " foreach (newrelic_get_request_metadata('Guzzle 6 ') as $k => $v) {"
441+ " foreach (newrelic_get_request_metadata('Guzzle 7 ') as $k => $v) {"
446442 " $request = $request->withHeader($k, $v);"
447443 " }"
448444
@@ -455,13 +451,142 @@ void nr_guzzle6_enable(TSRMLS_D) {
455451 " $promise = $handler($request, $options);"
456452 " $promise->then([$rh, 'onFulfilled'], [$rh, 'onRejected']);"
457453
454+ " return $promise;"
455+ " };"
456+ " }"
457+ "}" ,
458+ NULL , "newrelic/Guzzle7" TSRMLS_CC );
459+
460+ if (SUCCESS == _retval ) {
461+ nr_php_wrap_user_function (NR_PSTR ("GuzzleHttp\\Client::__construct" ),
462+ nr_guzzle_client_construct TSRMLS_CC );
463+ } else {
464+ nrl_warning (NRL_FRAMEWORK ,
465+ "%s: error evaluating PHP code; not installing handler" ,
466+ __func__ );
467+ }
468+ }
469+
470+ void nr_guzzle7_minit (TSRMLS_D ) {
471+ zend_class_entry ce ;
472+
473+ if (0 == NRINI (guzzle_enabled )) {
474+ return ;
475+ }
476+
477+ INIT_CLASS_ENTRY (ce , "newrelic\\Guzzle7\\RequestHandler" ,
478+ nr_guzzle6_requesthandler_functions );
479+ nr_guzzle6_requesthandler_ce
480+ = nr_php_zend_register_internal_class_ex (& ce , NULL TSRMLS_CC );
481+
482+ zend_declare_property_null (nr_guzzle6_requesthandler_ce , NR_PSTR ("request" ),
483+ ZEND_ACC_PRIVATE TSRMLS_CC );
484+ }
485+
486+
487+ #else /* PHP < 7.2 */
488+
489+ NR_PHP_WRAPPER_START (nr_guzzle7_client_construct ) {
490+ (void )wraprec ;
491+ NR_UNUSED_SPECIALFN ;
492+ NR_UNUSED_TSRMLS ;
493+ }
494+ NR_PHP_WRAPPER_END
495+
496+ void nr_guzzle7_enable (TSRMLS_D ) {
497+ NR_UNUSED_TSRMLS
498+ }
499+
500+ void nr_guzzle7_minit (TSRMLS_D ) {
501+ NR_UNUSED_TSRMLS ;
502+ }
503+
504+ #endif /* 7.2.x */
505+
506+ NR_PHP_WRAPPER_START (nr_guzzle6_client_construct ) {
507+ zval * this_var = nr_php_scope_get (NR_EXECUTE_ORIG_ARGS TSRMLS_CC );
508+
509+ (void )wraprec ;
510+ NR_UNUSED_SPECIALFN ;
511+
512+ NR_PHP_WRAPPER_CALL ;
513+ middleware = nr_php_zval_alloc ();
514+ nr_php_zval_str (middleware , "newrelic\\Guzzle6\\middleware" );
515+ if (!nr_php_is_zval_valid_callable (middleware TSRMLS_CC )) {
516+ nrl_verbosedebug (NRL_FRAMEWORK ,
517+ "%s: middleware string is not considered callable" ,
518+ __func__ );
519+
520+ nrm_force_add (NRTXN (unscoped_metrics ),
521+ "Supportability/library/Guzzle 6/MiddlewareNotCallable" , 0 );
522+
523+ goto end ;
524+ }
525+
526+ guzzle_client_ce = nr_php_find_class ("guzzlehttp\\client" TSRMLS_CC );
527+ if (NULL == guzzle_client_ce ) {
528+ nrl_verbosedebug (NRL_FRAMEWORK ,
529+ "%s: unable to get class entry for GuzzleHttp\\Client" ,
530+ __func__ );
531+ goto end ;
532+ }
533+
534+ config = nr_php_get_zval_object_property_with_class (
535+ this_var , guzzle_client_ce , "config" TSRMLS_CC );
536+ if (!nr_php_is_zval_valid_array (config )) {
537+ goto end ;
538+ }
539+
540+ handler_stack = nr_php_zend_hash_find (Z_ARRVAL_P (config ), "handler" );
541+ if (!nr_php_object_instanceof_class (handler_stack ,
542+ "GuzzleHttp\\HandlerStack" TSRMLS_CC )) {
543+ goto end ;
544+ }
545+
546+ retval = nr_php_call (handler_stack , "push" , middleware );
547+
548+ nr_php_zval_free (& retval );
549+
550+ end :
551+ nr_php_zval_free (& middleware );
552+ nr_php_scope_release (& this_var );
553+ }
554+ NR_PHP_WRAPPER_END
555+
556+
557+ void nr_guzzle6_enable (TSRMLS_D ) {
558+ int _retval ;
559+
560+ if (0 == NRINI (guzzle_enabled )) {
561+ return ;
562+ }
563+
564+ _retval = zend_eval_string (
565+ "namespace newrelic\\Guzzle6;"
566+
567+ "use Psr\\Http\\Message\\RequestInterface;"
568+
569+ "if (!function_exists('newrelic\\Guzzle6\\middleware')) {"
570+ " function middleware(callable $handler) {"
571+ " return function (RequestInterface $request, array $options) use "
572+ "($handler) {"
573+
574+ " foreach (newrelic_get_request_metadata('Guzzle 6') as $k => $v) {"
575+ " $request = $request->withHeader($k, $v);"
576+ " }"
577+
578+
579+ " $rh = new RequestHandler($request);"
580+ " $promise = $handler($request, $options);"
581+ " $promise->then([$rh, 'onFulfilled'], [$rh, 'onRejected']);"
582+
458583 " return $promise;"
459584 " };"
460585 " }"
461586 "}" ,
462587 NULL , "newrelic/Guzzle6" TSRMLS_CC );
463588
464- if (SUCCESS == retval ) {
589+ if (SUCCESS == _retval ) {
465590 nr_php_wrap_user_function (NR_PSTR ("GuzzleHttp\\Client::__construct" ),
466591 nr_guzzle_client_construct TSRMLS_CC );
467592 } else {
@@ -504,4 +629,4 @@ void nr_guzzle6_minit(TSRMLS_D) {
504629 NR_UNUSED_TSRMLS ;
505630}
506631
507- #endif /* 5.5.x */
632+ #endif /* 5.5.x */
0 commit comments