Skip to content

Commit c729150

Browse files
committed
Merge pull request #467
2 parents e0b83d8 + a916713 commit c729150

11 files changed

+249
-53
lines changed

src/BSON/Timestamp.c

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,14 @@ static bool php_phongo_timestamp_init_from_string(php_phongo_timestamp_t *intern
9191
* available on all platforms (e.g. HP-UX), and atoll() provides no error
9292
* reporting at all. */
9393

94-
#if defined(PHP_WIN32)
95-
increment = _atoi64(s_increment);
96-
#else
9794
increment = bson_ascii_strtoll(s_increment, &endptr, 10);
98-
#endif
9995

10096
if (errno || (endptr && endptr != ((const char *)s_increment + s_increment_len))) {
10197
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));
10298
return false;
10399
}
104100

105-
#if defined(PHP_WIN32)
106-
timestamp = _atoi64(s_timestamp);
107-
#else
108101
timestamp = bson_ascii_strtoll(s_timestamp, &endptr, 10);
109-
#endif
110102

111103
if (errno || (endptr && endptr != ((const char *)s_timestamp + s_timestamp_len))) {
112104
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,
148140
return false;
149141
}
150142

151-
/* {{{ proto void Timestamp::__construct(string $increment, string $timestamp)
143+
/* {{{ proto void Timestamp::__construct(int|string $increment, int|string $timestamp)
152144
Construct a new BSON timestamp type, which consists of a 4-byte increment and
153145
4-byte timestamp. */
154146
PHP_METHOD(Timestamp, __construct)
155147
{
156-
php_phongo_timestamp_t *intern;
157-
zend_error_handling error_handling;
158-
char *s_increment;
159-
phongo_zpp_char_len s_increment_len;
160-
char *s_timestamp;
161-
phongo_zpp_char_len s_timestamp_len;
148+
php_phongo_timestamp_t *intern;
149+
zend_error_handling error_handling;
150+
zval *increment = NULL, *timestamp = NULL;
162151

163152
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC);
164153
intern = Z_TIMESTAMP_OBJ_P(getThis());
165154

166-
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &s_increment, &s_increment_len, &s_timestamp, &s_timestamp_len) == FAILURE) {
155+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &increment, &timestamp) == FAILURE) {
167156
zend_restore_error_handling(&error_handling TSRMLS_CC);
168157
return;
169158
}
170159
zend_restore_error_handling(&error_handling TSRMLS_CC);
171160

172-
php_phongo_timestamp_init_from_string(intern, s_increment, s_increment_len, s_timestamp, s_timestamp_len TSRMLS_CC);
161+
if (Z_TYPE_P(increment) == IS_LONG && Z_TYPE_P(timestamp) == IS_LONG) {
162+
php_phongo_timestamp_init(intern, Z_LVAL_P(increment), Z_LVAL_P(timestamp) TSRMLS_CC);
163+
return;
164+
}
165+
166+
if (Z_TYPE_P(increment) == IS_LONG) {
167+
convert_to_string(increment);
168+
}
169+
170+
if (Z_TYPE_P(increment) != IS_STRING) {
171+
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)));
172+
return;
173+
}
174+
175+
if (Z_TYPE_P(timestamp) == IS_LONG) {
176+
convert_to_string(timestamp);
177+
}
178+
179+
if (Z_TYPE_P(timestamp) != IS_STRING) {
180+
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)));
181+
return;
182+
}
183+
184+
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);
173185
}
174186
/* }}} */
175187

src/BSON/UTCDateTime.c

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
/* External libs */
2828
#include <bson.h>
2929
#include <mongoc.h>
30+
#include <math.h>
3031

