• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2014 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 "message.h"
32 
33 #include <inttypes.h>
34 #include <php.h>
35 #include <stdlib.h>
36 
37 // This is not self-contained: it must be after other Zend includes.
38 #include <Zend/zend_exceptions.h>
39 
40 #include "arena.h"
41 #include "array.h"
42 #include "convert.h"
43 #include "def.h"
44 #include "map.h"
45 #include "php-upb.h"
46 #include "protobuf.h"
47 
48 // -----------------------------------------------------------------------------
49 // Message
50 // -----------------------------------------------------------------------------
51 
52 typedef struct {
53   zend_object std;
54   zval arena;
55   const Descriptor* desc;
56   upb_msg *msg;
57 } Message;
58 
59 zend_class_entry *message_ce;
60 static zend_object_handlers message_object_handlers;
61 
62 // PHP Object Handlers /////////////////////////////////////////////////////////
63 
64 /**
65  * Message_create()
66  *
67  * PHP class entry function to allocate and initialize a new Message object.
68  */
Message_create(zend_class_entry * class_type)69 static zend_object* Message_create(zend_class_entry *class_type) {
70   Message *intern = emalloc(sizeof(Message));
71   // XXX(haberman): verify whether we actually want to take this route.
72   class_type->default_properties_count = 0;
73   zend_object_std_init(&intern->std, class_type);
74   intern->std.handlers = &message_object_handlers;
75   Arena_Init(&intern->arena);
76   return &intern->std;
77 }
78 
79 /**
80  * Message_dtor()
81  *
82  * Object handler to destroy a Message. This releases all resources associated
83  * with the message. Note that it is possible to access a destroyed object from
84  * PHP in rare cases.
85  */
Message_dtor(zend_object * obj)86 static void Message_dtor(zend_object* obj) {
87   Message* intern = (Message*)obj;
88   ObjCache_Delete(intern->msg);
89   zval_dtor(&intern->arena);
90   zend_object_std_dtor(&intern->std);
91 }
92 
93 /**
94  * get_field()
95  *
96  * Helper function to look up a field given a member name (as a string).
97  */
get_field(Message * msg,PROTO_STR * member)98 static const upb_fielddef *get_field(Message *msg, PROTO_STR *member) {
99   const upb_msgdef *m = msg->desc->msgdef;
100   const upb_fielddef *f =
101       upb_msgdef_ntof(m, PROTO_STRVAL_P(member), PROTO_STRLEN_P(member));
102 
103   if (!f) {
104     zend_throw_exception_ex(NULL, 0, "No such property %s.",
105                             ZSTR_VAL(msg->desc->class_entry->name));
106   }
107 
108   return f;
109 }
110 
111 /**
112  * Message_has_property()
113  *
114  * Object handler for testing whether a property exists. Called when PHP code
115  * does any of:
116  *
117  *   isset($message->foobar);
118  *   property_exists($message->foobar);
119  *
120  * Note that all properties of generated messages are private, so this should
121  * only be possible to invoke from generated code, which has accessors like this
122  * (if the field has presence):
123  *
124  *   public function hasOptionalInt32()
125  *   {
126  *       return isset($this->optional_int32);
127  *   }
128  */
Message_has_property(PROTO_VAL * obj,PROTO_STR * member,int has_set_exists,void ** cache_slot)129 static int Message_has_property(PROTO_VAL *obj, PROTO_STR *member,
130                                 int has_set_exists,
131                                 void **cache_slot) {
132   Message* intern = PROTO_MSG_P(obj);
133   const upb_fielddef *f = get_field(intern, member);
134 
135   if (!f) return 0;
136 
137   if (!upb_fielddef_haspresence(f)) {
138     zend_throw_exception_ex(
139         NULL, 0,
140         "Cannot call isset() on field %s which does not have presence.",
141         ZSTR_VAL(intern->desc->class_entry->name));
142     return 0;
143   }
144 
145   return upb_msg_has(intern->msg, f);
146 }
147 
148 /**
149  * Message_unset_property()
150  *
151  * Object handler for unsetting a property. Called when PHP code calls:
152  * does any of:
153  *
154  *   unset($message->foobar);
155  *
156  * Note that all properties of generated messages are private, so this should
157  * only be possible to invoke from generated code, which has accessors like this
158  * (if the field has presence):
159  *
160  *   public function clearOptionalInt32()
161  *   {
162  *       unset($this->optional_int32);
163  *   }
164  */
Message_unset_property(PROTO_VAL * obj,PROTO_STR * member,void ** cache_slot)165 static void Message_unset_property(PROTO_VAL *obj, PROTO_STR *member,
166                                    void **cache_slot) {
167   Message* intern = PROTO_MSG_P(obj);
168   const upb_fielddef *f = get_field(intern, member);
169 
170   if (!f) return;
171 
172   if (!upb_fielddef_haspresence(f)) {
173     zend_throw_exception_ex(
174         NULL, 0,
175         "Cannot call unset() on field %s which does not have presence.",
176         ZSTR_VAL(intern->desc->class_entry->name));
177     return;
178   }
179 
180   upb_msg_clearfield(intern->msg, f);
181 }
182 
183 /**
184  * Message_read_property()
185  *
186  * Object handler for reading a property in PHP. Called when PHP code does:
187  *
188  *   $x = $message->foobar;
189  *
190  * Note that all properties of generated messages are private, so this should
191  * only be possible to invoke from generated code, which has accessors like:
192  *
193  *   public function getOptionalInt32()
194  *   {
195  *       return $this->optional_int32;
196  *   }
197  *
198  * We lookup the field and return the scalar, RepeatedField, or MapField for
199  * this field.
200  */
Message_read_property(PROTO_VAL * obj,PROTO_STR * member,int type,void ** cache_slot,zval * rv)201 static zval *Message_read_property(PROTO_VAL *obj, PROTO_STR *member,
202                                    int type, void **cache_slot, zval *rv) {
203   Message* intern = PROTO_MSG_P(obj);
204   const upb_fielddef *f = get_field(intern, member);
205   upb_arena *arena = Arena_Get(&intern->arena);
206 
207   if (!f) return NULL;
208 
209   if (upb_fielddef_ismap(f)) {
210     upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
211     MapField_GetPhpWrapper(rv, msgval.map, f, &intern->arena);
212   } else if (upb_fielddef_isseq(f)) {
213     upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
214     RepeatedField_GetPhpWrapper(rv, msgval.array, f, &intern->arena);
215   } else {
216     upb_msgval msgval = upb_msg_get(intern->msg, f);
217     const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
218     Convert_UpbToPhp(msgval, rv, upb_fielddef_type(f), subdesc, &intern->arena);
219   }
220 
221   return rv;
222 }
223 
224 /**
225  * Message_write_property()
226  *
227  * Object handler for writing a property in PHP. Called when PHP code does:
228  *
229  *   $message->foobar = $x;
230  *
231  * Note that all properties of generated messages are private, so this should
232  * only be possible to invoke from generated code, which has accessors like:
233  *
234  *   public function setOptionalInt32($var)
235  *   {
236  *       GPBUtil::checkInt32($var);
237  *       $this->optional_int32 = $var;
238  *
239  *       return $this;
240  *   }
241  *
242  * The C extension version of checkInt32() doesn't actually check anything, so
243  * we perform all checking and conversion in this function.
244  */
Message_write_property(PROTO_VAL * obj,PROTO_STR * member,zval * val,void ** cache_slot)245 static PROTO_RETURN_VAL Message_write_property(
246     PROTO_VAL *obj, PROTO_STR *member, zval *val, void **cache_slot) {
247   Message* intern = PROTO_MSG_P(obj);
248   const upb_fielddef *f = get_field(intern, member);
249   upb_arena *arena = Arena_Get(&intern->arena);
250   upb_msgval msgval;
251 
252   if (!f) goto error;
253 
254   if (upb_fielddef_ismap(f)) {
255     msgval.map_val = MapField_GetUpbMap(val, f, arena);
256     if (!msgval.map_val) goto error;
257   } else if (upb_fielddef_isseq(f)) {
258     msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
259     if (!msgval.array_val) goto error;
260   } else {
261     upb_fieldtype_t type = upb_fielddef_type(f);
262     const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
263     bool ok = Convert_PhpToUpb(val, &msgval, type, subdesc, arena);
264     if (!ok) goto error;
265   }
266 
267   upb_msg_set(intern->msg, f, msgval, arena);
268 #if PHP_VERSION_ID < 704000
269   return;
270 #else
271   return val;
272 #endif
273 
274 error:
275 #if PHP_VERSION_ID < 704000
276   return;
277 #else
278   return &EG(error_zval);
279 #endif
280 }
281 
282 /**
283  * Message_get_property_ptr_ptr()
284  *
285  * Object handler for the get_property_ptr_ptr event in PHP. This returns a
286  * reference to our internal properties. We don't support this, so we return
287  * NULL.
288  */
Message_get_property_ptr_ptr(PROTO_VAL * object,PROTO_STR * member,int type,void ** cache_slot)289 static zval *Message_get_property_ptr_ptr(PROTO_VAL *object, PROTO_STR *member,
290                                           int type,
291                                           void **cache_slot) {
292   return NULL;  // We do not have a properties table.
293 }
294 
295 /**
296  * Message_get_properties()
297  *
298  * Object handler for the get_properties event in PHP. This returns a HashTable
299  * of our internal properties. We don't support this, so we return NULL.
300  */
Message_get_properties(PROTO_VAL * object)301 static HashTable *Message_get_properties(PROTO_VAL *object) {
302   return NULL;  // We don't offer direct references to our properties.
303 }
304 
305 // C Functions from message.h. /////////////////////////////////////////////////
306 
307 // These are documented in the header file.
308 
Message_GetPhpWrapper(zval * val,const Descriptor * desc,upb_msg * msg,zval * arena)309 void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg,
310                            zval *arena) {
311   if (!msg) {
312     ZVAL_NULL(val);
313     return;
314   }
315 
316   if (!ObjCache_Get(msg, val)) {
317     Message *intern = emalloc(sizeof(Message));
318     // XXX(haberman): verify whether we actually want to take this route.
319     desc->class_entry->default_properties_count = 0;
320     zend_object_std_init(&intern->std, desc->class_entry);
321     intern->std.handlers = &message_object_handlers;
322     ZVAL_COPY(&intern->arena, arena);
323     intern->desc = desc;
324     intern->msg = msg;
325     ZVAL_OBJ(val, &intern->std);
326     ObjCache_Add(intern->msg, &intern->std);
327   }
328 }
329 
Message_GetUpbMessage(zval * val,const Descriptor * desc,upb_arena * arena,upb_msg ** msg)330 bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena,
331                            upb_msg **msg) {
332   PBPHP_ASSERT(desc);
333 
334   if (Z_ISREF_P(val)) {
335     ZVAL_DEREF(val);
336   }
337 
338   if (Z_TYPE_P(val) == IS_NULL) {
339     *msg = NULL;
340     return true;
341   }
342 
343   if (Z_TYPE_P(val) == IS_OBJECT &&
344       instanceof_function(Z_OBJCE_P(val), desc->class_entry)) {
345     Message *intern = (Message*)Z_OBJ_P(val);
346     upb_arena_fuse(arena, Arena_Get(&intern->arena));
347     *msg = intern->msg;
348     return true;
349   } else {
350     zend_throw_exception_ex(NULL, 0, "Given value is not an instance of %s.",
351                             ZSTR_VAL(desc->class_entry->name));
352     return false;
353   }
354 }
355 
356 // Message PHP methods /////////////////////////////////////////////////////////
357 
358 /**
359  * Message_InitFromPhp()
360  *
361  * Helper method to handle the initialization of a message from a PHP value, eg.
362  *
363  *   $m = new TestMessage([
364  *       'optional_int32' => -42,
365  *       'optional_bool' => true,
366  *       'optional_string' => 'a',
367  *       'optional_enum' => TestEnum::ONE,
368  *       'optional_message' => new Sub([
369  *           'a' => 33
370  *       ]),
371  *       'repeated_int32' => [-42, -52],
372  *       'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE],
373  *       'repeated_message' => [new Sub(['a' => 34]),
374  *                              new Sub(['a' => 35])],
375  *       'map_int32_int32' => [-62 => -62],
376  *       'map_int32_enum' => [1 => TestEnum::ONE],
377  *       'map_int32_message' => [1 => new Sub(['a' => 36])],
378  *   ]);
379  *
380  * The initializer must be an array.
381  */
Message_InitFromPhp(upb_msg * msg,const upb_msgdef * m,zval * init,upb_arena * arena)382 bool Message_InitFromPhp(upb_msg *msg, const upb_msgdef *m, zval *init,
383                          upb_arena *arena) {
384   HashTable* table = HASH_OF(init);
385   HashPosition pos;
386 
387   if (Z_ISREF_P(init)) {
388     ZVAL_DEREF(init);
389   }
390 
391   if (Z_TYPE_P(init) != IS_ARRAY) {
392     zend_throw_exception_ex(NULL, 0,
393                             "Initializer for a message %s must be an array.",
394                             upb_msgdef_fullname(m));
395     return false;
396   }
397 
398   zend_hash_internal_pointer_reset_ex(table, &pos);
399 
400   while (true) {  // Iterate over key/value pairs.
401     zval key;
402     zval *val;
403     const upb_fielddef *f;
404     upb_msgval msgval;
405 
406     zend_hash_get_current_key_zval_ex(table, &key, &pos);
407     val = zend_hash_get_current_data_ex(table, &pos);
408 
409     if (!val) return true;  // Finished iteration.
410 
411     if (Z_ISREF_P(val)) {
412       ZVAL_DEREF(val);
413     }
414 
415     f = upb_msgdef_ntof(m, Z_STRVAL_P(&key), Z_STRLEN_P(&key));
416 
417     if (!f) {
418       zend_throw_exception_ex(NULL, 0,
419                               "No such field %s", Z_STRVAL_P(&key));
420       return false;
421     }
422 
423     if (upb_fielddef_ismap(f)) {
424       msgval.map_val = MapField_GetUpbMap(val, f, arena);
425       if (!msgval.map_val) return false;
426     } else if (upb_fielddef_isseq(f)) {
427       msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
428       if (!msgval.array_val) return false;
429     } else {
430       const Descriptor *desc = Descriptor_GetFromFieldDef(f);
431       upb_fieldtype_t type = upb_fielddef_type(f);
432       if (!Convert_PhpToUpbAutoWrap(val, &msgval, type, desc, arena)) {
433         return false;
434       }
435     }
436 
437     upb_msg_set(msg, f, msgval, arena);
438     zend_hash_move_forward_ex(table, &pos);
439     zval_dtor(&key);
440   }
441 }
442 
443 /**
444  * Message::__construct()
445  *
446  * Constructor for Message.
447  * @param array Map of initial values ['k' = val]
448  */
PHP_METHOD(Message,__construct)449 PHP_METHOD(Message, __construct) {
450   Message* intern = (Message*)Z_OBJ_P(getThis());
451   const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis()));
452   const upb_msgdef *msgdef = desc->msgdef;
453   upb_arena *arena = Arena_Get(&intern->arena);
454   zval *init_arr = NULL;
455 
456   intern->desc = desc;
457   intern->msg = upb_msg_new(msgdef, arena);
458   ObjCache_Add(intern->msg, &intern->std);
459 
460   if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) {
461     return;
462   }
463 
464   if (init_arr) {
465     Message_InitFromPhp(intern->msg, desc->msgdef, init_arr, arena);
466   }
467 }
468 
469 /**
470  * Message::discardUnknownFields()
471  *
472  * Discards any unknown fields for this message or any submessages.
473  */
PHP_METHOD(Message,discardUnknownFields)474 PHP_METHOD(Message, discardUnknownFields) {
475   Message* intern = (Message*)Z_OBJ_P(getThis());
476   upb_msg_discardunknown(intern->msg, intern->desc->msgdef, 64);
477 }
478 
479 /**
480  * Message::clear()
481  *
482  * Clears all fields of this message.
483  */
PHP_METHOD(Message,clear)484 PHP_METHOD(Message, clear) {
485   Message* intern = (Message*)Z_OBJ_P(getThis());
486   upb_msg_clear(intern->msg, intern->desc->msgdef);
487 }
488 
489 /**
490  * Message::mergeFrom()
491  *
492  * Merges from the given message, which must be of the same class as us.
493  * @param object Message to merge from.
494  */
PHP_METHOD(Message,mergeFrom)495 PHP_METHOD(Message, mergeFrom) {
496   Message* intern = (Message*)Z_OBJ_P(getThis());
497   Message* from;
498   upb_arena *arena = Arena_Get(&intern->arena);
499   const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
500   zval* value;
501   char *pb;
502   size_t size;
503   bool ok;
504 
505   if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &value,
506                             intern->desc->class_entry) == FAILURE) {
507     return;
508   }
509 
510   from = (Message*)Z_OBJ_P(value);
511 
512   // Should be guaranteed since we passed the class type to
513   // zend_parse_parameters().
514   PBPHP_ASSERT(from->desc == intern->desc);
515 
516   // TODO(haberman): use a temp arena for this once we can make upb_decode()
517   // copy strings.
518   pb = upb_encode(from->msg, l, arena, &size);
519 
520   if (!pb) {
521     zend_throw_exception_ex(NULL, 0, "Max nesting exceeded");
522     return;
523   }
524 
525   ok = upb_decode(pb, size, intern->msg, l, arena);
526   PBPHP_ASSERT(ok);
527 }
528 
529 /**
530  * Message::mergeFromString()
531  *
532  * Merges from the given string.
533  * @param string Binary protobuf data to merge.
534  */
PHP_METHOD(Message,mergeFromString)535 PHP_METHOD(Message, mergeFromString) {
536   Message* intern = (Message*)Z_OBJ_P(getThis());
537   char *data = NULL;
538   char *data_copy = NULL;
539   zend_long data_len;
540   const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
541   upb_arena *arena = Arena_Get(&intern->arena);
542 
543   if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &data_len) ==
544       FAILURE) {
545     return;
546   }
547 
548   // TODO(haberman): avoid this copy when we can make the decoder copy.
549   data_copy = upb_arena_malloc(arena, data_len);
550   memcpy(data_copy, data, data_len);
551 
552   if (!upb_decode(data_copy, data_len, intern->msg, l, arena)) {
553     zend_throw_exception_ex(NULL, 0, "Error occurred during parsing");
554     return;
555   }
556 }
557 
558 /**
559  * Message::serializeToString()
560  *
561  * Serializes this message instance to protobuf data.
562  * @return string Serialized protobuf data.
563  */
PHP_METHOD(Message,serializeToString)564 PHP_METHOD(Message, serializeToString) {
565   Message* intern = (Message*)Z_OBJ_P(getThis());
566   const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
567   upb_arena *tmp_arena = upb_arena_new();
568   char *data;
569   size_t size;
570 
571   data = upb_encode(intern->msg, l, tmp_arena, &size);
572 
573   if (!data) {
574     zend_throw_exception_ex(NULL, 0, "Error occurred during serialization");
575     upb_arena_free(tmp_arena);
576     return;
577   }
578 
579   RETVAL_STRINGL(data, size);
580   upb_arena_free(tmp_arena);
581 }
582 
583 /**
584  * Message::mergeFromJsonString()
585  *
586  * Merges the JSON data parsed from the given string.
587  * @param string Serialized JSON data.
588  */
PHP_METHOD(Message,mergeFromJsonString)589 PHP_METHOD(Message, mergeFromJsonString) {
590   Message* intern = (Message*)Z_OBJ_P(getThis());
591   char *data = NULL;
592   char *data_copy = NULL;
593   zend_long data_len;
594   upb_arena *arena = Arena_Get(&intern->arena);
595   upb_status status;
596   zend_bool ignore_json_unknown = false;
597   int options = 0;
598 
599   if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &data, &data_len,
600                             &ignore_json_unknown) == FAILURE) {
601     return;
602   }
603 
604   // TODO(haberman): avoid this copy when we can make the decoder copy.
605   data_copy = upb_arena_malloc(arena, data_len + 1);
606   memcpy(data_copy, data, data_len);
607   data_copy[data_len] = '\0';
608 
609   if (ignore_json_unknown) {
610     options |= UPB_JSONDEC_IGNOREUNKNOWN;
611   }
612 
613   upb_status_clear(&status);
614   if (!upb_json_decode(data_copy, data_len, intern->msg, intern->desc->msgdef,
615                        DescriptorPool_GetSymbolTable(), options, arena,
616                        &status)) {
617     zend_throw_exception_ex(NULL, 0, "Error occurred during parsing: %s",
618                             upb_status_errmsg(&status));
619     return;
620   }
621 }
622 
623 /**
624  * Message::serializeToJsonString()
625  *
626  * Serializes this object to JSON.
627  * @return string Serialized JSON data.
628  */
PHP_METHOD(Message,serializeToJsonString)629 PHP_METHOD(Message, serializeToJsonString) {
630   Message* intern = (Message*)Z_OBJ_P(getThis());
631   size_t size;
632   int options = 0;
633   char buf[1024];
634   zend_bool preserve_proto_fieldnames = false;
635   upb_status status;
636 
637   if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b",
638                             &preserve_proto_fieldnames) == FAILURE) {
639     return;
640   }
641 
642   if (preserve_proto_fieldnames) {
643     options |= UPB_JSONENC_PROTONAMES;
644   }
645 
646   upb_status_clear(&status);
647   size = upb_json_encode(intern->msg, intern->desc->msgdef,
648                          DescriptorPool_GetSymbolTable(), options, buf,
649                          sizeof(buf), &status);
650 
651   if (!upb_ok(&status)) {
652     zend_throw_exception_ex(NULL, 0,
653                             "Error occurred during JSON serialization: %s",
654                             upb_status_errmsg(&status));
655     return;
656   }
657 
658   if (size >= sizeof(buf)) {
659     char *buf2 = malloc(size + 1);
660     upb_json_encode(intern->msg, intern->desc->msgdef,
661                     DescriptorPool_GetSymbolTable(), options, buf2, size + 1,
662                     &status);
663     RETVAL_STRINGL(buf2, size);
664     free(buf2);
665   } else {
666     RETVAL_STRINGL(buf, size);
667   }
668 }
669 
670 /**
671  * Message::readWrapperValue()
672  *
673  * Returns an unboxed value for the given field. This is called from generated
674  * methods for wrapper fields, eg.
675  *
676  *   public function getDoubleValueUnwrapped()
677  *   {
678  *       return $this->readWrapperValue("double_value");
679  *   }
680  *
681  * @return Unwrapped field value or null.
682  */
PHP_METHOD(Message,readWrapperValue)683 PHP_METHOD(Message, readWrapperValue) {
684   Message* intern = (Message*)Z_OBJ_P(getThis());
685   char* member;
686   const upb_fielddef *f;
687   zend_long size;
688 
689   if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &member, &size) == FAILURE) {
690     return;
691   }
692 
693   f = upb_msgdef_ntof(intern->desc->msgdef, member, size);
694 
695   if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) {
696     zend_throw_exception_ex(NULL, 0, "Message %s has no field %s",
697                             upb_msgdef_fullname(intern->desc->msgdef), member);
698     return;
699   }
700 
701   if (upb_msg_has(intern->msg, f)) {
702     const upb_msg *wrapper = upb_msg_get(intern->msg, f).msg_val;
703     const upb_msgdef *m = upb_fielddef_msgsubdef(f);
704     const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
705     const upb_fieldtype_t val_type = upb_fielddef_type(val_f);
706     upb_msgval msgval = upb_msg_get(wrapper, val_f);
707     zval ret;
708     Convert_UpbToPhp(msgval, &ret, val_type, NULL, &intern->arena);
709     RETURN_ZVAL(&ret, 1, 0);
710   } else {
711     RETURN_NULL();
712   }
713 }
714 
715 /**
716  * Message::writeWrapperValue()
717  *
718  * Sets the given wrapper field to the given unboxed value. This is called from
719  * generated methods for wrapper fields, eg.
720  *
721  *
722  *   public function setDoubleValueUnwrapped($var)
723  *   {
724  *       $this->writeWrapperValue("double_value", $var);
725  *       return $this;
726  *   }
727  *
728  * @param Unwrapped field value or null.
729  */
PHP_METHOD(Message,writeWrapperValue)730 PHP_METHOD(Message, writeWrapperValue) {
731   Message* intern = (Message*)Z_OBJ_P(getThis());
732   upb_arena *arena = Arena_Get(&intern->arena);
733   char* member;
734   const upb_fielddef *f;
735   upb_msgval msgval;
736   zend_long size;
737   zval* val;
738 
739   if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &member, &size, &val) ==
740       FAILURE) {
741     return;
742   }
743 
744   f = upb_msgdef_ntof(intern->desc->msgdef, member, size);
745 
746   if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) {
747     zend_throw_exception_ex(NULL, 0, "Message %s has no field %s",
748                             upb_msgdef_fullname(intern->desc->msgdef), member);
749     return;
750   }
751 
752   if (Z_ISREF_P(val)) {
753     ZVAL_DEREF(val);
754   }
755 
756   if (Z_TYPE_P(val) == IS_NULL) {
757     upb_msg_clearfield(intern->msg, f);
758   } else {
759     const upb_msgdef *m = upb_fielddef_msgsubdef(f);
760     const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
761     upb_fieldtype_t val_type = upb_fielddef_type(val_f);
762     upb_msg *wrapper;
763 
764     if (!Convert_PhpToUpb(val, &msgval, val_type, NULL, arena)) {
765       return;  // Error is already set.
766     }
767 
768     wrapper = upb_msg_mutable(intern->msg, f, arena).msg;
769     upb_msg_set(wrapper, val_f, msgval, arena);
770   }
771 }
772 
773 /**
774  * Message::whichOneof()
775  *
776  * Given a oneof name, returns the name of the field that is set for this oneof,
777  * or otherwise the empty string.
778  *
779  * @return string The field name in this oneof that is currently set.
780  */
PHP_METHOD(Message,whichOneof)781 PHP_METHOD(Message, whichOneof) {
782   Message* intern = (Message*)Z_OBJ_P(getThis());
783   const upb_oneofdef* oneof;
784   const upb_fielddef* field;
785   char* name;
786   zend_long len;
787 
788   if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &len) == FAILURE) {
789     return;
790   }
791 
792   oneof = upb_msgdef_ntoo(intern->desc->msgdef, name, len);
793 
794   if (!oneof) {
795     zend_throw_exception_ex(NULL, 0, "Message %s has no oneof %s",
796                             upb_msgdef_fullname(intern->desc->msgdef), name);
797     return;
798   }
799 
800   field = upb_msg_whichoneof(intern->msg, oneof);
801   RETURN_STRING(field ? upb_fielddef_name(field) : "");
802 }
803 
804 /**
805  * Message::readOneof()
806  *
807  * Returns the contents of the given oneof field, given a field number. Called
808  * from generated code methods such as:
809  *
810  *    public function getDoubleValueOneof()
811  *    {
812  *        return $this->readOneof(10);
813  *    }
814  *
815  * @return object The oneof's field value.
816  */
PHP_METHOD(Message,readOneof)817 PHP_METHOD(Message, readOneof) {
818   Message* intern = (Message*)Z_OBJ_P(getThis());
819   zend_long field_num;
820   const upb_fielddef* f;
821   zval ret;
822 
823   if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &field_num) == FAILURE) {
824     return;
825   }
826 
827   f = upb_msgdef_itof(intern->desc->msgdef, field_num);
828 
829   if (!f || !upb_fielddef_realcontainingoneof(f)) {
830     php_error_docref(NULL, E_USER_ERROR,
831                      "Internal error, no such oneof field %d\n",
832                      (int)field_num);
833   }
834 
835   {
836     upb_msgval msgval = upb_msg_get(intern->msg, f);
837     const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
838     Convert_UpbToPhp(msgval, &ret, upb_fielddef_type(f), subdesc,
839                      &intern->arena);
840   }
841 
842   RETURN_ZVAL(&ret, 1, 0);
843 }
844 
845 /**
846  * Message::writeOneof()
847  *
848  * Sets the contents of the given oneof field, given a field number. Called
849  * from generated code methods such as:
850  *
851  *    public function setDoubleValueOneof($var)
852  *   {
853  *       GPBUtil::checkMessage($var, \Google\Protobuf\DoubleValue::class);
854  *       $this->writeOneof(10, $var);
855  *
856  *       return $this;
857  *   }
858  *
859  * The C extension version of GPBUtil::check*() does nothing, so we perform
860  * all type checking and conversion here.
861  *
862  * @param integer The field number we are setting.
863  * @param object The field value we want to set.
864  */
PHP_METHOD(Message,writeOneof)865 PHP_METHOD(Message, writeOneof) {
866   Message* intern = (Message*)Z_OBJ_P(getThis());
867   zend_long field_num;
868   const upb_fielddef* f;
869   upb_arena *arena = Arena_Get(&intern->arena);
870   upb_msgval msgval;
871   zval* val;
872 
873   if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &field_num, &val) ==
874       FAILURE) {
875     return;
876   }
877 
878   f = upb_msgdef_itof(intern->desc->msgdef, field_num);
879 
880   if (!Convert_PhpToUpb(val, &msgval, upb_fielddef_type(f),
881                         Descriptor_GetFromFieldDef(f), arena)) {
882     return;
883   }
884 
885   upb_msg_set(intern->msg, f, msgval, arena);
886 }
887 
888 ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0)
889 ZEND_END_ARG_INFO()
890 
891 ZEND_BEGIN_ARG_INFO_EX(arginfo_mergeFrom, 0, 0, 1)
892   ZEND_ARG_INFO(0, data)
893 ZEND_END_ARG_INFO()
894 
895 ZEND_BEGIN_ARG_INFO_EX(arginfo_read, 0, 0, 1)
896   ZEND_ARG_INFO(0, field)
897 ZEND_END_ARG_INFO()
898 
899 ZEND_BEGIN_ARG_INFO_EX(arginfo_write, 0, 0, 2)
900   ZEND_ARG_INFO(0, field)
901   ZEND_ARG_INFO(0, value)
902 ZEND_END_ARG_INFO()
903 
904 static zend_function_entry Message_methods[] = {
905   PHP_ME(Message, clear,                 arginfo_void,      ZEND_ACC_PUBLIC)
906   PHP_ME(Message, discardUnknownFields,  arginfo_void,      ZEND_ACC_PUBLIC)
907   PHP_ME(Message, serializeToString,     arginfo_void,      ZEND_ACC_PUBLIC)
908   PHP_ME(Message, mergeFromString,       arginfo_mergeFrom, ZEND_ACC_PUBLIC)
909   PHP_ME(Message, serializeToJsonString, arginfo_void,      ZEND_ACC_PUBLIC)
910   PHP_ME(Message, mergeFromJsonString,   arginfo_mergeFrom, ZEND_ACC_PUBLIC)
911   PHP_ME(Message, mergeFrom,             arginfo_mergeFrom, ZEND_ACC_PUBLIC)
912   PHP_ME(Message, readWrapperValue,      arginfo_read,      ZEND_ACC_PROTECTED)
913   PHP_ME(Message, writeWrapperValue,     arginfo_write,     ZEND_ACC_PROTECTED)
914   PHP_ME(Message, readOneof,             arginfo_read,      ZEND_ACC_PROTECTED)
915   PHP_ME(Message, writeOneof,            arginfo_write,     ZEND_ACC_PROTECTED)
916   PHP_ME(Message, whichOneof,            arginfo_read,      ZEND_ACC_PROTECTED)
917   PHP_ME(Message, __construct,           arginfo_void,      ZEND_ACC_PROTECTED)
918   ZEND_FE_END
919 };
920 
921 /**
922  * Message_ModuleInit()
923  *
924  * Called when the C extension is loaded to register all types.
925  */
Message_ModuleInit()926 void Message_ModuleInit() {
927   zend_class_entry tmp_ce;
928   zend_object_handlers *h = &message_object_handlers;
929 
930   INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\Message",
931                    Message_methods);
932 
933   message_ce = zend_register_internal_class(&tmp_ce);
934   message_ce->create_object = Message_create;
935 
936   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
937   h->dtor_obj = Message_dtor;
938   h->read_property = Message_read_property;
939   h->write_property = Message_write_property;
940   h->has_property = Message_has_property;
941   h->unset_property = Message_unset_property;
942   h->get_properties = Message_get_properties;
943   h->get_property_ptr_ptr = Message_get_property_ptr_ptr;
944 }
945