Skip to content

Commit 41081da

Browse files
committed
PHPC-460: Javascript serialization and var_export
1 parent d68b4a9 commit 41081da

File tree

4 files changed

+318
-60
lines changed

4 files changed

+318
-60
lines changed

src/BSON/Javascript.c

Lines changed: 147 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,49 @@ PHONGO_API zend_class_entry *php_phongo_javascript_ce;
4646

4747
zend_object_handlers php_phongo_handler_javascript;
4848

49+
/* Initialize the object from a string and return whether it was successful. */
50+
static bool php_phongo_javascript_init(php_phongo_javascript_t *intern, const char *javascript, phongo_zpp_char_len javascript_len, zval *scope TSRMLS_DC)
51+
{
52+
if (scope && Z_TYPE_P(scope) != IS_OBJECT && Z_TYPE_P(scope) != IS_ARRAY && Z_TYPE_P(scope) != IS_NULL) {
53+
return false;
54+
}
55+
56+
intern->javascript = estrndup(javascript, javascript_len);
57+
intern->javascript_len = javascript_len;
58+
59+
if (scope && (Z_TYPE_P(scope) == IS_OBJECT || Z_TYPE_P(scope) == IS_ARRAY)) {
60+
intern->document = bson_new();
61+
phongo_zval_to_bson(scope, PHONGO_BSON_NONE, intern->document, NULL TSRMLS_CC);
62+
} else {
63+
intern->document = NULL;
64+
}
65+
66+
return true;
67+
}
68+
69+
/* Initialize the object from a HashTable and return whether it was successful. */
70+
static bool php_phongo_javascript_init_from_hash(php_phongo_javascript_t *intern, HashTable *props TSRMLS_DC)
71+
{
72+
#if PHP_VERSION_ID >= 70000
73+
zval *javascript, *scope;
74+
75+
if ((javascript = zend_hash_str_find(props, "javascript", sizeof("javascript")-1)) && Z_TYPE_P(javascript) == IS_STRING) {
76+
scope = zend_hash_str_find(props, "scope", sizeof("scope")-1);
77+
78+
return php_phongo_javascript_init(intern, Z_STRVAL_P(javascript), Z_STRLEN_P(javascript), scope TSRMLS_CC);
79+
}
80+
#else
81+
zval **javascript, **scope;
82+
83+
if (zend_hash_find(props, "javascript", sizeof("javascript"), (void**) &javascript) == SUCCESS && Z_TYPE_PP(javascript) == IS_STRING) {
84+
zval *tmp = zend_hash_find(props, "scope", sizeof("scope"), (void**) &scope) == SUCCESS ? *scope : NULL;
85+
86+
return php_phongo_javascript_init(intern, Z_STRVAL_PP(javascript), Z_STRLEN_PP(javascript), tmp TSRMLS_CC);
87+
}
88+
#endif
89+
return false;
90+
}
91+
4992
/* {{{ proto BSON\Javascript Javascript::__construct(string $javascript[, array|object $document])
5093
* The string is JavaScript code. The document is a mapping from identifiers to values, representing the scope in which the string should be evaluated
5194
* NOTE: eJSON does not support this type :( */
@@ -56,7 +99,6 @@ PHP_METHOD(Javascript, __construct)
5699
char *javascript;
57100
phongo_zpp_char_len javascript_len;
58101
zval *document = NULL;
59-
bson_t scope = BSON_INITIALIZER;
60102

61103

62104
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC);
@@ -68,12 +110,51 @@ PHP_METHOD(Javascript, __construct)
68110
}
69111
zend_restore_error_handling(&error_handling TSRMLS_CC);
70112

71-
if (document) {
72-
phongo_zval_to_bson(document, PHONGO_BSON_NONE, &scope, NULL TSRMLS_CC);
113+
php_phongo_javascript_init(intern, javascript, javascript_len, document TSRMLS_CC);
114+
}
115+
/* }}} */
116+
117+
/* {{{ proto Javascript::__set_state(array $properties)
118+
*/
119+
PHP_METHOD(Javascript, __set_state)
120+
{
121+
php_phongo_javascript_t *intern;
122+
HashTable *props;
123+
zval *array;
124+
125+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) {
126+
RETURN_FALSE;
73127
}
74128

