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