Skip to content

Commit 9943341

Browse files
committed
Merge pull request #77
2 parents 6f82d0e + 960c51a commit 9943341

11 files changed

+694
-36
lines changed

php_phongo.c

Lines changed: 211 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1504,10 +1504,26 @@ mongoc_uri_t *php_phongo_make_uri(const char *uri_string, bson_t *options TSRMLS
15041504
uri = mongoc_uri_new(uri_string);
15051505
MONGOC_DEBUG("Connection string: '%s'", uri_string);
15061506

1507+
if (!uri) {
1508+
return NULL;
1509+
}
1510+
15071511
if (options && bson_iter_init(&iter, options)) {
15081512
while (bson_iter_next (&iter)) {
15091513
const char *key = bson_iter_key(&iter);
15101514

1515+
/* Skip read preference and write concern options, as those must be
1516+
* processed after the mongoc_client_t is constructed. */
1517+
if (!strcasecmp(key, "journal") ||
1518+
!strcasecmp(key, "readpreference") ||
1519+
!strcasecmp(key, "readpreferencetags") ||
1520+
!strcasecmp(key, "safe") ||
1521+
!strcasecmp(key, "slaveok") ||
1522+
!strcasecmp(key, "w") ||
1523+
!strcasecmp(key, "wtimeoutms")) {
1524+
continue;
1525+
}
1526+
15111527
if (mongoc_uri_option_is_bool(key)) {
15121528
mongoc_uri_set_option_as_bool (uri, key, bson_iter_as_bool(&iter));
15131529
}
@@ -1591,6 +1607,199 @@ void php_phongo_populate_default_ssl_ctx(php_stream_context *ctx, zval *driverOp
15911607
#undef SET_STRING_CTX
15921608
} /* }}} */
15931609

1610+
bool php_phongo_apply_rp_options_to_client(mongoc_client_t *client, bson_t *options TSRMLS_DC) /* {{{ */
1611+
{
1612+
bson_iter_t iter;
1613+
mongoc_read_prefs_t *new_rp;
1614+
const mongoc_read_prefs_t *old_rp;
1615+
1616+
if (!(old_rp = mongoc_client_get_read_prefs(client))) {
1617+
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "Client does not have a read preference");
1618+
1619+
return false;
1620+
}
1621+
1622+
/* Return early if there are no options to apply */
1623+
if (bson_empty0(options)) {
1624+
return true;
1625+
}
1626+
1627+
if (!bson_iter_init_find_case(&iter, options, "slaveok") &&
1628+
!bson_iter_init_find_case(&iter, options, "readpreference") &&
1629+
!bson_iter_init_find_case(&iter, options, "readpreferencetags")) {
1630+
return true;
1631+
}
1632+
1633+
new_rp = mongoc_read_prefs_copy(old_rp);
1634+
1635+
if (bson_iter_init_find_case(&iter, options, "slaveok") && BSON_ITER_HOLDS_BOOL(&iter)) {
1636+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY_PREFERRED);
1637+
}
1638+
1639+
if (bson_iter_init_find_case(&iter, options, "readpreference") && BSON_ITER_HOLDS_UTF8(&iter)) {
1640+
const char *str = bson_iter_utf8(&iter, NULL);
1641+
1642+
if (0 == strcasecmp("primary", str)) {
1643+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_PRIMARY);
1644+
} else if (0 == strcasecmp("primarypreferred", str)) {
1645+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_PRIMARY_PREFERRED);
1646+
} else if (0 == strcasecmp("secondary", str)) {
1647+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY);
1648+
} else if (0 == strcasecmp("secondarypreferred", str)) {
1649+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY_PREFERRED);
1650+
} else if (0 == strcasecmp("nearest", str)) {
1651+
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_NEAREST);
1652+
} else {
1653+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Unsupported readPreference value: '%s'", str);
1654+
mongoc_read_prefs_destroy(new_rp);
1655+
1656+
return false;
1657+
}
1658+
}
1659+
1660+
if (bson_iter_init_find_case(&iter, options, "readpreferencetags") && BSON_ITER_HOLDS_ARRAY(&iter)) {
1661+
bson_t tags;
1662+
uint32_t len;
1663+
const uint8_t *data;
1664+
1665+
bson_iter_array(&iter, &len, &data);
1666+
1667+
if (bson_init_static(&tags, data, len)) {
1668+
mongoc_read_prefs_set_tags(new_rp, &tags);
1669+
}
1670+
}
1671+
1672+
if (mongoc_read_prefs_get_mode(new_rp) == MONGOC_READ_PRIMARY &&
1673+
!bson_empty(mongoc_read_prefs_get_tags(new_rp))) {
1674+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Primary read preference mode conflicts with tags");
1675+
mongoc_read_prefs_destroy(new_rp);
1676+
1677+
return false;
1678+
}
1679+
1680+
/* This may be redundant in light of the last check (primary with tags), but
1681+
* we'll check anyway in case additional validation is implemented. */
1682+
if (!mongoc_read_prefs_is_valid(new_rp)) {
1683+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Read preference is not valid");
1684+
mongoc_read_prefs_destroy(new_rp);
1685+
1686+
return false;
1687+
}
1688+
1689+
mongoc_client_set_read_prefs(client, new_rp);
1690+
mongoc_read_prefs_destroy(new_rp);
1691+
1692+
return true;
1693+
} /* }}} */
1694+
1695+
bool php_phongo_apply_wc_options_to_client(mongoc_client_t *client, bson_t *options TSRMLS_DC) /* {{{ */
1696+
{
1697+
bson_iter_t iter;
1698+
int32_t wtimeoutms;
1699+
mongoc_write_concern_t *new_wc;
1700+
const mongoc_write_concern_t *old_wc;
1701+
1702+
if (!(old_wc = mongoc_client_get_write_concern(client))) {
1703+
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "Client does not have a write concern");
1704+
1705+
return false;
1706+
}
1707+
1708+
/* Return early if there are no options to apply */
1709+
if (bson_empty0(options)) {
1710+
return true;
1711+
}
1712+
1713+
if (!bson_iter_init_find_case(&iter, options, "journal") &&
1714+
!bson_iter_init_find_case(&iter, options, "safe") &&
1715+
!bson_iter_init_find_case(&iter, options, "w") &&
1716+
!bson_iter_init_find_case(&iter, options, "wtimeoutms")) {
1717+
return true;
1718+
}
1719+
1720+
wtimeoutms = mongoc_write_concern_get_wtimeout(old_wc);
1721+
1722+
new_wc = mongoc_write_concern_copy(old_wc);
1723+
1724+
if (bson_iter_init_find_case(&iter, options, "safe") && BSON_ITER_HOLDS_BOOL(&iter)) {
1725+
mongoc_write_concern_set_w(new_wc, bson_iter_bool(&iter) ? 1 : MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED);
1726+
}
1727+
1728+
if (bson_iter_init_find_case(&iter, options, "wtimeoutms") && BSON_ITER_HOLDS_INT32(&iter)) {
1729+
wtimeoutms = bson_iter_int32(&iter);
1730+
}
1731+
1732+
if (bson_iter_init_find_case(&iter, options, "journal") && BSON_ITER_HOLDS_BOOL(&iter)) {
1733+
mongoc_write_concern_set_journal(new_wc, bson_iter_bool(&iter));
1734+
}
1735+
1736+
if (bson_iter_init_find_case(&iter, options, "w")) {
1737+
if (BSON_ITER_HOLDS_INT32(&iter)) {
1738+
int32_t value = bson_iter_int32(&iter);
1739+
1740+
switch (value) {
1741+
case MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED:
1742+
case MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED:
1743+
mongoc_write_concern_set_w(new_wc, value);
1744+
break;
1745+
1746+
default:
1747+
if (value > 0) {
1748+
mongoc_write_concern_set_w(new_wc, value);
1749+
break;
1750+
}
1751+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Unsupported w value: %d", value);
1752+
mongoc_write_concern_destroy(new_wc);
1753+
1754+
return false;
1755+
}
1756+
} else if (BSON_ITER_HOLDS_UTF8(&iter)) {
1757+
const char *str = bson_iter_utf8(&iter, NULL);
1758+
1759+
if (0 == strcasecmp("majority", str)) {
1760+
mongoc_write_concern_set_wmajority(new_wc, wtimeoutms);
1761+
} else {
1762+
mongoc_write_concern_set_wtag(new_wc, str);
1763+
}
1764+
}
1765+
}
1766+
1767+
/* Only set wtimeout if it's still applicable; otherwise, clear it. */
1768+
if (mongoc_write_concern_get_w(new_wc) > 1 ||
1769+
mongoc_write_concern_get_wmajority(new_wc) ||
1770+
mongoc_write_concern_get_wtag(new_wc)) {
1771+
mongoc_write_concern_set_wtimeout(new_wc, wtimeoutms);
1772+
} else {
1773+
mongoc_write_concern_set_wtimeout(new_wc, 0);
1774+
}
1775+
1776+
if (mongoc_write_concern_get_journal(new_wc)) {
1777+
int32_t w = mongoc_write_concern_get_w(new_wc);
1778+
1779+
if (w == MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED || w == MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED) {
1780+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Journal conflicts with w value: %d", w);
1781+
mongoc_write_concern_destroy(new_wc);
1782+
1783+
return false;
1784+
}
1785+
}
1786+
1787+
/* This may be redundant in light of the last check (unacknowledged w with
1788+
journal), but we'll check anyway in case additional validation is
1789+
implemented. */
1790+
if (!_mongoc_write_concern_is_valid(new_wc)) {
1791+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Write concern is not valid");
1792+
mongoc_write_concern_destroy(new_wc);
1793+
1794+
return false;
1795+
}
1796+
1797+
mongoc_client_set_write_concern(client, new_wc);
1798+
mongoc_write_concern_destroy(new_wc);
1799+
1800+
return true;
1801+
} /* }}} */
1802+
15941803
mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, zval *driverOptions TSRMLS_DC) /* {{{ */
15951804
{
15961805
zval **tmp;
@@ -1621,7 +1830,7 @@ mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, zval *dri
16211830
client = mongoc_client_new_from_uri(uri);
16221831

16231832
if (!client) {
1624-
RETURN(false);
1833+
RETURN(NULL);
16251834
}
16261835

16271836

@@ -1982,6 +2191,7 @@ void _phongo_debug_bson(bson_t *bson)
19822191
str = bson_as_json(bson, &str_len);
19832192

19842193
php_printf("JSON: %s\n", str);
2194+
bson_free(str);
19852195
}
19862196
/* LCOV_EXCL_STOP */
19872197
#endif

