1 /*
2  * Copyright 2018-present 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 #include <Zend/zend_interfaces.h>
19 #include <ext/standard/php_var.h>
20 #include <zend_smart_str.h>
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "phongo_compat.h"
27 #include "php_phongo.h"
28 #include "php_bson.h"
29 
30 zend_class_entry* php_phongo_int64_ce;
31 
32 /* Initialize the object and return whether it was successful. */
php_phongo_int64_init(php_phongo_int64_t * intern,int64_t integer)33 static bool php_phongo_int64_init(php_phongo_int64_t* intern, int64_t integer) /* {{{ */
34 {
35 	intern->integer     = integer;
36 	intern->initialized = true;
37 
38 	return true;
39 } /* }}} */
40 
41 /* Initialize the object from a numeric string and return whether it was
42  * successful. An exception will be thrown on error. */
php_phongo_int64_init_from_string(php_phongo_int64_t * intern,const char * s_integer,size_t s_integer_len)43 static bool php_phongo_int64_init_from_string(php_phongo_int64_t* intern, const char* s_integer, size_t s_integer_len) /* {{{ */
44 {
45 	int64_t integer;
46 
47 	if (!php_phongo_parse_int64(&integer, s_integer, s_integer_len)) {
48 		phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Error parsing \"%s\" as 64-bit integer for %s initialization", s_integer, ZSTR_VAL(php_phongo_int64_ce->name));
49 		return false;
50 	}
51 
52 	return php_phongo_int64_init(intern, integer);
53 } /* }}} */
54 
55 /* Initialize the object from a HashTable and return whether it was successful.
56  * An exception will be thrown on error. */
php_phongo_int64_init_from_hash(php_phongo_int64_t * intern,HashTable * props)57 static bool php_phongo_int64_init_from_hash(php_phongo_int64_t* intern, HashTable* props) /* {{{ */
58 {
59 	zval* value;
60 
61 	if ((value = zend_hash_str_find(props, "integer", sizeof("integer") - 1)) && Z_TYPE_P(value) == IS_STRING) {
62 		return php_phongo_int64_init_from_string(intern, Z_STRVAL_P(value), Z_STRLEN_P(value));
63 	}
64 
65 	phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "%s initialization requires \"integer\" string field", ZSTR_VAL(php_phongo_int64_ce->name));
66 	return false;
67 } /* }}} */
68 
69 /* {{{ proto string MongoDB\BSON\Int64::__toString()
70    Return the Int64's value as a string. */
PHP_METHOD(Int64,__toString)71 static PHP_METHOD(Int64, __toString)
72 {
73 	zend_error_handling error_handling;
74 	php_phongo_int64_t* intern;
75 
76 	zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
77 	if (zend_parse_parameters_none() == FAILURE) {
78 		zend_restore_error_handling(&error_handling);
79 		return;
80 	}
81 	zend_restore_error_handling(&error_handling);
82 
83 	intern = Z_INT64_OBJ_P(getThis());
84 
85 	ZVAL_INT64_STRING(return_value, intern->integer);
86 } /* }}} */
87 
88 /* {{{ proto array MongoDB\BSON\Int64::jsonSerialize()
89 */
PHP_METHOD(Int64,jsonSerialize)90 static PHP_METHOD(Int64, jsonSerialize)
91 {
92 	zend_error_handling error_handling;
93 	php_phongo_int64_t* intern;
94 
95 	zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
96 	if (zend_parse_parameters_none() == FAILURE) {
97 		zend_restore_error_handling(&error_handling);
98 		return;
99 	}
100 	zend_restore_error_handling(&error_handling);
101 
102 	intern = Z_INT64_OBJ_P(getThis());
103 
104 	array_init_size(return_value, 1);
105 
106 	ADD_ASSOC_INT64_AS_STRING(return_value, "$numberLong", intern->integer);
107 } /* }}} */
108 
109 /* {{{ proto string MongoDB\BSON\Int64::serialize()
110 */
PHP_METHOD(Int64,serialize)111 static PHP_METHOD(Int64, serialize)
112 {
113 	zend_error_handling  error_handling;
114 	php_phongo_int64_t*  intern;
115 	zval                 retval;
116 	php_serialize_data_t var_hash;
117 	smart_str            buf = { 0 };
118 
119 	zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
120 	if (zend_parse_parameters_none() == FAILURE) {
121 		zend_restore_error_handling(&error_handling);
122 		return;
123 	}
124 	zend_restore_error_handling(&error_handling);
125 
126 	intern = Z_INT64_OBJ_P(getThis());
127 
128 	array_init_size(&retval, 1);
129 	ADD_ASSOC_INT64_AS_STRING(&retval, "integer", intern->integer);
130 
131 	PHP_VAR_SERIALIZE_INIT(var_hash);
132 	php_var_serialize(&buf, &retval, &var_hash);
133 	smart_str_0(&buf);
134 	PHP_VAR_SERIALIZE_DESTROY(var_hash);
135 
136 	PHONGO_RETVAL_SMART_STR(buf);
137 
138 	smart_str_free(&buf);
139 	zval_ptr_dtor(&retval);
140 } /* }}} */
141 
142 /* {{{ proto void MongoDB\BSON\Int64::unserialize(string $serialized)
143 */
PHP_METHOD(Int64,unserialize)144 static PHP_METHOD(Int64, unserialize)
145 {
146 	zend_error_handling    error_handling;
147 	php_phongo_int64_t*    intern;
148 	char*                  serialized;
149 	size_t                 serialized_len;
150 	zval                   props;
151 	php_unserialize_data_t var_hash;
152 
153 	intern = Z_INT64_OBJ_P(getThis());
154 
155 	zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
156 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &serialized, &serialized_len) == FAILURE) {
157 		zend_restore_error_handling(&error_handling);
158 		return;
159 	}
160 	zend_restore_error_handling(&error_handling);
161 
162 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
163 	if (!php_var_unserialize(&props, (const unsigned char**) &serialized, (unsigned char*) serialized + serialized_len, &var_hash)) {
164 		zval_ptr_dtor(&props);
165 		phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "%s unserialization failed", ZSTR_VAL(php_phongo_int64_ce->name));
166 
167 		PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
168 		return;
169 	}
170 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
171 
172 	php_phongo_int64_init_from_hash(intern, HASH_OF(&props));
173 	zval_ptr_dtor(&props);
174 } /* }}} */
175 
176 /* {{{ MongoDB\BSON\Int64 function entries */
177 ZEND_BEGIN_ARG_INFO_EX(ai_Int64_unserialize, 0, 0, 1)
178 	ZEND_ARG_INFO(0, serialized)
179 ZEND_END_ARG_INFO()
180 
181 ZEND_BEGIN_ARG_INFO_EX(ai_Int64_void, 0, 0, 0)
182 ZEND_END_ARG_INFO()
183 
184 static zend_function_entry php_phongo_int64_me[] = {
185 	/* clang-format off */
186 	/* __set_state intentionally missing */
187 	PHP_ME(Int64, __toString, ai_Int64_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
188 	PHP_ME(Int64, jsonSerialize, ai_Int64_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
189 	PHP_ME(Int64, serialize, ai_Int64_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
190 	PHP_ME(Int64, unserialize, ai_Int64_unserialize, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
191 	ZEND_NAMED_ME(__construct, PHP_FN(MongoDB_disabled___construct), ai_Int64_void, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)
192 	PHP_FE_END
193 	/* clang-format on */
194 };
195 /* }}} */
196 
197 /* {{{ MongoDB\BSON\Int64 object handlers */
198 static zend_object_handlers php_phongo_handler_int64;
199 
php_phongo_int64_free_object(zend_object * object)200 static void php_phongo_int64_free_object(zend_object* object) /* {{{ */
201 {
202 	php_phongo_int64_t* intern = Z_OBJ_INT64(object);
203 
204 	zend_object_std_dtor(&intern->std);
205 
206 	if (intern->properties) {
207 		zend_hash_destroy(intern->properties);
208 		FREE_HASHTABLE(intern->properties);
209 	}
210 } /* }}} */
211 
php_phongo_int64_create_object(zend_class_entry * class_type)212 zend_object* php_phongo_int64_create_object(zend_class_entry* class_type) /* {{{ */
213 {
214 	php_phongo_int64_t* intern = NULL;
215 
216 	intern = PHONGO_ALLOC_OBJECT_T(php_phongo_int64_t, class_type);
217 	zend_object_std_init(&intern->std, class_type);
218 	object_properties_init(&intern->std, class_type);
219 
220 	intern->std.handlers = &php_phongo_handler_int64;
221 
222 	return &intern->std;
223 } /* }}} */
224 
php_phongo_int64_clone_object(phongo_compat_object_handler_type * object)225 static zend_object* php_phongo_int64_clone_object(phongo_compat_object_handler_type* object) /* {{{ */
226 {
227 	php_phongo_int64_t* intern;
228 	php_phongo_int64_t* new_intern;
229 	zend_object*        new_object;
230 
231 	intern     = Z_OBJ_INT64(PHONGO_COMPAT_GET_OBJ(object));
232 	new_object = php_phongo_int64_create_object(PHONGO_COMPAT_GET_OBJ(object)->ce);
233 
234 	new_intern = Z_OBJ_INT64(new_object);
235 	zend_objects_clone_members(&new_intern->std, &intern->std);
236 
237 	php_phongo_int64_init(new_intern, intern->integer);
238 
239 	return new_object;
240 } /* }}} */
241 
php_phongo_int64_compare_objects(zval * o1,zval * o2)242 static int php_phongo_int64_compare_objects(zval* o1, zval* o2) /* {{{ */
243 {
244 	php_phongo_int64_t *intern1, *intern2;
245 
246 	ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2);
247 
248 	intern1 = Z_INT64_OBJ_P(o1);
249 	intern2 = Z_INT64_OBJ_P(o2);
250 
251 	if (intern1->integer != intern2->integer) {
252 		return intern1->integer < intern2->integer ? -1 : 1;
253 	}
254 
255 	return 0;
256 } /* }}} */
257 
php_phongo_int64_get_properties_hash(phongo_compat_object_handler_type * object,bool is_debug)258 HashTable* php_phongo_int64_get_properties_hash(phongo_compat_object_handler_type* object, bool is_debug) /* {{{ */
259 {
260 	php_phongo_int64_t* intern;
261 	HashTable*          props;
262 
263 	intern = Z_OBJ_INT64(PHONGO_COMPAT_GET_OBJ(object));
264 
265 	PHONGO_GET_PROPERTY_HASH_INIT_PROPS(is_debug, intern, props, 2);
266 
267 	if (!intern->initialized) {
268 		return props;
269 	}
270 
271 	{
272 		zval value;
273 
274 		ZVAL_INT64_STRING(&value, intern->integer);
275 		zend_hash_str_update(props, "integer", sizeof("integer") - 1, &value);
276 	}
277 
278 	return props;
279 } /* }}} */
280 
php_phongo_int64_get_debug_info(phongo_compat_object_handler_type * object,int * is_temp)281 static HashTable* php_phongo_int64_get_debug_info(phongo_compat_object_handler_type* object, int* is_temp) /* {{{ */
282 {
283 	*is_temp = 1;
284 	return php_phongo_int64_get_properties_hash(object, true);
285 } /* }}} */
286 
php_phongo_int64_get_properties(phongo_compat_object_handler_type * object)287 static HashTable* php_phongo_int64_get_properties(phongo_compat_object_handler_type* object) /* {{{ */
288 {
289 	return php_phongo_int64_get_properties_hash(object, false);
290 } /* }}} */
291 /* }}} */
292 
php_phongo_int64_init_ce(INIT_FUNC_ARGS)293 void php_phongo_int64_init_ce(INIT_FUNC_ARGS) /* {{{ */
294 {
295 	zend_class_entry ce;
296 
297 	INIT_NS_CLASS_ENTRY(ce, "MongoDB\\BSON", "Int64", php_phongo_int64_me);
298 	php_phongo_int64_ce                = zend_register_internal_class(&ce);
299 	php_phongo_int64_ce->create_object = php_phongo_int64_create_object;
300 	PHONGO_CE_FINAL(php_phongo_int64_ce);
301 
302 	zend_class_implements(php_phongo_int64_ce, 1, php_phongo_json_serializable_ce);
303 	zend_class_implements(php_phongo_int64_ce, 1, php_phongo_type_ce);
304 	zend_class_implements(php_phongo_int64_ce, 1, zend_ce_serializable);
305 
306 	memcpy(&php_phongo_handler_int64, phongo_get_std_object_handlers(), sizeof(zend_object_handlers));
307 	PHONGO_COMPAT_SET_COMPARE_OBJECTS_HANDLER(int64);
308 	php_phongo_handler_int64.clone_obj      = php_phongo_int64_clone_object;
309 	php_phongo_handler_int64.get_debug_info = php_phongo_int64_get_debug_info;
310 	php_phongo_handler_int64.get_properties = php_phongo_int64_get_properties;
311 	php_phongo_handler_int64.free_obj       = php_phongo_int64_free_object;
312 	php_phongo_handler_int64.offset         = XtOffsetOf(php_phongo_int64_t, std);
313 } /* }}} */
314 
315 /*
316  * Local variables:
317  * tab-width: 4
318  * c-basic-offset: 4
319  * End:
320  * vim600: noet sw=4 ts=4 fdm=marker
321  * vim<600: noet sw=4 ts=4
322  */
323