Skip to content

Commit 1060cb8

Browse files
committed
PHPC-433: Refactor phongo_manager_init() option handling
BSON conversion of uriOptions moves to phongo_manager_init(). Merging of stream context options into driverOptions and removal of the context resource is now done in the Manager constructor. phongo_manager_init() now has zvals for both uriOptions and driverOptions and will be able to serialize them with the URI string for a client hash.
1 parent e5db9dd commit 1060cb8

File tree

3 files changed

+143
-91
lines changed

3 files changed

+143
-91
lines changed

php_phongo.c

Lines changed: 87 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,123 +1281,104 @@ static bool php_phongo_apply_wc_options_to_uri(mongoc_uri_t *uri, bson_t *option
12811281
return true;
12821282
} /* }}} */
12831283

1284-
static bool php_phongo_apply_ssl_opts(mongoc_client_t *client, zval *zdriverOptions TSRMLS_DC)
1284+
static inline char *php_phongo_fetch_ssl_opt_string(zval *zoptions, const char *key, int key_len)
12851285
{
1286-
mongoc_ssl_opt_t ssl_opt = {0};
1287-
zval *zoptions;
1288-
zend_bool free_pem_file = 0, free_pem_pwd = 0, free_ca_file = 0, free_ca_dir = 0, free_crl_file = 0;
1289-
int plen;
1290-
bool valid_options;
1286+
int plen;
1287+
zend_bool pfree;
1288+
char *pval, *value;
12911289

1292-
if (!zdriverOptions) {
1293-
return false;
1294-
}
1295-
1296-
zoptions = zdriverOptions;
1297-
1298-
/* Use SSL context options if provided for backwards compatibility */
1299-
if (php_array_existsc(zdriverOptions, "context")) {
1300-
php_stream_context *context;
1301-
zval *zcontext;
1290+
pval = php_array_fetchl_string(zoptions, key, key_len, &plen, &pfree);
1291+
value = pfree ? pval : estrndup(pval, plen);
13021292

1303-
zcontext = php_array_fetchc(zdriverOptions, "context");
1293+
return value;
1294+
}
13041295

1305-
context = php_stream_context_from_zval(zcontext, 1);
1296+
static mongoc_ssl_opt_t *php_phongo_make_ssl_opt(zval *zoptions TSRMLS_DC)
1297+
{
1298+
mongoc_ssl_opt_t *ssl_opt;
13061299

1307-
if (!context) {
1308-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "\"context\" driver option is not a valid Stream-Context resource");
1309-
return false;
1310-
}
1300+
if (!zoptions) {
1301+
return NULL;
1302+
}
13111303

1312-
#if PHP_VERSION_ID >= 70000
1313-
zoptions = php_array_fetchc_array(&context->options, "ssl");
1314-
#else
1315-
zoptions = php_array_fetchc_array(context->options, "ssl");
1304+
#if defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) || defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT)
1305+
if (php_array_existsc(zoptions, "ca_dir") || php_array_existsc(zoptions, "capath")) {
1306+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "\"ca_dir\" and \"capath\" options are not supported by Secure Channel and Secure Transport");
1307+
return NULL;
1308+
}
13161309
#endif
13171310

1318-
if (!zoptions) {
1319-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Stream-Context resource does not contain \"ssl\" options array");
1320-
return false;
1321-
}
1322-
}
1311+
ssl_opt = ecalloc(1, sizeof(mongoc_ssl_opt_t));
13231312

13241313
/* Check canonical option names first and fall back to SSL context options
13251314
* for backwards compatibility. */
13261315
if (php_array_existsc(zoptions, "allow_invalid_hostname")) {
1327-
ssl_opt.allow_invalid_hostname = php_array_fetchc_bool(zoptions, "allow_invalid_hostname");
1316+
ssl_opt->allow_invalid_hostname = php_array_fetchc_bool(zoptions, "allow_invalid_hostname");
13281317
}
13291318

13301319
if (php_array_existsc(zoptions, "weak_cert_validation")) {
1331-
ssl_opt.weak_cert_validation = php_array_fetchc_bool(zoptions, "weak_cert_validation");
1320+
ssl_opt->weak_cert_validation = php_array_fetchc_bool(zoptions, "weak_cert_validation");
13321321
} else if (php_array_existsc(zoptions, "allow_self_signed")) {
1333-
ssl_opt.weak_cert_validation = php_array_fetchc_bool(zoptions, "allow_self_signed");
1322+
ssl_opt->weak_cert_validation = php_array_fetchc_bool(zoptions, "allow_self_signed");
13341323
}
13351324

