Skip to content

Commit 324182a

Browse files
authored
PHPC-1804: Manager::addSubscriber and removeSubscriber (#1213)
* Use zend_exception_ce instead of zend_exception_get_default() zend_exception_get_default() has been deprecated since PHP 7.0. See: php/php-src@f9e9d3a * Check retval from php_phongo_set_monitoring_callbacks This ensures that php_phongo_client_register is never called in the event this method fails (however unlikely). * Index subscriber HashTable by numeric object handles This also updates add/removeSubscriber to use the new argument parsing macros from deead96. * PHPC-1804: Manager::addSubscriber and removeSubscriber Implements per-client event subscribers. Moves existing APM code to a new phongo_apm.c module and adds logic to coordinate dispatching between global and per-client subscribers.
1 parent 4ba48e5 commit 324182a

16 files changed

+958
-236
lines changed

config.m4

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ if test "$PHP_MONGODB" != "no"; then
105105
phongo_compat.c \
106106
src/bson.c \
107107
src/bson-encode.c \
108+
src/phongo_apm.c \
108109
src/BSON/Binary.c \
109110
src/BSON/BinaryInterface.c \
110111
src/BSON/DBPointer.c \

config.w32

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ if (PHP_MONGODB != "no") {
118118
var PHP_MONGODB_MONGOC_SOURCES="mongoc-aggregate.c mongoc-apm.c mongoc-array.c mongoc-async.c mongoc-async-cmd.c mongoc-buffer.c mongoc-bulk-operation.c mongoc-change-stream.c mongoc-client.c mongoc-client-pool.c mongoc-client-session.c mongoc-client-side-encryption.c mongoc-cluster-aws.c mongoc-cluster.c mongoc-cluster-cyrus.c mongoc-cluster-sasl.c mongoc-cluster-sspi.c mongoc-cmd.c mongoc-collection.c mongoc-compression.c mongoc-counters.c mongoc-crypt.c mongoc-crypto.c mongoc-crypto-cng.c mongoc-crypto-common-crypto.c mongoc-crypto-openssl.c mongoc-cursor-array.c mongoc-cursor.c mongoc-cursor-change-stream.c mongoc-cursor-cmd.c mongoc-cursor-cmd-deprecated.c mongoc-cursor-find.c mongoc-cursor-find-cmd.c mongoc-cursor-find-opquery.c mongoc-cursor-legacy.c mongoc-cyrus.c mongoc-database.c mongoc-error.c mongoc-find-and-modify.c mongoc-gridfs-bucket.c mongoc-gridfs-bucket-file.c mongoc-gridfs.c mongoc-gridfs-file.c mongoc-gridfs-file-list.c mongoc-gridfs-file-page.c mongoc-handshake.c mongoc-host-list.c mongoc-http.c mongoc-index.c mongoc-init.c mongoc-interrupt.c mongoc-libressl.c mongoc-linux-distro-scanner.c mongoc-list.c mongoc-log.c mongoc-matcher.c mongoc-matcher-op.c mongoc-memcmp.c mongoc-ocsp-cache.c mongoc-openssl.c mongoc-optional.c mongoc-opts.c mongoc-opts-helpers.c mongoc-queue.c mongoc-rand-cng.c mongoc-rand-common-crypto.c mongoc-rand-openssl.c mongoc-read-concern.c mongoc-read-prefs.c mongoc-rpc.c mongoc-sasl.c mongoc-scram.c mongoc-secure-channel.c mongoc-secure-transport.c mongoc-server-api.c mongoc-server-description.c mongoc-server-monitor.c mongoc-server-stream.c mongoc-set.c mongoc-socket.c mongoc-ssl.c mongoc-sspi.c mongoc-stream-buffered.c mongoc-stream.c mongoc-stream-file.c mongoc-stream-gridfs.c mongoc-stream-gridfs-download.c mongoc-stream-gridfs-upload.c mongoc-stream-socket.c mongoc-stream-tls.c mongoc-stream-tls-libressl.c mongoc-stream-tls-openssl-bio.c mongoc-stream-tls-openssl.c mongoc-stream-tls-secure-channel.c mongoc-stream-tls-secure-transport.c mongoc-timeout.c mongoc-topology-background-monitoring.c mongoc-topology.c mongoc-topology-description-apm.c mongoc-topology-description.c mongoc-topology-scanner.c mongoc-uri.c mongoc-util.c mongoc-version-functions.c mongoc-write-command.c mongoc-write-command-legacy.c mongoc-write-concern.c";
119119

120120
EXTENSION("mongodb", "php_phongo.c phongo_compat.c", null, PHP_MONGODB_CFLAGS);
121-
MONGODB_ADD_SOURCES("/src", "bson.c bson-encode.c");
121+
MONGODB_ADD_SOURCES("/src", "bson.c bson-encode.c phongo_apm.c");
122122
MONGODB_ADD_SOURCES("/src/BSON", "Binary.c BinaryInterface.c DBPointer.c Decimal128.c Decimal128Interface.c Int64.c Javascript.c JavascriptInterface.c MaxKey.c MaxKeyInterface.c MinKey.c MinKeyInterface.c ObjectId.c ObjectIdInterface.c Persistable.c Regex.c RegexInterface.c Serializable.c Symbol.c Timestamp.c TimestampInterface.c Type.c Undefined.c Unserializable.c UTCDateTime.c UTCDateTimeInterface.c functions.c");
123123
MONGODB_ADD_SOURCES("/src/MongoDB", "BulkWrite.c ClientEncryption.c Command.c Cursor.c CursorId.c CursorInterface.c Manager.c Query.c ReadConcern.c ReadPreference.c Server.c ServerApi.c Session.c WriteConcern.c WriteConcernError.c WriteError.c WriteResult.c");
124124
MONGODB_ADD_SOURCES("/src/MongoDB/Exception", "AuthenticationException.c BulkWriteException.c CommandException.c ConnectionException.c ConnectionTimeoutException.c EncryptionException.c Exception.c ExecutionTimeoutException.c InvalidArgumentException.c LogicException.c RuntimeException.c ServerException.c SSLConnectionException.c UnexpectedValueException.c WriteException.c");

php_phongo.c

+5-176
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
/* Our stuffz */
6767
#include "php_phongo.h"
6868
#include "php_bson.h"
69+
#include "src/phongo_apm.h"
6970
#include "src/BSON/functions.h"
7071
#include "src/MongoDB/Monitoring/functions.h"
7172

@@ -2330,181 +2331,6 @@ static bool php_phongo_apply_driver_options_to_uri(mongoc_uri_t* uri, zval* zopt
23302331
}
23312332
#endif
23322333

2333-
/* APM callbacks */
2334-
static void php_phongo_dispatch_handlers(const char* name, zval* z_event)
2335-
{
2336-
zval* value;
2337-
2338-
ZEND_HASH_FOREACH_VAL_IND(MONGODB_G(subscribers), value)
2339-
{
2340-
if (EG(exception)) {
2341-
break;
2342-
}
2343-
/* We can't use the zend_call_method_with_1_params macro here, as it
2344-
* does a sizeof() on the name argument, which does only work with
2345-
* constant names, but not with parameterized ones as it does
2346-
* "sizeof(char*)" in that case. */
2347-
zend_call_method(PHONGO_COMPAT_OBJ_P(value), NULL, NULL, name, strlen(name), NULL, 1, z_event, NULL);
2348-
}
2349-
ZEND_HASH_FOREACH_END();
2350-
}
2351-
2352-
/* Search for a Manager associated with the given client in the request-scoped
2353-
* registry. If any Manager is found, copy it into the output parameter
2354-
* (incrementing its ref-count) and return true; otherwise, set the output
2355-
* parameter to undefined and return false. */
2356-
static bool php_phongo_copy_manager_for_client(mongoc_client_t* client, zval* out)
2357-
{
2358-
php_phongo_manager_t* manager;
2359-
2360-
if (!MONGODB_G(managers) || zend_hash_num_elements(MONGODB_G(managers)) == 0) {
2361-
return false;
2362-
}
2363-
2364-
ZEND_HASH_FOREACH_PTR(MONGODB_G(managers), manager)
2365-
{
2366-
if (manager->client == client) {
2367-
ZVAL_OBJ(out, &manager->std);
2368-
Z_ADDREF_P(out);
2369-
2370-
return true;
2371-
}
2372-
}
2373-
ZEND_HASH_FOREACH_END();
2374-
2375-
ZVAL_UNDEF(out);
2376-
2377-
return false;
2378-
}
2379-
2380-
static void php_phongo_command_started(const mongoc_apm_command_started_t* event)
2381-
{
2382-
php_phongo_commandstartedevent_t* p_event;
2383-
zval z_event;
2384-
2385-
/* Return early if there are no APM subscribers to notify */
2386-
if (!MONGODB_G(subscribers) || zend_hash_num_elements(MONGODB_G(subscribers)) == 0) {
2387-
return;
2388-
}
2389-
2390-
object_init_ex(&z_event, php_phongo_commandstartedevent_ce);
2391-
p_event = Z_COMMANDSTARTEDEVENT_OBJ_P(&z_event);
2392-
2393-
p_event->command_name = estrdup(mongoc_apm_command_started_get_command_name(event));
2394-
p_event->server_id = mongoc_apm_command_started_get_server_id(event);
2395-
p_event->operation_id = mongoc_apm_command_started_get_operation_id(event);
2396-
p_event->request_id = mongoc_apm_command_started_get_request_id(event);
2397-
p_event->command = bson_copy(mongoc_apm_command_started_get_command(event));
2398-
p_event->database_name = estrdup(mongoc_apm_command_started_get_database_name(event));
2399-
2400-
if (!php_phongo_copy_manager_for_client(mongoc_apm_command_started_get_context(event), &p_event->manager)) {
2401-
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Found no Manager for client in APM event context");
2402-
zval_ptr_dtor(&z_event);
2403-
2404-
return;
2405-
}
2406-
2407-
php_phongo_dispatch_handlers("commandStarted", &z_event);
2408-
zval_ptr_dtor(&z_event);
2409-
}
2410-
2411-
static void php_phongo_command_succeeded(const mongoc_apm_command_succeeded_t* event)
2412-
{
2413-
php_phongo_commandsucceededevent_t* p_event;
2414-
zval z_event;
2415-
2416-
/* Return early if there are no APM subscribers to notify */
2417-
if (!MONGODB_G(subscribers) || zend_hash_num_elements(MONGODB_G(subscribers)) == 0) {
2418-
return;
2419-
}
2420-
2421-
object_init_ex(&z_event, php_phongo_commandsucceededevent_ce);
2422-
p_event = Z_COMMANDSUCCEEDEDEVENT_OBJ_P(&z_event);
2423-
2424-
p_event->command_name = estrdup(mongoc_apm_command_succeeded_get_command_name(event));
2425-
p_event->server_id = mongoc_apm_command_succeeded_get_server_id(event);
2426-
p_event->operation_id = mongoc_apm_command_succeeded_get_operation_id(event);
2427-
p_event->request_id = mongoc_apm_command_succeeded_get_request_id(event);
2428-
p_event->duration_micros = mongoc_apm_command_succeeded_get_duration(event);
2429-
p_event->reply = bson_copy(mongoc_apm_command_succeeded_get_reply(event));
2430-
2431-
if (!php_phongo_copy_manager_for_client(mongoc_apm_command_succeeded_get_context(event), &p_event->manager)) {
2432-
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Found no Manager for client in APM event context");
2433-
zval_ptr_dtor(&z_event);
2434-
2435-
return;
2436-
}
2437-
2438-
php_phongo_dispatch_handlers("commandSucceeded", &z_event);
2439-
zval_ptr_dtor(&z_event);
2440-
}
2441-
2442-
static void php_phongo_command_failed(const mongoc_apm_command_failed_t* event)
2443-
{
2444-
php_phongo_commandfailedevent_t* p_event;
2445-
zval z_event;
2446-
bson_error_t tmp_error = { 0 };
2447-
zend_class_entry* default_exception_ce;
2448-
2449-
default_exception_ce = zend_exception_get_default();
2450-
2451-
/* Return early if there are no APM subscribers to notify */
2452-
if (!MONGODB_G(subscribers) || zend_hash_num_elements(MONGODB_G(subscribers)) == 0) {
2453-
return;
2454-
}
2455-
2456-
object_init_ex(&z_event, php_phongo_commandfailedevent_ce);
2457-
p_event = Z_COMMANDFAILEDEVENT_OBJ_P(&z_event);
2458-
2459-
p_event->command_name = estrdup(mongoc_apm_command_failed_get_command_name(event));
2460-
p_event->server_id = mongoc_apm_command_failed_get_server_id(event);
2461-
p_event->operation_id = mongoc_apm_command_failed_get_operation_id(event);
2462-
p_event->request_id = mongoc_apm_command_failed_get_request_id(event);
2463-
p_event->duration_micros = mongoc_apm_command_failed_get_duration(event);
2464-
p_event->reply = bson_copy(mongoc_apm_command_failed_get_reply(event));
2465-
2466-
if (!php_phongo_copy_manager_for_client(mongoc_apm_command_failed_get_context(event), &p_event->manager)) {
2467-
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Found no Manager for client in APM event context");
2468-
zval_ptr_dtor(&z_event);
2469-
2470-
return;
2471-
}
2472-
2473-
/* We need to process and convert the error right here, otherwise
2474-
* debug_info will turn into a recursive loop, and with the wrong trace
2475-
* locations */
2476-
mongoc_apm_command_failed_get_error(event, &tmp_error);
2477-
2478-
object_init_ex(&p_event->z_error, phongo_exception_from_mongoc_domain(tmp_error.domain, tmp_error.code));
2479-
zend_update_property_string(default_exception_ce, PHONGO_COMPAT_OBJ_P(&p_event->z_error), ZEND_STRL("message"), tmp_error.message);
2480-
zend_update_property_long(default_exception_ce, PHONGO_COMPAT_OBJ_P(&p_event->z_error), ZEND_STRL("code"), tmp_error.code);
2481-
2482-
php_phongo_dispatch_handlers("commandFailed", &z_event);
2483-
zval_ptr_dtor(&z_event);
2484-
}
2485-
2486-
/* Sets the callbacks for APM */
2487-
bool php_phongo_set_monitoring_callbacks(mongoc_client_t* client)
2488-
{
2489-
bool retval;
2490-
2491-
mongoc_apm_callbacks_t* callbacks = mongoc_apm_callbacks_new();
2492-
2493-
mongoc_apm_set_command_started_cb(callbacks, php_phongo_command_started);
2494-
mongoc_apm_set_command_succeeded_cb(callbacks, php_phongo_command_succeeded);
2495-
mongoc_apm_set_command_failed_cb(callbacks, php_phongo_command_failed);
2496-
2497-
retval = mongoc_client_set_apm_callbacks(client, callbacks, client);
2498-
2499-
if (!retval) {
2500-
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Failed to set APM callbacks");
2501-
}
2502-
2503-
mongoc_apm_callbacks_destroy(callbacks);
2504-
2505-
return retval;
2506-
}
2507-
25082334
static zval* php_phongo_manager_prepare_manager_for_hash(zval* driverOptions, bool* free)
25092335
{
25102336
php_phongo_manager_t* manager;
@@ -3485,7 +3311,10 @@ void phongo_manager_init(php_phongo_manager_t* manager, const char* uri_string,
34853311
goto cleanup;
34863312
}
34873313

3488-
php_phongo_set_monitoring_callbacks(manager->client);
3314+
if (!phongo_apm_set_callbacks(manager->client)) {
3315+
/* Exception should already have been thrown */
3316+
goto cleanup;
3317+
}
34893318

34903319
MONGOC_DEBUG("Created client with hash: %s", manager->client_hash);
34913320

php_phongo_structs.h

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ typedef struct {
7575
size_t client_hash_len;
7676
bool use_persistent_client;
7777
zval key_vault_client_manager;
78+
HashTable* subscribers;
7879
zend_object std;
7980
} php_phongo_manager_t;
8081

src/MongoDB/Manager.c

+59
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "phongo_compat.h"
2828
#include "php_phongo.h"
2929
#include "Session.h"
30+
#include "src/phongo_apm.h"
3031

3132
#define PHONGO_MANAGER_URI_DEFAULT "mongodb://127.0.0.1/"
3233

@@ -281,6 +282,28 @@ static PHP_METHOD(Manager, __construct)
281282
}
282283
} /* }}} */
283284

285+
/* {{{ proto void MongoDB\Driver\Manager::addSubscriber(MongoDB\Driver\Monitoring\Subscriber $subscriber)
286+
Registers an event subscriber for this Manager */
287+
static PHP_METHOD(Manager, addSubscriber)
288+
{
289+
php_phongo_manager_t* intern;
290+
zval* subscriber;
291+
292+
PHONGO_PARSE_PARAMETERS_START(1, 1)
293+
Z_PARAM_OBJECT_OF_CLASS(subscriber, php_phongo_subscriber_ce)
294+
PHONGO_PARSE_PARAMETERS_END();
295+
296+
intern = Z_MANAGER_OBJ_P(getThis());
297+
298+
/* Lazily initialize the subscriber HashTable */
299+
if (!intern->subscribers) {
300+
ALLOC_HASHTABLE(intern->subscribers);
301+
zend_hash_init(intern->subscribers, 0, NULL, ZVAL_PTR_DTOR, 0);
302+
}
303+
304+
phongo_apm_add_subscriber(intern->subscribers, subscriber);
305+
} /* }}} */
306+
284307
/* {{{ proto MongoDB\Driver\ClientEncryption MongoDB\Driver\Manager::createClientEncryption(array $options)
285308
Return a ClientEncryption instance */
286309
static PHP_METHOD(Manager, createClientEncryption)
@@ -677,6 +700,27 @@ static PHP_METHOD(Manager, getWriteConcern)
677700
phongo_writeconcern_init(return_value, mongoc_client_get_write_concern(intern->client));
678701
} /* }}} */
679702

