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