13361325
if (php_array_existsc(zoptions, "pem_file")) {
1337-
ssl_opt.pem_file = php_array_fetchc_string(zoptions, "pem_file", &plen, &free_pem_file);
1326+
ssl_opt->pem_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("pem_file"));
13381327
} else if (php_array_existsc(zoptions, "local_cert")) {
1339-
ssl_opt.pem_file = php_array_fetchc_string(zoptions, "local_cert", &plen, &free_pem_file);
1328+
ssl_opt->pem_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("local_cert"));
13401329
}
13411330

13421331
if (php_array_existsc(zoptions, "pem_pwd")) {
1343-
ssl_opt.pem_pwd = php_array_fetchc_string(zoptions, "pem_pwd", &plen, &free_pem_pwd);
1332+
ssl_opt->pem_pwd = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("pem_pwd"));
13441333
} else if (php_array_existsc(zoptions, "passphrase")) {
1345-
ssl_opt.pem_pwd = php_array_fetchc_string(zoptions, "passphrase", &plen, &free_pem_pwd);
1334+
ssl_opt->pem_pwd = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("passphrase"));
13461335
}
13471336

13481337
if (php_array_existsc(zoptions, "ca_file")) {
1349-
ssl_opt.ca_file = php_array_fetchc_string(zoptions, "ca_file", &plen, &free_ca_file);
1338+
ssl_opt->ca_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("ca_file"));
13501339
} else if (php_array_existsc(zoptions, "cafile")) {
1351-
ssl_opt.ca_file = php_array_fetchc_string(zoptions, "cafile", &plen, &free_ca_file);
1340+
ssl_opt->ca_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("cafile"));
13521341
}
13531342

13541343
if (php_array_existsc(zoptions, "ca_dir")) {
1355-
ssl_opt.ca_dir = php_array_fetchc_string(zoptions, "ca_dir", &plen, &free_ca_dir);
1344+
ssl_opt->ca_dir = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("ca_dir"));
13561345
} else if (php_array_existsc(zoptions, "capath")) {
1357-
ssl_opt.ca_dir = php_array_fetchc_string(zoptions, "capath", &plen, &free_ca_dir);
1346+
ssl_opt->ca_dir = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("capath"));
13581347
}
13591348

13601349
if (php_array_existsc(zoptions, "crl_file")) {
1361-
ssl_opt.crl_file = php_array_fetchc_string(zoptions, "crl_file", &plen, &free_crl_file);
1350+
ssl_opt->crl_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("crl_file"));
13621351
}
13631352

1364-
valid_options = true;
1365-
1366-
#if defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) || defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT)
1367-
if (ssl_opt.ca_dir) {
1368-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "\"ca_dir\" and \"capath\" options are not supported by Secure Channel and Secure Transport");
1369-
valid_options = false;
1370-
}
1371-
#endif
1372-
1373-
if (valid_options) {
1374-
mongoc_client_set_ssl_opts(client, &ssl_opt);
1375-
}
1353+
return ssl_opt;
1354+
}
13761355