75-
php_phongo_new_javascript_from_javascript_and_scope(0, getThis(), javascript, javascript_len, &scope TSRMLS_CC);
76-
bson_destroy(&scope);
129+
object_init_ex(return_value, php_phongo_javascript_ce);
130+
131+
intern = Z_JAVASCRIPT_OBJ_P(return_value);
132+
props = Z_ARRVAL_P(array);
133+
134+
if (!php_phongo_javascript_init_from_hash(intern, props TSRMLS_CC)) {
135+
php_error(E_ERROR, "Invalid serialization data for Javascript object");
136+
}
137+
}
138+
/* }}} */
139+
140+
/* {{{ proto Javascript::__wakeup()
141+
*/
142+
PHP_METHOD(Javascript, __wakeup)
143+
{
144+
php_phongo_javascript_t *intern;
145+
HashTable *props;
146+
147+
if (zend_parse_parameters_none() == FAILURE) {
148+
return;
149+
}
150+
151+
intern = Z_JAVASCRIPT_OBJ_P(getThis());
152+
props = zend_std_get_properties(getThis() TSRMLS_CC);
153+
154+
if (!php_phongo_javascript_init_from_hash(intern, props TSRMLS_CC)) {
155+
php_error(E_ERROR, "Invalid serialization data for Javascript object");
156+
} else {
157+
}
77158
}
78159
/* }}} */
79160

@@ -84,9 +165,17 @@ ZEND_BEGIN_ARG_INFO_EX(ai_Javascript___construct, 0, 0, 1)
84165
ZEND_ARG_INFO(0, scope)
85166
ZEND_END_ARG_INFO();
86167