3132
/* PHP Core stuff */
3233
#include <php.h>
@@ -77,11 +78,7 @@ static bool php_phongo_utcdatetime_init_from_string(php_phongo_utcdatetime_t *in
7778

7879
errno = 0;
7980

80-
#if defined(PHP_WIN32)
81-
milliseconds = _atoi64(s_milliseconds);
82-
#else
8381
milliseconds = bson_ascii_strtoll(s_milliseconds, &endptr, 10);
84-
#endif
8582

8683
/* errno will set errno if conversion fails; however, we do not need to
8784
* specify the type of error.
@@ -161,49 +158,64 @@ static bool php_phongo_utcdatetime_init_from_date(php_phongo_utcdatetime_t *inte
161158
return true;
162159
}
163160

164-
/* {{{ proto void UTCDateTime::__construct([string|DateTimeInterface $milliseconds = null])
161+
/* {{{ proto void UTCDateTime::__construct([int|float|string|DateTimeInterface $milliseconds = null])
165162
Construct a new BSON UTCDateTime type from either the current time,
166-
milliseconds since the epoch, or a DateTimeInterface object. */
163+
milliseconds since the epoch, or a DateTimeInterface object. Defaults to the
164+
current time. */
167165
PHP_METHOD(UTCDateTime, __construct)
168166
{
169167
php_phongo_utcdatetime_t *intern;
170168
zend_error_handling error_handling;
171-
zval *datetime = NULL;
169+
zval *milliseconds = NULL;
172170

173171
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC);
174172
intern = Z_UTCDATETIME_OBJ_P(getThis());
175173

176-
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|o!", &datetime) == SUCCESS) {
177-
if (datetime == NULL) {
178-
php_phongo_utcdatetime_init_from_current_time(intern);
179-
} else if (instanceof_function(Z_OBJCE_P(datetime), php_date_get_date_ce() TSRMLS_CC)) {
180-
php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(datetime));
174+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!", &milliseconds) == FAILURE) {
175+
zend_restore_error_handling(&error_handling TSRMLS_CC);
176+
return;
177+
}
178+
zend_restore_error_handling(&error_handling TSRMLS_CC);
179+
180+
if (milliseconds == NULL) {
181+
php_phongo_utcdatetime_init_from_current_time(intern);
182+
return;
183+
}
184+
185+
if (Z_TYPE_P(milliseconds) == IS_OBJECT) {
186+
if (instanceof_function(Z_OBJCE_P(milliseconds), php_date_get_date_ce() TSRMLS_CC)) {
187+
php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(milliseconds));
181188
#if PHP_VERSION_ID >= 50500
182-
} else if (instanceof_function(Z_OBJCE_P(datetime), php_date_get_immutable_ce() TSRMLS_CC)) {
183-
php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(datetime));
189+
} else if (instanceof_function(Z_OBJCE_P(milliseconds), php_date_get_immutable_ce() TSRMLS_CC)) {
190+
php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(milliseconds));
184191
#endif
185192
} else {
186-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected instance of DateTimeInterface, %s given", ZSTR_VAL(Z_OBJCE_P(datetime)->name));
193+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected instance of DateTimeInterface, %s given", ZSTR_VAL(Z_OBJCE_P(milliseconds)->name));
187194
}
195+
return;
196+
}
188197

189-
zend_restore_error_handling(&error_handling TSRMLS_CC);
198+
if (Z_TYPE_P(milliseconds) == IS_LONG) {
199+
php_phongo_utcdatetime_init(intern, Z_LVAL_P(milliseconds));
190200
return;
191201
}
192202

193-
{
194-
char *s_milliseconds;
195-
phongo_zpp_char_len s_milliseconds_len;
203+
if (Z_TYPE_P(milliseconds) == IS_DOUBLE) {
204+
char tmp[24];
205+
int tmp_len;
196206

197-
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s_milliseconds, &s_milliseconds_len) == FAILURE) {
198-
zend_restore_error_handling(&error_handling TSRMLS_CC);
199-
return;
200-
}
207+
tmp_len = snprintf(tmp, sizeof(tmp), "%.0f", Z_DVAL_P(milliseconds) > 0 ? floor(Z_DVAL_P(milliseconds)) : ceil(Z_DVAL_P(milliseconds)));
201208

202-
php_phongo_utcdatetime_init_from_string(intern, s_milliseconds, s_milliseconds_len TSRMLS_CC);
209+
php_phongo_utcdatetime_init_from_string(intern, tmp, tmp_len TSRMLS_CC);
210+
return;
203211
}
204212