1377-
if (free_pem_file) {
1378-
str_efree(ssl_opt.pem_file);
1356+
static void php_phongo_free_ssl_opt(mongoc_ssl_opt_t *ssl_opt)
1357+
{
1358+
if (ssl_opt->pem_file) {
1359+
str_efree(ssl_opt->pem_file);
13791360
}
13801361

1381-
if (free_pem_pwd) {
1382-
str_efree(ssl_opt.pem_pwd);
1362+
if (ssl_opt->pem_pwd) {
1363+
str_efree(ssl_opt->pem_pwd);
13831364
}
13841365

1385-
if (free_ca_file) {
1386-
str_efree(ssl_opt.ca_file);
1366+
if (ssl_opt->ca_file) {
1367+
str_efree(ssl_opt->ca_file);
13871368
}
13881369

1389-
if (free_ca_dir) {
1390-
str_efree(ssl_opt.ca_dir);
1370+
if (ssl_opt->ca_dir) {
1371+
str_efree(ssl_opt->ca_dir);
13911372
}
13921373

1393-
if (free_crl_file) {
1394-
str_efree(ssl_opt.crl_file);
1374+
if (ssl_opt->crl_file) {
1375+
str_efree(ssl_opt->crl_file);
13951376
}
13961377

1397-
return valid_options;
1378+
efree(ssl_opt);
13981379
}
13991380

1400-
static mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, zval *driverOptions TSRMLS_DC) /* {{{ */
1381+
static mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, mongoc_ssl_opt_t *ssl_opt TSRMLS_DC) /* {{{ */
14011382
{
14021383
const char *mongoc_version, *bson_version;
14031384
mongoc_client_t *client;
@@ -1429,42 +1410,63 @@ static mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, zv
14291410
return NULL;
14301411
}
14311412

1432-
if (mongoc_uri_get_ssl(uri) && driverOptions) {
1433-
if (!php_phongo_apply_ssl_opts(client, driverOptions TSRMLS_CC)) {
1434-
mongoc_client_destroy(client);
1435-
return NULL;
1436-
}
1413+
if (mongoc_uri_get_ssl(uri) && ssl_opt) {
1414+
mongoc_client_set_ssl_opts(client, ssl_opt);
14371415
}
14381416

14391417
return client;
14401418
} /* }}} */
14411419

1442-
bool phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string, bson_t *bson_options, zval *driverOptions TSRMLS_DC) /* {{{ */
1420+
void phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string, zval *options, zval *driverOptions TSRMLS_DC) /* {{{ */
14431421
{
1444-
mongoc_uri_t *uri;
1422+
bson_t bson_options = BSON_INITIALIZER;
1423+
mongoc_uri_t *uri = NULL;
1424+
mongoc_ssl_opt_t *ssl_opt = NULL;
1425+
1426+
if (options) {
1427+
phongo_zval_to_bson(options, PHONGO_BSON_NONE, &bson_options, NULL TSRMLS_CC);
1428+
}
1429+
1430+
/* An exception may be thrown during BSON conversion */
1431+
if (EG(exception)) {
1432+
goto cleanup;
1433+
}
14451434

1446-
if (!(uri = php_phongo_make_uri(uri_string, bson_options))) {
1435+
if (!(uri = php_phongo_make_uri(uri_string, &bson_options))) {
14471436
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Failed to parse MongoDB URI: '%s'", uri_string);
1448-
return false;
1437+
goto cleanup;
14491438
}
14501439

1451-
if (!php_phongo_apply_rc_options_to_uri(uri, bson_options TSRMLS_CC) ||
1452-
!php_phongo_apply_rp_options_to_uri(uri, bson_options TSRMLS_CC) ||
1453-
!php_phongo_apply_wc_options_to_uri(uri, bson_options TSRMLS_CC)) {
1440+
if (!php_phongo_apply_rc_options_to_uri(uri, &bson_options TSRMLS_CC) ||
1441+
!php_phongo_apply_rp_options_to_uri(uri, &bson_options TSRMLS_CC) ||
1442+
!php_phongo_apply_wc_options_to_uri(uri, &bson_options TSRMLS_CC)) {
14541443
/* Exception should already have been thrown */
1455-
mongoc_uri_destroy(uri);
1456-
return false;
1444+
goto cleanup;
1445+
}
1446+
1447+
ssl_opt = php_phongo_make_ssl_opt(driverOptions TSRMLS_CC);
1448+
1449+
/* An exception may be thrown during SSL option creation */
1450+
if (EG(exception)) {
1451+
goto cleanup;
14571452
}
14581453

1459-
manager->client = php_phongo_make_mongo_client(uri, driverOptions TSRMLS_CC);
1460-
mongoc_uri_destroy(uri);
1454+
manager->client = php_phongo_make_mongo_client(uri, ssl_opt TSRMLS_CC);
14611455

14621456
if (!manager->client) {
14631457
phongo_throw_exception(PHONGO_ERROR_RUNTIME TSRMLS_CC, "Failed to create Manager from URI: '%s'", uri_string);
1464-
return false;
14651458
}
14661459

1467-
return true;
1460+
cleanup:
1461+
bson_destroy(&bson_options);
1462+
1463+
if (uri) {
1464+
mongoc_uri_destroy(uri);
1465+
}
1466+
1467+
if (ssl_opt) {
1468+
php_phongo_free_ssl_opt(ssl_opt);
1469+
}
14681470
} /* }}} */
14691471

14701472
void php_phongo_new_utcdatetime_from_epoch(zval *object, int64_t msec_since_epoch TSRMLS_DC) /* {{{ */

php_phongo.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ void php_phongo_read_preference_to_zval(zval *retval, const mongoc_read_prefs_t
134134
void php_phongo_write_concern_to_zval(zval *retval, const mongoc_write_concern_t *write_concern);
135135
void php_phongo_cursor_to_zval(zval *retval, const mongoc_cursor_t *cursor);
136136

137-
bool phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string, bson_t *bson_options, zval *driverOptions TSRMLS_DC);
137+
void phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string, zval *options, zval *driverOptions TSRMLS_DC);
138138
void php_phongo_objectid_new_from_oid(zval *object, const bson_oid_t *oid TSRMLS_DC);
139139
void php_phongo_cursor_id_new_from_id(zval *object, int64_t cursorid TSRMLS_DC);
140140
void php_phongo_new_utcdatetime_from_epoch(zval *object, int64_t msec_since_epoch TSRMLS_DC);

src/MongoDB/Manager.c

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,55 @@ PHONGO_API zend_class_entry *php_phongo_manager_ce;
5151

5252
zend_object_handlers php_phongo_handler_manager;
5353

54+
/* Checks if driverOptions contains a stream context resource in the "context"
55+
* key and incorporates any of its SSL options into the base array that did not
56+
* already exist (i.e. array union). The "context" key is then unset from the
57+
* base array.
58+
*
59+
* This handles the merging of any legacy SSL context options and also makes
60+
* driverOptions suitable for serialization by removing the resource zval. */
61+
static bool php_phongo_manager_merge_context_options(zval *zdriverOptions TSRMLS_DC)
62+
{
63+
php_stream_context *context;
64+
zval *zcontext, *zcontextOptions;
65+
66+
if (!php_array_existsc(zdriverOptions, "context")) {
67+
return true;
68+
}
69+
70+
zcontext = php_array_fetchc(zdriverOptions, "context");
71+
context = php_stream_context_from_zval(zcontext, 1);
72+
73+
if (!context) {
74+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "\"context\" driver option is not a valid Stream-Context resource");
75+
return false;
76+
}
77+
78+
#if PHP_VERSION_ID >= 70000
79+
zcontextOptions = php_array_fetchc_array(&context->options, "ssl");
80+
#else
81+
zcontextOptions = php_array_fetchc_array(context->options, "ssl");
82+
#endif
83+
84+
if (!zcontextOptions) {
85+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Stream-Context resource does not contain \"ssl\" options array");
86+
return false;
87+
}
88+
89+
/* Perform array union (see: add_function() in zend_operators.c) */
90+
#if PHP_VERSION_ID >= 70000
91+
zend_hash_merge(Z_ARRVAL_P(zdriverOptions), Z_ARRVAL_P(zcontextOptions), zval_add_ref, 0);
92+
#else
93+
{
94+
zval *tmp;
95+
zend_hash_merge(Z_ARRVAL_P(zdriverOptions), Z_ARRVAL_P(zcontextOptions), (void (*)(void *pData)) zval_add_ref, (void *) &tmp, sizeof(zval *), 0);
96+
}
97+
#endif
98+
99+
php_array_unsetc(zdriverOptions, "context");
100+
return true;
101+
}
102+
54103
/* {{{ proto void Manager::__construct([string $uri = "mongodb://127.0.0.1/"[, array $options = array()[, array $driverOptions = array()]]])
55104
Constructs a new Manager */
56105
PHP_METHOD(Manager, __construct)
@@ -60,26 +109,27 @@ PHP_METHOD(Manager, __construct)
60109
char *uri_string = NULL;
61110
phongo_zpp_char_len uri_string_len = 0;
62111
zval *options = NULL;
63-
bson_t bson_options = BSON_INITIALIZER;
64112
zval *driverOptions = NULL;
65113
SUPPRESS_UNUSED_WARNING(return_value) SUPPRESS_UNUSED_WARNING(return_value_ptr) SUPPRESS_UNUSED_WARNING(return_value_used)
66114

