Skip to content

Commit 10e1879

Browse files
committed
PHPC-460: UTCDateTime serialization and var_export
1 parent 0a32ce9 commit 10e1879

8 files changed

+318
-56
lines changed

php_phongo.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2043,6 +2043,7 @@ void php_phongo_new_utcdatetime_from_epoch(zval *object, int64_t msec_since_epoc
20432043

20442044
intern = Z_UTCDATETIME_OBJ_P(object);
20452045
intern->milliseconds = msec_since_epoch;
2046+
intern->initialized = true;
20462047
} /* }}} */
20472048

20482049
void php_phongo_new_timestamp_from_increment_and_timestamp(zval *object, uint32_t increment, uint32_t timestamp TSRMLS_DC) /* {{{ */

php_phongo_structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ typedef struct {
210210

211211
typedef struct {
212212
PHONGO_ZEND_OBJECT_PRE
213+
bool initialized;
213214
int64_t milliseconds;
214215
PHONGO_ZEND_OBJECT_POST
215216
} php_phongo_utcdatetime_t;

src/BSON/UTCDateTime.c

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

5353
zend_object_handlers php_phongo_handler_utcdatetime;
5454

55-
static void php_phongo_utcdatetime_init_from_current_time(php_phongo_utcdatetime_t *intern)
55+
/* Initialize the object from an integer and return whether it was successful. */
56+
static bool php_phongo_utcdatetime_init(php_phongo_utcdatetime_t *intern, int64_t milliseconds)
57+
{
58+
intern->milliseconds = milliseconds;
59+
intern->initialized = true;
60+
61+
return true;
62+
}
63+
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)
66+
{
67+
#if PHP_VERSION_ID >= 70000
68+
zval *milliseconds;
69+
70+
if ((milliseconds = zend_hash_str_find(props, "milliseconds", sizeof("milliseconds")-1)) && Z_TYPE_P(milliseconds) == IS_LONG) {
71+
return php_phongo_utcdatetime_init(intern, Z_LVAL_P(milliseconds));
72+
}
73+
74+
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)));
76+
}
77+
#else
78+
zval **milliseconds;
79+
80+
if (zend_hash_find(props, "milliseconds", sizeof("milliseconds"), (void**) &milliseconds) == SUCCESS && Z_TYPE_PP(milliseconds) == IS_LONG) {
81+
return php_phongo_utcdatetime_init(intern, Z_LVAL_PP(milliseconds));
82+
}
83+
84+
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)));
86+
}
87+
#endif
88+
return false;
89+
}
90+
91+
/* Initialize the object from the current time and return whether it was successful. */
92+
static bool php_phongo_utcdatetime_init_from_current_time(php_phongo_utcdatetime_t *intern)
5693
{
5794
int64_t sec, usec;
5895
struct timeval cur_time;
@@ -62,9 +99,13 @@ static void php_phongo_utcdatetime_init_from_current_time(php_phongo_utcdatetime
6299
usec = cur_time.tv_usec;
63100

64101
intern->milliseconds = (sec * 1000) + (usec / 1000);
102+
intern->initialized = true;
103+
104+
return true;
65105
}
66106

67-
static void php_phongo_utcdatetime_init_from_date(php_phongo_utcdatetime_t *intern, php_date_obj *datetime_obj)
107+
/* Initialize the object from a DateTime object and return whether it was successful. */
108+
static bool php_phongo_utcdatetime_init_from_date(php_phongo_utcdatetime_t *intern, php_date_obj *datetime_obj)
68109
{
69110
int64_t sec, usec;
70111

@@ -73,6 +114,9 @@ static void php_phongo_utcdatetime_init_from_date(php_phongo_utcdatetime_t *inte
73114
usec = (int64_t) floor(datetime_obj->time->f * 1000000 + 0.5);
74115

75116
intern->milliseconds = (sec * 1000) + (usec / 1000);
117+
intern->initialized = true;
118+
119+
return true;
76120
}
77121

78122
/* {{{ proto BSON\UTCDateTime UTCDateTime::__construct([integer|DateTimeInterface $milliseconds = null])
@@ -115,7 +159,7 @@ PHP_METHOD(UTCDateTime, __construct)
115159
return;
116160
}
117161

118-
intern->milliseconds = milliseconds;
162+
php_phongo_utcdatetime_init(intern, milliseconds);
119163
}
120164
#elif SIZEOF_PHONGO_LONG == 4
121165
{
@@ -127,7 +171,7 @@ PHP_METHOD(UTCDateTime, __construct)
127171
return;
128172
}
129173

130-
intern->milliseconds = STRTOLL(s_milliseconds);
174+
php_phongo_utcdatetime_init(intern, STRTOLL(s_milliseconds));
131175
}
132176
#else
133177
# error Unsupported architecture (integers are neither 32-bit nor 64-bit)
@@ -137,6 +181,30 @@ PHP_METHOD(UTCDateTime, __construct)
137181

138182
}
139183
/* }}} */
184+
185+
/* {{{ proto UTCDateTime::__set_state(array $properties)
186+
*/
187+
PHP_METHOD(UTCDateTime, __set_state)
188+
{
189+
php_phongo_utcdatetime_t *intern;
190+
HashTable *props;
191+
zval *array;
192+
193+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
194+
RETURN_FALSE;
195+
}
196+
197+
object_init_ex(return_value, php_phongo_utcdatetime_ce);
198+
199+
intern = Z_UTCDATETIME_OBJ_P(return_value);
200+
props = Z_ARRVAL_P(array);
201+
202+
if (!php_phongo_utcdatetime_init_from_hash(intern, props)) {
203+
php_error(E_ERROR, "Invalid serialization data for UTCDateTime object");
204+
}
205+
}
206+
/* }}} */
207+
140208
/* {{{ proto string UTCDateTime::__toString()
141209
Returns the string representation of the UTCDateTime */
142210
PHP_METHOD(UTCDateTime, __toString)
@@ -157,6 +225,27 @@ PHP_METHOD(UTCDateTime, __toString)
157225
efree(tmp);
158226
}
159227
/* }}} */
228+
229+
/* {{{ proto UTCDateTime::__wakeup()
230+
*/
231+
PHP_METHOD(UTCDateTime, __wakeup)
232+
{
233+
php_phongo_utcdatetime_t *intern;
234+
HashTable *props;
235+
236+
if (zend_parse_parameters_none() == FAILURE) {
237+
return;
238+
}
239+
240+
intern = Z_UTCDATETIME_OBJ_P(getThis());
241+
props = zend_std_get_properties(getThis() TSRMLS_CC);
242+
243+
if (!php_phongo_utcdatetime_init_from_hash(intern, props)) {
244+
php_error(E_ERROR, "Invalid serialization data for UTCDateTime object");
245+
}
246+
}
247+
/* }}} */
248+
160249
/* {{{ proto string UTCDateTime::toDateTime()
161250
Returns DateTime object representing this UTCDateTime */
162251
PHP_METHOD(UTCDateTime, toDateTime)
@@ -191,18 +280,20 @@ ZEND_BEGIN_ARG_INFO_EX(ai_UTCDateTime___construct, 0, 0, 1)
191280
ZEND_ARG_INFO(0, milliseconds)
192281
ZEND_END_ARG_INFO();
193282

