Skip to content

Commit a8663c7

Browse files
fix(agent): fix mongodb instrumentation for PHP 8.0+ (#878)
- split mongodb instrumentation into before/after inline with OAPI paradigm. - remove broken integration tests in favor of Multiverse tests
1 parent 756c1aa commit a8663c7

File tree

10 files changed

+196
-232
lines changed

10 files changed

+196
-232
lines changed

agent/lib_mongodb.c

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ void nr_mongodb_get_host_and_port_path_or_id(zval* server,
108108
}
109109
}
110110

111+
#if ZEND_MODULE_API_NO < ZEND_8_0_X_API_NO \
112+
|| defined OVERWRITE_ZEND_EXECUTE_DATA
111113
NR_PHP_WRAPPER(nr_mongodb_operation) {
112114
const char* this_klass = "MongoDB\\Operation\\Executable";
113115
zval* collection = NULL;
@@ -173,7 +175,169 @@ NR_PHP_WRAPPER(nr_mongodb_operation) {
173175
}
174176
NR_PHP_WRAPPER_END
175177

176-
void nr_mongodb_enable(TSRMLS_D) {
178+
#else
179+
180+
NR_PHP_WRAPPER(nr_mongodb_operation_before) {
181+
(void)wraprec;
182+
nr_segment_t* segment = NULL;
183+
segment = nr_segment_start(NRPRG(txn), NULL, NULL);
184+
if (NULL != segment) {
185+
segment->wraprec = auto_segment->wraprec;
186+
}
187+
}
188+
NR_PHP_WRAPPER_END
189+
190+
NR_PHP_WRAPPER(nr_mongodb_operation_after) {
191+
const char* this_klass = "MongoDB\\Operation\\Executable";
192+
zval* collection = NULL;
193+
zval* database = NULL;
194+
zval* server = NULL;
195+
zval* this_var = NULL;
196+
bool discard_segment = false;
197+
nr_datastore_instance_t instance = {
198+
.host = NULL,
199+
.port_path_or_id = NULL,
200+
.database_name = NULL,
201+
};
202+
203+
// tell the compiler to ignore the cast from const char * to char *
204+
// to save having to do a strdup operation
205+
#pragma GCC diagnostic push
206+
#pragma GCC diagnostic ignored "-Wcast-qual"
207+
nr_segment_datastore_params_t params = {
208+
.collection = NULL,
209+
.datastore = {
210+
.type = NR_DATASTORE_MONGODB,
211+
},
212+
.operation = (char *)wraprec->extra,
213+
.instance = &instance,
214+
.callbacks = {
215+
.backtrace = nr_php_backtrace_callback,
216+
},
217+
};
218+
#pragma GCC diagnostic pop
219+
/*
220+
* We check for the interface all Collection operations extend, rather than
221+
* their specific class. Not all operations have the properties we need but
222+
* the ones we hook do (as of mongo-php-library v.1.1).
223+
*/
224+
this_var = nr_php_scope_get(NR_EXECUTE_ORIG_ARGS);
225+
if (!nr_php_object_instanceof_class(this_var, this_klass)) {
226+
nrl_verbosedebug(NRL_FRAMEWORK, "%s: operation is not %s", __func__,
227+
this_klass);
228+
discard_segment = true;
229+
goto leave;
230+
}
231+
232+
collection = nr_php_get_zval_object_property(this_var, "collectionName");
233+
if (nr_php_is_zval_valid_string(collection)) {
234+
params.collection = Z_STRVAL_P(collection);
235+
}
236+
237+
database = nr_php_get_zval_object_property(this_var, "databaseName");
238+
if (nr_php_is_zval_valid_string(database)) {
239+
instance.database_name = Z_STRVAL_P(database);
240+
}
241+
242+
server = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS);
243+
nr_mongodb_get_host_and_port_path_or_id(server, &instance.host,
244+
&instance.port_path_or_id);
245+
246+
leave:
247+
if (discard_segment) {
248+
nr_segment_discard(&auto_segment);
249+
} else {
250+
nr_segment_datastore_end(&auto_segment, &params);
251+
}
252+
nr_php_arg_release(&server);
253+
nr_php_scope_release(&this_var);
254+
nr_free(instance.host);
255+
nr_free(instance.port_path_or_id);
256+
}
257+
NR_PHP_WRAPPER_END
258+
259+
#endif /* OAPI */
260+
261+
void nr_mongodb_enable() {
262+
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
263+
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
264+
265+
nr_php_wrap_user_function_before_after_clean_extra(
266+
NR_PSTR("MongoDB\\Operation\\Aggregate::execute"),
267+
nr_mongodb_operation_before, nr_mongodb_operation_after,
268+
nr_mongodb_operation_after, "aggregate");
269+
270+
nr_php_wrap_user_function_before_after_clean_extra(
271+
NR_PSTR("MongoDB\\Operation\\BulkWrite::execute"),
272+
nr_mongodb_operation_before, nr_mongodb_operation_after,
273+
nr_mongodb_operation_after, "bulkWrite");
274+
275+
nr_php_wrap_user_function_before_after_clean_extra(
276+
NR_PSTR("MongoDB\\Operation\\Count::execute"),
277+
nr_mongodb_operation_before, nr_mongodb_operation_after,
278+
nr_mongodb_operation_after, "count");
279+
280+
nr_php_wrap_user_function_before_after_clean_extra(
281+
NR_PSTR("MongoDB\\Operation\\CreateIndexes::execute"),
282+
nr_mongodb_operation_before, nr_mongodb_operation_after,
283+
nr_mongodb_operation_after, "createIndexes");
284+
285+
nr_php_wrap_user_function_before_after_clean_extra(
286+
NR_PSTR("MongoDB\\Operation\\Delete::execute"),
287+
nr_mongodb_operation_before, nr_mongodb_operation_after,
288+
nr_mongodb_operation_after, "delete");
289+
290+
nr_php_wrap_user_function_before_after_clean_extra(
291+
NR_PSTR("MongoDB\\Operation\\Distinct::execute"),
292+
nr_mongodb_operation_before, nr_mongodb_operation_after,
293+
nr_mongodb_operation_after, "distinct");
294+
295+
nr_php_wrap_user_function_before_after_clean_extra(
296+
NR_PSTR("MongoDB\\Operation\\DropCollection::execute"),
297+
nr_mongodb_operation_before, nr_mongodb_operation_after,
298+
nr_mongodb_operation_after, "dropCollection");
299+
300+
nr_php_wrap_user_function_before_after_clean_extra(
301+
NR_PSTR("MongoDB\\Operation\\DropIndexes::execute"),
302+
nr_mongodb_operation_before, nr_mongodb_operation_after,
303+
nr_mongodb_operation_after, "dropIndexes");
304+
305+
nr_php_wrap_user_function_before_after_clean_extra(
306+
NR_PSTR("MongoDB\\Operation\\Find::execute"), nr_mongodb_operation_before,
307+
nr_mongodb_operation_after, nr_mongodb_operation_after, "find");
308+
309+
nr_php_wrap_user_function_before_after_clean_extra(
310+
NR_PSTR("MongoDB\\Operation\\FindAndModify::execute"),
311+
nr_mongodb_operation_before, nr_mongodb_operation_after,
312+
nr_mongodb_operation_after, "findAndModify");
313+
314+
nr_php_wrap_user_function_before_after_clean_extra(
315+
NR_PSTR("MongoDB\\Operation\\InsertMany::execute"),
316+
nr_mongodb_operation_before, nr_mongodb_operation_after,
317+
nr_mongodb_operation_after, "insertMany");
318+
319+
nr_php_wrap_user_function_before_after_clean_extra(
320+
NR_PSTR("MongoDB\\Operation\\InsertOne::execute"),
321+
nr_mongodb_operation_before, nr_mongodb_operation_after,
322+
nr_mongodb_operation_after, "insertOne");
323+
324+
nr_php_wrap_user_function_before_after_clean_extra(
325+
NR_PSTR("MongoDB\\Operation\\ListIndexes::execute"),
326+
nr_mongodb_operation_before, nr_mongodb_operation_after,
327+
nr_mongodb_operation_after, "listIndexes");
328+
329+
nr_php_wrap_user_function_before_after_clean_extra(
330+
NR_PSTR("MongoDB\\Operation\\Update::execute"),
331+
nr_mongodb_operation_before, nr_mongodb_operation_after,
332+
nr_mongodb_operation_after, "update");
333+
334+
nr_php_wrap_user_function_before_after_clean_extra(
335+
NR_PSTR("MongoDB\\Operation\\DatabaseCommand::execute"),
336+
nr_mongodb_operation_before, nr_mongodb_operation_after,
337+
nr_mongodb_operation_after, "databaseCommand");
338+
339+
#else /* Non-OAPI */
340+
177341
/*
178342
* We instrument interesting methods on the MongoDB\Collection class via their
179343
* associated MongoDB\Operation classes.
@@ -265,6 +429,8 @@ void nr_mongodb_enable(TSRMLS_D) {
265429
NR_PSTR("MongoDB\\Operation\\DatabaseCommand::execute"),
266430
nr_mongodb_operation, "databaseCommand" TSRMLS_CC);
267431

432+
#endif /* OAPI */
433+
268434
if (NRINI(vulnerability_management_package_detection_enabled)) {
269435
nr_txn_add_php_package(NRPRG(txn), "mongodb/mongodb",
270436
PHP_PACKAGE_VERSION_UNKNOWN);

agent/php_wrapper.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,27 @@ nruserfn_t* nr_php_wrap_user_function_before_after_clean(
7373
return wraprec;
7474
}
7575

76+
nruserfn_t* nr_php_wrap_user_function_before_after_clean_extra(
77+
const char* name,
78+
size_t namelen,
79+
nrspecialfn_t before_callback,
80+
nrspecialfn_t after_callback,
81+
nrspecialfn_t clean_callback,
82+
const char* extra) {
83+
nruserfn_t* wraprec = nr_php_wrap_user_function_before_after_clean(
84+
name, namelen, before_callback, after_callback, clean_callback);
85+
86+
if (nrunlikely(NULL == wraprec)) {
87+
nrl_warning(NRL_INSTRUMENT, "%s: unable to wrap '%s'", __func__,
88+
NRSAFESTR(name));
89+
return wraprec;
90+
}
91+
92+
wraprec->extra = extra;
93+
94+
return wraprec;
95+
}
96+
7697
nruserfn_t* nr_php_wrap_callable_before_after_clean(
7798
zend_function* callable,
7899
nrspecialfn_t before_callback,

agent/php_wrapper.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,14 @@ extern nruserfn_t* nr_php_wrap_callable_before_after_clean(
150150
nrspecialfn_t before_callback,
151151
nrspecialfn_t after_callback,
152152
nrspecialfn_t clean_callback);
153+
154+
extern nruserfn_t* nr_php_wrap_user_function_before_after_clean_extra(
155+
const char* name,
156+
size_t namelen,
157+
nrspecialfn_t before_callback,
158+
nrspecialfn_t after_callback,
159+
nrspecialfn_t clean_callback,
160+
const char* extra);
153161
#endif
154162
extern nruserfn_t* nr_php_wrap_user_function(const char* name,
155163
size_t namelen,

docker-compose.yaml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,6 @@ services:
3636
ports:
3737
- "11211:11211"
3838
container_name: memcached
39-
# mongodb:
40-
# image: mongo:latest
41-
# restart: always
42-
# ports:
43-
# - "27019:27019"
4439
postgres:
4540
image: postgres
4641
restart: always
@@ -57,8 +52,6 @@ services:
5752
environment:
5853
MEMCACHE_HOST: memcached
5954

60-
# MONGO_HOST: mongodb
61-
6255
MYSQL_DB: database
6356
MYSQL_USER: admin
6457
MYSQL_PASSWD: admin
@@ -86,8 +79,6 @@ services:
8679
environment:
8780
MEMCACHE_HOST: memcached
8881

89-
# MONGO_HOST: mongodb
90-
9182
MYSQL_DB: database
9283
MYSQL_USER: admin
9384
MYSQL_PASSWD: admin

tests/include/config.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,6 @@ function isset_or($check, $alternate = NULL)
2424
$MYSQL_SERVER = $MYSQL_HOST . ":" . $MYSQL_PORT;
2525
}
2626

27-
if (class_exists('MongoClient')) {
28-
$MONGO_HOST = isset_or('MONGO_HOST', MongoClient::DEFAULT_HOST);
29-
$MONGO_PORT = isset_or('MONGO_PORT', MongoClient::DEFAULT_PORT);
30-
} else {
31-
$MONGO_HOST = null;
32-
$MONGO_PORT = null;
33-
}
34-
3527
$MEMCACHE_HOST = isset_or('MEMCACHE_HOST', '127.0.0.1');
3628
$MEMCACHE_PORT = isset_or('MEMCACHE_PORT', '11211');
3729

tests/integration/mongo/mongo.inc

Lines changed: 0 additions & 14 deletions
This file was deleted.

tests/integration/mongo/skipif.inc

Lines changed: 0 additions & 18 deletions
This file was deleted.

tests/integration/mongo/test_execute.php

Lines changed: 0 additions & 55 deletions
This file was deleted.

0 commit comments

Comments
 (0)