1 /*
2 * Copyright 2014-2017 MongoDB, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <php.h>
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "phongo_compat.h"
24 #include "php_phongo.h"
25 #include "php_bson.h"
26
27 typedef enum {
28 PHONGO_JSON_MODE_LEGACY,
29 PHONGO_JSON_MODE_CANONICAL,
30 PHONGO_JSON_MODE_RELAXED,
31 } php_phongo_json_mode_t;
32
33 /* {{{ proto string MongoDB\BSON\fromPHP(array|object $value)
34 Returns the BSON representation of a PHP value */
PHP_FUNCTION(MongoDB_BSON_fromPHP)35 PHP_FUNCTION(MongoDB_BSON_fromPHP)
36 {
37 zend_error_handling error_handling;
38 zval* data;
39 bson_t* bson;
40
41 zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
42 if (zend_parse_parameters(ZEND_NUM_ARGS(), "A", &data) == FAILURE) {
43 zend_restore_error_handling(&error_handling);
44 return;
45 }
46 zend_restore_error_handling(&error_handling);
47
48 bson = bson_new();
49 php_phongo_zval_to_bson(data, PHONGO_BSON_NONE, bson, NULL);
50
51 RETVAL_STRINGL((const char*) bson_get_data(bson), bson->len);
52 bson_destroy(bson);
53 } /* }}} */
54
55 /* {{{ proto array|object MongoDB\BSON\toPHP(string $bson [, array $typemap = array()])
56 Returns the PHP representation of a BSON value, optionally converting it into a custom class */
PHP_FUNCTION(MongoDB_BSON_toPHP)57 PHP_FUNCTION(MongoDB_BSON_toPHP)
58 {
59 zend_error_handling error_handling;
60 char* data;
61 size_t data_len;
62 zval* typemap = NULL;
63 php_phongo_bson_state state;
64
65 PHONGO_BSON_INIT_STATE(state);
66
67 zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
68 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a!", &data, &data_len, &typemap) == FAILURE) {
69 zend_restore_error_handling(&error_handling);
70 return;
71 }
72 zend_restore_error_handling(&error_handling);
73
74 if (!php_phongo_bson_typemap_to_state(typemap, &state.map)) {
75 return;
76 }
77
78 if (!php_phongo_bson_to_zval_ex((const unsigned char*) data, data_len, &state)) {
79 zval_ptr_dtor(&state.zchild);
80 php_phongo_bson_typemap_dtor(&state.map);
81 RETURN_NULL();
82 }
83
84 php_phongo_bson_typemap_dtor(&state.map);
85
86 RETURN_ZVAL(&state.zchild, 0, 1);
87 } /* }}} */
88
89 /* {{{ proto string MongoDB\BSON\fromJSON(string $json)
90 Returns the BSON representation of a JSON value */
PHP_FUNCTION(MongoDB_BSON_fromJSON)91 PHP_FUNCTION(MongoDB_BSON_fromJSON)
92 {
93 zend_error_handling error_handling;
94 char* json;
95 size_t json_len;
96 bson_t bson = BSON_INITIALIZER;
97 bson_error_t error = { 0 };
98
99 zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
100 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &json, &json_len) == FAILURE) {
101 zend_restore_error_handling(&error_handling);
102 return;
103 }
104 zend_restore_error_handling(&error_handling);
105
106 if (bson_init_from_json(&bson, (const char*) json, json_len, &error)) {
107 RETVAL_STRINGL((const char*) bson_get_data(&bson), bson.len);
108 bson_destroy(&bson);
109 } else {
110 phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "%s", error.domain == BSON_ERROR_JSON ? error.message : "Error parsing JSON");
111 }
112 } /* }}} */
113
phongo_bson_to_json(INTERNAL_FUNCTION_PARAMETERS,php_phongo_json_mode_t mode)114 static void phongo_bson_to_json(INTERNAL_FUNCTION_PARAMETERS, php_phongo_json_mode_t mode)
115 {
116 zend_error_handling error_handling;
117 char* data;
118 size_t data_len;
119 const bson_t* bson;
120 bool eof = false;
121 bson_reader_t* reader;
122 char* json = NULL;
123 size_t json_len;
124
125 zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
126 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &data_len) == FAILURE) {
127 zend_restore_error_handling(&error_handling);
128 return;
129 }
130 zend_restore_error_handling(&error_handling);
131
132 reader = bson_reader_new_from_data((const unsigned char*) data, data_len);
133 bson = bson_reader_read(reader, NULL);
134
135 if (!bson) {
136 phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Could not read document from BSON reader");
137 bson_reader_destroy(reader);
138 return;
139 }
140
141 if (mode == PHONGO_JSON_MODE_LEGACY) {
142 json = bson_as_json(bson, &json_len);
143 } else if (mode == PHONGO_JSON_MODE_CANONICAL) {
144 json = bson_as_canonical_extended_json(bson, &json_len);
145 } else if (mode == PHONGO_JSON_MODE_RELAXED) {
146 json = bson_as_relaxed_extended_json(bson, &json_len);
147 }
148
149 if (!json) {
150 phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Could not convert BSON document to a JSON string");
151 bson_reader_destroy(reader);
152 return;
153 }
154
155 RETVAL_STRINGL(json, json_len);
156 bson_free(json);
157
158 if (bson_reader_read(reader, &eof) || !eof) {
159 phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Reading document did not exhaust input buffer");
160 }
161
162 bson_reader_destroy(reader);
163 } /* }}} */
164
165 /* {{{ proto string MongoDB\BSON\toJSON(string $bson)
166 Returns the legacy extended JSON representation of a BSON value */
PHP_FUNCTION(MongoDB_BSON_toJSON)167 PHP_FUNCTION(MongoDB_BSON_toJSON)
168 {
169 phongo_bson_to_json(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHONGO_JSON_MODE_LEGACY);
170 } /* }}} */
171
172 /* {{{ proto string MongoDB\BSON\toCanonicalExtendedJSON(string $bson)
173 Returns the canonical extended JSON representation of a BSON value */
PHP_FUNCTION(MongoDB_BSON_toCanonicalExtendedJSON)174 PHP_FUNCTION(MongoDB_BSON_toCanonicalExtendedJSON)
175 {
176 phongo_bson_to_json(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHONGO_JSON_MODE_CANONICAL);
177 } /* }}} */
178
179 /* {{{ proto string MongoDB\BSON\toRelaxedExtendedJSON(string $bson)
180 Returns the relaxed extended JSON representation of a BSON value */
PHP_FUNCTION(MongoDB_BSON_toRelaxedExtendedJSON)181 PHP_FUNCTION(MongoDB_BSON_toRelaxedExtendedJSON)
182 {
183 phongo_bson_to_json(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHONGO_JSON_MODE_RELAXED);
184 } /* }}} */
185
186 /*
187 * Local variables:
188 * tab-width: 4
189 * c-basic-offset: 4
190 * End:
191 * vim600: noet sw=4 ts=4 fdm=marker
192 * vim<600: noet sw=4 ts=4
193 */
194