Skip to content

Commit cdff70f

Browse files
committed
PHPC-1411: Accept 64-bit integers for wTimeoutMS
1 parent 2871dfe commit cdff70f

23 files changed

+281
-150
lines changed

phongo_compat.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@
9191
#define ADD_ASSOC_ZVAL(_zv, _key, _value) add_assoc_zval(_zv, _key, _value);
9292
#define ADD_ASSOC_NULL_EX(_zv, _key) add_assoc_null_ex(_zv, ZEND_STRL(_key));
9393
#define ADD_ASSOC_BOOL_EX(_zv, _key, _value) add_assoc_bool_ex(_zv, ZEND_STRL(_key), _value);
94+
#define ADD_ASSOC_INT64_AS_STRING(_zv, _key, _value) \
95+
do { \
96+
zval z_int; \
97+
php_phongo_int64_to_zval((_value), &z_int, false TSRMLS_CC); \
98+
ADD_ASSOC_ZVAL_EX((_zv), (_key), &z_int); \
99+
} while (0)
94100
#define ADD_NEXT_INDEX_STRINGL(_zv, _value, _len) add_next_index_stringl(_zv, _value, _len);
95101
#define phongo_free_object_arg zend_object
96102
#define phongo_zpp_char_len size_t
@@ -127,6 +133,14 @@
127133
#define ADD_ASSOC_ZVAL(_zv, _key, _value) add_assoc_zval(_zv, _key, _value);
128134
#define ADD_ASSOC_NULL_EX(_zv, _key) add_assoc_null_ex(_zv, ZEND_STRS(_key));
129135
#define ADD_ASSOC_BOOL_EX(_zv, _key, _value) add_assoc_bool_ex(_zv, ZEND_STRS(_key), _value);
136+
#define ADD_ASSOC_INT64_AS_STRING(_zv, _key, _value) \
137+
do { \
138+
zval* z_int; \
139+
TSRMLS_FETCH(); \
140+
MAKE_STD_ZVAL(z_int); \
141+
php_phongo_int64_to_zval((_value), z_int, false TSRMLS_CC); \
142+
ADD_ASSOC_ZVAL_EX((_zv), (_key), z_int); \
143+
} while (0)
130144
#define ADD_NEXT_INDEX_STRINGL(_zv, _value, _len) add_next_index_stringl(_zv, _value, _len, 1);
131145
#define Z_PHPDATE_P(object) ((php_date_obj*) zend_object_store_get_object(object TSRMLS_CC))
132146
#define Z_ISUNDEF(x) !x

php_phongo.c

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,10 +1365,19 @@ void php_phongo_write_concern_to_zval(zval* retval, const mongoc_write_concern_t
13651365
}
13661366