php_phongo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ void php_phongo_read_preference_to_zval(zval *retval, const mongoc_read_prefs_t
130130
void php_phongo_write_concern_to_zval(zval *retval, const mongoc_write_concern_t *write_concern);
131131
void php_phongo_cursor_to_zval(zval *retval, php_phongo_cursor_t *cursor);
132132

133+
bool php_phongo_apply_rp_options_to_client(mongoc_client_t *client, bson_t *options TSRMLS_DC);
134+
bool php_phongo_apply_wc_options_to_client(mongoc_client_t *client, bson_t *options TSRMLS_DC);
133135
mongoc_uri_t *php_phongo_make_uri(const char *uri_string, bson_t *options TSRMLS_DC);
134136
mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, zval *driverOptions TSRMLS_DC);
135137
void php_phongo_objectid_new_from_oid(zval *object, const bson_oid_t *oid TSRMLS_DC);

src/MongoDB/Manager.c

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,32 @@ PHP_METHOD(Manager, __construct)
8181
zval_to_bson(options, PHONGO_BSON_NONE, &bson_options, NULL TSRMLS_CC);
8282
}
8383

84-
uri = php_phongo_make_uri (uri_string, &bson_options TSRMLS_CC);
85-
if (uri) {
86-
intern->client = php_phongo_make_mongo_client(uri, driverOptions TSRMLS_CC);
87-
mongoc_uri_destroy(uri);
88-
} else {
89-
phongo_throw_exception(PHONGO_ERROR_RUNTIME TSRMLS_CC, "%s", "Failed to parse MongoDB URI");
84+
if (!(uri = php_phongo_make_uri(uri_string, &bson_options TSRMLS_CC))) {
85+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Failed to parse MongoDB URI: '%s'", uri_string);
86+
bson_destroy(&bson_options);
87+
88+
return;
9089
}
90+
91+
intern->client = php_phongo_make_mongo_client(uri, driverOptions TSRMLS_CC);
92+
mongoc_uri_destroy(uri);
93+
9194
if (!intern->client) {
92-
phongo_throw_exception(PHONGO_ERROR_RUNTIME TSRMLS_CC, "%s", "Failed to create Manager from URI");
95+
phongo_throw_exception(PHONGO_ERROR_RUNTIME TSRMLS_CC, "Failed to create Manager from URI: '%s'", uri_string);
96+
bson_destroy(&bson_options);
97+
98+
return;
99+
}
100+
101+
if (!php_phongo_apply_rp_options_to_client(intern->client, &bson_options TSRMLS_CC) ||
102+
!php_phongo_apply_wc_options_to_client(intern->client, &bson_options TSRMLS_CC)) {
103+
/* Exception should already have been thrown */
104+
bson_destroy(&bson_options);
105+
106+
return;
93107
}
94108

109+
bson_destroy(&bson_options);
95110
}
96111
/* }}} */
97112
/* {{{ proto MongoDB\Driver\Cursor Manager::executeCommand(string $db, MongoDB\Driver\Command $command[, MongoDB\Driver\ReadPreference $readPreference = null])
@@ -260,6 +275,24 @@ PHP_METHOD(Manager, executeDelete)
260275
bson_clear(&bson);
261276
}
262277
/* }}} */
278+
/* {{{ proto MongoDB\Driver\ReadPreference Manager::getReadPreference()
279+
Returns the ReadPreference associated with this Manager */
280+
PHP_METHOD(Manager, getReadPreference)
281+
{
282+
php_phongo_manager_t *intern;
283+
(void)return_value_ptr;
284+
285+
intern = (php_phongo_manager_t *)zend_object_store_get_object(getThis() TSRMLS_CC);
286+
287+
if (zend_parse_parameters_none() == FAILURE) {
288+
return;
289+
}
290+
291+
if (return_value_used) {
292+
php_phongo_read_preference_to_zval(return_value, mongoc_client_get_read_prefs(intern->client));
293+
}
294+
}
295+
/* }}} */
263296
/* {{{ proto MongoDB\Driver\Server[] Manager::getServers()
264297
Returns the Servers associated with this Manager */
265298
PHP_METHOD(Manager, getServers)
@@ -288,6 +321,24 @@ PHP_METHOD(Manager, getServers)
288321
}
289322
}
290323
/* }}} */
324+
/* {{{ proto MongoDB\Driver\WriteConcern Manager::getWriteConcern()
325+
Returns the WriteConcern associated with this Manager */
326+
PHP_METHOD(Manager, getWriteConcern)
327+
{
328+
php_phongo_manager_t *intern;
329+
(void)return_value_ptr;
330+
331+
intern = (php_phongo_manager_t *)zend_object_store_get_object(getThis() TSRMLS_CC);
332+
333+
if (zend_parse_parameters_none() == FAILURE) {
334+
return;
335+
}
336+
337+
if (return_value_used) {
338+
php_phongo_write_concern_to_zval(return_value, mongoc_client_get_write_concern(intern->client));
339+
}
340+
}
341+
/* }}} */
291342
/* {{{ proto MongoDB\Driver\Server Manager::selectServers(MongoDB\Driver\ReadPreference $readPreference)
292343
Returns a suitable Server for the given $readPreference */
293344
PHP_METHOD(Manager, selectServer)
@@ -383,9 +434,15 @@ ZEND_BEGIN_ARG_INFO_EX(ai_Manager_executeDelete, 0, 0, 2)
383434
ZEND_ARG_OBJ_INFO(0, writeConcern, MongoDB\\Driver\\WriteConcern, 1)
384435
ZEND_END_ARG_INFO();
385436

