Skip to content

Commit 088e7df

Browse files
committed
Merged pull request #679
2 parents 619cee8 + f2efe65 commit 088e7df

18 files changed

+810
-58
lines changed

php_phongo.c

Lines changed: 117 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -448,13 +448,66 @@ mongoc_bulk_operation_t *phongo_bulkwrite_init(zend_bool ordered) { /* {{{ */
448448
return mongoc_bulk_operation_new(ordered);
449449
} /* }}} */
450450

451-
#define PHONGO_WRITECONCERN_ALLOWED 0x01
452-
#define PHONGO_READPREFERENCE_ALLOWED 0x02
451+
static bool process_read_concern(zval *option, bson_t *mongoc_opts TSRMLS_DC)
452+
{
453+
if (Z_TYPE_P(option) == IS_OBJECT && instanceof_function(Z_OBJCE_P(option), php_phongo_readconcern_ce TSRMLS_CC)) {
454+
const mongoc_read_concern_t *read_concern = phongo_read_concern_from_zval(option TSRMLS_CC);
455+
456+
if (!mongoc_read_concern_append((mongoc_read_concern_t*)read_concern, mongoc_opts)) {
457+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error appending \"%s\" option", "readConcern");
458+
return false;
459+
}
460+
} else {
461+
phongo_throw_exception(
462+
PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC,
463+
"Expected 'readConcern' option to be 'MongoDB\\Driver\\ReadConcern', %s given",
464+
zend_get_type_by_const(Z_TYPE_P(option))
465+
);
466+
return false;
467+
}
468+
return true;
469+
}
470+
471+
static uint32_t phongo_do_select_server(mongoc_client_t *client, bson_t *opts, zval *zreadPreference, int server_id TSRMLS_DC)
472+
{
473+
bson_error_t error;
474+
uint32_t selected_server_id = 0;
475+
476+
if (server_id > 0) {
477+
bson_append_int32(opts, "serverId", -1, server_id);
478+
selected_server_id = server_id;
479+
} else {
480+
mongoc_server_description_t *selected_server = NULL;
453481

454-
static int process_read_preference(zval *option, zval **zreadPreference TSRMLS_DC)
482+
selected_server = mongoc_client_select_server(client, false, (zreadPreference ? phongo_read_preference_from_zval(zreadPreference TSRMLS_CC) : mongoc_client_get_read_prefs(client)), &error);
483+
if (selected_server) {
484+
selected_server_id = mongoc_server_description_id(selected_server);
485+
bson_append_int32(opts, "serverId", -1, selected_server_id);
486+
mongoc_server_description_destroy(selected_server);
487+
} else {
488+
/* Check for connection related exceptions */
489+
if (!EG(exception)) {
490+
phongo_throw_exception_from_bson_error_t(&error TSRMLS_CC);
491+
}
492+
}
493+
}
494+
495+
return selected_server_id;
496+
}
497+
498+
static bool process_read_preference(zval *option, bson_t *mongoc_opts, zval **zreadPreference, mongoc_client_t *client, int server_id TSRMLS_DC)
455499
{
456500
if (Z_TYPE_P(option) == IS_OBJECT && instanceof_function(Z_OBJCE_P(option), php_phongo_readpreference_ce TSRMLS_CC)) {
457-
*zreadPreference = option;
501+
int selected_server_id;
502+
503+
if (zreadPreference) {
504+
*zreadPreference = option;
505+
}
506+
507+
selected_server_id = phongo_do_select_server(client, mongoc_opts, *zreadPreference, server_id TSRMLS_CC);
508+
if (!selected_server_id) {
509+
return false;
510+
}
458511
} else {
459512
phongo_throw_exception(
460513
PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC,
@@ -466,10 +519,18 @@ static int process_read_preference(zval *option, zval **zreadPreference TSRMLS_D
466519
return true;
467520
}
468521

469-
static int process_write_concern(zval *option, zval **zwriteConcern TSRMLS_DC)
522+
static bool process_write_concern(zval *option, bson_t *mongoc_opts, zval **zwriteConcern TSRMLS_DC)
470523
{
471524
if (Z_TYPE_P(option) == IS_OBJECT && instanceof_function(Z_OBJCE_P(option), php_phongo_writeconcern_ce TSRMLS_CC)) {
472-
*zwriteConcern = option;
525+
const mongoc_write_concern_t *write_concern = phongo_write_concern_from_zval(option TSRMLS_CC);
526+
527+
if (zwriteConcern) {
528+
*zwriteConcern = option;
529+
}
530+
531+
if (!mongoc_write_concern_append((mongoc_write_concern_t*) write_concern, mongoc_opts)) {
532+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error appending \"%s\" option", "writeConcern");
533+
}
473534
} else {
474535
phongo_throw_exception(
475536
PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC,
@@ -481,26 +542,31 @@ static int process_write_concern(zval *option, zval **zwriteConcern TSRMLS_DC)
481542
return true;
482543
}
483544

484-
static int phongo_execute_parse_options(zval *options, int flags, zval **zreadPreference, zval **zwriteConcern TSRMLS_DC)
545+
static int phongo_execute_parse_options(mongoc_client_t* client, int server_id, zval *driver_options, int type, bson_t *mongoc_opts, zval **zreadPreference, zval **zwriteConcern TSRMLS_DC)
485546
{
486-
if (options && Z_TYPE_P(options) == IS_ARRAY) {
487-
HashTable *ht_data = HASH_OF(options);
547+
if (driver_options && Z_TYPE_P(driver_options) == IS_ARRAY) {
548+
HashTable *ht_data = HASH_OF(driver_options);
488549
#if PHP_VERSION_ID >= 70000
489550
zend_string *string_key = NULL;
490551
zend_ulong num_key = 0;
491-
zval *option;
552+
zval *driver_option;
492553

493-
ZEND_HASH_FOREACH_KEY_VAL(ht_data, num_key, string_key, option) {
554+
ZEND_HASH_FOREACH_KEY_VAL(ht_data, num_key, string_key, driver_option) {
494555
if (!string_key) {
495556
continue;
496557
}
497558

498-
if ((!strcasecmp(ZSTR_VAL(string_key), "readPreference")) && (flags & PHONGO_READPREFERENCE_ALLOWED)) {
499-
if (!process_read_preference(option, zreadPreference)) {
559+
/* URI options are case-insensitive */
560+
if ((!strcasecmp(ZSTR_VAL(string_key), "readConcern")) && (type & PHONGO_COMMAND_READ)) {
561+
if (!process_read_concern(driver_option, mongoc_opts)) {
562+
return false;
563+
}
564+
} else if ((!strcasecmp(ZSTR_VAL(string_key), "readPreference")) && (type == PHONGO_COMMAND_READ || type == PHONGO_COMMAND_RAW)) {
565+
if (!process_read_preference(driver_option, mongoc_opts, zreadPreference, client, server_id)) {
500566
return false;
501567
}
502-
} else if ((!strcasecmp(ZSTR_VAL(string_key), "writeConcern")) && (flags & PHONGO_WRITECONCERN_ALLOWED)) {
503-
if (!process_write_concern(option, zwriteConcern)) {
568+
} else if ((!strcasecmp(ZSTR_VAL(string_key), "writeConcern")) && (type & PHONGO_COMMAND_WRITE)) {
569+
if (!process_write_concern(driver_option, mongoc_opts, zwriteConcern)) {
504570
return false;
505571
}
506572
} else {
@@ -510,10 +576,10 @@ static int phongo_execute_parse_options(zval *options, int flags, zval **zreadPr
510576
} ZEND_HASH_FOREACH_END();
511577
#else
512578
HashPosition pos;
513-
zval **option;
579+
zval **driver_option;
514580

515581
for (zend_hash_internal_pointer_reset_ex(ht_data, &pos);
516-
zend_hash_get_current_data_ex(ht_data, (void **) &option, &pos) == SUCCESS;
582+
zend_hash_get_current_data_ex(ht_data, (void **) &driver_option, &pos) == SUCCESS;
517583
zend_hash_move_forward_ex(ht_data, &pos)) {
518584
char *string_key = NULL;
519585
uint string_key_len = 0;
@@ -524,12 +590,16 @@ static int phongo_execute_parse_options(zval *options, int flags, zval **zreadPr
524590
}
525591

526592
/* URI options are case-insensitive */
527-
if ((!strcasecmp(string_key, "readPreference")) && (flags & PHONGO_READPREFERENCE_ALLOWED)) {
528-
if (!process_read_preference(*option, zreadPreference TSRMLS_CC)) {
593+
if ((!strcasecmp(string_key, "readConcern")) && (type & PHONGO_COMMAND_READ)) {
594+
if (!process_read_concern(*driver_option, mongoc_opts TSRMLS_CC)) {
529595
return false;
530596
}
531-
} else if ((!strcasecmp(string_key, "writeConcern")) && (flags & PHONGO_WRITECONCERN_ALLOWED)) {
532-
if (!process_write_concern(*option, zwriteConcern TSRMLS_CC)) {
597+
} else if ((!strcasecmp(string_key, "readPreference")) && (type == PHONGO_COMMAND_READ)) {
598+
if (!process_read_preference(*driver_option, mongoc_opts, zreadPreference, client, server_id TSRMLS_CC)) {
599+
return false;
600+
}
601+
} else if ((!strcasecmp(string_key, "writeConcern")) && (type & PHONGO_COMMAND_WRITE)) {
602+
if (!process_write_concern(*driver_option, mongoc_opts, zwriteConcern TSRMLS_CC)) {
533603
return false;
534604
}
535605
} else {
@@ -565,7 +635,7 @@ bool phongo_execute_write(mongoc_client_t *client, const char *namespace, php_ph
565635
/* FIXME: Legacy way of specifying the writeConcern option into this function */
566636
if (options && Z_TYPE_P(options) == IS_OBJECT && instanceof_function(Z_OBJCE_P(options), php_phongo_writeconcern_ce TSRMLS_CC)) {
567637
zwriteConcern = options;
568-
} else if (!phongo_execute_parse_options(options, PHONGO_WRITECONCERN_ALLOWED, NULL, &zwriteConcern TSRMLS_CC)) {
638+
} else if (!phongo_execute_parse_options(client, server_id, options, PHONGO_COMMAND_WRITE, NULL, NULL, &zwriteConcern TSRMLS_CC)) {
569639
return false;
570640
}
571641

@@ -672,7 +742,7 @@ int phongo_execute_query(mongoc_client_t *client, const char *namespace, zval *z
672742
/* FIXME: Legacy way of specifying the readPreference option into this function */
673743
if (options && Z_TYPE_P(options) == IS_OBJECT && instanceof_function(Z_OBJCE_P(options), php_phongo_readpreference_ce TSRMLS_CC)) {
674744
zreadPreference = options;
675-
} else if (!phongo_execute_parse_options(options, PHONGO_READPREFERENCE_ALLOWED, &zreadPreference, NULL TSRMLS_CC)) {
745+
} else if (!phongo_execute_parse_options(client, server_id, options, PHONGO_COMMAND_READ, NULL, &zreadPreference, NULL TSRMLS_CC)) {
676746
return false;
677747
}
678748

@@ -715,36 +785,7 @@ static bson_t *create_wrapped_command_envelope(const char *db, bson_t *reply)
715785
return tmp;
716786
}
717787

718-
static int phongo_do_select_server(mongoc_client_t *client, bson_t *opts, zval *zreadPreference, int server_id TSRMLS_DC)
719-
{
720-
bson_error_t error;
721-
uint32_t selected_server_id;
722-
723-
if (server_id > 0) {
724-
bson_append_int32(opts, "serverId", -1, server_id);
725-
selected_server_id = server_id;
726-
} else {
727-
mongoc_server_description_t *selected_server = NULL;
728-
729-
selected_server = mongoc_client_select_server(client, false, (zreadPreference ? phongo_read_preference_from_zval(zreadPreference TSRMLS_CC) : mongoc_client_get_read_prefs(client)), &error);
730-
if (selected_server) {
731-
selected_server_id = mongoc_server_description_id(selected_server);
732-
bson_append_int32(opts, "serverId", -1, selected_server_id);
733-
mongoc_server_description_destroy(selected_server);
734-
} else {
735-
/* Check for connection related exceptions */
736-
if (!EG(exception)) {
737-
phongo_throw_exception_from_bson_error_t(&error TSRMLS_CC);
738-
}
739-
740-
return false;
741-
}
742-
}
743-
744-
return selected_server_id;
745-
}
746-
747-
int phongo_execute_command(mongoc_client_t *client, const char *db, zval *zcommand, zval *options, int server_id, zval *return_value, int return_value_used TSRMLS_DC) /* {{{ */
788+
int phongo_execute_command(mongoc_client_t *client, php_phongo_command_type_t type, const char *db, zval *zcommand, zval *options, int server_id, zval *return_value, int return_value_used TSRMLS_DC) /* {{{ */
748789
{
749790
const php_phongo_command_t *command;
750791
bson_iter_t iter;
@@ -754,6 +795,7 @@ int phongo_execute_command(mongoc_client_t *client, const char *db, zval *zcomma
754795
mongoc_cursor_t *cmd_cursor;
755796
uint32_t selected_server_id;
756797
zval *zreadPreference = NULL;
798+
int result;
757799

758800
command = Z_COMMAND_OBJ_P(zcommand);
759801

@@ -762,7 +804,7 @@ int phongo_execute_command(mongoc_client_t *client, const char *db, zval *zcomma
762804
/* FIXME: Legacy way of specifying the readPreference option into this function */
763805
if (options && Z_TYPE_P(options) == IS_OBJECT && instanceof_function(Z_OBJCE_P(options), php_phongo_readpreference_ce TSRMLS_CC)) {
764806
zreadPreference = options;
765-
} else if (!phongo_execute_parse_options(options, PHONGO_READPREFERENCE_ALLOWED, &zreadPreference, NULL TSRMLS_CC)) {
807+
} else if (!phongo_execute_parse_options(client, server_id, options, type, opts, &zreadPreference, NULL TSRMLS_CC)) {
766808
return false;
767809
}
768810

@@ -775,7 +817,27 @@ int phongo_execute_command(mongoc_client_t *client, const char *db, zval *zcomma
775817
/* Although "opts" already always includes the serverId option, the read
776818
* preference is added to the command parts, which is relevant for mongos
777819
* command construction. */
778-
if (!mongoc_client_command_with_opts(client, db, command->bson, phongo_read_preference_from_zval(zreadPreference TSRMLS_CC), opts, &reply, &error)) {
820+
switch (type) {
821+
case PHONGO_COMMAND_RAW:
822+
result = mongoc_client_command_with_opts(client, db, command->bson, phongo_read_preference_from_zval(zreadPreference TSRMLS_CC), opts, &reply, &error);
823+
break;
824+
case PHONGO_COMMAND_READ:
825+
result = mongoc_client_read_command_with_opts(client, db, command->bson, phongo_read_preference_from_zval(zreadPreference TSRMLS_CC), opts, &reply, &error);
826+
break;
827+
case PHONGO_COMMAND_WRITE:
828+
result = mongoc_client_write_command_with_opts(client, db, command->bson, opts, &reply, &error);
829+
break;
830+
case PHONGO_COMMAND_READ_WRITE:
831+
/* We can pass NULL as readPreference, as this argument was added historically, but has no function */
832+
result = mongoc_client_read_write_command_with_opts(client, db, command->bson, NULL, opts, &reply, &error);
833+
break;
834+
default:
835+
/* Should never happen, but if it does: exception */
836+
phongo_throw_exception(PHONGO_ERROR_LOGIC TSRMLS_CC, "Type '%d' should never have been passed to phongo_execute_command, please file a bug report", type);
837+
bson_free(opts);
838+
return false;
839+
}
840+
if (!result) {
779841
phongo_throw_exception_from_bson_error_t(&error TSRMLS_CC);
780842
bson_free(opts);
781843
return false;

php_phongo.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,17 @@ void phongo_throw_exception(php_phongo_error_domain_t domain TSRMLS_DC, const ch
111111
;
112112
void phongo_throw_exception_from_bson_error_t(bson_error_t *error TSRMLS_DC);
113113

114+
/* This enum is used for libmongoc function selection for the
115+
* phongo_execute_command types. The values are important, as the READ and
116+
* WRITE fields are also used as a bit field to see whether ReadPreference,
117+
* ReadConcern, and WriteConcern are supported for each type. */
118+
typedef enum {
119+
PHONGO_COMMAND_RAW = 0x13,
120+
PHONGO_COMMAND_READ = 0x01,
121+
PHONGO_COMMAND_WRITE = 0x02,
122+
PHONGO_COMMAND_READ_WRITE = 0x03
123+
} php_phongo_command_type_t;
124+
114125
zend_object_handlers *phongo_get_std_object_handlers(void);
115126

116127
void phongo_server_init (zval *return_value, mongoc_client_t *client, int server_id TSRMLS_DC);
@@ -119,7 +130,7 @@ void phongo_readpreference_init (zval *return_value, const
119130
void phongo_writeconcern_init (zval *return_value, const mongoc_write_concern_t *write_concern TSRMLS_DC);
120131
mongoc_bulk_operation_t* phongo_bulkwrite_init (zend_bool ordered);
121132
bool phongo_execute_write (mongoc_client_t *client, const char *namespace, php_phongo_bulkwrite_t *bulk_write, zval *zwriteConcern, int server_id, zval *return_value, int return_value_used TSRMLS_DC);
122-
int phongo_execute_command (mongoc_client_t *client, const char *db, zval *zcommand, zval *zreadPreference, int server_id, zval *return_value, int return_value_used TSRMLS_DC);
133+
int phongo_execute_command (mongoc_client_t *client, php_phongo_command_type_t type, const char *db, zval *zcommand, zval *zreadPreference, int server_id, zval *return_value, int return_value_used TSRMLS_DC);
123134
int phongo_execute_query (mongoc_client_t *client, const char *namespace, zval *zquery, zval *zreadPreference, int server_id, zval *return_value, int return_value_used TSRMLS_DC);
124135

125136
const mongoc_read_concern_t* phongo_read_concern_from_zval (zval *zread_concern TSRMLS_DC);

src/MongoDB/Manager.c

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,70 @@ static PHP_METHOD(Manager, executeCommand)
301301

302302
intern = Z_MANAGER_OBJ_P(getThis());
303303

304-
phongo_execute_command(intern->client, db, command, options, -1, return_value, return_value_used TSRMLS_CC);
304+
phongo_execute_command(intern->client, PHONGO_COMMAND_RAW, db, command, options, -1, return_value, return_value_used TSRMLS_CC);
305+
} /* }}} */
306+
307+
/* {{{ proto MongoDB\Driver\Cursor MongoDB\Driver\Manager::executeReadCommand(string $db, MongoDB\Driver\Command $command[, array $options = null])
308+
Execute a ReadCommand */
309+
static PHP_METHOD(Manager, executeReadCommand)
310+
{
311+
php_phongo_manager_t *intern;
312+
char *db;
313+
phongo_zpp_char_len db_len;
314+
zval *command;
315+
zval *options = NULL;
316+
DECLARE_RETURN_VALUE_USED
317+
SUPPRESS_UNUSED_WARNING(return_value_ptr)
318+
319+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sO|a!", &db, &db_len, &command, php_phongo_command_ce, &options) == FAILURE) {
320+
return;
321+
}
322+
323+
intern = Z_MANAGER_OBJ_P(getThis());
324+
325+
phongo_execute_command(intern->client, PHONGO_COMMAND_READ, db, command, options, -1, return_value, return_value_used TSRMLS_CC);
326+
} /* }}} */
327+
328+
/* {{{ proto MongoDB\Driver\Cursor MongoDB\Driver\Manager::executeWriteCommand(string $db, MongoDB\Driver\Command $command[, array $options = null])
329+
Execute a WriteCommand */
330+
static PHP_METHOD(Manager, executeWriteCommand)
331+
{
332+
php_phongo_manager_t *intern;
333+
char *db;
334+
phongo_zpp_char_len db_len;
335+
zval *command;
336+
zval *options = NULL;
337+
DECLARE_RETURN_VALUE_USED
338+
SUPPRESS_UNUSED_WARNING(return_value_ptr)
339+
340+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sO|a!", &db, &db_len, &command, php_phongo_command_ce, &options) == FAILURE) {
341+
return;
342+
}
343+
344+
intern = Z_MANAGER_OBJ_P(getThis());
345+
346+
phongo_execute_command(intern->client, PHONGO_COMMAND_WRITE, db, command, options, -1, return_value, return_value_used TSRMLS_CC);
347+
} /* }}} */
348+
349+
/* {{{ proto MongoDB\Driver\Cursor MongoDB\Driver\Manager::executeReadWriteCommand(string $db, MongoDB\Driver\Command $command[, array $options = null])
350+
Execute a ReadWriteCommand */
351+
static PHP_METHOD(Manager, executeReadWriteCommand)
352+
{
353+
php_phongo_manager_t *intern;
354+
char *db;
355+
phongo_zpp_char_len db_len;
356+
zval *command;
357+
zval *options = NULL;
358+
DECLARE_RETURN_VALUE_USED
359+
SUPPRESS_UNUSED_WARNING(return_value_ptr)
360+
361+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sO|a!", &db, &db_len, &command, php_phongo_command_ce, &options) == FAILURE) {
362+
return;
363+
}
364+
365+
intern = Z_MANAGER_OBJ_P(getThis());
366+
367+
phongo_execute_command(intern->client, PHONGO_COMMAND_READ_WRITE, db, command, options, -1, return_value, return_value_used TSRMLS_CC);
305368
} /* }}} */
306369

307370
/* {{{ proto MongoDB\Driver\Cursor MongoDB\Driver\Manager::executeQuery(string $namespace, MongoDB\Driver\Query $query[, array $options = null])
@@ -488,6 +551,12 @@ ZEND_BEGIN_ARG_INFO_EX(ai_Manager_executeCommand, 0, 0, 2)
488551
ZEND_ARG_INFO(0, options)
489552
ZEND_END_ARG_INFO()
490553

554+
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_executeRWCommand, 0, 0, 2)
555+
ZEND_ARG_INFO(0, db)
556+
ZEND_ARG_OBJ_INFO(0, command, MongoDB\\Driver\\Command, 0)
557+
ZEND_ARG_ARRAY_INFO(0, options, 0)
558+
ZEND_END_ARG_INFO()
559+
491560
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_executeQuery, 0, 0, 2)
492561
ZEND_ARG_INFO(0, namespace)
493562
ZEND_ARG_OBJ_INFO(0, zquery, MongoDB\\Driver\\Query, 0)
@@ -510,6 +579,9 @@ ZEND_END_ARG_INFO()
510579
static zend_function_entry php_phongo_manager_me[] = {
511580
PHP_ME(Manager, __construct, ai_Manager___construct, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
512581
PHP_ME(Manager, executeCommand, ai_Manager_executeCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
582+
PHP_ME(Manager, executeReadCommand, ai_Manager_executeRWCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
583+
PHP_ME(Manager, executeWriteCommand, ai_Manager_executeRWCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
584+
PHP_ME(Manager, executeReadWriteCommand, ai_Manager_executeCommand, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
513585
PHP_ME(Manager, executeQuery, ai_Manager_executeQuery, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
514586
PHP_ME(Manager, executeBulkWrite, ai_Manager_executeBulkWrite, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
515587
PHP_ME(Manager, getReadConcern, ai_Manager_void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)

0 commit comments

Comments
 (0)