205-
zend_restore_error_handling(&error_handling TSRMLS_CC);
213+
if (Z_TYPE_P(milliseconds) != IS_STRING) {
214+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected integer or string, %s given", zend_get_type_by_const(Z_TYPE_P(milliseconds)));
215+
return;
216+
}
206217

218+
php_phongo_utcdatetime_init_from_string(intern, Z_STRVAL_P(milliseconds), Z_STRLEN_P(milliseconds) TSRMLS_CC);
207219
}
208220
/* }}} */
209221

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
MongoDB\BSON\Timestamp unserialization requires strings to parse as 64-bit integers
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/tools.php';
7+
8+
echo throws(function() {
9+
unserialize('C:22:"MongoDB\BSON\Timestamp":60:{a:2:{s:9:"increment";s:4:"1.23";s:9:"timestamp";s:4:"5678";}}');
10+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
11+
12+
echo throws(function() {
13+
unserialize('C:22:"MongoDB\BSON\Timestamp":60:{a:2:{s:9:"increment";s:4:"1234";s:9:"timestamp";s:4:"5.67";}}');
14+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
15+
16+
?>
17+
===DONE===
18+
<?php exit(0); ?>
19+
--EXPECT--
20+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
21+
Error parsing "1.23" as 64-bit integer increment for MongoDB\BSON\Timestamp initialization
22+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
23+
Error parsing "5.67" as 64-bit integer timestamp for MongoDB\BSON\Timestamp initialization
24+
===DONE===
Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
11
--TEST--
2-
MongoDB\BSON\Timestamp::__set_state() with broken numeric strings
3-
--SKIPIF--
4-
<?php if (8 !== PHP_INT_SIZE) { die('skip Only for 64-bit platform'); } ?>
2+
MongoDB\BSON\Timestamp::__set_state() requires strings to parse as 64-bit integers
53
--FILE--
64
<?php
75

86
require_once __DIR__ . '/../utils/tools.php';
97

108
echo throws(function() {
11-
MongoDB\BSON\Timestamp::__set_state(['increment' => 'broken', 'timestamp' => '5678']);
9+
MongoDB\BSON\Timestamp::__set_state(['increment' => '1.23', 'timestamp' => '5678']);
1210
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
1311

1412
echo throws(function() {
15-
MongoDB\BSON\Timestamp::__set_state(['increment' => '1234', 'timestamp' => 'broken']);
13+
MongoDB\BSON\Timestamp::__set_state(['increment' => '1234', 'timestamp' => '5.67']);
1614
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
1715

1816
?>
1917
===DONE===
2018
<?php exit(0); ?>
2119
--EXPECT--
2220
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
23-
Error parsing "broken" as 64-bit integer increment for MongoDB\BSON\Timestamp initialization
21+
Error parsing "1.23" as 64-bit integer increment for MongoDB\BSON\Timestamp initialization
2422
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
25-
Error parsing "broken" as 64-bit integer timestamp for MongoDB\BSON\Timestamp initialization
23+
Error parsing "5.67" as 64-bit integer timestamp for MongoDB\BSON\Timestamp initialization
2624
===DONE===
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
MongoDB\BSON\Timestamp constructor requires strings to parse as 64-bit integers
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/tools.php';
7+
8+
echo throws(function() {
9+
new MongoDB\BSON\Timestamp('1.23', '5678');
10+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
11+
12+
echo throws(function() {
13+
new MongoDB\BSON\Timestamp('1234', '5.67');
14+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
15+
16+
?>
17+
===DONE===
18+
<?php exit(0); ?>
19+
--EXPECT--
20+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
21+
Error parsing "1.23" as 64-bit integer increment for MongoDB\BSON\Timestamp initialization
22+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
23+
Error parsing "5.67" as 64-bit integer timestamp for MongoDB\BSON\Timestamp initialization
24+
===DONE===
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
MongoDB\BSON\Timestamp constructor requires integer or string arguments
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/tools.php';
7+
8+
$invalidValues = [null, 1234.5, true, [], new stdClass];
9+
10+
foreach ($invalidValues as $invalidValue) {
11+
echo throws(function() use ($invalidValue) {
12+
new MongoDB\BSON\Timestamp($invalidValue, 0);
13+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
14+
}
15+
16+
foreach ($invalidValues as $invalidValue) {
17+
echo throws(function() use ($invalidValue) {
18+
new MongoDB\BSON\Timestamp(0, $invalidValue);
19+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
20+
}
21+
22+
?>
23+
===DONE===
24+
<?php exit(0); ?>
25+
--EXPECTF--
26+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
27+
Expected increment to be an unsigned 32-bit integer or string, %r(null|NULL)%r given
28+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
29+
Expected increment to be an unsigned 32-bit integer or string, %r(double|float)%r given
30+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
31+
Expected increment to be an unsigned 32-bit integer or string, boolean given
32+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
33+
Expected increment to be an unsigned 32-bit integer or string, array given
34+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
35+
Expected increment to be an unsigned 32-bit integer or string, object given
36+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
37+
Expected timestamp to be an unsigned 32-bit integer or string, %r(null|NULL)%r given
38+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
39+
Expected timestamp to be an unsigned 32-bit integer or string, %r(double|float)%r given
40+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
41+
Expected timestamp to be an unsigned 32-bit integer or string, boolean given
42+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
43+
Expected timestamp to be an unsigned 32-bit integer or string, array given
44+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
45+
Expected timestamp to be an unsigned 32-bit integer or string, object given
46+
===DONE===

tests/bson/bson-utcdatetime-007.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
MongoDB\BSON\UTCDateTime constructor truncates floating point values
3+
--FILE--
4+
<?php
5+
6+
$tests = [
7+
new MongoDB\BSON\UTCDateTime(1416445411987.0),
8+
new MongoDB\BSON\UTCDateTime(2147483647.0),
9+
new MongoDB\BSON\UTCDateTime(1234.5678),
10+
];
11+
12+
foreach ($tests as $test) {
13+
var_dump($test);
14+
}
15+
16+
?>
17+
===DONE===
18+
<?php exit(0); ?>
19+
--EXPECTF--
20+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
21+
["milliseconds"]=>
22+
string(13) "1416445411987"
23+
}
24+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
25+
["milliseconds"]=>
26+
string(10) "2147483647"
27+
}
28+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
29+
["milliseconds"]=>
30+
string(4) "1234"
31+
}
32+
===DONE===

tests/bson/bson-utcdatetime-serialization_error-002.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ MongoDB\BSON\UTCDateTime unserialization requires "milliseconds" string to parse
66
require_once __DIR__ . '/../utils/tools.php';
77

88
echo throws(function() {
9-
unserialize('C:24:"MongoDB\BSON\UTCDateTime":40:{a:1:{s:12:"milliseconds";s:7:"INVALID";}}');
9+
unserialize('C:24:"MongoDB\BSON\UTCDateTime":42:{a:1:{s:12:"milliseconds";s:9:"1234.5678";}}');
1010
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
1111

1212
/* TODO: Add tests for out-of-range values once CDRIVER-1377 is resolved */
@@ -16,5 +16,5 @@ echo throws(function() {
1616
<?php exit(0); ?>
1717
--EXPECT--
1818
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
19-
Error parsing "INVALID" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization
19+
Error parsing "1234.5678" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization
2020
===DONE===

tests/bson/bson-utcdatetime-set_state_error-002.phpt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
MongoDB\BSON\UTCDateTime::__set_state() requires "milliseconds" string to parse as 64-bit integer
33
--FILE--
44
<?php
5+
56
require_once __DIR__ . '/../utils/tools.php';
67

78
echo throws(function() {
8-
MongoDB\BSON\UTCDateTime::__set_state(['milliseconds' => 'INVALID']);
9+
MongoDB\BSON\UTCDateTime::__set_state(['milliseconds' => '1234.5678']);
910
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
1011

1112
/* TODO: Add tests for out-of-range values once CDRIVER-1377 is resolved */
@@ -15,5 +16,5 @@ echo throws(function() {
1516
<?php exit(0); ?>
1617
--EXPECT--
1718
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
18-
Error parsing "INVALID" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization
19+
Error parsing "1234.5678" as 64-bit integer for MongoDB\BSON\UTCDateTime initialization
1920
===DONE===

0 commit comments

Comments
 (0)