@@ -448,13 +448,109 @@ mongoc_bulk_operation_t *phongo_bulkwrite_init(zend_bool ordered) { /* {{{ */
448448 return mongoc_bulk_operation_new (ordered );
449449} /* }}} */
450450
451- bool phongo_execute_write (mongoc_client_t * client , const char * namespace , php_phongo_bulkwrite_t * bulk_write , const mongoc_write_concern_t * write_concern , int server_id , zval * return_value , int return_value_used TSRMLS_DC ) /* {{{ */
451+ #define PHONGO_WRITECONCERN_ALLOWED 0x01
452+ #define PHONGO_READPREFERENCE_ALLOWED 0x02
453+
454+ static int process_read_preference (zval * option , zval * * zreadPreference TSRMLS_DC )
455+ {
456+ if (Z_TYPE_P (option ) == IS_OBJECT && instanceof_function (Z_OBJCE_P (option ), php_phongo_readpreference_ce TSRMLS_CC )) {
457+ * zreadPreference = option ;
458+ } else {
459+ phongo_throw_exception (
460+ PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC ,
461+ "Expected 'readPreference' option to be 'MongoDB\\Driver\\ReadPreference', %s given" ,
462+ zend_get_type_by_const (Z_TYPE_P (option ))
463+ );
464+ return false;
465+ }
466+ return true;
467+ }
468+
469+ static int process_write_concern (zval * option , zval * * zwriteConcern TSRMLS_DC )
470+ {
471+ if (Z_TYPE_P (option ) == IS_OBJECT && instanceof_function (Z_OBJCE_P (option ), php_phongo_writeconcern_ce TSRMLS_CC )) {
472+ * zwriteConcern = option ;
473+ } else {
474+ phongo_throw_exception (
475+ PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC ,
476+ "Expected 'writeConcern' option to be 'MongoDB\\Driver\\WriteConcern', %s given" ,
477+ zend_get_type_by_const (Z_TYPE_P (option ))
478+ );
479+ return false;
480+ }
481+ return true;
482+ }
483+
484+ static int phongo_execute_parse_options (zval * options , int flags , zval * * zreadPreference , zval * * zwriteConcern TSRMLS_DC )
485+ {
486+ if (options && Z_TYPE_P (options ) == IS_ARRAY ) {
487+ HashTable * ht_data = HASH_OF (options );
488+ #if PHP_VERSION_ID >= 70000
489+ zend_string * string_key = NULL ;
490+ zend_ulong num_key = 0 ;
491+ zval * option ;
492+
493+ ZEND_HASH_FOREACH_KEY_VAL (ht_data , num_key , string_key , option ) {
494+ if (!string_key ) {
495+ continue ;
496+ }
497+
498+ if ((!strcasecmp (ZSTR_VAL (string_key ), "readPreference" )) && (flags & PHONGO_READPREFERENCE_ALLOWED )) {
499+ if (!process_read_preference (option , zreadPreference )) {
500+ return false;
501+ }
502+ } else if ((!strcasecmp (ZSTR_VAL (string_key ), "writeConcern" )) && (flags & PHONGO_WRITECONCERN_ALLOWED )) {
503+ if (!process_write_concern (option , zwriteConcern )) {
504+ return false;
505+ }
506+ } else {
507+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Unknown option '%s'" , ZSTR_VAL (string_key ));
508+ return false;
509+ }
510+ } ZEND_HASH_FOREACH_END ();
511+ #else
512+ HashPosition pos ;
513+ zval * * option ;
514+
515+ for (zend_hash_internal_pointer_reset_ex (ht_data , & pos );
516+ zend_hash_get_current_data_ex (ht_data , (void * * ) & option , & pos ) == SUCCESS ;
517+ zend_hash_move_forward_ex (ht_data , & pos )) {
518+ char * string_key = NULL ;
519+ uint string_key_len = 0 ;
520+ ulong num_key = 0 ;
521+
522+ if (HASH_KEY_IS_STRING != zend_hash_get_current_key_ex (ht_data , & string_key , & string_key_len , & num_key , 0 , & pos )) {
523+ continue ;
524+ }
525+
526+ /* URI options are case-insensitive */
527+ if ((!strcasecmp (string_key , "readPreference" )) && (flags & PHONGO_READPREFERENCE_ALLOWED )) {
528+ if (!process_read_preference (* option , zreadPreference TSRMLS_CC )) {
529+ return false;
530+ }
531+ } else if ((!strcasecmp (string_key , "writeConcern" )) && (flags & PHONGO_WRITECONCERN_ALLOWED )) {
532+ if (!process_write_concern (* option , zwriteConcern TSRMLS_CC )) {
533+ return false;
534+ }
535+ } else {
536+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Unknown option '%s'" , string_key );
537+ return false;
538+ }
539+ }
540+ #endif
541+ }
542+ return true;
543+ }
544+
545+ bool phongo_execute_write (mongoc_client_t * client , const char * namespace , php_phongo_bulkwrite_t * bulk_write , zval * options , int server_id , zval * return_value , int return_value_used TSRMLS_DC ) /* {{{ */
452546{
453547 bson_error_t error ;
454548 int success ;
455549 bson_t reply = BSON_INITIALIZER ;
456550 mongoc_bulk_operation_t * bulk = bulk_write -> bulk ;
457551 php_phongo_writeresult_t * writeresult ;
552+ zval * zwriteConcern = NULL ;
553+ const mongoc_write_concern_t * write_concern ;
458554
459555 if (bulk_write -> executed ) {
460556 phongo_throw_exception (PHONGO_ERROR_WRITE_FAILED TSRMLS_CC , "BulkWrite objects may only be executed once and this instance has already been executed" );
@@ -466,12 +562,20 @@ bool phongo_execute_write(mongoc_client_t *client, const char *namespace, php_ph
466562 return false;
467563 }
468564
565+ /* FIXME: Legacy way of specifying the writeConcern option into this function */
566+ if (options && Z_TYPE_P (options ) == IS_OBJECT && instanceof_function (Z_OBJCE_P (options ), php_phongo_writeconcern_ce TSRMLS_CC )) {
567+ zwriteConcern = options ;
568+ } else if (!phongo_execute_parse_options (options , PHONGO_WRITECONCERN_ALLOWED , NULL , & zwriteConcern TSRMLS_CC )) {
569+ return false;
570+ }
571+
469572 mongoc_bulk_operation_set_database (bulk , bulk_write -> database );
470573 mongoc_bulk_operation_set_collection (bulk , bulk_write -> collection );
471574 mongoc_bulk_operation_set_client (bulk , client );
472575
473576 /* If a write concern was not specified, libmongoc will use the client's
474577 * write concern; however, we should still fetch it for the write result. */
578+ write_concern = phongo_write_concern_from_zval (zwriteConcern TSRMLS_CC );
475579 if (write_concern ) {
476580 mongoc_bulk_operation_set_write_concern (bulk , write_concern );
477581 } else {
@@ -542,13 +646,14 @@ static bool phongo_advance_cursor_and_check_for_error(mongoc_cursor_t *cursor TS
542646 return true;
543647}
544648
545- 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 ) /* {{{ */
649+ int phongo_execute_query (mongoc_client_t * client , const char * namespace , zval * zquery , zval * options , int server_id , zval * return_value , int return_value_used TSRMLS_DC ) /* {{{ */
546650{
547651 const php_phongo_query_t * query ;
548652 mongoc_cursor_t * cursor ;
549653 char * dbname ;
550654 char * collname ;
551655 mongoc_collection_t * collection ;
656+ zval * zreadPreference = NULL ;
552657
553658 if (!phongo_split_namespace (namespace , & dbname , & collname )) {
554659 phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "%s: %s" , "Invalid namespace provided" , namespace );
@@ -564,6 +669,13 @@ int phongo_execute_query(mongoc_client_t *client, const char *namespace, zval *z
564669 mongoc_collection_set_read_concern (collection , query -> read_concern );
565670 }
566671
672+ /* FIXME: Legacy way of specifying the readPreference option into this function */
673+ if (options && Z_TYPE_P (options ) == IS_OBJECT && instanceof_function (Z_OBJCE_P (options ), php_phongo_readpreference_ce TSRMLS_CC )) {
674+ zreadPreference = options ;
675+ } else if (!phongo_execute_parse_options (options , PHONGO_READPREFERENCE_ALLOWED , & zreadPreference , NULL TSRMLS_CC )) {
676+ return false;
677+ }
678+
567679 cursor = mongoc_collection_find_with_opts (collection , query -> filter , query -> opts , phongo_read_preference_from_zval (zreadPreference TSRMLS_CC ));
568680 mongoc_collection_destroy (collection );
569681
@@ -603,7 +715,7 @@ static bson_t *create_wrapped_command_envelope(const char *db, bson_t *reply)
603715 return tmp ;
604716}
605717
606- static int phongo_do_select_server (mongoc_client_t * client , bson_t * opts , zval * zreadPreference , int server_id )
718+ static int phongo_do_select_server (mongoc_client_t * client , bson_t * opts , zval * zreadPreference , int server_id TSRMLS_DC )
607719{
608720 bson_error_t error ;
609721 uint32_t selected_server_id ;
@@ -632,7 +744,7 @@ static int phongo_do_select_server(mongoc_client_t *client, bson_t *opts, zval *
632744 return selected_server_id ;
633745}
634746
635- 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 ) /* {{{ */
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 ) /* {{{ */
636748{
637749 const php_phongo_command_t * command ;
638750 bson_iter_t iter ;
@@ -641,17 +753,28 @@ int phongo_execute_command(mongoc_client_t *client, const char *db, zval *zcomma
641753 bson_t * opts ;
642754 mongoc_cursor_t * cmd_cursor ;
643755 uint32_t selected_server_id ;
756+ zval * zreadPreference = NULL ;
644757
645758 command = Z_COMMAND_OBJ_P (zcommand );
646759
647760 opts = bson_new ();
648761
649- selected_server_id = phongo_do_select_server (client , opts , zreadPreference , server_id );
762+ /* FIXME: Legacy way of specifying the readPreference option into this function */
763+ if (options && Z_TYPE_P (options ) == IS_OBJECT && instanceof_function (Z_OBJCE_P (options ), php_phongo_readpreference_ce TSRMLS_CC )) {
764+ zreadPreference = options ;
765+ } else if (!phongo_execute_parse_options (options , PHONGO_READPREFERENCE_ALLOWED , & zreadPreference , NULL TSRMLS_CC )) {
766+ return false;
767+ }
768+
769+ selected_server_id = phongo_do_select_server (client , opts , zreadPreference , server_id TSRMLS_CC );
650770 if (!selected_server_id ) {
651771 bson_free (opts );
652772 return false;
653773 }
654774
775+ /* Although "opts" already always includes the serverId option, the read
776+ * preference is added to the command parts, which is relevant for mongos
777+ * command construction. */
655778 if (!mongoc_client_command_with_opts (client , db , command -> bson , phongo_read_preference_from_zval (zreadPreference TSRMLS_CC ), opts , & reply , & error )) {
656779 phongo_throw_exception_from_bson_error_t (& error TSRMLS_CC );
657780 bson_free (opts );
0 commit comments