703+
/* {{{ proto void MongoDB\Driver\Manager::removeSubscriber(MongoDB\Driver\Monitoring\Subscriber $subscriber)
704+
Unregisters an event subscriber for this Manager */
705+
static PHP_METHOD(Manager, removeSubscriber)
706+
{
707+
php_phongo_manager_t* intern;
708+
zval* subscriber;
709+
710+
PHONGO_PARSE_PARAMETERS_START(1, 1)
711+
Z_PARAM_OBJECT_OF_CLASS(subscriber, php_phongo_subscriber_ce)
712+
PHONGO_PARSE_PARAMETERS_END();
713+
714+
intern = Z_MANAGER_OBJ_P(getThis());
715+
716+
/* NOP if subscribers HashTable was never initialized by addSubscriber */
717+
if (!intern->subscribers) {
718+
return;
719+
}
720+
721+
phongo_apm_remove_subscriber(intern->subscribers, subscriber);
722+
} /* }}} */
723+
680724
/* {{{ proto MongoDB\Driver\Server MongoDB\Driver\Manager::selectServers(MongoDB\Driver\ReadPreference $readPreference)
681725
Returns a suitable Server for the given ReadPreference */
682726
static PHP_METHOD(Manager, selectServer)
@@ -787,6 +831,10 @@ ZEND_BEGIN_ARG_INFO_EX(ai_Manager___construct, 0, 0, 0)
787831
ZEND_ARG_ARRAY_INFO(0, driverOptions, 0)
788832
ZEND_END_ARG_INFO()
789833