437+
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_getReadPreference, 0, 0, 0)
438+
ZEND_END_ARG_INFO();
439+
386440
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_getServers, 0, 0, 0)
387441
ZEND_END_ARG_INFO();
388442

443+
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_getWriteConcern, 0, 0, 0)
444+
ZEND_END_ARG_INFO();
445+
389446
ZEND_BEGIN_ARG_INFO_EX(ai_Manager_selectServer, 0, 0, 1)
390447
ZEND_ARG_OBJ_INFO(0, readPreference, MongoDB\\Driver\\ReadPreference, 1)
391448
ZEND_END_ARG_INFO();
@@ -398,7 +455,9 @@ static zend_function_entry php_phongo_manager_me[] = {
398455
PHP_ME(Manager, executeInsert, ai_Manager_executeInsert, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
399456
PHP_ME(Manager, executeUpdate, ai_Manager_executeUpdate, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
400457
PHP_ME(Manager, executeDelete, ai_Manager_executeDelete, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
458+
PHP_ME(Manager, getReadPreference, ai_Manager_getReadPreference, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
401459
PHP_ME(Manager, getServers, ai_Manager_getServers, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
460+
PHP_ME(Manager, getWriteConcern, ai_Manager_getWriteConcern, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
402461
PHP_ME(Manager, selectServer, ai_Manager_selectServer, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
403462
PHP_ME(Manager, __wakeUp, NULL, ZEND_ACC_PUBLIC)
404463
PHP_FE_END
@@ -414,7 +473,9 @@ static void php_phongo_manager_free_object(void *object TSRMLS_DC) /* {{{ */
414473

415474
zend_object_std_dtor(&intern->std TSRMLS_CC);
416475

417-
mongoc_client_destroy(intern->client);
476+
if (intern->client) {
477+
mongoc_client_destroy(intern->client);
478+
}
418479

419480
efree(intern);
420481
} /* }}} */

0 commit comments

Comments
 (0)