Skip to content

Commit f1e057d

Browse files
committed
PHPC-741: Consistent exceptions for UTCDateTime init methods
1 parent 9c514b5 commit f1e057d

7 files changed

+127
-16
lines changed

src/BSON/UTCDateTime.c

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ PHONGO_API zend_class_entry *php_phongo_utcdatetime_ce;
5252

5353
zend_object_handlers php_phongo_handler_utcdatetime;
5454

55-
/* Initialize the object from an integer and return whether it was successful. */
55+
/* Initialize the object and return whether it was successful. */
5656
static bool php_phongo_utcdatetime_init(php_phongo_utcdatetime_t *intern, int64_t milliseconds)
5757
{
5858
intern->milliseconds = milliseconds;
@@ -61,8 +61,39 @@ static bool php_phongo_utcdatetime_init(php_phongo_utcdatetime_t *intern, int64_
6161
return true;
6262
}
6363

64-
/* Initialize the object from a HashTable and return whether it was successful. */
65-
static bool php_phongo_utcdatetime_init_from_hash(php_phongo_utcdatetime_t *intern, HashTable *props)
64+
/* Initialize the object from a numeric string and return whether it was
65+
* successful. An exception will be thrown on error. */
66+
static bool php_phongo_utcdatetime_init_from_string(php_phongo_utcdatetime_t *intern, const char *s_milliseconds, phongo_zpp_char_len s_milliseconds_len TSRMLS_DC)
67+
{
68+
int64_t milliseconds;
69+
char *endptr = NULL;
70+
71+
errno = 0;
72+
73+
#if defined(PHP_WIN32)
74+
milliseconds = _atoi64(s);
75+
#else
76+
milliseconds = bson_ascii_strtoll(s_milliseconds, &endptr, 10);
77+
#endif
78+
79+
/* errno will set errno if conversion fails; however, we do not need to
80+
* specify the type of error.
81+
*
82+
* Note: bson_ascii_strtoll() does not properly detect out-of-range values
83+
* (see: CDRIVER-1377). strtoll() would be preferable, but it is not
84+
* available on all platforms (e.g. HP-UX), and atoll() provides no error
85+
* reporting at all. */
86+
if (errno || (endptr && endptr != ((const char *)s_milliseconds + s_milliseconds_len))) {
87+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error parsing \"%s\" as 64-bit integer for %s initialization", s_milliseconds, ZSTR_VAL(php_phongo_utcdatetime_ce->name));
88+
return false;
89+
}
90+
91+
return php_phongo_utcdatetime_init(intern, milliseconds);
92+
}
93+
94+
/* Initialize the object from a HashTable and return whether it was successful.
95+
* An exception will be thrown on error. */
96+
static bool php_phongo_utcdatetime_init_from_hash(php_phongo_utcdatetime_t *intern, HashTable *props TSRMLS_DC)
6697
{
6798
#if PHP_VERSION_ID >= 70000
6899
zval *milliseconds;
@@ -72,7 +103,7 @@ static bool php_phongo_utcdatetime_init_from_hash(php_phongo_utcdatetime_t *inte
72103
}
73104

74105
if ((milliseconds = zend_hash_str_find(props, "milliseconds", sizeof("milliseconds")-1)) && Z_TYPE_P(milliseconds) == IS_STRING) {
75-
return php_phongo_utcdatetime_init(intern, STRTOLL(Z_STRVAL_P(milliseconds)));
106+
return php_phongo_utcdatetime_init_from_string(intern, Z_STRVAL_P(milliseconds), Z_STRLEN_P(milliseconds) TSRMLS_CC);
76107
}
77108
#else
78109
zval **milliseconds;
@@ -82,13 +113,16 @@ static bool php_phongo_utcdatetime_init_from_hash(php_phongo_utcdatetime_t *inte
82113
}
83114

84115
if (zend_hash_find(props, "milliseconds", sizeof("milliseconds"), (void**) &milliseconds) == SUCCESS && Z_TYPE_PP(milliseconds) == IS_STRING) {
85-
return php_phongo_utcdatetime_init(intern, STRTOLL(Z_STRVAL_PP(milliseconds)));
116+
return php_phongo_utcdatetime_init_from_string(intern, Z_STRVAL_PP(milliseconds), Z_STRLEN_PP(milliseconds) TSRMLS_CC);
86117
}
87118
#endif
119+
120+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"milliseconds\" integer or numeric string field", ZSTR_VAL(php_phongo_utcdatetime_ce->name));
88121
return false;
89122
}
90123