194-
ZEND_BEGIN_ARG_INFO_EX(ai_UTCDateTime___toString, 0, 0, 0)
195-
ZEND_END_ARG_INFO();
283+
ZEND_BEGIN_ARG_INFO_EX(ai_UTCDateTime___set_state, 0, 0, 1)
284+
ZEND_ARG_ARRAY_INFO(0, properties, 0)
285+
ZEND_END_ARG_INFO()
196286

197-
ZEND_BEGIN_ARG_INFO_EX(ai_UTCDateTime_toDateTime, 0, 0, 0)
198-
ZEND_END_ARG_INFO();
287+
ZEND_BEGIN_ARG_INFO_EX(ai_UTCDateTime_void, 0, 0, 0)
288+
ZEND_END_ARG_INFO()
199289

200290

201291
static zend_function_entry php_phongo_utcdatetime_me[] = {
202292
PHP_ME(UTCDateTime, __construct, ai_UTCDateTime___construct, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
203-
PHP_ME(UTCDateTime, __toString, ai_UTCDateTime___toString, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
204-
PHP_ME(UTCDateTime, toDateTime, ai_UTCDateTime_toDateTime, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
205-
PHP_ME(Manager, __wakeUp, NULL, ZEND_ACC_PUBLIC)
293+
PHP_ME(UTCDateTime, __set_state, ai_UTCDateTime___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
294+
PHP_ME(UTCDateTime, __toString, ai_UTCDateTime_void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
295+
PHP_ME(UTCDateTime, __wakeup, ai_UTCDateTime_void, ZEND_ACC_PUBLIC)
296+
PHP_ME(UTCDateTime, toDateTime, ai_UTCDateTime_void, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
206297
PHP_FE_END
207298
};
208299

@@ -245,33 +336,53 @@ phongo_create_object_retval php_phongo_utcdatetime_create_object(zend_class_entr
245336
#endif
246337
} /* }}} */
247338

248-
HashTable *php_phongo_utcdatetime_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
339+
HashTable *php_phongo_utcdatetime_get_properties(zval *object TSRMLS_DC) /* {{{ */
249340
{
250341
php_phongo_utcdatetime_t *intern;
251-
#if PHP_VERSION_ID >= 70000
252-
zval retval;
253-
#else
254-
zval retval = zval_used_for_init;
255-
#endif
342+
HashTable *props;
256343

257-
*is_temp = 1;
258-
intern = Z_UTCDATETIME_OBJ_P(object);
344+
intern = Z_UTCDATETIME_OBJ_P(object);
345+
props = zend_std_get_properties(object TSRMLS_CC);
259346

260-
array_init(&retval);
347+
if (!intern->initialized) {
348+
return props;
349+
}
261350

262351
#if SIZEOF_LONG == 4
263352
{
264-
char tmp[24];
265-
int tmp_len;
353+
char s_milliseconds[24];
354+
int s_milliseconds_len;
266355

267-
tmp_len = snprintf(tmp, sizeof(tmp), "%" PRId64, intern->milliseconds);
268-
ADD_ASSOC_STRINGL(&retval, "milliseconds", tmp, tmp_len);
356+
s_milliseconds_len = snprintf(s_milliseconds, sizeof(s_milliseconds), "%" PRId64, intern->milliseconds);
269357
}
358+
#endif
359+
360+
#if PHP_VERSION_ID >= 70000
361+
{
362+
zval milliseconds;
363+
364+
#if SIZEOF_LONG == 4
365+
ZVAL_STRINGL(&milliseconds, s_milliseconds, s_milliseconds_len);
270366
#else
271-
ADD_ASSOC_LONG_EX(&retval, "milliseconds", intern->milliseconds);
367+
ZVAL_LONG(&milliseconds, intern->milliseconds);
368+
#endif
369+
zend_hash_str_update(props, "milliseconds", sizeof("milliseconds")-1, &milliseconds);
370+
}
371+
#else
372+
{
373+
zval *milliseconds;
374+
375+
MAKE_STD_ZVAL(milliseconds);
376+
#if SIZEOF_LONG == 4
377+
ZVAL_STRINGL(milliseconds, s_milliseconds, s_milliseconds_len, 1);
378+
#else
379+
ZVAL_LONG(milliseconds, intern->milliseconds);
380+
#endif
381+
zend_hash_update(props, "milliseconds", sizeof("milliseconds"), &milliseconds, sizeof(milliseconds), NULL);
382+
}
272383
#endif
273384

274-
return Z_ARRVAL(retval);
385+
return props;
275386
} /* }}} */
276387
/* }}} */
277388

@@ -285,11 +396,10 @@ PHP_MINIT_FUNCTION(UTCDateTime)
285396
php_phongo_utcdatetime_ce = zend_register_internal_class(&ce TSRMLS_CC);
286397
php_phongo_utcdatetime_ce->create_object = php_phongo_utcdatetime_create_object;
287398
PHONGO_CE_FINAL(php_phongo_utcdatetime_ce);
288-
PHONGO_CE_DISABLE_SERIALIZATION(php_phongo_utcdatetime_ce);
289399

290400
zend_class_implements(php_phongo_utcdatetime_ce TSRMLS_CC, 1, php_phongo_type_ce);
291401
memcpy(&php_phongo_handler_utcdatetime, phongo_get_std_object_handlers(), sizeof(zend_object_handlers));
292-
php_phongo_handler_utcdatetime.get_debug_info = php_phongo_utcdatetime_get_debug_info;
402+
php_phongo_handler_utcdatetime.get_properties = php_phongo_utcdatetime_get_properties;
293403
#if PHP_VERSION_ID >= 70000
294404
php_phongo_handler_utcdatetime.free_obj = php_phongo_utcdatetime_free_object;
295405
php_phongo_handler_utcdatetime.offset = XtOffsetOf(php_phongo_utcdatetime_t, std);

tests/bson/bson-utcdatetime-007.phpt

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
BSON BSON\UTCDateTime serialization
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
5+
--FILE--
6+
<?php
7+
require_once __DIR__ . "/../utils/basic.inc";
8+
9+
$tests = [
10+
'0',
11+
'-1416445411987',
12+
'1416445411987',
13+
];
14+
15+
foreach ($tests as $milliseconds) {
16+
var_dump($udt = new MongoDB\BSON\UTCDateTime($milliseconds));
17+
var_dump($s = serialize($udt));
18+
var_dump(unserialize($s));
19+
echo "\n";
20+
}
21+
22+
?>
23+
===DONE===
24+
<?php exit(0); ?>
25+
--EXPECTF--
26+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
27+
["milliseconds"]=>
28+
%rint\(|string\(1\) "|%r0%r"|\)%r
29+
}
30+
string(60) "O:24:"MongoDB\BSON\UTCDateTime":1:{s:12:"milliseconds";%ri:|s:1:"%r0%r"?%r;}"
31+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
32+
["milliseconds"]=>
33+
%rint\(|string\(1\) "|%r0%r"|\)%r
34+
}
35+
36+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
37+
["milliseconds"]=>
38+
%rint\(|string\(14\) "|%r-1416445411987%r"|\)%r
39+
}
40+
string(73) "O:24:"MongoDB\BSON\UTCDateTime":1:{s:12:"milliseconds";%ri:|s:14:"%r-1416445411987%r"?%r;}"
41+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
42+
["milliseconds"]=>
43+
%rint\(|string\(14\) "|%r-1416445411987%r"|\)%r
44+
}
45+
46+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
47+
["milliseconds"]=>
48+
%rint\(|string\(13\) "|%r1416445411987%r"|\)%r
49+
}
50+
string(72) "O:24:"MongoDB\BSON\UTCDateTime":1:{s:12:"milliseconds";%ri:|s:13:"%r1416445411987%r"?%r;}"
51+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
52+
["milliseconds"]=>
53+
%rint\(|string\(13\) "|%r1416445411987%r"|\)%r
54+
}
55+
56+
===DONE===

