Skip to content

Commit bbfdc54

Browse files
committed
fix(agent): address that aws-sdk-php versioning changed
Concerns about future/past proofing to the checking prioritized the non-eval implementation vs using the eval method for checking the version. However, aws-sdk-php internal functions frequently change without notice in the release notes. An extra eval is used to avoid the fatal error that would occur in the VERY unlikely event of Aws/Sdk.php not being found and which addresses the previous concerns while decoupling versioning from internal functions.
1 parent c9c5992 commit bbfdc54

File tree

1 file changed

+46
-57
lines changed

1 file changed

+46
-57
lines changed

agent/lib_aws_sdk_php.c

Lines changed: 46 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -22,49 +22,43 @@
2222
* In a normal course of events, the following line will always work
2323
* zend_eval_string("Aws\\Sdk::VERSION;", &retval, "Get AWS Version")
2424
* By the time we have detected the existence of the aws-sdk-php and with
25-
* default composer profject settings, it callable even from
25+
* default composer project settings, it is callable even from
2626
* nr_aws_sdk_php_enable which will automatically load the class if it isn't
27-
* loaded yet and then evaluate the string. However, in the rare case that files
27+
* loaded yet and then evaluate the string. In the rare case that files
2828
* are not loaded via autoloader and/or have non-default composer classload
29-
* settings, if the class is not found, PHP 8.2+ will generate a fatal
30-
* unrecoverable uncatchable error error whenever it cannot find a class. While
31-
* calling this from nr_aws_sdk_php_enable would have been great and would allow
32-
* the sdk version value to be set only once, to avoid the very unlikely but not
33-
* impossible fatal error, this will be called from the
34-
* "Aws\\ClientResolver::_apply_user_agent" wrapper which GUARANTEES that
35-
* aws/sdk exists and is already loaded.
36-
*
37-
*
38-
* Additionally given that aws-sdk-php is currently detected from the
39-
* AwsClient.php file, this method will always be called when a client is
40-
* created unlike Sdk::construct which doesn't show with PHP 8.2+.
41-
*
42-
* Using Aws/Sdk::__construct for version is currently nonviable as it is
43-
* unreliable as a version determiner.
44-
* Having separate functionality to extract from Aws/Sdk::__construct
45-
* is both not required and is redundant and causes additional overhead and
46-
* so only one function is needed to extract version.
47-
*
48-
* Aws\\ClientResolver::_apply_user_agent a reliable function as it is
49-
* always called on client initialization since it is key to populating
50-
* the request headers, and it loads Sdk by default.
51-
*
52-
* Concerns about future/past proofing to the checking prioritized the following
53-
* implementation vs using the eval method.
29+
* settings, if the class is not found, PHP 8.2+ will generate an
30+
* error whenever it cannot find a class which must be caught. Calling this
31+
* from nr_aws_sdk_php_enable would allow the sdk version value to be set only
32+
* once. To avoid the VERY unlikely but not impossible fatal error, we need to
33+
* take an extra step to wrap the call in a try/catch block. This means we need
34+
* to have an additionial zend_string_eval to get the result, but we avoid fatal
35+
* errors.
5436
*/
5537
void nr_lib_aws_sdk_php_handle_version() {
56-
zval* zval_version = NULL;
57-
zend_class_entry* class_entry = NULL;
5838
char* version = NULL;
59-
60-
class_entry = nr_php_find_class("aws\\sdk");
61-
if (NULL != class_entry) {
62-
zval_version = nr_php_get_class_constant(class_entry, "VERSION");
63-
64-
if (nr_php_is_zval_non_empty_string(zval_version)) {
65-
version = Z_STRVAL_P(zval_version);
39+
zval retval;
40+
int result = FAILURE;
41+
42+
result = zend_eval_string(
43+
"$nr_aws_sdk_version = '';"
44+
"try {"
45+
" $nr_aws_sdk_version = Aws\\Sdk::VERSION;"
46+
"} catch(Throwable $_e) {"
47+
"}",
48+
NULL, "Set nr_aws_sdk_version");
49+
50+
if (SUCCESS == result) {
51+
result = zend_eval_string("$nr_aws_sdk_version", &retval,
52+
"Get nr_aws_sdk_version");
53+
54+
/* See if we got a non-empty/non-null string for version. */
55+
if (SUCCESS == result) {
56+
if (nr_php_is_zval_non_empty_string(&retval)) {
57+
version = Z_STRVAL(retval);
58+
}
6659
}
6760
}
61+
6862
if (NRINI(vulnerability_management_package_detection_enabled)) {
6963
/* Add php package to transaction */
7064
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version);
@@ -73,7 +67,7 @@ void nr_lib_aws_sdk_php_handle_version() {
7367
nr_txn_suggest_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME,
7468
version);
7569

76-
nr_php_zval_free(&zval_version);
70+
zval_dtor(&retval);
7771
}
7872

7973
void nr_lib_aws_sdk_php_add_supportability_service_metric(
@@ -96,13 +90,6 @@ void nr_lib_aws_sdk_php_add_supportability_service_metric(
9690
nrm_force_add(NRPRG(txn) ? NRTXN(unscoped_metrics) : 0, buf, 0);
9791
}
9892

99-
NR_PHP_WRAPPER(nr_create_aws_sdk_version_metrics) {
100-
(void)wraprec;
101-
NR_PHP_WRAPPER_CALL;
102-
nr_lib_aws_sdk_php_handle_version();
103-
}
104-
NR_PHP_WRAPPER_END
105-
10693
/*
10794
* AwsClient::parseClass
10895
* This is called from the base AwsClient class for every client associated
@@ -144,7 +131,7 @@ NR_PHP_WRAPPER_END
144131
* optimizable library. Small overhead incurred when encountering an autoload
145132
* file, but detects aws-sdk-php immediately before any sdk code executes
146133
* (changes needed for this are detailed in the original PR)
147-
* 2. use a file that gets called later and only when AwsClient.php file file is
134+
* 2. use a file that gets called later and only when AwsClient.php file is
148135
* called. It's called later and we'll miss some instrumentation, but if we're
149136
* only ever going to be interested in Client calls anyway, maybe that's ok?
150137
* Doesn't detect Sdk.php (optimized out) so when customers only use that or
@@ -158,22 +145,24 @@ NR_PHP_WRAPPER_END
158145
* to wrap, this will add overhead to every hash map lookup. Currently
159146
* implemented option is 2, use the AwsClient.php as this is our main focus.
160147
* This means until a call to an Aws/AwsClient function,
161-
* all calls including aws\sdk calls are ignored. Version detection will be
162-
* tied to Aws/ClientResolver::_apply_user_agent which is ALWAYS called when
163-
* dealing with aws clients. It will not be computed from
164-
* Aws/Sdk::__constructor which would at best be duplicate info and worst would
165-
* never be ignored until a client is called.
148+
* all calls including aws\sdk calls are ignored.
149+
*
150+
* Version detection will be called directly from Aws\Sdk.php
166151
*/
167152
void nr_aws_sdk_php_enable() {
168-
/* This will be used to extract the version. */
169-
nr_php_wrap_user_function(NR_PSTR("Aws\\ClientResolver::_apply_user_agent"),
170-
nr_create_aws_sdk_version_metrics);
171-
/* Called when initializing all other Clients */
172-
nr_php_wrap_user_function(NR_PSTR("Aws\\AwsClient::parseClass"),
173-
nr_create_aws_service_metric);
174-
153+
/*
154+
* Set the UNKNOWN package first, so it doesn't overwrite what we find with
155+
* nr_lib_aws_sdk_php_handle_version.
156+
*/
175157
if (NRINI(vulnerability_management_package_detection_enabled)) {
176158
nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME,
177159
PHP_PACKAGE_VERSION_UNKNOWN);
178160
}
161+
162+
/* Extract the version for aws-sdk 3+ */
163+
nr_lib_aws_sdk_php_handle_version();
164+
165+
/* Called when initializing all Clients */
166+
nr_php_wrap_user_function(NR_PSTR("Aws\\AwsClient::parseClass"),
167+
nr_create_aws_service_metric);
179168
}

0 commit comments

Comments
 (0)