13671367
if (wtimeout != 0) {
1368-
/* Note: PHP currently enforces that wimeoutMS is a 32-bit integer, so
1369-
* casting will never truncate the value. This may change with
1370-
* PHPC-1411. */
1371-
ADD_ASSOC_LONG_EX(retval, "wtimeout", (int32_t) wtimeout);
1368+
#if SIZEOF_LONG == 4
1369+
if (wtimeout > INT32_MAX || wtimeout < INT32_MIN) {
1370+
char tmp[24];
1371+
int tmp_len;
1372+
1373+
tmp_len = snprintf(tmp, sizeof(tmp), "%" PRId64, wtimeout);
1374+
ADD_ASSOC_STRINGL(&retval, "wtimeout", tmp, tmp_len);
1375+
} else {
1376+
ADD_ASSOC_LONG_EX(retval, "wtimeout", wtimeout);
1377+
}
1378+
#else
1379+
ADD_ASSOC_LONG_EX(retval, "wtimeout", wtimeout);
1380+
#endif
13721381
}
13731382
} /* }}} */
13741383
/* }}} */
@@ -1966,10 +1975,8 @@ static bool php_phongo_apply_wc_options_to_uri(mongoc_uri_t* uri, bson_t* option
19661975
if (!strcasecmp(key, MONGOC_URI_WTIMEOUTMS)) {
19671976
int64_t wtimeout;
19681977

1969-
/* Although the write concern spec defines wtimeoutMS as 64-bit, PHP has
1970-
* historically required 32-bit. This may change with PHPC-1411. */
1971-
if (!BSON_ITER_HOLDS_INT32(&iter)) {
1972-
PHONGO_URI_INVALID_TYPE(iter, "32-bit integer");
1978+
if (!BSON_ITER_HOLDS_INT(&iter)) {
1979+
PHONGO_URI_INVALID_TYPE(iter, "integer");
19731980
mongoc_write_concern_destroy(new_wc);
19741981

19751982
return false;
@@ -2746,6 +2753,41 @@ void phongo_manager_init(php_phongo_manager_t* manager, const char* uri_string,
27462753
#endif
27472754
} /* }}} */
27482755

2756+
void php_phongo_int64_to_zval(const int64_t data, zval* zv, bool is_bson TSRMLS_DC) /* {{{ */
2757+
{
2758+
if (is_bson) {
2759+
php_phongo_bson_new_int64(zv, data TSRMLS_CC);
2760+
} else {
2761+
char tmp[24];
2762+
int tmp_len;
2763+
2764+
tmp_len = snprintf(tmp, sizeof(tmp), "%" PRId64, data);
2765+
#if PHP_VERSION_ID >= 70000
2766+
ZVAL_STRINGL(zv, tmp, tmp_len);
2767+
#else
2768+
ZVAL_STRINGL(zv, tmp, tmp_len, 1);
2769+
#endif
2770+
}
2771+
} /* }}} */
2772+
2773+
bool php_phongo_parse_int64(int64_t* retval, const char* data, phongo_zpp_char_len data_len) /* {{{ */
2774+
{
2775+
int64_t value;
2776+
char* endptr = NULL;
2777+
2778+
/* bson_ascii_strtoll() sets errno if conversion fails. If conversion
2779+
* succeeds, we still want to ensure that the entire string was parsed. */
2780+
value = bson_ascii_strtoll(data, &endptr, 10);
2781+
2782+
if (errno || (endptr && endptr != ((const char*) data + data_len))) {
2783+
return false;
2784+
}
2785+
2786+
*retval = value;
2787+
2788+
return true;
2789+
} /* }}} */
2790+
27492791
/* {{{ Memory allocation wrappers */
27502792
static void* php_phongo_malloc(size_t num_bytes) /* {{{ */
27512793
{

php_phongo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ void php_phongo_cursor_to_zval(zval* retval, const mongoc_cursor_t* cursor);
163163
void phongo_manager_init(php_phongo_manager_t* manager, const char* uri_string, zval* options, zval* driverOptions TSRMLS_DC);
164164
int php_phongo_set_monitoring_callbacks(mongoc_client_t* client);
165165

166+
void php_phongo_int64_to_zval(int64_t data, zval* zv, bool is_bson TSRMLS_DC);
167+
bool php_phongo_parse_int64(int64_t* retval, const char* data, phongo_zpp_char_len data_len);
168+
166169
zend_bool phongo_writeerror_init(zval* return_value, bson_t* bson TSRMLS_DC);
167170
zend_bool phongo_writeconcernerror_init(zval* return_value, bson_t* bson TSRMLS_DC);
168171

src/MongoDB/WriteConcern.c

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,19 @@ static bool php_phongo_writeconcern_init_from_hash(php_phongo_writeconcern_t* in
6666
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"wtimeout\" integer field to be >= 0", ZSTR_VAL(php_phongo_writeconcern_ce->name));
6767
goto failure;
6868
}
69-
if (Z_LVAL_P(wtimeout) > INT32_MAX) {
70-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"wtimeout\" integer field to be <= %" PRId32, ZSTR_VAL(php_phongo_writeconcern_ce->name), INT32_MAX);
71-
goto failure;
72-
}
7369

7470
mongoc_write_concern_set_wtimeout_int64(intern->write_concern, (int64_t) Z_LVAL_P(wtimeout));
71+
} else if (Z_TYPE_P(wtimeout) == IS_STRING) {
72+
int64_t timeout;
73+
74+
if (!php_phongo_parse_int64(&timeout, Z_STRVAL_P(wtimeout), Z_STRLEN_P(wtimeout))) {
75+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error parsing \"%s\" as 64-bit value for %s initialization", Z_STRVAL_P(wtimeout), ZSTR_VAL(php_phongo_writeconcern_ce->name));
76+
return false;
77+
}
78+
79+
mongoc_write_concern_set_wtimeout_int64(intern->write_concern, timeout);
7580
} else {
76-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"wtimeout\" field to be integer", ZSTR_VAL(php_phongo_writeconcern_ce->name));
81+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"wtimeout\" field to be integer or string", ZSTR_VAL(php_phongo_writeconcern_ce->name));
7782
goto failure;
7883
}
7984
}
@@ -116,14 +121,19 @@ static bool php_phongo_writeconcern_init_from_hash(php_phongo_writeconcern_t* in
116121
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"wtimeout\" integer field to be >= 0", ZSTR_VAL(php_phongo_writeconcern_ce->name));
117122
goto failure;
118123
}
119-
if (Z_LVAL_PP(wtimeout) > INT32_MAX) {
120-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"wtimeout\" integer field to be <= %" PRId32, ZSTR_VAL(php_phongo_writeconcern_ce->name), INT32_MAX);
121-
goto failure;
122-
}
123124

