Skip to content

Commit 1c730d3

Browse files
committed
Merge pull request #1053
2 parents a9b2736 + 0a30941 commit 1c730d3

File tree

2 files changed

+167
-1
lines changed

2 files changed

+167
-1
lines changed

src/MongoDB/CursorId.c

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616

1717
#include <php.h>
1818
#include <Zend/zend_interfaces.h>
19+
#include <ext/standard/php_var.h>
20+
#if PHP_VERSION_ID >= 70000
21+
#include <zend_smart_str.h>
22+
#else
23+
#include <ext/standard/php_smart_str.h>
24+
#endif
1925

2026
#ifdef HAVE_CONFIG_H
2127
#include "config.h"
@@ -26,6 +32,48 @@
2632

2733
zend_class_entry* php_phongo_cursorid_ce;
2834

35+
/* Initialize the object from a numeric string and return whether it was
36+
* successful. An exception will be thrown on error. */
37+
static bool php_phongo_cursorid_init_from_string(php_phongo_cursorid_t* intern, const char* s_id, phongo_zpp_char_len s_id_len TSRMLS_DC) /* {{{ */
38+
{
39+
int64_t id;
40+
char* endptr = NULL;
41+
42+
/* bson_ascii_strtoll() sets errno if conversion fails. If conversion
43+
* succeeds, we still want to ensure that the entire string was parsed. */
44+
id = bson_ascii_strtoll(s_id, &endptr, 10);
45+
46+
if (errno || (endptr && endptr != ((const char*) s_id + s_id_len))) {
47+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Error parsing \"%s\" as 64-bit id for %s initialization", s_id, ZSTR_VAL(php_phongo_cursorid_ce->name));
48+
return false;
49+
}
50+
51+
intern->id = id;
52+
return true;
53+
} /* }}} */
54+
55+
/* Initialize the object from a HashTable and return whether it was successful.
56+
* An exception will be thrown on error. */
57+
static bool php_phongo_cursorid_init_from_hash(php_phongo_cursorid_t* intern, HashTable* props TSRMLS_DC) /* {{{ */
58+
{
59+
#if PHP_VERSION_ID >= 70000
60+
zval* value;
61+
62+
if ((value = zend_hash_str_find(props, "id", sizeof("id") - 1)) && Z_TYPE_P(value) == IS_STRING) {
63+
return php_phongo_cursorid_init_from_string(intern, Z_STRVAL_P(value), Z_STRLEN_P(value) TSRMLS_CC);
64+
}
65+
#else
66+
zval** value;
67+
68+
if (zend_hash_find(props, "id", sizeof("id"), (void**) &value) == SUCCESS && Z_TYPE_PP(value) == IS_STRING) {
69+
return php_phongo_cursorid_init_from_string(intern, Z_STRVAL_PP(value), Z_STRLEN_PP(value) TSRMLS_CC);
70+
}
71+
#endif
72+
73+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s initialization requires \"id\" string field", ZSTR_VAL(php_phongo_cursorid_ce->name));
74+
return false;
75+
} /* }}} */
76+
2977
/* {{{ proto string MongoDB\Driver\CursorId::__toString()
3078
Returns the string representation of the CursorId */
3179
static PHP_METHOD(CursorId, __toString)
@@ -45,13 +93,104 @@ static PHP_METHOD(CursorId, __toString)
4593
efree(tmp);
4694
} /* }}} */
4795

