diff --git a/src/BSON/Timestamp.c b/src/BSON/Timestamp.c index 91a92d1f2..2fcf95a23 100644 --- a/src/BSON/Timestamp.c +++ b/src/BSON/Timestamp.c @@ -91,22 +91,14 @@ static bool php_phongo_timestamp_init_from_string(php_phongo_timestamp_t *intern * available on all platforms (e.g. HP-UX), and atoll() provides no error * reporting at all. */ -#if defined(PHP_WIN32) - increment = _atoi64(s_increment); -#else increment = bson_ascii_strtoll(s_increment, &endptr, 10); -#endif if (errno || (endptr && endptr != ((const char *)s_increment + s_increment_len))) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error parsing \"%s\" as 64-bit integer increment for %s initialization", s_increment, ZSTR_VAL(php_phongo_timestamp_ce->name)); return false; } -#if defined(PHP_WIN32) - timestamp = _atoi64(s_timestamp); -#else timestamp = bson_ascii_strtoll(s_timestamp, &endptr, 10); -#endif if (errno || (endptr && endptr != ((const char *)s_timestamp + s_timestamp_len))) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error parsing \"%s\" as 64-bit integer timestamp for %s initialization", s_timestamp, ZSTR_VAL(php_phongo_timestamp_ce->name)); @@ -148,28 +140,48 @@ static bool php_phongo_timestamp_init_from_hash(php_phongo_timestamp_t *intern, return false; } -/* {{{ proto void Timestamp::__construct(string $increment, string $timestamp) +/* {{{ proto void Timestamp::__construct(int|string $increment, int|string $timestamp) Construct a new BSON timestamp type, which consists of a 4-byte increment and 4-byte timestamp. */ PHP_METHOD(Timestamp, __construct) { - php_phongo_timestamp_t *intern; - zend_error_handling error_handling; - char *s_increment; - phongo_zpp_char_len s_increment_len; - char *s_timestamp; - phongo_zpp_char_len s_timestamp_len; + php_phongo_timestamp_t *intern; + zend_error_handling error_handling; + zval *increment = NULL, *timestamp = NULL; zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC); intern = Z_TIMESTAMP_OBJ_P(getThis()); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &s_increment, &s_increment_len, &s_timestamp, &s_timestamp_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &increment, ×tamp) == FAILURE) { zend_restore_error_handling(&error_handling TSRMLS_CC); return; } zend_restore_error_handling(&error_handling TSRMLS_CC); - php_phongo_timestamp_init_from_string(intern, s_increment, s_increment_len, s_timestamp, s_timestamp_len TSRMLS_CC); + if (Z_TYPE_P(increment) == IS_LONG && Z_TYPE_P(timestamp) == IS_LONG) { + php_phongo_timestamp_init(intern, Z_LVAL_P(increment), Z_LVAL_P(timestamp) TSRMLS_CC); + return; + } + + if (Z_TYPE_P(increment) == IS_LONG) { + convert_to_string(increment); + } + + if (Z_TYPE_P(increment) != IS_STRING) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected increment to be an unsigned 32-bit integer or string, %s given", zend_get_type_by_const(Z_TYPE_P(increment))); + return; + } + + if (Z_TYPE_P(timestamp) == IS_LONG) { + convert_to_string(timestamp); + } + + if (Z_TYPE_P(timestamp) != IS_STRING) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected timestamp to be an unsigned 32-bit integer or string, %s given", zend_get_type_by_const(Z_TYPE_P(timestamp))); + return; + } + + php_phongo_timestamp_init_from_string(intern, Z_STRVAL_P(increment), Z_STRLEN_P(increment), Z_STRVAL_P(timestamp), Z_STRLEN_P(timestamp) TSRMLS_CC); } /* }}} */ diff --git a/src/BSON/UTCDateTime.c b/src/BSON/UTCDateTime.c index aceb14942..558d90b65 100644 --- a/src/BSON/UTCDateTime.c +++ b/src/BSON/UTCDateTime.c @@ -27,6 +27,7 @@ /* External libs */ #include #include +#include /* PHP Core stuff */ #include @@ -77,11 +78,7 @@ static bool php_phongo_utcdatetime_init_from_string(php_phongo_utcdatetime_t *in errno = 0; -#if defined(PHP_WIN32) - milliseconds = _atoi64(s_milliseconds); -#else milliseconds = bson_ascii_strtoll(s_milliseconds, &endptr, 10); -#endif /* errno will set errno if conversion fails; however, we do not need to * specify the type of error. @@ -161,49 +158,64 @@ static bool php_phongo_utcdatetime_init_from_date(php_phongo_utcdatetime_t *inte return true; } -/* {{{ proto void UTCDateTime::__construct([string|DateTimeInterface $milliseconds = null]) +/* {{{ proto void UTCDateTime::__construct([int|float|string|DateTimeInterface $milliseconds = null]) Construct a new BSON UTCDateTime type from either the current time, - milliseconds since the epoch, or a DateTimeInterface object. */ + milliseconds since the epoch, or a DateTimeInterface object. Defaults to the + current time. */ PHP_METHOD(UTCDateTime, __construct) { php_phongo_utcdatetime_t *intern; zend_error_handling error_handling; - zval *datetime = NULL; + zval *milliseconds = NULL; zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC); intern = Z_UTCDATETIME_OBJ_P(getThis()); - if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|o!", &datetime) == SUCCESS) { - if (datetime == NULL) { - php_phongo_utcdatetime_init_from_current_time(intern); - } else if (instanceof_function(Z_OBJCE_P(datetime), php_date_get_date_ce() TSRMLS_CC)) { - php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(datetime)); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!", &milliseconds) == FAILURE) { + zend_restore_error_handling(&error_handling TSRMLS_CC); + return; + } + zend_restore_error_handling(&error_handling TSRMLS_CC); + + if (milliseconds == NULL) { + php_phongo_utcdatetime_init_from_current_time(intern); + return; + } + + if (Z_TYPE_P(milliseconds) == IS_OBJECT) { + if (instanceof_function(Z_OBJCE_P(milliseconds), php_date_get_date_ce() TSRMLS_CC)) { + php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(milliseconds)); #if PHP_VERSION_ID >= 50500 - } else if (instanceof_function(Z_OBJCE_P(datetime), php_date_get_immutable_ce() TSRMLS_CC)) { - php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(datetime)); + } else if (instanceof_function(Z_OBJCE_P(milliseconds), php_date_get_immutable_ce() TSRMLS_CC)) { + php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(milliseconds)); #endif } else { - phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected instance of DateTimeInterface, %s given", ZSTR_VAL(Z_OBJCE_P(datetime)->name)); + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected instance of DateTimeInterface, %s given", ZSTR_VAL(Z_OBJCE_P(milliseconds)->name)); } + return; + } - zend_restore_error_handling(&error_handling TSRMLS_CC); + if (Z_TYPE_P(milliseconds) == IS_LONG) { + php_phongo_utcdatetime_init(intern, Z_LVAL_P(milliseconds)); return; } - { - char *s_milliseconds; - phongo_zpp_char_len s_milliseconds_len; + if (Z_TYPE_P(milliseconds) == IS_DOUBLE) { + char tmp[24]; + int tmp_len; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s_milliseconds, &s_milliseconds_len) == FAILURE) { - zend_restore_error_handling(&error_handling TSRMLS_CC); - return; - } + tmp_len = snprintf(tmp, sizeof(tmp), "%.0f", Z_DVAL_P(milliseconds) > 0 ? floor(Z_DVAL_P(milliseconds)) : ceil(Z_DVAL_P(milliseconds))); - php_phongo_utcdatetime_init_from_string(intern, s_milliseconds, s_milliseconds_len TSRMLS_CC); + php_phongo_utcdatetime_init_from_string(intern, tmp, tmp_len TSRMLS_CC); + return; } - zend_restore_error_handling(&error_handling TSRMLS_CC); + if (Z_TYPE_P(milliseconds) != IS_STRING) { + phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected integer or string, %s given", zend_get_type_by_const(Z_TYPE_P(milliseconds))); + return; + } + php_phongo_utcdatetime_init_from_string(intern, Z_STRVAL_P(milliseconds), Z_STRLEN_P(milliseconds) TSRMLS_CC); } /* }}} */ diff --git a/tests/bson/bson-timestamp-serialization_error-004.phpt b/tests/bson/bson-timestamp-serialization_error-004.phpt new file mode 100644 index 000000000..f1eb2becc --- /dev/null +++ b/tests/bson/bson-timestamp-serialization_error-004.phpt @@ -0,0 +1,24 @@ +--TEST-- +MongoDB\BSON\Timestamp unserialization requires strings to parse as 64-bit integers +--FILE-- + +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Error parsing "1.23" as 64-bit integer increment for MongoDB\BSON\Timestamp initialization +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Error parsing "5.67" as 64-bit integer timestamp for MongoDB\BSON\Timestamp initialization +===DONE=== diff --git a/tests/bson/bson-timestamp-set_state_error-004.phpt b/tests/bson/bson-timestamp-set_state_error-004.phpt index 473a2eb81..6d5aedc61 100644 --- a/tests/bson/bson-timestamp-set_state_error-004.phpt +++ b/tests/bson/bson-timestamp-set_state_error-004.phpt @@ -1,18 +1,16 @@ --TEST-- -MongoDB\BSON\Timestamp::__set_state() with broken numeric strings ---SKIPIF-- - +MongoDB\BSON\Timestamp::__set_state() requires strings to parse as 64-bit integers --FILE-- 'broken', 'timestamp' => '5678']); + MongoDB\BSON\Timestamp::__set_state(['increment' => '1.23', 'timestamp' => '5678']); }, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n"; echo throws(function() { - MongoDB\BSON\Timestamp::__set_state(['increment' => '1234', 'timestamp' => 'broken']); + MongoDB\BSON\Timestamp::__set_state(['increment' => '1234', 'timestamp' => '5.67']); }, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n"; ?> @@ -20,7 +18,7 @@ echo throws(function() { --EXPECT-- OK: Got MongoDB\Driver\Exception\InvalidArgumentException -Error parsing "broken" as 64-bit integer increment for MongoDB\BSON\Timestamp initialization +Error parsing "1.23" as 64-bit integer increment for MongoDB\BSON\Timestamp initialization OK: Got MongoDB\Driver\Exception\InvalidArgumentException -Error parsing "broken" as 64-bit integer timestamp for MongoDB\BSON\Timestamp initialization +Error parsing "5.67" as 64-bit integer timestamp for MongoDB\BSON\Timestamp initialization ===DONE=== diff --git a/tests/bson/bson-timestamp_error-005.phpt b/tests/bson/bson-timestamp_error-005.phpt new file mode 100644 index 000000000..88ad08156 --- /dev/null +++ b/tests/bson/bson-timestamp_error-005.phpt @@ -0,0 +1,24 @@ +--TEST-- +MongoDB\BSON\Timestamp constructor requires strings to parse as 64-bit integers +--FILE-- + +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Error parsing "1.23" as 64-bit integer increment for MongoDB\BSON\Timestamp initialization +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Error parsing "5.67" as 64-bit integer timestamp for MongoDB\BSON\Timestamp initialization +===DONE=== diff --git a/tests/bson/bson-timestamp_error-006.phpt b/tests/bson/bson-timestamp_error-006.phpt new file mode 100644 index 000000000..23ce8a604 --- /dev/null +++ b/tests/bson/bson-timestamp_error-006.phpt @@ -0,0 +1,46 @@ +--TEST-- +MongoDB\BSON\Timestamp constructor requires integer or string arguments +--FILE-- + +===DONE=== + +--EXPECTF-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected increment to be an unsigned 32-bit integer or string, %r(null|NULL)%r given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected increment to be an unsigned 32-bit integer or string, %r(double|float)%r given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected increment to be an unsigned 32-bit integer or string, boolean given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected increment to be an unsigned 32-bit integer or string, array given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected increment to be an unsigned 32-bit integer or string, object given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected timestamp to be an unsigned 32-bit integer or string, %r(null|NULL)%r given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected timestamp to be an unsigned 32-bit integer or string, %r(double|float)%r given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected timestamp to be an unsigned 32-bit integer or string, boolean given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected timestamp to be an unsigned 32-bit integer or string, array given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected timestamp to be an unsigned 32-bit integer or string, object given +===DONE=== diff --git a/tests/bson/bson-utcdatetime-007.phpt b/tests/bson/bson-utcdatetime-007.phpt new file mode 100644 index 000000000..ec6e53789 --- /dev/null +++ b/tests/bson/bson-utcdatetime-007.phpt @@ -0,0 +1,32 @@ +--TEST-- +MongoDB\BSON\UTCDateTime constructor truncates floating point values +--FILE-- + +===DONE=== + +--EXPECTF-- +object(MongoDB\BSON\UTCDateTime)#%d (%d) { + ["milliseconds"]=> + string(13) "1416445411987" +} +object(MongoDB\BSON\UTCDateTime)#%d (%d) { + ["milliseconds"]=> + string(10) "2147483647" +} +object(MongoDB\BSON\UTCDateTime)#%d (%d) { + ["milliseconds"]=> + string(4) "1234" +} +===DONE=== diff --git a/tests/bson/bson-utcdatetime-serialization_error-002.phpt b/tests/bson/bson-utcdatetime-serialization_error-002.phpt index bc77c316b..46ae4586c 100644 --- a/tests/bson/bson-utcdatetime-serialization_error-002.phpt +++ b/tests/bson/bson-utcdatetime-serialization_error-002.phpt @@ -6,7 +6,7 @@ MongoDB\BSON\UTCDateTime unserialization requires "milliseconds" string to parse require_once __DIR__ . '/../utils/tools.php'; echo throws(function() { - unserialize('C:24:"MongoDB\BSON\UTCDateTime":40:{a:1:{s:12:"milliseconds";s:7:"INVALID";}}'); + unserialize('C:24:"MongoDB\BSON\UTCDateTime":42:{a:1:{s:12:"milliseconds";s:9:"1234.5678";}}'); }, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n"; /* TODO: Add tests for out-of-range values once CDRIVER-1377 is resolved */ @@ -16,5 +16,5 @@ echo throws(function() { --EXPECT-- OK: Got MongoDB\Driver\Exception\InvalidArgumentException -Error parsing "INVALID" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization +Error parsing "1234.5678" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization ===DONE=== diff --git a/tests/bson/bson-utcdatetime-set_state_error-002.phpt b/tests/bson/bson-utcdatetime-set_state_error-002.phpt index 66036654b..172a281cb 100644 --- a/tests/bson/bson-utcdatetime-set_state_error-002.phpt +++ b/tests/bson/bson-utcdatetime-set_state_error-002.phpt @@ -2,10 +2,11 @@ MongoDB\BSON\UTCDateTime::__set_state() requires "milliseconds" string to parse as 64-bit integer --FILE-- 'INVALID']); + MongoDB\BSON\UTCDateTime::__set_state(['milliseconds' => '1234.5678']); }, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n"; /* TODO: Add tests for out-of-range values once CDRIVER-1377 is resolved */ @@ -15,5 +16,5 @@ echo throws(function() { --EXPECT-- OK: Got MongoDB\Driver\Exception\InvalidArgumentException -Error parsing "INVALID" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization +Error parsing "1234.5678" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization ===DONE=== diff --git a/tests/bson/bson-utcdatetime_error-003.phpt b/tests/bson/bson-utcdatetime_error-003.phpt new file mode 100644 index 000000000..fd9204225 --- /dev/null +++ b/tests/bson/bson-utcdatetime_error-003.phpt @@ -0,0 +1,20 @@ +--TEST-- +MongoDB\BSON\UTCDateTime constructor requires strings to parse as 64-bit integers +--FILE-- + +===DONE=== + +--EXPECT-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Error parsing "1234.5678" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization +===DONE=== diff --git a/tests/bson/bson-utcdatetime_error-004.phpt b/tests/bson/bson-utcdatetime_error-004.phpt new file mode 100644 index 000000000..6509ed6b9 --- /dev/null +++ b/tests/bson/bson-utcdatetime_error-004.phpt @@ -0,0 +1,27 @@ +--TEST-- +MongoDB\BSON\UTCDateTime constructor requires integer or string argument +--FILE-- + +===DONE=== + +--EXPECTF-- +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected integer or string, boolean given +OK: Got MongoDB\Driver\Exception\InvalidArgumentException +Expected integer or string, array given +===DONE===