124125
mongoc_write_concern_set_wtimeout_int64(intern->write_concern, (int64_t) Z_LVAL_PP(wtimeout));
126+
} else if (Z_TYPE_PP(wtimeout) == IS_STRING) {
127+
int64_t timeout;
128+
129+
if (!php_phongo_parse_int64(&timeout, Z_STRVAL_PP(wtimeout), Z_STRLEN_PP(wtimeout))) {
130+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error parsing \"%s\" as 64-bit value for %s initialization", Z_STRVAL_PP(wtimeout), ZSTR_VAL(php_phongo_writeconcern_ce->name));
131+
return false;
132+
}
133+
134+
mongoc_write_concern_set_wtimeout_int64(intern->write_concern, timeout);
125135
} else {
126-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"wtimeout\" field to be integer", ZSTR_VAL(php_phongo_writeconcern_ce->name));
136+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"wtimeout\" field to be integer or string", ZSTR_VAL(php_phongo_writeconcern_ce->name));
127137
goto failure;
128138
}
129139
}
@@ -200,11 +210,6 @@ static PHP_METHOD(WriteConcern, __construct)
200210
return;
201211
}
202212

203-
if (wtimeout > INT32_MAX) {
204-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected wtimeout to be <= %" PRId32 ", %" PHONGO_LONG_FORMAT " given", INT32_MAX, wtimeout);
205-
return;
206-
}
207-
208213
mongoc_write_concern_set_wtimeout_int64(intern->write_concern, (int64_t) wtimeout);
209214
}
210215
} /* }}} */
@@ -271,9 +276,7 @@ static PHP_METHOD(WriteConcern, getWtimeout)
271276
return;
272277
}
273278

274-
/* Note: PHP currently enforces that wimeoutMS is a 32-bit integer, so
275-
* casting will never truncate the value. This may change with PHPC-1411. */
276-
RETURN_LONG((int32_t) mongoc_write_concern_get_wtimeout_int64(intern->write_concern));
279+
RETURN_LONG(mongoc_write_concern_get_wtimeout_int64(intern->write_concern));
277280
} /* }}} */
278281

279282
/* {{{ proto null|boolean MongoDB\Driver\WriteConcern::getJournal()
@@ -311,13 +314,13 @@ static PHP_METHOD(WriteConcern, isDefault)
311314
RETURN_BOOL(mongoc_write_concern_is_default(intern->write_concern));
312315
} /* }}} */
313316

