Skip to content

Commit f937228

Browse files
committed
Merge pull request #504
2 parents d65aa7a + 84dbfe0 commit f937228

File tree

3 files changed

+117
-80
lines changed

3 files changed

+117
-80
lines changed

src/bson.c

Lines changed: 28 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,44 +1271,6 @@ static void phongo_bson_append(bson_t *bson, php_phongo_bson_flags_t flags, cons
12711271
}
12721272
}
12731273

1274-
#if PHP_VERSION_ID >= 70000
1275-
static bool is_public_property(zend_class_entry *ce, zend_string *name, zend_string **member TSRMLS_DC) /* {{{ */
1276-
#else
1277-
static bool is_public_property(zend_class_entry *ce, const char *prop_name, int prop_name_len TSRMLS_DC) /* {{{ */
1278-
#endif
1279-
{
1280-
zend_property_info *property_info = NULL;
1281-
1282-
#if PHP_VERSION_ID >= 70000
1283-
if (ZSTR_VAL(name)[0] == 0) {
1284-
const char *prop_name,
1285-
*class_name;
1286-
size_t prop_name_len;
1287-
1288-
zend_unmangle_property_name_ex(name,
1289-
&class_name, &prop_name, &prop_name_len);
1290-
(*member) = zend_string_init(prop_name, prop_name_len, 0);
1291-
} else (*member) = zend_string_copy(name);
1292-
property_info = zend_get_property_info(ce, (*member), 1 TSRMLS_CC);
1293-
1294-
if (!property_info) /* undefined property */
1295-
return true;
1296-
1297-
if (property_info == ZEND_WRONG_PROPERTY_INFO) {
1298-
return false;
1299-
}
1300-
1301-
return (property_info->flags & ZEND_ACC_PUBLIC);
1302-
#else
1303-
zval member;
1304-
ZVAL_STRINGL(&member, prop_name, prop_name_len, 0);
1305-
property_info = zend_get_property_info(ce, &member, 1 TSRMLS_CC);
1306-
1307-
return (property_info && (property_info->flags & ZEND_ACC_PUBLIC));
1308-
#endif
1309-
}
1310-
/* }}} */
1311-
13121274
void phongo_zval_to_bson(zval *data, php_phongo_bson_flags_t flags, bson_t *bson, bson_t **bson_out TSRMLS_DC) /* {{{ */
13131275
{
13141276
HashTable *ht_data = NULL;
@@ -1422,49 +1384,37 @@ void phongo_zval_to_bson(zval *data, php_phongo_bson_flags_t flags, bson_t *bson
14221384
zval *value;
14231385

14241386
ZEND_HASH_FOREACH_KEY_VAL(ht_data, num_key, string_key, value) {
1425-
/* Ensure we're working with a string key */
1426-
if (!string_key) {
1427-
string_key = zend_long_to_str(num_key);
1428-
} else {
1429-
zend_string_addref(string_key);
1430-
}
1431-
1432-
if (Z_TYPE_P(data) == IS_OBJECT) {
1433-
zend_string *member = NULL;
1434-
1435-
/* Ignore non-public properties */
1436-
if (!instanceof_function(Z_OBJCE_P(data), php_phongo_serializable_ce) &&
1437-
!is_public_property(Z_OBJCE_P(data), string_key, &member TSRMLS_CC)) {
1438-
if (member) {
1439-
zend_string_release(member);
1440-
}
1441-
zend_string_release(string_key);
1442-
continue;
1443-
}
1444-
1445-
if (flags & PHONGO_BSON_ADD_ID) {
1446-
if (!strcmp(member ? ZSTR_VAL(member) : ZSTR_VAL(string_key), "_id")) {
1447-
flags &= ~PHONGO_BSON_ADD_ID;
1387+
if (string_key) {
1388+
if (ht_data_from_properties) {
1389+
/* Skip protected and private properties */
1390+
if (ZSTR_VAL(string_key)[0] == '\0' && ZSTR_LEN(string_key) > 0) {
1391+
zend_string_release(string_key);
1392+
continue;
14481393
}
14491394
}
14501395

1451-
phongo_bson_append(bson, flags & ~PHONGO_BSON_ADD_ID,
1452-
member ? ZSTR_VAL(member) : ZSTR_VAL(string_key),
1453-
member ? ZSTR_LEN(member) : ZSTR_LEN(string_key),
1454-
value TSRMLS_CC);
1396+
if (strlen(ZSTR_VAL(string_key)) != ZSTR_LEN(string_key)) {
1397+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "BSON keys cannot contain null bytes. Unexpected null byte after \"%s\".", ZSTR_VAL(string_key));
14551398

1456-
if (member) {
1457-
zend_string_release(member);
1399+
return;
14581400
}
1459-
} else {
1401+
14601402
if (flags & PHONGO_BSON_ADD_ID) {
14611403
if (!strcmp(ZSTR_VAL(string_key), "_id")) {
14621404
flags &= ~PHONGO_BSON_ADD_ID;
14631405
}
14641406
}
1465-
phongo_bson_append(bson, flags & ~PHONGO_BSON_ADD_ID, ZSTR_VAL(string_key), ZSTR_LEN(string_key), value TSRMLS_CC);
14661407
}
14671408

1409+
/* Ensure we're working with a string key */
1410+
if (!string_key) {
1411+
string_key = zend_long_to_str(num_key);
1412+
} else {
1413+
zend_string_addref(string_key);
1414+
}
1415+
1416+
phongo_bson_append(bson, flags & ~PHONGO_BSON_ADD_ID, ZSTR_VAL(string_key), strlen(ZSTR_VAL(string_key)), value TSRMLS_CC);
1417+
14681418
zend_string_release(string_key);
14691419
} ZEND_HASH_FOREACH_END();
14701420
}
@@ -1489,17 +1439,16 @@ void phongo_zval_to_bson(zval *data, php_phongo_bson_flags_t flags, bson_t *bson
14891439

14901440
if (hash_type == HASH_KEY_IS_STRING) {
14911441
if (ht_data_from_properties) {
1492-
const char *class_name;
1493-
zend_unmangle_property_name(string_key, string_key_len-1, &class_name, (const char **)&string_key);
1494-
string_key_len = strlen(string_key);
1495-
1496-
/* Ignore non-public properties */
1497-
if (!is_public_property(Z_OBJCE_P(data), string_key, string_key_len TSRMLS_CC)) {
1442+
/* Skip protected and private properties */
1443+
if (string_key[0] == '\0' && string_key_len > 1) {
14981444
continue;
14991445
}
1500-
} else {
1501-
/* Chop off the \0 from string lengths */
1502-
string_key_len -= 1;
1446+
}
1447+
1448+
if (strlen(string_key) != string_key_len - 1) {
1449+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "BSON keys cannot contain null bytes. Unexpected null byte after \"%s\".", ZSTR_VAL(string_key));
1450+
1451+
return;
15031452
}
15041453

15051454
if (flags & PHONGO_BSON_ADD_ID) {
@@ -1512,10 +1461,9 @@ void phongo_zval_to_bson(zval *data, php_phongo_bson_flags_t flags, bson_t *bson
15121461
/* Ensure we're working with a string key */
15131462
if (hash_type == HASH_KEY_IS_LONG) {
15141463
spprintf(&string_key, 0, "%ld", num_key);
1515-
string_key_len = strlen(string_key);
15161464
}
15171465

1518-
phongo_bson_append(bson, flags & ~PHONGO_BSON_ADD_ID, string_key, string_key_len, *value TSRMLS_CC);
1466+
phongo_bson_append(bson, flags & ~PHONGO_BSON_ADD_ID, string_key, strlen(string_key), *value TSRMLS_CC);
15191467

15201468
if (hash_type == HASH_KEY_IS_LONG) {
15211469
efree(string_key);

tests/bson/bson-fromPHP-005.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
BSON\fromPHP(): PHP document with public property whose name is an empty string
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/tools.php';
7+
8+
$tests = [
9+
['' => 1],
10+
(object) ['' => 1],
11+
];
12+
13+
foreach ($tests as $document) {
14+
$s = fromPHP($document);
15+
echo "Test ", toJSON($s), "\n";
16+
hex_dump($s);
17+
}
18+
19+
?>
20+
===DONE===
21+
<?php exit(0); ?>
22+
--EXPECT--
23+
Test { "" : 1 }
24+
0 : 0b 00 00 00 10 00 01 00 00 00 00 [...........]
25+
Test { "" : 1 }
26+
0 : 0b 00 00 00 10 00 01 00 00 00 00 [...........]
27+
===DONE===

tests/bson/bson-fromPHP-006.phpt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
--TEST--
2+
BSON\fromPHP(): PHP documents with null bytes in field name
3+
--FILE--
4+
<?php
5+
6+
require_once __DIR__ . '/../utils/tools.php';
7+
8+
echo "\nTesting array with one leading null byte in field name\n";
9+
echo throws(function() {
10+
fromPHP(["\0" => 1]);
11+
}, 'MongoDB\Driver\Exception\UnexpectedValueException'), "\n";
12+
13+
echo "\nTesting array with one trailing null byte in field name\n";
14+
echo throws(function() {
15+
fromPHP(["a\0" => 1]);
16+
}, 'MongoDB\Driver\Exception\UnexpectedValueException'), "\n";
17+
18+
echo "\nTesting array with multiple null bytes in field name\n";
19+
echo throws(function() {
20+
fromPHP(["\0\0\0" => 1]);
21+
}, 'MongoDB\Driver\Exception\UnexpectedValueException'), "\n";
22+
23+
/* Per PHPC-884, field names with a leading null byte are ignored when encoding
24+
* a document from an object's property hash table, since PHP uses leading bytes
25+
* to denote protected and private properties. */
26+
echo "\nTesting object with one leading null byte in field name\n";
27+
hex_dump(fromPHP((object) ["\0" => 1]));
28+
29+
echo "\nTesting object with one trailing null byte in field name\n";
30+
echo throws(function() {
31+
fromPHP((object) ["a\0" => 1]);
32+
}, 'MongoDB\Driver\Exception\UnexpectedValueException'), "\n";
33+
34+
echo "\nTesting object with multiple null bytes in field name\n";
35+
hex_dump(fromPHP((object) ["\0\0\0" => 1]));
36+
37+
?>
38+
===DONE===
39+
<?php exit(0); ?>
40+
--EXPECT--
41+
Testing array with one leading null byte in field name
42+
OK: Got MongoDB\Driver\Exception\UnexpectedValueException
43+
BSON keys cannot contain null bytes. Unexpected null byte after "".
44+
45+
Testing array with one trailing null byte in field name
46+
OK: Got MongoDB\Driver\Exception\UnexpectedValueException
47+
BSON keys cannot contain null bytes. Unexpected null byte after "a".
48+
49+
Testing array with multiple null bytes in field name
50+
OK: Got MongoDB\Driver\Exception\UnexpectedValueException
51+
BSON keys cannot contain null bytes. Unexpected null byte after "".
52+
53+
Testing object with one leading null byte in field name
54+
0 : 05 00 00 00 00 [.....]
55+
56+
Testing object with one trailing null byte in field name
57+
OK: Got MongoDB\Driver\Exception\UnexpectedValueException
58+
BSON keys cannot contain null bytes. Unexpected null byte after "a".
59+
60+
Testing object with multiple null bytes in field name
61+
0 : 05 00 00 00 00 [.....]
62+
===DONE===

0 commit comments

Comments
 (0)