tests/bson/bson-utcdatetime-008.phpt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
BSON BSON\UTCDateTime serialization (unserialize 32-bit data on 64-bit)
3+
--SKIPIF--
4+
<?php if (8 !== PHP_INT_SIZE) { die('skip Only for 64-bit platform'); } ?>
5+
<?php require __DIR__ . "/../utils/basic-skipif.inc"?>
6+
--FILE--
7+
<?php
8+
require_once __DIR__ . "/../utils/basic.inc";
9+
10+
$tests = [
11+
'0',
12+
'-1416445411987',
13+
'1416445411987',
14+
];
15+
16+
foreach ($tests as $milliseconds) {
17+
$s = sprintf('O:24:"MongoDB\BSON\UTCDateTime":1:{s:12:"milliseconds";s:%d:"%s";}', strlen($milliseconds), $milliseconds);
18+
19+
var_dump($s);
20+
var_dump(unserialize($s));
21+
echo "\n";
22+
}
23+
24+
?>
25+
===DONE===
26+
<?php exit(0); ?>
27+
--EXPECTF--
28+
string(64) "O:24:"MongoDB\BSON\UTCDateTime":1:{s:12:"milliseconds";s:1:"0";}"
29+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
30+
["milliseconds"]=>
31+
int(0)
32+
}
33+
34+
string(78) "O:24:"MongoDB\BSON\UTCDateTime":1:{s:12:"milliseconds";s:14:"-1416445411987";}"
35+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
36+
["milliseconds"]=>
37+
int(-1416445411987)
38+
}
39+
40+
string(77) "O:24:"MongoDB\BSON\UTCDateTime":1:{s:12:"milliseconds";s:13:"1416445411987";}"
41+
object(MongoDB\BSON\UTCDateTime)#%d (%d) {
42+
["milliseconds"]=>
43+
int(1416445411987)
44+
}
45+
46+
===DONE===

0 commit comments

Comments
 (0)