67115

68116
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC);
69117
intern = Z_MANAGER_OBJ_P(getThis());
70118

119+
/* Separate the driverOptions zval, since we may end up modifying it in
120+
* php_phongo_manager_merge_context_options() below. */
71121
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!a!a!", &uri_string, &uri_string_len, &options, &driverOptions) == FAILURE) {
72122
zend_restore_error_handling(&error_handling TSRMLS_CC);
73123
return;
74124
}
75125
zend_restore_error_handling(&error_handling TSRMLS_CC);
76126

77-
if (options) {
78-
phongo_zval_to_bson(options, PHONGO_BSON_NONE, &bson_options, NULL TSRMLS_CC);
127+
if (driverOptions && !php_phongo_manager_merge_context_options(driverOptions TSRMLS_CC)) {
128+
/* Exception should already have been thrown */
129+
return;
79130
}
80131

81-
phongo_manager_init(intern, uri_string ? uri_string : PHONGO_MANAGER_URI_DEFAULT, &bson_options, driverOptions TSRMLS_CC);
82-
bson_destroy(&bson_options);
132+
phongo_manager_init(intern, uri_string ? uri_string : PHONGO_MANAGER_URI_DEFAULT, options, driverOptions TSRMLS_CC);
83133
}
84134
/* }}} */
85135

0 commit comments

Comments
 (0)