1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 #include "protobuf.h"
32
33 #include <zend_hash.h>
34
35 ZEND_DECLARE_MODULE_GLOBALS(protobuf)
36 static PHP_GINIT_FUNCTION(protobuf);
37 static PHP_GSHUTDOWN_FUNCTION(protobuf);
38 static PHP_RINIT_FUNCTION(protobuf);
39 static PHP_RSHUTDOWN_FUNCTION(protobuf);
40 static PHP_MINIT_FUNCTION(protobuf);
41 static PHP_MSHUTDOWN_FUNCTION(protobuf);
42
43 // Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
44 // instances.
45 static HashTable* upb_def_to_php_obj_map;
46 // Global map from message/enum's php class entry to corresponding wrapper
47 // Descriptor/EnumDescriptor instances.
48 static HashTable* ce_to_php_obj_map;
49 // Global map from message/enum's proto fully-qualified name to corresponding
50 // wrapper Descriptor/EnumDescriptor instances.
51 static HashTable* proto_to_php_obj_map;
52 static HashTable* reserved_names;
53
54 // -----------------------------------------------------------------------------
55 // Global maps.
56 // -----------------------------------------------------------------------------
57
add_to_table(HashTable * t,const void * def,void * value)58 static void add_to_table(HashTable* t, const void* def, void* value) {
59 uint nIndex = (ulong)def & t->nTableMask;
60
61 zval* pDest = NULL;
62 php_proto_zend_hash_index_update_mem(t, (zend_ulong)def, &value,
63 sizeof(zval*), (void**)&pDest);
64 }
65
get_from_table(const HashTable * t,const void * def)66 static void* get_from_table(const HashTable* t, const void* def) {
67 void** value;
68 if (php_proto_zend_hash_index_find_mem(t, (zend_ulong)def, (void**)&value) ==
69 FAILURE) {
70 return NULL;
71 }
72 return *value;
73 }
74
exist_in_table(const HashTable * t,const void * def)75 static bool exist_in_table(const HashTable* t, const void* def) {
76 void** value;
77 return (php_proto_zend_hash_index_find_mem(t, (zend_ulong)def,
78 (void**)&value) == SUCCESS);
79 }
80
add_to_list(HashTable * t,void * value)81 static void add_to_list(HashTable* t, void* value) {
82 zval* pDest = NULL;
83 php_proto_zend_hash_next_index_insert_mem(t, &value, sizeof(void*),
84 (void**)&pDest);
85 }
86
add_to_strtable(HashTable * t,const char * key,int key_size,void * value)87 static void add_to_strtable(HashTable* t, const char* key, int key_size,
88 void* value) {
89 zval* pDest = NULL;
90 php_proto_zend_hash_update_mem(t, key, key_size, &value, sizeof(void*),
91 (void**)&pDest);
92 }
93
get_from_strtable(const HashTable * t,const char * key,int key_size)94 static void* get_from_strtable(const HashTable* t, const char* key, int key_size) {
95 void** value;
96 if (php_proto_zend_hash_find_mem(t, key, key_size, (void**)&value) ==
97 FAILURE) {
98 return NULL;
99 }
100 return *value;
101 }
102
add_def_obj(const void * def,PHP_PROTO_HASHTABLE_VALUE value)103 void add_def_obj(const void* def, PHP_PROTO_HASHTABLE_VALUE value) {
104 #if PHP_MAJOR_VERSION < 7
105 Z_ADDREF_P(value);
106 #else
107 GC_ADDREF(value);
108 #endif
109 add_to_table(upb_def_to_php_obj_map, def, value);
110 }
111
get_def_obj(const void * def)112 PHP_PROTO_HASHTABLE_VALUE get_def_obj(const void* def) {
113 return (PHP_PROTO_HASHTABLE_VALUE)get_from_table(upb_def_to_php_obj_map, def);
114 }
115
add_ce_obj(const void * ce,PHP_PROTO_HASHTABLE_VALUE value)116 void add_ce_obj(const void* ce, PHP_PROTO_HASHTABLE_VALUE value) {
117 #if PHP_MAJOR_VERSION < 7
118 Z_ADDREF_P(value);
119 #else
120 GC_ADDREF(value);
121 #endif
122 add_to_table(ce_to_php_obj_map, ce, value);
123 }
124
get_ce_obj(const void * ce)125 PHP_PROTO_HASHTABLE_VALUE get_ce_obj(const void* ce) {
126 return (PHP_PROTO_HASHTABLE_VALUE)get_from_table(ce_to_php_obj_map, ce);
127 }
128
class_added(const void * ce)129 bool class_added(const void* ce) {
130 return exist_in_table(ce_to_php_obj_map, ce);
131 }
132
add_proto_obj(const char * proto,PHP_PROTO_HASHTABLE_VALUE value)133 void add_proto_obj(const char* proto, PHP_PROTO_HASHTABLE_VALUE value) {
134 #if PHP_MAJOR_VERSION < 7
135 Z_ADDREF_P(value);
136 #else
137 GC_ADDREF(value);
138 #endif
139 add_to_strtable(proto_to_php_obj_map, proto, strlen(proto), value);
140 }
141
get_proto_obj(const char * proto)142 PHP_PROTO_HASHTABLE_VALUE get_proto_obj(const char* proto) {
143 return (PHP_PROTO_HASHTABLE_VALUE)get_from_strtable(proto_to_php_obj_map,
144 proto, strlen(proto));
145 }
146
147 // -----------------------------------------------------------------------------
148 // Well Known Types.
149 // -----------------------------------------------------------------------------
150
151 bool is_inited_file_any;
152 bool is_inited_file_api;
153 bool is_inited_file_duration;
154 bool is_inited_file_field_mask;
155 bool is_inited_file_empty;
156 bool is_inited_file_source_context;
157 bool is_inited_file_struct;
158 bool is_inited_file_timestamp;
159 bool is_inited_file_type;
160 bool is_inited_file_wrappers;
161
162 // -----------------------------------------------------------------------------
163 // Reserved Name.
164 // -----------------------------------------------------------------------------
165
166 // Although we already have kReservedNames, we still add them to hash table to
167 // speed up look up.
168 const char *const kReservedNames[] = {
169 "abstract", "and", "array", "as", "break",
170 "callable", "case", "catch", "class", "clone",
171 "const", "continue", "declare", "default", "die",
172 "do", "echo", "else", "elseif", "empty",
173 "enddeclare", "endfor", "endforeach", "endif", "endswitch",
174 "endwhile", "eval", "exit", "extends", "final",
175 "for", "foreach", "function", "global", "goto",
176 "if", "implements", "include", "include_once", "instanceof",
177 "insteadof", "interface", "isset", "list", "namespace",
178 "new", "or", "print", "private", "protected",
179 "public", "require", "require_once", "return", "static",
180 "switch", "throw", "trait", "try", "unset",
181 "use", "var", "while", "xor", "int",
182 "float", "bool", "string", "true", "false",
183 "null", "void", "iterable"};
184 const int kReservedNamesSize = 73;
185
is_reserved_name(const char * name)186 bool is_reserved_name(const char* name) {
187 void** value;
188 return (php_proto_zend_hash_find(reserved_names, name, strlen(name),
189 (void**)&value) == SUCCESS);
190 }
191
192 // -----------------------------------------------------------------------------
193 // Utilities.
194 // -----------------------------------------------------------------------------
195
196 zend_function_entry protobuf_functions[] = {
197 ZEND_FE_END
198 };
199
200 static const zend_module_dep protobuf_deps[] = {
201 ZEND_MOD_OPTIONAL("date")
202 ZEND_MOD_END
203 };
204
205 zend_module_entry protobuf_module_entry = {
206 STANDARD_MODULE_HEADER_EX,
207 NULL,
208 protobuf_deps,
209 PHP_PROTOBUF_EXTNAME, // extension name
210 protobuf_functions, // function list
211 PHP_MINIT(protobuf), // process startup
212 PHP_MSHUTDOWN(protobuf), // process shutdown
213 PHP_RINIT(protobuf), // request shutdown
214 PHP_RSHUTDOWN(protobuf), // request shutdown
215 NULL, // extension info
216 PHP_PROTOBUF_VERSION, // extension version
217 PHP_MODULE_GLOBALS(protobuf), // globals descriptor
218 PHP_GINIT(protobuf), // globals ctor
219 PHP_GSHUTDOWN(protobuf), // globals dtor
220 NULL, // post deactivate
221 STANDARD_MODULE_PROPERTIES_EX
222 };
223
224 // install module
225 ZEND_GET_MODULE(protobuf)
226
227 // global variables
PHP_GINIT_FUNCTION(protobuf)228 static PHP_GINIT_FUNCTION(protobuf) {
229 }
230
PHP_GSHUTDOWN_FUNCTION(protobuf)231 static PHP_GSHUTDOWN_FUNCTION(protobuf) {
232 }
233
234 #if PHP_MAJOR_VERSION >= 7
php_proto_hashtable_descriptor_release(zval * value)235 static void php_proto_hashtable_descriptor_release(zval* value) {
236 void* ptr = Z_PTR_P(value);
237 zend_object* object = *(zend_object**)ptr;
238 GC_DELREF(object);
239 if(GC_REFCOUNT(object) == 0) {
240 zend_objects_store_del(object);
241 }
242 efree(ptr);
243 }
244 #endif
245
PHP_RINIT_FUNCTION(protobuf)246 static PHP_RINIT_FUNCTION(protobuf) {
247 int i = 0;
248
249 ALLOC_HASHTABLE(upb_def_to_php_obj_map);
250 zend_hash_init(upb_def_to_php_obj_map, 16, NULL, HASHTABLE_VALUE_DTOR, 0);
251
252 ALLOC_HASHTABLE(ce_to_php_obj_map);
253 zend_hash_init(ce_to_php_obj_map, 16, NULL, HASHTABLE_VALUE_DTOR, 0);
254
255 ALLOC_HASHTABLE(proto_to_php_obj_map);
256 zend_hash_init(proto_to_php_obj_map, 16, NULL, HASHTABLE_VALUE_DTOR, 0);
257
258 ALLOC_HASHTABLE(reserved_names);
259 zend_hash_init(reserved_names, 16, NULL, NULL, 0);
260 for (i = 0; i < kReservedNamesSize; i++) {
261 php_proto_zend_hash_update(reserved_names, kReservedNames[i],
262 strlen(kReservedNames[i]));
263 }
264
265 generated_pool = NULL;
266 generated_pool_php = NULL;
267 internal_generated_pool_php = NULL;
268
269 is_inited_file_any = false;
270 is_inited_file_api = false;
271 is_inited_file_duration = false;
272 is_inited_file_field_mask = false;
273 is_inited_file_empty = false;
274 is_inited_file_source_context = false;
275 is_inited_file_struct = false;
276 is_inited_file_timestamp = false;
277 is_inited_file_type = false;
278 is_inited_file_wrappers = false;
279
280 return 0;
281 }
282
PHP_RSHUTDOWN_FUNCTION(protobuf)283 static PHP_RSHUTDOWN_FUNCTION(protobuf) {
284 zend_hash_destroy(upb_def_to_php_obj_map);
285 FREE_HASHTABLE(upb_def_to_php_obj_map);
286
287 zend_hash_destroy(ce_to_php_obj_map);
288 FREE_HASHTABLE(ce_to_php_obj_map);
289
290 zend_hash_destroy(proto_to_php_obj_map);
291 FREE_HASHTABLE(proto_to_php_obj_map);
292
293 zend_hash_destroy(reserved_names);
294 FREE_HASHTABLE(reserved_names);
295
296 #if PHP_MAJOR_VERSION < 7
297 if (generated_pool_php != NULL) {
298 zval_dtor(generated_pool_php);
299 FREE_ZVAL(generated_pool_php);
300 }
301 if (internal_generated_pool_php != NULL) {
302 zval_dtor(internal_generated_pool_php);
303 FREE_ZVAL(internal_generated_pool_php);
304 }
305 #else
306 if (generated_pool_php != NULL) {
307 zval tmp;
308 ZVAL_OBJ(&tmp, generated_pool_php);
309 zval_dtor(&tmp);
310 }
311 if (internal_generated_pool_php != NULL) {
312 zval tmp;
313 ZVAL_OBJ(&tmp, internal_generated_pool_php);
314 zval_dtor(&tmp);
315 }
316 #endif
317
318 is_inited_file_any = true;
319 is_inited_file_api = true;
320 is_inited_file_duration = true;
321 is_inited_file_field_mask = true;
322 is_inited_file_empty = true;
323 is_inited_file_source_context = true;
324 is_inited_file_struct = true;
325 is_inited_file_timestamp = true;
326 is_inited_file_type = true;
327 is_inited_file_wrappers = true;
328
329 return 0;
330 }
331
PHP_MINIT_FUNCTION(protobuf)332 static PHP_MINIT_FUNCTION(protobuf) {
333 descriptor_pool_init(TSRMLS_C);
334 descriptor_init(TSRMLS_C);
335 enum_descriptor_init(TSRMLS_C);
336 enum_value_descriptor_init(TSRMLS_C);
337 field_descriptor_init(TSRMLS_C);
338 gpb_type_init(TSRMLS_C);
339 internal_descriptor_pool_init(TSRMLS_C);
340 map_field_init(TSRMLS_C);
341 map_field_iter_init(TSRMLS_C);
342 message_init(TSRMLS_C);
343 oneof_descriptor_init(TSRMLS_C);
344 repeated_field_init(TSRMLS_C);
345 repeated_field_iter_init(TSRMLS_C);
346 util_init(TSRMLS_C);
347
348 gpb_metadata_any_init(TSRMLS_C);
349 gpb_metadata_api_init(TSRMLS_C);
350 gpb_metadata_duration_init(TSRMLS_C);
351 gpb_metadata_field_mask_init(TSRMLS_C);
352 gpb_metadata_empty_init(TSRMLS_C);
353 gpb_metadata_source_context_init(TSRMLS_C);
354 gpb_metadata_struct_init(TSRMLS_C);
355 gpb_metadata_timestamp_init(TSRMLS_C);
356 gpb_metadata_type_init(TSRMLS_C);
357 gpb_metadata_wrappers_init(TSRMLS_C);
358
359 any_init(TSRMLS_C);
360 api_init(TSRMLS_C);
361 bool_value_init(TSRMLS_C);
362 bytes_value_init(TSRMLS_C);
363 double_value_init(TSRMLS_C);
364 duration_init(TSRMLS_C);
365 enum_init(TSRMLS_C);
366 enum_value_init(TSRMLS_C);
367 field_cardinality_init(TSRMLS_C);
368 field_init(TSRMLS_C);
369 field_kind_init(TSRMLS_C);
370 field_mask_init(TSRMLS_C);
371 float_value_init(TSRMLS_C);
372 empty_init(TSRMLS_C);
373 int32_value_init(TSRMLS_C);
374 int64_value_init(TSRMLS_C);
375 list_value_init(TSRMLS_C);
376 method_init(TSRMLS_C);
377 mixin_init(TSRMLS_C);
378 null_value_init(TSRMLS_C);
379 option_init(TSRMLS_C);
380 source_context_init(TSRMLS_C);
381 string_value_init(TSRMLS_C);
382 struct_init(TSRMLS_C);
383 syntax_init(TSRMLS_C);
384 timestamp_init(TSRMLS_C);
385 type_init(TSRMLS_C);
386 u_int32_value_init(TSRMLS_C);
387 u_int64_value_init(TSRMLS_C);
388 value_init(TSRMLS_C);
389
390 return 0;
391 }
392
PHP_MSHUTDOWN_FUNCTION(protobuf)393 static PHP_MSHUTDOWN_FUNCTION(protobuf) {
394 PEFREE(message_handlers);
395 PEFREE(repeated_field_handlers);
396 PEFREE(repeated_field_iter_handlers);
397 PEFREE(map_field_handlers);
398 PEFREE(map_field_iter_handlers);
399
400 return 0;
401 }
402