314-
static HashTable* php_phongo_write_concern_get_properties_hash(zval* object, bool is_debug TSRMLS_DC) /* {{{ */
317+
static HashTable* php_phongo_write_concern_get_properties_hash(zval* object, bool is_debug, bool is_bson TSRMLS_DC) /* {{{ */
315318
{
316319
php_phongo_writeconcern_t* intern;
317320
HashTable* props;
318321
const char* wtag;
319322
int32_t w;
320-
int32_t wtimeout;
323+
int64_t wtimeout;
321324

322325
intern = Z_WRITECONCERN_OBJ_P(object);
323326

@@ -329,9 +332,7 @@ static HashTable* php_phongo_write_concern_get_properties_hash(zval* object, boo
329332

330333
wtag = mongoc_write_concern_get_wtag(intern->write_concern);
331334
w = mongoc_write_concern_get_w(intern->write_concern);
332-
/* Note: PHP currently enforces that wimeoutMS is a 32-bit integer, so
333-
* casting will never truncate the value. This may change with PHPC-1411. */
334-
wtimeout = (int32_t) mongoc_write_concern_get_wtimeout_int64(intern->write_concern);
335+
wtimeout = mongoc_write_concern_get_wtimeout_int64(intern->write_concern);
335336

336337
#if PHP_VERSION_ID >= 70000
337338
{
@@ -358,7 +359,7 @@ static HashTable* php_phongo_write_concern_get_properties_hash(zval* object, boo
358359
if (wtimeout != 0) {
359360
zval z_wtimeout;
360361

361-
ZVAL_LONG(&z_wtimeout, wtimeout);
362+
php_phongo_int64_to_zval(wtimeout, &z_wtimeout, is_bson TSRMLS_CC);
362363
zend_hash_str_update(props, "wtimeout", sizeof("wtimeout") - 1, &z_wtimeout);
363364
}
364365
#else
@@ -391,7 +392,8 @@ static HashTable* php_phongo_write_concern_get_properties_hash(zval* object, boo
391392
zval* z_wtimeout;
392393

393394
MAKE_STD_ZVAL(z_wtimeout);
394-
ZVAL_LONG(z_wtimeout, wtimeout);
395+
php_phongo_int64_to_zval(wtimeout, z_wtimeout, is_bson TSRMLS_CC);
396+
395397
zend_hash_update(props, "wtimeout", sizeof("wtimeout"), &z_wtimeout, sizeof(z_wtimeout), NULL);
396398
}
397399
#endif
@@ -408,7 +410,7 @@ static PHP_METHOD(WriteConcern, bsonSerialize)
408410
return;
409411
}
410412

411-
ZVAL_ARR(return_value, php_phongo_write_concern_get_properties_hash(getThis(), true TSRMLS_CC));
413+
ZVAL_ARR(return_value, php_phongo_write_concern_get_properties_hash(getThis(), true, true TSRMLS_CC));
412414
convert_to_object(return_value);
413415
} /* }}} */
414416

@@ -422,7 +424,7 @@ static PHP_METHOD(WriteConcern, serialize)
422424
smart_str buf = { 0 };
423425
const char* wtag;
424426
int32_t w;
425-
int32_t wtimeout;
427+
int64_t wtimeout;
426428

427429
intern = Z_WRITECONCERN_OBJ_P(getThis());
428430

@@ -436,9 +438,7 @@ static PHP_METHOD(WriteConcern, serialize)
436438

437439
wtag = mongoc_write_concern_get_wtag(intern->write_concern);
438440
w = mongoc_write_concern_get_w(intern->write_concern);
439-
/* Note: PHP currently enforces that wimeoutMS is a 32-bit integer, so
440-
* casting will never truncate the value. This may change with PHPC-1411. */
441-
wtimeout = (int32_t) mongoc_write_concern_get_wtimeout_int64(intern->write_concern);
441+
wtimeout = mongoc_write_concern_get_wtimeout_int64(intern->write_concern);
442442

443443
#if PHP_VERSION_ID >= 70000
444444
array_init_size(&retval, 3);
@@ -456,7 +456,7 @@ static PHP_METHOD(WriteConcern, serialize)
456456
}
457457

458458
if (wtimeout != 0) {
459-
ADD_ASSOC_LONG_EX(&retval, "wtimeout", wtimeout);
459+
ADD_ASSOC_INT64_AS_STRING(&retval, "wtimeout", wtimeout);
460460
}
461461
#else
462462
ALLOC_INIT_ZVAL(retval);
@@ -475,7 +475,7 @@ static PHP_METHOD(WriteConcern, serialize)
475475
}
476476

477477
if (wtimeout != 0) {
478-
ADD_ASSOC_LONG_EX(retval, "wtimeout", wtimeout);
478+
ADD_ASSOC_INT64_AS_STRING(retval, "wtimeout", wtimeout);
479479
}
480480
#endif
481481