168+
ZEND_BEGIN_ARG_INFO_EX(ai_Javascript___set_state, 0, 0, 1)
169+
ZEND_ARG_ARRAY_INFO(0, properties, 0)
170+
ZEND_END_ARG_INFO();
171+
172+
ZEND_BEGIN_ARG_INFO_EX(ai_Javascript_void, 0, 0, 0)
173+
ZEND_END_ARG_INFO();
174+
87175
static zend_function_entry php_phongo_javascript_me[] = {
88176
PHP_ME(Javascript, __construct, ai_Javascript___construct, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
89-
PHP_ME(Manager, __wakeUp, NULL, ZEND_ACC_PUBLIC)
177+
PHP_ME(Javascript, __set_state, ai_Javascript___set_state, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
178+
PHP_ME(Javascript, __wakeup, ai_Javascript_void, ZEND_ACC_PUBLIC)
90179
PHP_FE_END
91180
};
92181

@@ -138,42 +227,69 @@ phongo_create_object_retval php_phongo_javascript_create_object(zend_class_entry
138227
#endif
139228
} /* }}} */
140229

141-
HashTable *php_phongo_javascript_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
230+
HashTable *php_phongo_javascript_get_properties(zval *object TSRMLS_DC) /* {{{ */
142231
{
143232
php_phongo_javascript_t *intern;
144-
#if PHP_VERSION_ID >= 70000
145-
zval retval;
146-
#else
147-
zval retval = zval_used_for_init;
148-
#endif
233+
HashTable *props;
234+
235+
intern = Z_JAVASCRIPT_OBJ_P(object);
236+
props = zend_std_get_properties(object TSRMLS_CC);
149237

238+
if (!intern->javascript) {
239+
return props;
240+
}
150241

151-
*is_temp = 1;
152-
intern = Z_JAVASCRIPT_OBJ_P(object);
242+
#if PHP_VERSION_ID >= 70000
243+
{
244+
zval javascript;
153245

154-
array_init(&retval);
246+
ZVAL_STRING(&javascript, intern->javascript);
247+
zend_hash_str_update(props, "javascript", sizeof("javascript")-1, &javascript);
155248

156-
ADD_ASSOC_STRINGL(&retval, "javascript", intern->javascript, intern->javascript_len);
249+
if (intern->document) {
250+
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
157251

158-
if (intern->document) {
159-
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
252+
if (phongo_bson_to_zval_ex(bson_get_data(intern->document), intern->document->len, &state)) {
253+
Z_ADDREF(state.zchild);
254+
zend_hash_str_update(props, "scope", sizeof("scope")-1, &state.zchild);
255+
} else {
256+
zval scope;
160257

161-
if (phongo_bson_to_zval_ex(bson_get_data(intern->document), intern->document->len, &state)) {
162-
#if PHP_VERSION_ID >= 70000
163-
Z_ADDREF(state.zchild);
164-
ADD_ASSOC_ZVAL_EX(&retval, "scope", &state.zchild);
165-
#else
166-
Z_ADDREF_P(state.zchild);
167-
ADD_ASSOC_ZVAL_EX(&retval, "scope", state.zchild);
168-
#endif
169-
} else {
170-
ADD_ASSOC_NULL_EX(&retval, "scope");
258+
ZVAL_NULL(&scope);
259+
zend_hash_str_update(props, "scope", sizeof("scope")-1, &scope);
260+
}
261+
262+
zval_ptr_dtor(&state.zchild);
171263
}
264+
}
265+
#else
266+
{
267+
zval *javascript;
268+
269+
MAKE_STD_ZVAL(javascript);
270+
ZVAL_STRING(javascript, intern->javascript, 1);
271+
zend_hash_update(props, "javascript", sizeof("javascript"), &javascript, sizeof(javascript), NULL);
272+
273+
if (intern->document) {
274+
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
172275

173-
zval_ptr_dtor(&state.zchild);
276+
if (phongo_bson_to_zval_ex(bson_get_data(intern->document), intern->document->len, &state)) {
277+
Z_ADDREF_P(state.zchild);
278+
zend_hash_update(props, "scope", sizeof("scope"), &state.zchild, sizeof(state.zchild), NULL);
279+
} else {
280+
zval *scope;
281+
282+
MAKE_STD_ZVAL(scope);
283+
ZVAL_NULL(scope);
284+
zend_hash_update(props, "scope", sizeof("scope"), &scope, sizeof(scope), NULL);
285+
}
286+
287+
zval_ptr_dtor(&state.zchild);
288+
}
174289
}
290+
#endif
175291

176-
return Z_ARRVAL(retval);
292+
return props;
177293
} /* }}} */
178294
/* }}} */
179295

@@ -188,12 +304,11 @@ PHP_MINIT_FUNCTION(Javascript)
188304
php_phongo_javascript_ce = zend_register_internal_class(&ce TSRMLS_CC);
189305
php_phongo_javascript_ce->create_object = php_phongo_javascript_create_object;
190306
PHONGO_CE_FINAL(php_phongo_javascript_ce);
191-
PHONGO_CE_DISABLE_SERIALIZATION(php_phongo_javascript_ce);
192307

193308
zend_class_implements(php_phongo_javascript_ce TSRMLS_CC, 1, php_phongo_type_ce);
194309

195310
memcpy(&php_phongo_handler_javascript, phongo_get_std_object_handlers(), sizeof(zend_object_handlers));
196-
php_phongo_handler_javascript.get_debug_info = php_phongo_javascript_get_debug_info;
311+
php_phongo_handler_javascript.get_properties = php_phongo_javascript_get_properties;
197312
#if PHP_VERSION_ID >= 70000
198313
php_phongo_handler_javascript.free_obj = php_phongo_javascript_free_object;
199314
php_phongo_handler_javascript.offset = XtOffsetOf(php_phongo_javascript_t, std);

tests/bson/bson-javascript-003.phpt

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
--TEST--
2+
MongoDB\BSON\Javascript 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+
['function foo(bar) { return bar; }', null],
11+
['function foo(bar) { return bar; }', []],
12+
['function foo() { return foo; }', ['foo' => 42]],
13+
['function foo() { return id; }', ['id' => new MongoDB\BSON\ObjectId('53e2a1c40640fd72175d4603')]],
14+
];
15+
16+
foreach ($tests as $test) {
17+
list($code, $scope) = $test;
18+
19+
var_dump($js = new MongoDB\BSON\Javascript($code, $scope));
20+
var_dump($s = serialize($js));
21+
var_dump(unserialize($s));
22+
echo "\n";
23+
}
24+
25+
?>
26+
===DONE===
27+
<?php exit(0); ?>
28+
--EXPECTF--
29+
object(MongoDB\BSON\Javascript)#%d (%d) {
30+
["javascript"]=>
31+
string(33) "function foo(bar) { return bar; }"
32+
}
33+
string(94) "O:23:"MongoDB\BSON\Javascript":1:{s:10:"javascript";s:33:"function foo(bar) { return bar; }";}"
34+
object(MongoDB\BSON\Javascript)#%d (%d) {
35+
["javascript"]=>
36+
string(33) "function foo(bar) { return bar; }"
37+
}
38+
39+
object(MongoDB\BSON\Javascript)#%d (%d) {
40+
["javascript"]=>
41+
string(33) "function foo(bar) { return bar; }"
42+
["scope"]=>
43+
object(stdClass)#%d (%d) {
44+
}
45+
}
46+
string(125) "O:23:"MongoDB\BSON\Javascript":2:{s:10:"javascript";s:33:"function foo(bar) { return bar; }";s:5:"scope";O:8:"stdClass":0:{}}"
47+
object(MongoDB\BSON\Javascript)#%d (%d) {
48+
["javascript"]=>
49+
string(33) "function foo(bar) { return bar; }"
50+
["scope"]=>
51+
object(stdClass)#%d (%d) {
52+
}
53+
}
54+
55+
object(MongoDB\BSON\Javascript)#%d (%d) {
56+
["javascript"]=>
57+
string(30) "function foo() { return foo; }"
58+
["scope"]=>
59+
object(stdClass)#%d (%d) {
60+
["foo"]=>
61+
int(42)
62+
}
63+
}
64+
string(137) "O:23:"MongoDB\BSON\Javascript":2:{s:10:"javascript";s:30:"function foo() { return foo; }";s:5:"scope";O:8:"stdClass":1:{s:3:"foo";i:42;}}"
65+
object(MongoDB\BSON\Javascript)#%d (%d) {
66+
["javascript"]=>
67+
string(30) "function foo() { return foo; }"
68+
["scope"]=>
69+
object(stdClass)#%d (%d) {
70+
["foo"]=>
71+
int(42)
72+
}
73+
}
74+
75+
object(MongoDB\BSON\Javascript)#%d (%d) {
76+
["javascript"]=>
77+
string(29) "function foo() { return id; }"
78+
["scope"]=>
79+
object(stdClass)#%d (%d) {
80+
["id"]=>
81+
object(MongoDB\BSON\ObjectID)#%d (%d) {
82+
["oid"]=>
83+
string(24) "53e2a1c40640fd72175d4603"
84+
}
85+
}
86+
}
87+
string(205) "O:23:"MongoDB\BSON\Javascript":2:{s:10:"javascript";s:29:"function foo() { return id; }";s:5:"scope";O:8:"stdClass":1:{s:2:"id";O:21:"MongoDB\BSON\ObjectID":1:{s:3:"oid";s:24:"53e2a1c40640fd72175d4603";}}}"
88+
object(MongoDB\BSON\Javascript)#%d (%d) {
89+
["javascript"]=>
90+
string(29) "function foo() { return id; }"
91+
["scope"]=>
92+
object(stdClass)#%d (%d) {
93+
["id"]=>
94+
object(MongoDB\BSON\ObjectID)#%d (%d) {
95+
["oid"]=>
96+
string(24) "53e2a1c40640fd72175d4603"
97+
}
98+
}
99+
}
100+
101+
===DONE===

0 commit comments

Comments
 (0)