Skip to content

Commit 8d7e358

Browse files
committed
PHPC-804: Relax zpp types in Timestamp and UTCDateTime constructors
Changing "s" to "z" in zend_parse_parameters() allows integers to be accepted in strict types mode. If all necessary parameters are integers, we can also skip conversion to a string and back to a 64-bit integer.
1 parent 1f785d2 commit 8d7e358

File tree

4 files changed

+128
-33
lines changed

4 files changed

+128
-33
lines changed

src/BSON/Timestamp.c

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -140,28 +140,48 @@ static bool php_phongo_timestamp_init_from_hash(php_phongo_timestamp_t *intern,
140140
return false;
141141
}
142142

143-
/* {{{ proto void Timestamp::__construct(string $increment, string $timestamp)
143+
/* {{{ proto void Timestamp::__construct(int|string $increment, int|string $timestamp)
144144
Construct a new BSON timestamp type, which consists of a 4-byte increment and
145145
4-byte timestamp. */
146146
PHP_METHOD(Timestamp, __construct)
147147
{
148-
php_phongo_timestamp_t *intern;
149-
zend_error_handling error_handling;
150-
char *s_increment;
151-
phongo_zpp_char_len s_increment_len;
152-
char *s_timestamp;
153-
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;
154151

155152
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC);
156153
intern = Z_TIMESTAMP_OBJ_P(getThis());
157154

158-
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) {
159156
zend_restore_error_handling(&error_handling TSRMLS_CC);
160157
return;
161158
}
162159
zend_restore_error_handling(&error_handling TSRMLS_CC);
163160

164-
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);
165185
}
166186
/* }}} */
167187

src/BSON/UTCDateTime.c

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -157,49 +157,54 @@ static bool php_phongo_utcdatetime_init_from_date(php_phongo_utcdatetime_t *inte
157157
return true;
158158
}
159159

160-
/* {{{ proto void UTCDateTime::__construct([string|DateTimeInterface $milliseconds = null])
160+
/* {{{ proto void UTCDateTime::__construct([int|string|DateTimeInterface $milliseconds = null])
161161
Construct a new BSON UTCDateTime type from either the current time,
162-
milliseconds since the epoch, or a DateTimeInterface object. */
162+
milliseconds since the epoch, or a DateTimeInterface object. Defaults to the
163+
current time. */
163164
PHP_METHOD(UTCDateTime, __construct)
164165
{
165166
php_phongo_utcdatetime_t *intern;
166167
zend_error_handling error_handling;
167-
zval *datetime = NULL;
168+
zval *milliseconds = NULL;
168169

169170
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC);
170171
intern = Z_UTCDATETIME_OBJ_P(getThis());
171172

172-
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "|o!", &datetime) == SUCCESS) {
173-
if (datetime == NULL) {
174-
php_phongo_utcdatetime_init_from_current_time(intern);
175-
} else if (instanceof_function(Z_OBJCE_P(datetime), php_date_get_date_ce() TSRMLS_CC)) {
176-
php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(datetime));
177-
#if PHP_VERSION_ID >= 50500
178-
} else if (instanceof_function(Z_OBJCE_P(datetime), php_date_get_immutable_ce() TSRMLS_CC)) {
179-
php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(datetime));
180-
#endif
181-
} else {
182-
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected instance of DateTimeInterface, %s given", ZSTR_VAL(Z_OBJCE_P(datetime)->name));
183-
}
184-
173+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!", &milliseconds) == FAILURE) {
185174
zend_restore_error_handling(&error_handling TSRMLS_CC);
186175
return;
187176
}
177+
zend_restore_error_handling(&error_handling TSRMLS_CC);
188178

189-
{
190-
char *s_milliseconds;
191-
phongo_zpp_char_len s_milliseconds_len;
179+
if (milliseconds == NULL) {
180+
php_phongo_utcdatetime_init_from_current_time(intern);
181+
return;
182+
}
192183

193-
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s_milliseconds, &s_milliseconds_len) == FAILURE) {
194-
zend_restore_error_handling(&error_handling TSRMLS_CC);
195-
return;
184+
if (Z_TYPE_P(milliseconds) == IS_OBJECT) {
185+
if (instanceof_function(Z_OBJCE_P(milliseconds), php_date_get_date_ce() TSRMLS_CC)) {
186+
php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(milliseconds));
187+
#if PHP_VERSION_ID >= 50500
188+
} else if (instanceof_function(Z_OBJCE_P(milliseconds), php_date_get_immutable_ce() TSRMLS_CC)) {
189+
php_phongo_utcdatetime_init_from_date(intern, Z_PHPDATE_P(milliseconds));
190+
#endif
191+
} else {
192+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected instance of DateTimeInterface, %s given", ZSTR_VAL(Z_OBJCE_P(milliseconds)->name));
196193
}
194+
return;
195+
}
197196

198-
php_phongo_utcdatetime_init_from_string(intern, s_milliseconds, s_milliseconds_len TSRMLS_CC);
197+
if (Z_TYPE_P(milliseconds) == IS_LONG) {
198+
php_phongo_utcdatetime_init(intern, Z_LVAL_P(milliseconds));
199+
return;
199200
}
200201

201-
zend_restore_error_handling(&error_handling TSRMLS_CC);
202+
if (Z_TYPE_P(milliseconds) != IS_STRING) {
203+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected integer or string, %s given", zend_get_type_by_const(Z_TYPE_P(milliseconds)));
204+
return;
205+
}
202206

207+
php_phongo_utcdatetime_init_from_string(intern, Z_STRVAL_P(milliseconds), Z_STRLEN_P(milliseconds) TSRMLS_CC);
203208
}
204209
/* }}} */
205210

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===
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
MongoDB\BSON\UTCDateTime constructor requires integer or string argument
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/tools.php';
7+
8+
$invalidValues = [true, []];
9+
10+
foreach ($invalidValues as $invalidValue) {
11+
echo throws(function() use ($invalidValue) {
12+
new MongoDB\BSON\UTCDateTime($invalidValue);
13+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
14+
}
15+
16+
?>
17+
===DONE===
18+
<?php exit(0); ?>
19+
--EXPECTF--
20+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
21+
Expected integer or string, boolean given
22+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
23+
Expected integer or string, array given
24+
===DONE===

0 commit comments

Comments
 (0)