@@ -624,12 +624,12 @@ static phongo_create_object_retval php_phongo_writeconcern_create_object(zend_cl
624624
static HashTable* php_phongo_writeconcern_get_debug_info(zval* object, int* is_temp TSRMLS_DC) /* {{{ */
625625
{
626626
*is_temp = 1;
627-
return php_phongo_write_concern_get_properties_hash(object, true TSRMLS_CC);
627+
return php_phongo_write_concern_get_properties_hash(object, true, false TSRMLS_CC);
628628
} /* }}} */
629629

630630
static HashTable* php_phongo_writeconcern_get_properties(zval* object TSRMLS_DC) /* {{{ */
631631
{
632-
return php_phongo_write_concern_get_properties_hash(object, false TSRMLS_CC);
632+
return php_phongo_write_concern_get_properties_hash(object, false, false TSRMLS_CC);
633633
} /* }}} */
634634

635635
void php_phongo_writeconcern_init_ce(INIT_FUNC_ARGS) /* {{{ */

tests/manager/manager-ctor-write_concern-002.phpt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,46 +27,46 @@ foreach ($tests as $test) {
2727
--EXPECTF--
2828
object(MongoDB\Driver\WriteConcern)#%d (%d) {
2929
["wtimeout"]=>
30-
int(1000)
30+
string(4) "1000"
3131
}
3232
object(MongoDB\Driver\WriteConcern)#%d (%d) {
3333
["w"]=>
3434
int(2)
3535
["wtimeout"]=>
36-
int(1000)
36+
string(4) "1000"
3737
}
3838
object(MongoDB\Driver\WriteConcern)#%d (%d) {
3939
["w"]=>
4040
string(8) "majority"
4141
["wtimeout"]=>
42-
int(1000)
42+
string(4) "1000"
4343
}
4444
object(MongoDB\Driver\WriteConcern)#%d (%d) {
4545
["w"]=>
4646
string(12) "customTagSet"
4747
["wtimeout"]=>
48-
int(1000)
48+
string(4) "1000"
4949
}
5050
object(MongoDB\Driver\WriteConcern)#%d (%d) {
5151
["wtimeout"]=>
52-
int(1000)
52+
string(4) "1000"
5353
}
5454
object(MongoDB\Driver\WriteConcern)#%d (%d) {
5555
["w"]=>
5656
int(2)
5757
["wtimeout"]=>
58-
int(1000)
58+
string(4) "1000"
5959
}
6060
object(MongoDB\Driver\WriteConcern)#%d (%d) {
6161
["w"]=>
6262
string(8) "majority"
6363
["wtimeout"]=>
64-
int(1000)
64+
string(4) "1000"
6565
}
6666
object(MongoDB\Driver\WriteConcern)#%d (%d) {
6767
["w"]=>
6868
string(12) "customTagSet"
6969
["wtimeout"]=>
70-
int(1000)
70+
string(4) "1000"
7171
}
7272
===DONE===
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
MongoDB\Driver\Manager::__construct(): write concern options (64-bit wtimeoutms)
3+
--FILE--
4+
<?php
5+
6+
$tests = [
7+
'mongodb://127.0.0.1/?w=2&wtimeoutms=4294967296',
8+
'mongodb://127.0.0.1/?w=majority&wtimeoutms=4294967296',
9+
'mongodb://127.0.0.1/?w=customTagSet&wtimeoutms=4294967296',
10+
];
11+
12+
foreach ($tests as $uri) {
13+
$manager = new MongoDB\Driver\Manager($uri);
14+
var_dump($manager->getWriteConcern());
15+
}
16+
17+
?>
18+
===DONE===
19+
<?php exit(0); ?>
20+
--EXPECTF--
21+
object(MongoDB\Driver\WriteConcern)#%d (%d) {
22+
["w"]=>
23+
int(2)
24+
["wtimeout"]=>
25+
%rint\(4294967296\)|string\(10\) "4294967296"%r
26+
}
27+
object(MongoDB\Driver\WriteConcern)#%d (%d) {
28+
["w"]=>
29+
string(8) "majority"
30+
["wtimeout"]=>
31+
%rint\(4294967296\)|string\(10\) "4294967296"%r
32+
}
33+
object(MongoDB\Driver\WriteConcern)#%d (%d) {
34+
["w"]=>
35+
string(12) "customTagSet"
36+
["wtimeout"]=>
37+
%rint\(4294967296\)|string\(10\) "4294967296"%r
38+
}
39+
===DONE===

0 commit comments

Comments
 (0)