834+
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_addSubscriber, 0, 0, 1)
835+
ZEND_ARG_OBJ_INFO(0, subscriber, MongoDB\\Driver\\Monitoring\\Subscriber, 0)
836+
ZEND_END_ARG_INFO()
837+
790838
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_createClientEncryption, 0, 0, 1)
791839
ZEND_ARG_ARRAY_INFO(0, options, 0)
792840
ZEND_END_ARG_INFO()
@@ -815,6 +863,10 @@ ZEND_BEGIN_ARG_INFO_EX(ai_Manager_executeBulkWrite, 0, 0, 2)
815863
ZEND_ARG_INFO(0, options)
816864
ZEND_END_ARG_INFO()
817865

866+
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_removeSubscriber, 0, 0, 1)
867+
ZEND_ARG_OBJ_INFO(0, subscriber, MongoDB\\Driver\\Monitoring\\Subscriber, 0)
868+
ZEND_END_ARG_INFO()
869+
818870
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_selectServer, 0, 0, 1)
819871
ZEND_ARG_OBJ_INFO(0, readPreference, MongoDB\\Driver\\ReadPreference, 0)
820872
ZEND_END_ARG_INFO()
@@ -829,6 +881,7 @@ ZEND_END_ARG_INFO()
829881
static zend_function_entry php_phongo_manager_me[] = {
830882
/* clang-format off */
831883
PHP_ME(Manager, __construct, ai_Manager___construct, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
884+
PHP_ME(Manager, addSubscriber, ai_Manager_addSubscriber, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
832885
PHP_ME(Manager, createClientEncryption, ai_Manager_createClientEncryption, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
833886
PHP_ME(Manager, executeCommand, ai_Manager_executeCommand, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
834887
PHP_ME(Manager, executeReadCommand, ai_Manager_executeRWCommand, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
@@ -840,6 +893,7 @@ static zend_function_entry php_phongo_manager_me[] = {
840893
PHP_ME(Manager, getReadPreference, ai_Manager_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
841894
PHP_ME(Manager, getServers, ai_Manager_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
842895
PHP_ME(Manager, getWriteConcern, ai_Manager_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
896+
PHP_ME(Manager, removeSubscriber, ai_Manager_removeSubscriber, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
843897
PHP_ME(Manager, selectServer, ai_Manager_selectServer, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
844898
PHP_ME(Manager, startSession, ai_Manager_startSession, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
845899
ZEND_NAMED_ME(__wakeup, PHP_FN(MongoDB_disabled___wakeup), ai_Manager_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
@@ -880,6 +934,11 @@ static void php_phongo_manager_free_object(zend_object* object) /* {{{ */
880934
if (!Z_ISUNDEF(intern->key_vault_client_manager)) {
881935
zval_ptr_dtor(&intern->key_vault_client_manager);
882936
}
937+
938+
if (intern->subscribers) {
939+
zend_hash_destroy(intern->subscribers);
940+
FREE_HASHTABLE(intern->subscribers);
941+
}
883942
} /* }}} */
884943

885944
static zend_object* php_phongo_manager_create_object(zend_class_entry* class_type) /* {{{ */

0 commit comments

Comments
 (0)