91-
/* Initialize the object from the current time and return whether it was successful. */
124+
/* Initialize the object from the current time and return whether it was
125+
* successful. */
92126
static bool php_phongo_utcdatetime_init_from_current_time(php_phongo_utcdatetime_t *intern)
93127
{
94128
int64_t sec, usec;
@@ -104,7 +138,8 @@ static bool php_phongo_utcdatetime_init_from_current_time(php_phongo_utcdatetime
104138
return true;
105139
}
106140

107-
/* Initialize the object from a DateTime object and return whether it was successful. */
141+
/* Initialize the object from a DateTime object and return whether it was
142+
* successful. */
108143
static bool php_phongo_utcdatetime_init_from_date(php_phongo_utcdatetime_t *intern, php_date_obj *datetime_obj)
109144
{
110145
int64_t sec, usec;
@@ -168,7 +203,7 @@ PHP_METHOD(UTCDateTime, __construct)
168203
return;
169204
}
170205

171-
php_phongo_utcdatetime_init(intern, STRTOLL(s_milliseconds));
206+
php_phongo_utcdatetime_init_from_string(intern, s_milliseconds, s_milliseconds_len);
172207
}
173208
#else
174209
# error Unsupported architecture (integers are neither 32-bit nor 64-bit)
@@ -196,9 +231,7 @@ PHP_METHOD(UTCDateTime, __set_state)
196231
intern = Z_UTCDATETIME_OBJ_P(return_value);
197232
props = Z_ARRVAL_P(array);
198233

199-
if (!php_phongo_utcdatetime_init_from_hash(intern, props)) {
200-
php_error(E_ERROR, "Invalid serialization data for UTCDateTime object");
201-
}
234+
php_phongo_utcdatetime_init_from_hash(intern, props TSRMLS_CC);
202235
}
203236
/* }}} */
204237

@@ -237,9 +270,7 @@ PHP_METHOD(UTCDateTime, __wakeup)
237270
intern = Z_UTCDATETIME_OBJ_P(getThis());
238271
props = zend_std_get_properties(getThis() TSRMLS_CC);
239272

240-
if (!php_phongo_utcdatetime_init_from_hash(intern, props)) {
241-
php_error(E_ERROR, "Invalid serialization data for UTCDateTime object");
242-
}
273+
php_phongo_utcdatetime_init_from_hash(intern, props TSRMLS_CC);
243274
}
244275
/* }}} */
245276

tests/bson/bson-utcdatetime-007.phpt renamed to tests/bson/bson-utcdatetime-serialization-001.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
BSON BSON\UTCDateTime serialization
2+
MongoDB\BSON\UTCDateTime serialization
33
--SKIPIF--
44
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
55
--FILE--

tests/bson/bson-utcdatetime-008.phpt renamed to tests/bson/bson-utcdatetime-serialization-002.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
BSON BSON\UTCDateTime serialization (unserialize 32-bit data on 64-bit)
2+
MongoDB\BSON\UTCDateTime serialization (unserialize 32-bit data on 64-bit)
33
--SKIPIF--
44
<?php if (8 !== PHP_INT_SIZE) { die('skip Only for 64-bit platform'); } ?>
55
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
MongoDB\BSON\UTCDateTime unserialization requires "milliseconds" integer or numeric string field
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
5+
--FILE--
6+
<?php
7+
require_once __DIR__ . "/../utils/basic.inc";
8+
9+
echo throws(function() {
10+
unserialize('O:24:"MongoDB\BSON\UTCDateTime":1:{s:12:"milliseconds";d:1;}');
11+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
12+
13+
?>
14+
===DONE===
15+
<?php exit(0); ?>
16+
--EXPECT--
17+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
18+
MongoDB\BSON\UTCDateTime initialization requires "milliseconds" integer or numeric string field
19+
===DONE===
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
MongoDB\BSON\UTCDateTime unserialization requires "milliseconds" string to parse as 64-bit integer
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
5+
--FILE--
6+
<?php
7+
require_once __DIR__ . "/../utils/basic.inc";
8+
9+
echo throws(function() {
10+
unserialize('O:24:"MongoDB\BSON\UTCDateTime":1:{s:12:"milliseconds";s:7:"INVALID";}');
11+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
12+
13+
/* TODO: Add tests for out-of-range values once CDRIVER-1377 is resolved */
14+
15+
?>
16+
===DONE===
17+
<?php exit(0); ?>
18+
--EXPECT--
19+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
20+
Error parsing "INVALID" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization
21+
===DONE===
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
MongoDB\BSON\UTCDateTime::__set_state() requires "milliseconds" integer or numeric string field
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
5+
--FILE--
6+
<?php
7+
require_once __DIR__ . "/../utils/basic.inc";
8+
9+
echo throws(function() {
10+
MongoDB\BSON\UTCDateTime::__set_state(['milliseconds' => 1.0]);
11+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
12+
13+
?>
14+
===DONE===
15+
<?php exit(0); ?>
16+
--EXPECT--
17+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
18+
MongoDB\BSON\UTCDateTime initialization requires "milliseconds" integer or numeric string field
19+
===DONE===
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
MongoDB\BSON\UTCDateTime::__set_state() requires "milliseconds" string to parse as 64-bit integer
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
5+
--FILE--
6+
<?php
7+
require_once __DIR__ . "/../utils/basic.inc";
8+
9+
echo throws(function() {
10+
MongoDB\BSON\UTCDateTime::__set_state(['milliseconds' => 'INVALID']);
11+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
12+
13+
/* TODO: Add tests for out-of-range values once CDRIVER-1377 is resolved */
14+
15+
?>
16+
===DONE===
17+
<?php exit(0); ?>
18+
--EXPECT--
19+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
20+
Error parsing "INVALID" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization
21+
===DONE===

0 commit comments

Comments
 (0)