96+
/* {{{ proto string MongoDB\Driver\CursorId::serialize()
97+
*/
98+
static PHP_METHOD(CursorId, serialize)
99+
{
100+
php_phongo_cursorid_t* intern;
101+
ZVAL_RETVAL_TYPE retval;
102+
php_serialize_data_t var_hash;
103+
smart_str buf = { 0 };
104+
char s_id[24];
105+
int s_id_len;
106+
107+
if (zend_parse_parameters_none() == FAILURE) {
108+
return;
109+
}
110+
111+
intern = Z_CURSORID_OBJ_P(getThis());
112+
113+
s_id_len = snprintf(s_id, sizeof(s_id), "%" PRId64, intern->id);
114+
115+
#if PHP_VERSION_ID >= 70000
116+
array_init_size(&retval, 1);
117+
ADD_ASSOC_STRINGL(&retval, "id", s_id, s_id_len);
118+
#else
119+
ALLOC_INIT_ZVAL(retval);
120+
array_init_size(retval, 1);
121+
ADD_ASSOC_STRINGL(retval, "id", s_id, s_id_len);
122+
#endif
123+
124+
PHP_VAR_SERIALIZE_INIT(var_hash);
125+
php_var_serialize(&buf, &retval, &var_hash TSRMLS_CC);
126+
smart_str_0(&buf);
127+
PHP_VAR_SERIALIZE_DESTROY(var_hash);
128+
129+
PHONGO_RETVAL_SMART_STR(buf);
130+
131+
smart_str_free(&buf);
132+
zval_ptr_dtor(&retval);
133+
} /* }}} */
134+
135+
/* {{{ proto void MongoDB\Driver\CursorId::unserialize(string $serialized)
136+
*/
137+
static PHP_METHOD(CursorId, unserialize)
138+
{
139+
php_phongo_cursorid_t* intern;
140+
zend_error_handling error_handling;
141+
char* serialized;
142+
phongo_zpp_char_len serialized_len;
143+
#if PHP_VERSION_ID >= 70000
144+
zval props;
145+
#else
146+
zval* props;
147+
#endif
148+
php_unserialize_data_t var_hash;
149+
150+
intern = Z_CURSORID_OBJ_P(getThis());
151+
152+
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC);
153+
154+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &serialized, &serialized_len) == FAILURE) {
155+
zend_restore_error_handling(&error_handling TSRMLS_CC);
156+
return;
157+
}
158+
zend_restore_error_handling(&error_handling TSRMLS_CC);
159+
160+
#if PHP_VERSION_ID < 70000
161+
ALLOC_INIT_ZVAL(props);
162+
#endif
163+
PHP_VAR_UNSERIALIZE_INIT(var_hash);
164+
if (!php_var_unserialize(&props, (const unsigned char**) &serialized, (unsigned char*) serialized + serialized_len, &var_hash TSRMLS_CC)) {
165+
zval_ptr_dtor(&props);
166+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "%s unserialization failed", ZSTR_VAL(php_phongo_cursorid_ce->name));
167+
168+
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
169+
return;
170+
}
171+
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
172+
173+
#if PHP_VERSION_ID >= 70000
174+
php_phongo_cursorid_init_from_hash(intern, HASH_OF(&props) TSRMLS_CC);
175+
#else
176+
php_phongo_cursorid_init_from_hash(intern, HASH_OF(props) TSRMLS_CC);
177+
#endif
178+
zval_ptr_dtor(&props);
179+
} /* }}} */
180+
48181
/* {{{ MongoDB\Driver\CursorId function entries */
182+
ZEND_BEGIN_ARG_INFO_EX(ai_CursorId_unserialize, 0, 0, 1)
183+
ZEND_ARG_INFO(0, serialized)
184+
ZEND_END_ARG_INFO()
185+
49186
ZEND_BEGIN_ARG_INFO_EX(ai_CursorId_void, 0, 0, 0)
50187
ZEND_END_ARG_INFO()
51188

52189
static zend_function_entry php_phongo_cursorid_me[] = {
53190
/* clang-format off */
54191
PHP_ME(CursorId, __toString, ai_CursorId_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
192+
PHP_ME(CursorId, serialize, ai_CursorId_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
193+
PHP_ME(CursorId, unserialize, ai_CursorId_unserialize, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
55194
ZEND_NAMED_ME(__construct, PHP_FN(MongoDB_disabled___construct), ai_CursorId_void, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)
56195
ZEND_NAMED_ME(__wakeup, PHP_FN(MongoDB_disabled___wakeup), ai_CursorId_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
57196
PHP_FE_END
@@ -131,7 +270,8 @@ void php_phongo_cursorid_init_ce(INIT_FUNC_ARGS) /* {{{ */
131270
php_phongo_cursorid_ce = zend_register_internal_class(&ce TSRMLS_CC);
132271
php_phongo_cursorid_ce->create_object = php_phongo_cursorid_create_object;
133272
PHONGO_CE_FINAL(php_phongo_cursorid_ce);
134-
PHONGO_CE_DISABLE_SERIALIZATION(php_phongo_cursorid_ce);
273+
274+
zend_class_implements(php_phongo_cursorid_ce TSRMLS_CC, 1, zend_ce_serializable);
135275

136276
memcpy(&php_phongo_handler_cursorid, phongo_get_std_object_handlers(), sizeof(zend_object_handlers));
137277
php_phongo_handler_cursorid.get_debug_info = php_phongo_cursorid_get_debug_info;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
MongoDB\Driver\CursorID serialization
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . "/../utils/basic.inc";
7+
8+
$serialized = 'C:23:"MongoDB\Driver\CursorId":42:{a:1:{s:2:"id";s:19:"7250031947823432848";}}';
9+
10+
$cursorId = unserialize($serialized);
11+
12+
var_dump($cursorId);
13+
var_dump($cursorId instanceof Serializable);
14+
echo serialize($cursorId), "\n";
15+
16+
?>
17+
===DONE===
18+
<?php exit(0); ?>
19+
--EXPECTF--
20+
object(MongoDB\Driver\CursorId)#%d (%d) {
21+
["id"]=>
22+
%rint\(\d+\)|string\(\d+\) "\d+"%r
23+
}
24+
bool(true)
25+
C:23:"MongoDB\Driver\CursorId":42:{a:1:{s:2:"id";s:19:"7250031947823432848";}}
26+
===DONE===

0 commit comments

Comments
 (0)