• 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 #include <Zend/zend_inheritance.h>
40 
41 #include "arena.h"
42 #include "array.h"
43 #include "convert.h"
44 #include "def.h"
45 #include "map.h"
46 #include "php-upb.h"
47 #include "protobuf.h"
48 
49 // -----------------------------------------------------------------------------
50 // Message
51 // -----------------------------------------------------------------------------
52 
53 typedef struct {
54   zend_object std;
55   zval arena;
56   const Descriptor* desc;
57   upb_msg *msg;
58 } Message;
59 
60 zend_class_entry *message_ce;
61 static zend_object_handlers message_object_handlers;
62 
63 // PHP Object Handlers /////////////////////////////////////////////////////////
64 
65 /**
66  * Message_create()
67  *
68  * PHP class entry function to allocate and initialize a new Message object.
69  */
Message_create(zend_class_entry * class_type)70 static zend_object* Message_create(zend_class_entry *class_type) {
71   Message *intern = emalloc(sizeof(Message));
72   // XXX(haberman): verify whether we actually want to take this route.
73   class_type->default_properties_count = 0;
74   zend_object_std_init(&intern->std, class_type);
75   intern->std.handlers = &message_object_handlers;
76   Arena_Init(&intern->arena);
77   return &intern->std;
78 }
79 
80 /**
81  * Message_dtor()
82  *
83  * Object handler to destroy a Message. This releases all resources associated
84  * with the message. Note that it is possible to access a destroyed object from
85  * PHP in rare cases.
86  */
Message_dtor(zend_object * obj)87 static void Message_dtor(zend_object* obj) {
88   Message* intern = (Message*)obj;
89   ObjCache_Delete(intern->msg);
90   zval_dtor(&intern->arena);
91   zend_object_std_dtor(&intern->std);
92 }
93 
94 /**
95  * get_field()
96  *
97  * Helper function to look up a field given a member name (as a string).
98  */
get_field(Message * msg,PROTO_STR * member)99 static const upb_fielddef *get_field(Message *msg, PROTO_STR *member) {
100   const upb_msgdef *m = msg->desc->msgdef;
101   const upb_fielddef *f =
102       upb_msgdef_ntof(m, PROTO_STRVAL_P(member), PROTO_STRLEN_P(member));
103 
104   if (!f) {
105     zend_throw_exception_ex(NULL, 0, "No such property %s.",
106                             ZSTR_VAL(msg->desc->class_entry->name));
107   }
108 
109   return f;
110 }
111 
Message_get(Message * intern,const upb_fielddef * f,zval * rv)112 static void Message_get(Message *intern, const upb_fielddef *f, zval *rv) {
113   upb_arena *arena = Arena_Get(&intern->arena);
114 
115   if (upb_fielddef_ismap(f)) {
116     upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
117     MapField_GetPhpWrapper(rv, msgval.map, f, &intern->arena);
118   } else if (upb_fielddef_isseq(f)) {
119     upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
120     RepeatedField_GetPhpWrapper(rv, msgval.array, f, &intern->arena);
121   } else {
122     upb_msgval msgval = upb_msg_get(intern->msg, f);
123     const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
124     Convert_UpbToPhp(msgval, rv, upb_fielddef_type(f), subdesc, &intern->arena);
125   }
126 }
127 
Message_set(Message * intern,const upb_fielddef * f,zval * val)128 static bool Message_set(Message *intern, const upb_fielddef *f, zval *val) {
129   upb_arena *arena = Arena_Get(&intern->arena);
130   upb_msgval msgval;
131 
132   if (upb_fielddef_ismap(f)) {
133     msgval.map_val = MapField_GetUpbMap(val, f, arena);
134     if (!msgval.map_val) return false;
135   } else if (upb_fielddef_isseq(f)) {
136     msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
137     if (!msgval.array_val) return false;
138   } else {
139     upb_fieldtype_t type = upb_fielddef_type(f);
140     const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
141     bool ok = Convert_PhpToUpb(val, &msgval, type, subdesc, arena);
142     if (!ok) return false;
143   }
144 
145   upb_msg_set(intern->msg, f, msgval, arena);
146   return true;
147 }
148 
149 static bool MessageEq(const upb_msg *m1, const upb_msg *m2, const upb_msgdef *m);
150 
151 /**
152  * ValueEq()()
153  */
ValueEq(upb_msgval val1,upb_msgval val2,upb_fieldtype_t type,const upb_msgdef * m)154 bool ValueEq(upb_msgval val1, upb_msgval val2, upb_fieldtype_t type,
155              const upb_msgdef *m) {
156   switch (type) {
157     case UPB_TYPE_BOOL:
158       return val1.bool_val == val2.bool_val;
159     case UPB_TYPE_INT32:
160     case UPB_TYPE_UINT32:
161     case UPB_TYPE_ENUM:
162       return val1.int32_val == val2.int32_val;
163     case UPB_TYPE_INT64:
164     case UPB_TYPE_UINT64:
165       return val1.int64_val == val2.int64_val;
166     case UPB_TYPE_FLOAT:
167       return val1.float_val == val2.float_val;
168     case UPB_TYPE_DOUBLE:
169       return val1.double_val == val2.double_val;
170     case UPB_TYPE_STRING:
171     case UPB_TYPE_BYTES:
172       return val1.str_val.size == val2.str_val.size &&
173           memcmp(val1.str_val.data, val2.str_val.data, val1.str_val.size) == 0;
174     case UPB_TYPE_MESSAGE:
175       return MessageEq(val1.msg_val, val2.msg_val, m);
176     default:
177       return false;
178   }
179 }
180 
181 /**
182  * MessageEq()
183  */
MessageEq(const upb_msg * m1,const upb_msg * m2,const upb_msgdef * m)184 static bool MessageEq(const upb_msg *m1, const upb_msg *m2, const upb_msgdef *m) {
185   upb_msg_field_iter i;
186 
187   for(upb_msg_field_begin(&i, m);
188       !upb_msg_field_done(&i);
189       upb_msg_field_next(&i)) {
190     const upb_fielddef *f = upb_msg_iter_field(&i);
191     upb_msgval val1 = upb_msg_get(m1, f);
192     upb_msgval val2 = upb_msg_get(m2, f);
193     upb_fieldtype_t type = upb_fielddef_type(f);
194     const upb_msgdef *sub_m = upb_fielddef_msgsubdef(f);
195 
196     if (upb_fielddef_haspresence(f)) {
197       if (upb_msg_has(m1, f) != upb_msg_has(m2, f)) {
198         return false;
199       }
200       if (!upb_msg_has(m1, f)) continue;
201     }
202 
203     if (upb_fielddef_ismap(f)) {
204       const upb_fielddef *key_f = upb_msgdef_itof(sub_m, 1);
205       const upb_fielddef *val_f = upb_msgdef_itof(sub_m, 2);
206       upb_fieldtype_t key_type = upb_fielddef_type(key_f);
207       upb_fieldtype_t val_type = upb_fielddef_type(val_f);
208       const upb_msgdef *val_m = upb_fielddef_msgsubdef(val_f);
209       if (!MapEq(val1.map_val, val2.map_val, key_type, val_type, val_m)) {
210         return false;
211       }
212     } else if (upb_fielddef_isseq(f)) {
213       if (!ArrayEq(val1.array_val, val2.array_val, type, sub_m)) return false;
214     } else {
215       if (!ValueEq(val1, val2, type, sub_m)) return false;
216     }
217   }
218 
219   return true;
220 }
221 
222 /**
223  * Message_compare_objects()
224  *
225  * Object handler for comparing two message objects. Called whenever PHP code
226  * does:
227  *
228  *   $m1 == $m2
229  */
Message_compare_objects(zval * m1,zval * m2)230 static int Message_compare_objects(zval *m1, zval *m2) {
231   Message* intern1 = (Message*)Z_OBJ_P(m1);
232   Message* intern2 = (Message*)Z_OBJ_P(m2);
233   const upb_msgdef *m = intern1->desc->msgdef;
234 
235   if (intern2->desc->msgdef != m) return 1;
236 
237   return MessageEq(intern1->msg, intern2->msg, m) ? 0 : 1;
238 }
239 
240 /**
241  * Message_has_property()
242  *
243  * Object handler for testing whether a property exists. Called when PHP code
244  * does any of:
245  *
246  *   isset($message->foobar);
247  *   property_exists($message->foobar);
248  *
249  * Note that all properties of generated messages are private, so this should
250  * only be possible to invoke from generated code, which has accessors like this
251  * (if the field has presence):
252  *
253  *   public function hasOptionalInt32()
254  *   {
255  *       return isset($this->optional_int32);
256  *   }
257  */
Message_has_property(PROTO_VAL * obj,PROTO_STR * member,int has_set_exists,void ** cache_slot)258 static int Message_has_property(PROTO_VAL *obj, PROTO_STR *member,
259                                 int has_set_exists,
260                                 void **cache_slot) {
261   Message* intern = PROTO_MSG_P(obj);
262   const upb_fielddef *f = get_field(intern, member);
263 
264   if (!f) return 0;
265 
266   if (!upb_fielddef_haspresence(f)) {
267     zend_throw_exception_ex(
268         NULL, 0,
269         "Cannot call isset() on field %s which does not have presence.",
270         ZSTR_VAL(intern->desc->class_entry->name));
271     return 0;
272   }
273 
274   return upb_msg_has(intern->msg, f);
275 }
276 
277 /**
278  * Message_unset_property()
279  *
280  * Object handler for unsetting a property. Called when PHP code calls:
281  * does any of:
282  *
283  *   unset($message->foobar);
284  *
285  * Note that all properties of generated messages are private, so this should
286  * only be possible to invoke from generated code, which has accessors like this
287  * (if the field has presence):
288  *
289  *   public function clearOptionalInt32()
290  *   {
291  *       unset($this->optional_int32);
292  *   }
293  */
Message_unset_property(PROTO_VAL * obj,PROTO_STR * member,void ** cache_slot)294 static void Message_unset_property(PROTO_VAL *obj, PROTO_STR *member,
295                                    void **cache_slot) {
296   Message* intern = PROTO_MSG_P(obj);
297   const upb_fielddef *f = get_field(intern, member);
298 
299   if (!f) return;
300 
301   if (!upb_fielddef_haspresence(f)) {
302     zend_throw_exception_ex(
303         NULL, 0,
304         "Cannot call unset() on field %s which does not have presence.",
305         ZSTR_VAL(intern->desc->class_entry->name));
306     return;
307   }
308 
309   upb_msg_clearfield(intern->msg, f);
310 }
311 
312 
313 /**
314  * Message_read_property()
315  *
316  * Object handler for reading a property in PHP. Called when PHP code does:
317  *
318  *   $x = $message->foobar;
319  *
320  * Note that all properties of generated messages are private, so this should
321  * only be possible to invoke from generated code, which has accessors like:
322  *
323  *   public function getOptionalInt32()
324  *   {
325  *       return $this->optional_int32;
326  *   }
327  *
328  * We lookup the field and return the scalar, RepeatedField, or MapField for
329  * this field.
330  */
Message_read_property(PROTO_VAL * obj,PROTO_STR * member,int type,void ** cache_slot,zval * rv)331 static zval *Message_read_property(PROTO_VAL *obj, PROTO_STR *member,
332                                    int type, void **cache_slot, zval *rv) {
333   Message* intern = PROTO_MSG_P(obj);
334   const upb_fielddef *f = get_field(intern, member);
335 
336   if (!f) return NULL;
337   Message_get(intern, f, rv);
338   return rv;
339 }
340 
341 /**
342  * Message_write_property()
343  *
344  * Object handler for writing a property in PHP. Called when PHP code does:
345  *
346  *   $message->foobar = $x;
347  *
348  * Note that all properties of generated messages are private, so this should
349  * only be possible to invoke from generated code, which has accessors like:
350  *
351  *   public function setOptionalInt32($var)
352  *   {
353  *       GPBUtil::checkInt32($var);
354  *       $this->optional_int32 = $var;
355  *
356  *       return $this;
357  *   }
358  *
359  * The C extension version of checkInt32() doesn't actually check anything, so
360  * we perform all checking and conversion in this function.
361  */
Message_write_property(PROTO_VAL * obj,PROTO_STR * member,zval * val,void ** cache_slot)362 static PROTO_RETURN_VAL Message_write_property(
363     PROTO_VAL *obj, PROTO_STR *member, zval *val, void **cache_slot) {
364   Message* intern = PROTO_MSG_P(obj);
365   const upb_fielddef *f = get_field(intern, member);
366 
367   if (f && Message_set(intern, f, val)) {
368 #if PHP_VERSION_ID < 70400
369     return;
370 #else
371     return val;
372 #endif
373   } else {
374 #if PHP_VERSION_ID < 70400
375     return;
376 #else
377     return &EG(error_zval);
378 #endif
379   }
380 }
381 
382 /**
383  * Message_get_property_ptr_ptr()
384  *
385  * Object handler for the get_property_ptr_ptr event in PHP. This returns a
386  * reference to our internal properties. We don't support this, so we return
387  * NULL.
388  */
Message_get_property_ptr_ptr(PROTO_VAL * object,PROTO_STR * member,int type,void ** cache_slot)389 static zval *Message_get_property_ptr_ptr(PROTO_VAL *object, PROTO_STR *member,
390                                           int type,
391                                           void **cache_slot) {
392   return NULL;  // We do not have a properties table.
393 }
394 
395 /**
396  * Message_get_properties()
397  *
398  * Object handler for the get_properties event in PHP. This returns a HashTable
399  * of our internal properties. We don't support this, so we return NULL.
400  */
Message_get_properties(PROTO_VAL * object)401 static HashTable *Message_get_properties(PROTO_VAL *object) {
402   return NULL;  // We don't offer direct references to our properties.
403 }
404 
405 // C Functions from message.h. /////////////////////////////////////////////////
406 
407 // These are documented in the header file.
408 
Message_GetPhpWrapper(zval * val,const Descriptor * desc,upb_msg * msg,zval * arena)409 void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg,
410                            zval *arena) {
411   if (!msg) {
412     ZVAL_NULL(val);
413     return;
414   }
415 
416   if (!ObjCache_Get(msg, val)) {
417     Message *intern = emalloc(sizeof(Message));
418     // XXX(haberman): verify whether we actually want to take this route.
419     desc->class_entry->default_properties_count = 0;
420     zend_object_std_init(&intern->std, desc->class_entry);
421     intern->std.handlers = &message_object_handlers;
422     ZVAL_COPY(&intern->arena, arena);
423     intern->desc = desc;
424     intern->msg = msg;
425     ZVAL_OBJ(val, &intern->std);
426     ObjCache_Add(intern->msg, &intern->std);
427   }
428 }
429 
Message_GetUpbMessage(zval * val,const Descriptor * desc,upb_arena * arena,upb_msg ** msg)430 bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena,
431                            upb_msg **msg) {
432   PBPHP_ASSERT(desc);
433 
434   if (Z_ISREF_P(val)) {
435     ZVAL_DEREF(val);
436   }
437 
438   if (Z_TYPE_P(val) == IS_NULL) {
439     *msg = NULL;
440     return true;
441   }
442 
443   if (Z_TYPE_P(val) == IS_OBJECT &&
444       instanceof_function(Z_OBJCE_P(val), desc->class_entry)) {
445     Message *intern = (Message*)Z_OBJ_P(val);
446     upb_arena_fuse(arena, Arena_Get(&intern->arena));
447     *msg = intern->msg;
448     return true;
449   } else {
450     zend_throw_exception_ex(NULL, 0, "Given value is not an instance of %s.",
451                             ZSTR_VAL(desc->class_entry->name));
452     return false;
453   }
454 }
455 
456 // Message PHP methods /////////////////////////////////////////////////////////
457 
458 /**
459  * Message_InitFromPhp()
460  *
461  * Helper method to handle the initialization of a message from a PHP value, eg.
462  *
463  *   $m = new TestMessage([
464  *       'optional_int32' => -42,
465  *       'optional_bool' => true,
466  *       'optional_string' => 'a',
467  *       'optional_enum' => TestEnum::ONE,
468  *       'optional_message' => new Sub([
469  *           'a' => 33
470  *       ]),
471  *       'repeated_int32' => [-42, -52],
472  *       'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE],
473  *       'repeated_message' => [new Sub(['a' => 34]),
474  *                              new Sub(['a' => 35])],
475  *       'map_int32_int32' => [-62 => -62],
476  *       'map_int32_enum' => [1 => TestEnum::ONE],
477  *       'map_int32_message' => [1 => new Sub(['a' => 36])],
478  *   ]);
479  *
480  * The initializer must be an array.
481  */
Message_InitFromPhp(upb_msg * msg,const upb_msgdef * m,zval * init,upb_arena * arena)482 bool Message_InitFromPhp(upb_msg *msg, const upb_msgdef *m, zval *init,
483                          upb_arena *arena) {
484   HashTable* table = HASH_OF(init);
485   HashPosition pos;
486 
487   if (Z_ISREF_P(init)) {
488     ZVAL_DEREF(init);
489   }
490 
491   if (Z_TYPE_P(init) != IS_ARRAY) {
492     zend_throw_exception_ex(NULL, 0,
493                             "Initializer for a message %s must be an array.",
494                             upb_msgdef_fullname(m));
495     return false;
496   }
497 
498   zend_hash_internal_pointer_reset_ex(table, &pos);
499 
500   while (true) {  // Iterate over key/value pairs.
501     zval key;
502     zval *val;
503     const upb_fielddef *f;
504     upb_msgval msgval;
505 
506     zend_hash_get_current_key_zval_ex(table, &key, &pos);
507     val = zend_hash_get_current_data_ex(table, &pos);
508 
509     if (!val) return true;  // Finished iteration.
510 
511     if (Z_ISREF_P(val)) {
512       ZVAL_DEREF(val);
513     }
514 
515     f = upb_msgdef_ntof(m, Z_STRVAL_P(&key), Z_STRLEN_P(&key));
516 
517     if (!f) {
518       zend_throw_exception_ex(NULL, 0,
519                               "No such field %s", Z_STRVAL_P(&key));
520       return false;
521     }
522 
523     if (upb_fielddef_ismap(f)) {
524       msgval.map_val = MapField_GetUpbMap(val, f, arena);
525       if (!msgval.map_val) return false;
526     } else if (upb_fielddef_isseq(f)) {
527       msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
528       if (!msgval.array_val) return false;
529     } else {
530       const Descriptor *desc = Descriptor_GetFromFieldDef(f);
531       upb_fieldtype_t type = upb_fielddef_type(f);
532       if (!Convert_PhpToUpbAutoWrap(val, &msgval, type, desc, arena)) {
533         return false;
534       }
535     }
536 
537     upb_msg_set(msg, f, msgval, arena);
538     zend_hash_move_forward_ex(table, &pos);
539     zval_dtor(&key);
540   }
541 }
542 
Message_Initialize(Message * intern,const Descriptor * desc)543 static void Message_Initialize(Message *intern, const Descriptor *desc) {
544   intern->desc = desc;
545   intern->msg = upb_msg_new(desc->msgdef, Arena_Get(&intern->arena));
546   ObjCache_Add(intern->msg, &intern->std);
547 }
548 
549 /**
550  * Message::__construct()
551  *
552  * Constructor for Message.
553  * @param array Map of initial values ['k' = val]
554  */
PHP_METHOD(Message,__construct)555 PHP_METHOD(Message, __construct) {
556   Message* intern = (Message*)Z_OBJ_P(getThis());
557   const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis()));
558   upb_arena *arena = Arena_Get(&intern->arena);
559   zval *init_arr = NULL;
560 
561   Message_Initialize(intern, desc);
562 
563   if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) {
564     return;
565   }
566 
567   if (init_arr) {
568     Message_InitFromPhp(intern->msg, desc->msgdef, init_arr, arena);
569   }
570 }
571 
572 /**
573  * Message::discardUnknownFields()
574  *
575  * Discards any unknown fields for this message or any submessages.
576  */
PHP_METHOD(Message,discardUnknownFields)577 PHP_METHOD(Message, discardUnknownFields) {
578   Message* intern = (Message*)Z_OBJ_P(getThis());
579   upb_msg_discardunknown(intern->msg, intern->desc->msgdef, 64);
580 }
581 
582 /**
583  * Message::clear()
584  *
585  * Clears all fields of this message.
586  */
PHP_METHOD(Message,clear)587 PHP_METHOD(Message, clear) {
588   Message* intern = (Message*)Z_OBJ_P(getThis());
589   upb_msg_clear(intern->msg, intern->desc->msgdef);
590 }
591 
592 /**
593  * Message::mergeFrom()
594  *
595  * Merges from the given message, which must be of the same class as us.
596  * @param object Message to merge from.
597  */
PHP_METHOD(Message,mergeFrom)598 PHP_METHOD(Message, mergeFrom) {
599   Message* intern = (Message*)Z_OBJ_P(getThis());
600   Message* from;
601   upb_arena *arena = Arena_Get(&intern->arena);
602   const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
603   zval* value;
604   char *pb;
605   size_t size;
606   bool ok;
607 
608   if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &value,
609                             intern->desc->class_entry) == FAILURE) {
610     return;
611   }
612 
613   from = (Message*)Z_OBJ_P(value);
614 
615   // Should be guaranteed since we passed the class type to
616   // zend_parse_parameters().
617   PBPHP_ASSERT(from->desc == intern->desc);
618 
619   // TODO(haberman): use a temp arena for this once we can make upb_decode()
620   // copy strings.
621   pb = upb_encode(from->msg, l, arena, &size);
622 
623   if (!pb) {
624     zend_throw_exception_ex(NULL, 0, "Max nesting exceeded");
625     return;
626   }
627 
628   ok = upb_decode(pb, size, intern->msg, l, arena);
629   PBPHP_ASSERT(ok);
630 }
631 
632 /**
633  * Message::mergeFromString()
634  *
635  * Merges from the given string.
636  * @param string Binary protobuf data to merge.
637  */
PHP_METHOD(Message,mergeFromString)638 PHP_METHOD(Message, mergeFromString) {
639   Message* intern = (Message*)Z_OBJ_P(getThis());
640   char *data = NULL;
641   char *data_copy = NULL;
642   zend_long data_len;
643   const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
644   upb_arena *arena = Arena_Get(&intern->arena);
645 
646   if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &data_len) ==
647       FAILURE) {
648     return;
649   }
650 
651   // TODO(haberman): avoid this copy when we can make the decoder copy.
652   data_copy = upb_arena_malloc(arena, data_len);
653   memcpy(data_copy, data, data_len);
654 
655   if (!upb_decode(data_copy, data_len, intern->msg, l, arena)) {
656     zend_throw_exception_ex(NULL, 0, "Error occurred during parsing");
657     return;
658   }
659 }
660 
661 /**
662  * Message::serializeToString()
663  *
664  * Serializes this message instance to protobuf data.
665  * @return string Serialized protobuf data.
666  */
PHP_METHOD(Message,serializeToString)667 PHP_METHOD(Message, serializeToString) {
668   Message* intern = (Message*)Z_OBJ_P(getThis());
669   const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
670   upb_arena *tmp_arena = upb_arena_new();
671   char *data;
672   size_t size;
673 
674   data = upb_encode(intern->msg, l, tmp_arena, &size);
675 
676   if (!data) {
677     zend_throw_exception_ex(NULL, 0, "Error occurred during serialization");
678     upb_arena_free(tmp_arena);
679     return;
680   }
681 
682   RETVAL_STRINGL(data, size);
683   upb_arena_free(tmp_arena);
684 }
685 
686 /**
687  * Message::mergeFromJsonString()
688  *
689  * Merges the JSON data parsed from the given string.
690  * @param string Serialized JSON data.
691  */
PHP_METHOD(Message,mergeFromJsonString)692 PHP_METHOD(Message, mergeFromJsonString) {
693   Message* intern = (Message*)Z_OBJ_P(getThis());
694   char *data = NULL;
695   char *data_copy = NULL;
696   zend_long data_len;
697   upb_arena *arena = Arena_Get(&intern->arena);
698   upb_status status;
699   zend_bool ignore_json_unknown = false;
700   int options = 0;
701 
702   if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &data, &data_len,
703                             &ignore_json_unknown) == FAILURE) {
704     return;
705   }
706 
707   // TODO(haberman): avoid this copy when we can make the decoder copy.
708   data_copy = upb_arena_malloc(arena, data_len + 1);
709   memcpy(data_copy, data, data_len);
710   data_copy[data_len] = '\0';
711 
712   if (ignore_json_unknown) {
713     options |= UPB_JSONDEC_IGNOREUNKNOWN;
714   }
715 
716   upb_status_clear(&status);
717   if (!upb_json_decode(data_copy, data_len, intern->msg, intern->desc->msgdef,
718                        DescriptorPool_GetSymbolTable(), options, arena,
719                        &status)) {
720     zend_throw_exception_ex(NULL, 0, "Error occurred during parsing: %s",
721                             upb_status_errmsg(&status));
722     return;
723   }
724 }
725 
726 /**
727  * Message::serializeToJsonString()
728  *
729  * Serializes this object to JSON.
730  * @return string Serialized JSON data.
731  */
PHP_METHOD(Message,serializeToJsonString)732 PHP_METHOD(Message, serializeToJsonString) {
733   Message* intern = (Message*)Z_OBJ_P(getThis());
734   size_t size;
735   int options = 0;
736   char buf[1024];
737   zend_bool preserve_proto_fieldnames = false;
738   upb_status status;
739 
740   if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b",
741                             &preserve_proto_fieldnames) == FAILURE) {
742     return;
743   }
744 
745   if (preserve_proto_fieldnames) {
746     options |= UPB_JSONENC_PROTONAMES;
747   }
748 
749   upb_status_clear(&status);
750   size = upb_json_encode(intern->msg, intern->desc->msgdef,
751                          DescriptorPool_GetSymbolTable(), options, buf,
752                          sizeof(buf), &status);
753 
754   if (!upb_ok(&status)) {
755     zend_throw_exception_ex(NULL, 0,
756                             "Error occurred during JSON serialization: %s",
757                             upb_status_errmsg(&status));
758     return;
759   }
760 
761   if (size >= sizeof(buf)) {
762     char *buf2 = malloc(size + 1);
763     upb_json_encode(intern->msg, intern->desc->msgdef,
764                     DescriptorPool_GetSymbolTable(), options, buf2, size + 1,
765                     &status);
766     RETVAL_STRINGL(buf2, size);
767     free(buf2);
768   } else {
769     RETVAL_STRINGL(buf, size);
770   }
771 }
772 
773 /**
774  * Message::readWrapperValue()
775  *
776  * Returns an unboxed value for the given field. This is called from generated
777  * methods for wrapper fields, eg.
778  *
779  *   public function getDoubleValueUnwrapped()
780  *   {
781  *       return $this->readWrapperValue("double_value");
782  *   }
783  *
784  * @return Unwrapped field value or null.
785  */
PHP_METHOD(Message,readWrapperValue)786 PHP_METHOD(Message, readWrapperValue) {
787   Message* intern = (Message*)Z_OBJ_P(getThis());
788   char* member;
789   const upb_fielddef *f;
790   zend_long size;
791 
792   if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &member, &size) == FAILURE) {
793     return;
794   }
795 
796   f = upb_msgdef_ntof(intern->desc->msgdef, member, size);
797 
798   if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) {
799     zend_throw_exception_ex(NULL, 0, "Message %s has no field %s",
800                             upb_msgdef_fullname(intern->desc->msgdef), member);
801     return;
802   }
803 
804   if (upb_msg_has(intern->msg, f)) {
805     const upb_msg *wrapper = upb_msg_get(intern->msg, f).msg_val;
806     const upb_msgdef *m = upb_fielddef_msgsubdef(f);
807     const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
808     const upb_fieldtype_t val_type = upb_fielddef_type(val_f);
809     upb_msgval msgval = upb_msg_get(wrapper, val_f);
810     zval ret;
811     Convert_UpbToPhp(msgval, &ret, val_type, NULL, &intern->arena);
812     RETURN_ZVAL(&ret, 1, 0);
813   } else {
814     RETURN_NULL();
815   }
816 }
817 
818 /**
819  * Message::writeWrapperValue()
820  *
821  * Sets the given wrapper field to the given unboxed value. This is called from
822  * generated methods for wrapper fields, eg.
823  *
824  *
825  *   public function setDoubleValueUnwrapped($var)
826  *   {
827  *       $this->writeWrapperValue("double_value", $var);
828  *       return $this;
829  *   }
830  *
831  * @param Unwrapped field value or null.
832  */
PHP_METHOD(Message,writeWrapperValue)833 PHP_METHOD(Message, writeWrapperValue) {
834   Message* intern = (Message*)Z_OBJ_P(getThis());
835   upb_arena *arena = Arena_Get(&intern->arena);
836   char* member;
837   const upb_fielddef *f;
838   upb_msgval msgval;
839   zend_long size;
840   zval* val;
841 
842   if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &member, &size, &val) ==
843       FAILURE) {
844     return;
845   }
846 
847   f = upb_msgdef_ntof(intern->desc->msgdef, member, size);
848 
849   if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) {
850     zend_throw_exception_ex(NULL, 0, "Message %s has no field %s",
851                             upb_msgdef_fullname(intern->desc->msgdef), member);
852     return;
853   }
854 
855   if (Z_ISREF_P(val)) {
856     ZVAL_DEREF(val);
857   }
858 
859   if (Z_TYPE_P(val) == IS_NULL) {
860     upb_msg_clearfield(intern->msg, f);
861   } else {
862     const upb_msgdef *m = upb_fielddef_msgsubdef(f);
863     const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
864     upb_fieldtype_t val_type = upb_fielddef_type(val_f);
865     upb_msg *wrapper;
866 
867     if (!Convert_PhpToUpb(val, &msgval, val_type, NULL, arena)) {
868       return;  // Error is already set.
869     }
870 
871     wrapper = upb_msg_mutable(intern->msg, f, arena).msg;
872     upb_msg_set(wrapper, val_f, msgval, arena);
873   }
874 }
875 
876 /**
877  * Message::whichOneof()
878  *
879  * Given a oneof name, returns the name of the field that is set for this oneof,
880  * or otherwise the empty string.
881  *
882  * @return string The field name in this oneof that is currently set.
883  */
PHP_METHOD(Message,whichOneof)884 PHP_METHOD(Message, whichOneof) {
885   Message* intern = (Message*)Z_OBJ_P(getThis());
886   const upb_oneofdef* oneof;
887   const upb_fielddef* field;
888   char* name;
889   zend_long len;
890 
891   if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &len) == FAILURE) {
892     return;
893   }
894 
895   oneof = upb_msgdef_ntoo(intern->desc->msgdef, name, len);
896 
897   if (!oneof) {
898     zend_throw_exception_ex(NULL, 0, "Message %s has no oneof %s",
899                             upb_msgdef_fullname(intern->desc->msgdef), name);
900     return;
901   }
902 
903   field = upb_msg_whichoneof(intern->msg, oneof);
904   RETURN_STRING(field ? upb_fielddef_name(field) : "");
905 }
906 
907 /**
908  * Message::hasOneof()
909  *
910  * Returns the presence of the given oneof field, given a field number. Called
911  * from generated code methods such as:
912  *
913  *    public function hasDoubleValueOneof()
914  *    {
915  *        return $this->hasOneof(10);
916  *    }
917  *
918  * @return boolean
919  */
PHP_METHOD(Message,hasOneof)920 PHP_METHOD(Message, hasOneof) {
921   Message* intern = (Message*)Z_OBJ_P(getThis());
922   zend_long field_num;
923   const upb_fielddef* f;
924 
925   if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &field_num) == FAILURE) {
926     return;
927   }
928 
929   f = upb_msgdef_itof(intern->desc->msgdef, field_num);
930 
931   if (!f || !upb_fielddef_realcontainingoneof(f)) {
932     php_error_docref(NULL, E_USER_ERROR,
933                      "Internal error, no such oneof field %d\n",
934                      (int)field_num);
935   }
936 
937   RETVAL_BOOL(upb_msg_has(intern->msg, f));
938 }
939 
940 /**
941  * Message::readOneof()
942  *
943  * Returns the contents of the given oneof field, given a field number. Called
944  * from generated code methods such as:
945  *
946  *    public function getDoubleValueOneof()
947  *    {
948  *        return $this->readOneof(10);
949  *    }
950  *
951  * @return object The oneof's field value.
952  */
PHP_METHOD(Message,readOneof)953 PHP_METHOD(Message, readOneof) {
954   Message* intern = (Message*)Z_OBJ_P(getThis());
955   zend_long field_num;
956   const upb_fielddef* f;
957   zval ret;
958 
959   if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &field_num) == FAILURE) {
960     return;
961   }
962 
963   f = upb_msgdef_itof(intern->desc->msgdef, field_num);
964 
965   if (!f || !upb_fielddef_realcontainingoneof(f)) {
966     php_error_docref(NULL, E_USER_ERROR,
967                      "Internal error, no such oneof field %d\n",
968                      (int)field_num);
969   }
970 
971   {
972     upb_msgval msgval = upb_msg_get(intern->msg, f);
973     const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
974     Convert_UpbToPhp(msgval, &ret, upb_fielddef_type(f), subdesc,
975                      &intern->arena);
976   }
977 
978   RETURN_ZVAL(&ret, 1, 0);
979 }
980 
981 /**
982  * Message::writeOneof()
983  *
984  * Sets the contents of the given oneof field, given a field number. Called
985  * from generated code methods such as:
986  *
987  *    public function setDoubleValueOneof($var)
988  *   {
989  *       GPBUtil::checkMessage($var, \Google\Protobuf\DoubleValue::class);
990  *       $this->writeOneof(10, $var);
991  *
992  *       return $this;
993  *   }
994  *
995  * The C extension version of GPBUtil::check*() does nothing, so we perform
996  * all type checking and conversion here.
997  *
998  * @param integer The field number we are setting.
999  * @param object The field value we want to set.
1000  */
PHP_METHOD(Message,writeOneof)1001 PHP_METHOD(Message, writeOneof) {
1002   Message* intern = (Message*)Z_OBJ_P(getThis());
1003   zend_long field_num;
1004   const upb_fielddef* f;
1005   upb_arena *arena = Arena_Get(&intern->arena);
1006   upb_msgval msgval;
1007   zval* val;
1008 
1009   if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &field_num, &val) ==
1010       FAILURE) {
1011     return;
1012   }
1013 
1014   f = upb_msgdef_itof(intern->desc->msgdef, field_num);
1015 
1016   if (!Convert_PhpToUpb(val, &msgval, upb_fielddef_type(f),
1017                         Descriptor_GetFromFieldDef(f), arena)) {
1018     return;
1019   }
1020 
1021   upb_msg_set(intern->msg, f, msgval, arena);
1022 }
1023 
1024 ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0)
1025 ZEND_END_ARG_INFO()
1026 
1027 ZEND_BEGIN_ARG_INFO_EX(arginfo_mergeFrom, 0, 0, 1)
1028   ZEND_ARG_INFO(0, data)
1029 ZEND_END_ARG_INFO()
1030 
1031 ZEND_BEGIN_ARG_INFO_EX(arginfo_read, 0, 0, 1)
1032   ZEND_ARG_INFO(0, field)
1033 ZEND_END_ARG_INFO()
1034 
1035 ZEND_BEGIN_ARG_INFO_EX(arginfo_write, 0, 0, 2)
1036   ZEND_ARG_INFO(0, field)
1037   ZEND_ARG_INFO(0, value)
1038 ZEND_END_ARG_INFO()
1039 
1040 static zend_function_entry Message_methods[] = {
1041   PHP_ME(Message, clear,                 arginfo_void,      ZEND_ACC_PUBLIC)
1042   PHP_ME(Message, discardUnknownFields,  arginfo_void,      ZEND_ACC_PUBLIC)
1043   PHP_ME(Message, serializeToString,     arginfo_void,      ZEND_ACC_PUBLIC)
1044   PHP_ME(Message, mergeFromString,       arginfo_mergeFrom, ZEND_ACC_PUBLIC)
1045   PHP_ME(Message, serializeToJsonString, arginfo_void,      ZEND_ACC_PUBLIC)
1046   PHP_ME(Message, mergeFromJsonString,   arginfo_mergeFrom, ZEND_ACC_PUBLIC)
1047   PHP_ME(Message, mergeFrom,             arginfo_mergeFrom, ZEND_ACC_PUBLIC)
1048   PHP_ME(Message, readWrapperValue,      arginfo_read,      ZEND_ACC_PROTECTED)
1049   PHP_ME(Message, writeWrapperValue,     arginfo_write,     ZEND_ACC_PROTECTED)
1050   PHP_ME(Message, hasOneof,              arginfo_read,      ZEND_ACC_PROTECTED)
1051   PHP_ME(Message, readOneof,             arginfo_read,      ZEND_ACC_PROTECTED)
1052   PHP_ME(Message, writeOneof,            arginfo_write,     ZEND_ACC_PROTECTED)
1053   PHP_ME(Message, whichOneof,            arginfo_read,      ZEND_ACC_PROTECTED)
1054   PHP_ME(Message, __construct,           arginfo_void,      ZEND_ACC_PROTECTED)
1055   ZEND_FE_END
1056 };
1057 
1058 // Well-known types ////////////////////////////////////////////////////////////
1059 
1060 static const char TYPE_URL_PREFIX[] = "type.googleapis.com/";
1061 
Message_getval(Message * intern,const char * field_name)1062 static upb_msgval Message_getval(Message *intern, const char *field_name) {
1063   const upb_fielddef *f = upb_msgdef_ntofz(intern->desc->msgdef, field_name);
1064   PBPHP_ASSERT(f);
1065   return upb_msg_get(intern->msg, f);
1066 }
1067 
Message_setval(Message * intern,const char * field_name,upb_msgval val)1068 static void Message_setval(Message *intern, const char *field_name,
1069                            upb_msgval val) {
1070   const upb_fielddef *f = upb_msgdef_ntofz(intern->desc->msgdef, field_name);
1071   PBPHP_ASSERT(f);
1072   return upb_msg_set(intern->msg, f, val, Arena_Get(&intern->arena));
1073 }
1074 
StringVal(upb_strview view)1075 static upb_msgval StringVal(upb_strview view) {
1076   upb_msgval ret;
1077   ret.str_val = view;
1078   return ret;
1079 }
1080 
TryStripUrlPrefix(upb_strview * str)1081 static bool TryStripUrlPrefix(upb_strview *str) {
1082   size_t size = strlen(TYPE_URL_PREFIX);
1083   if (str->size < size || memcmp(TYPE_URL_PREFIX, str->data, size) != 0) {
1084     return false;
1085   }
1086   str->data += size;
1087   str->size -= size;
1088   return true;
1089 }
1090 
StrViewEq(upb_strview view,const char * str)1091 static bool StrViewEq(upb_strview view, const char *str) {
1092   size_t size = strlen(str);
1093   return view.size == size && memcmp(view.data, str, size) == 0;
1094 }
1095 
PHP_METHOD(google_protobuf_Any,unpack)1096 PHP_METHOD(google_protobuf_Any, unpack) {
1097   Message* intern = (Message*)Z_OBJ_P(getThis());
1098   upb_strview type_url = Message_getval(intern, "type_url").str_val;
1099   upb_strview value = Message_getval(intern, "value").str_val;
1100   upb_symtab *symtab = DescriptorPool_GetSymbolTable();
1101   const upb_msgdef *m;
1102   Descriptor *desc;
1103   zval ret;
1104 
1105   // Ensure that type_url has TYPE_URL_PREFIX as a prefix.
1106   if (!TryStripUrlPrefix(&type_url)) {
1107     zend_throw_exception(
1108         NULL, "Type url needs to be type.googleapis.com/fully-qualified",
1109         0 TSRMLS_CC);
1110     return;
1111   }
1112 
1113   m = upb_symtab_lookupmsg2(symtab, type_url.data, type_url.size);
1114 
1115   if (m == NULL) {
1116     zend_throw_exception(
1117         NULL, "Specified message in any hasn't been added to descriptor pool",
1118         0 TSRMLS_CC);
1119     return;
1120   }
1121 
1122   desc = Descriptor_GetFromMessageDef(m);
1123   PBPHP_ASSERT(desc->class_entry->create_object == Message_create);
1124   zend_object *obj = Message_create(desc->class_entry);
1125   Message *msg = (Message*)obj;
1126   Message_Initialize(msg, desc);
1127   ZVAL_OBJ(&ret, obj);
1128 
1129   // Get value.
1130   if (!upb_decode(value.data, value.size, msg->msg,
1131                   upb_msgdef_layout(desc->msgdef), Arena_Get(&msg->arena))) {
1132     zend_throw_exception_ex(NULL, 0, "Error occurred during parsing");
1133     return;
1134   }
1135 
1136   // Fuse since the parsed message could alias "value".
1137   upb_arena_fuse(Arena_Get(&intern->arena), Arena_Get(&msg->arena));
1138 
1139   RETURN_ZVAL(&ret, 1, 0);
1140 }
1141 
PHP_METHOD(google_protobuf_Any,pack)1142 PHP_METHOD(google_protobuf_Any, pack) {
1143   Message* intern = (Message*)Z_OBJ_P(getThis());
1144   upb_arena *arena = Arena_Get(&intern->arena);
1145   zval *val;
1146   Message *msg;
1147   upb_strview value;
1148   upb_strview type_url;
1149   const char *full_name;
1150   char *buf;
1151 
1152   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &val) ==
1153       FAILURE) {
1154     return;
1155   }
1156 
1157   if (!instanceof_function(Z_OBJCE_P(val), message_ce)) {
1158     zend_error(E_USER_ERROR, "Given value is not an instance of Message.");
1159     return;
1160   }
1161 
1162   msg = (Message*)Z_OBJ_P(val);
1163 
1164   // Serialize and set value.
1165   value.data = upb_encode(msg->msg, upb_msgdef_layout(msg->desc->msgdef), arena,
1166                           &value.size);
1167   Message_setval(intern, "value", StringVal(value));
1168 
1169   // Set type url: type_url_prefix + fully_qualified_name
1170   full_name = upb_msgdef_fullname(msg->desc->msgdef);
1171   type_url.size = strlen(TYPE_URL_PREFIX) + strlen(full_name);
1172   buf = upb_arena_malloc(arena, type_url.size + 1);
1173   memcpy(buf, TYPE_URL_PREFIX, strlen(TYPE_URL_PREFIX));
1174   memcpy(buf + strlen(TYPE_URL_PREFIX), full_name, strlen(full_name));
1175   type_url.data = buf;
1176   Message_setval(intern, "type_url", StringVal(type_url));
1177 }
1178 
PHP_METHOD(google_protobuf_Any,is)1179 PHP_METHOD(google_protobuf_Any, is) {
1180   Message* intern = (Message*)Z_OBJ_P(getThis());
1181   upb_strview type_url = Message_getval(intern, "type_url").str_val;
1182   zend_class_entry *klass = NULL;
1183   const upb_msgdef *m;
1184 
1185   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "C", &klass) ==
1186       FAILURE) {
1187     return;
1188   }
1189 
1190   m = NameMap_GetMessage(klass);
1191 
1192   if (m == NULL) {
1193     RETURN_BOOL(false);
1194   }
1195 
1196   RETURN_BOOL(TryStripUrlPrefix(&type_url) &&
1197               StrViewEq(type_url, upb_msgdef_fullname(m)));
1198 }
1199 
PHP_METHOD(google_protobuf_Timestamp,fromDateTime)1200 PHP_METHOD(google_protobuf_Timestamp, fromDateTime) {
1201   Message* intern = (Message*)Z_OBJ_P(getThis());
1202   zval* datetime;
1203   const char *classname = "\\DatetimeInterface";
1204   zend_string *classname_str = zend_string_init(classname, strlen(classname), 0);
1205   zend_class_entry *date_interface_ce = zend_lookup_class(classname_str);
1206 
1207   if (date_interface_ce == NULL) {
1208     zend_error(E_ERROR, "Make sure date extension is enabled.");
1209     return;
1210   }
1211 
1212   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &datetime,
1213                             date_interface_ce) == FAILURE) {
1214     zend_error(E_USER_ERROR, "Expect DatetimeInterface.");
1215     return;
1216   }
1217 
1218   upb_msgval timestamp_seconds;
1219   {
1220     zval retval;
1221     zval function_name;
1222 
1223     ZVAL_STRING(&function_name, "date_timestamp_get");
1224 
1225     if (call_user_function(EG(function_table), NULL, &function_name, &retval, 1,
1226                            datetime) == FAILURE ||
1227         !Convert_PhpToUpb(&retval, &timestamp_seconds, UPB_TYPE_INT64, NULL,
1228                           NULL)) {
1229       zend_error(E_ERROR, "Cannot get timestamp from DateTime.");
1230       return;
1231     }
1232 
1233     zval_dtor(&retval);
1234     zval_dtor(&function_name);
1235   }
1236 
1237   upb_msgval timestamp_nanos;
1238   {
1239     zval retval;
1240     zval function_name;
1241     zval format_string;
1242 
1243     ZVAL_STRING(&function_name, "date_format");
1244     ZVAL_STRING(&format_string, "u");
1245 
1246     zval params[2] = {
1247         *datetime,
1248         format_string,
1249     };
1250 
1251     if (call_user_function(EG(function_table), NULL, &function_name, &retval, 2,
1252                            params) == FAILURE ||
1253         !Convert_PhpToUpb(&retval, &timestamp_nanos, UPB_TYPE_INT32, NULL,
1254                           NULL)) {
1255       zend_error(E_ERROR, "Cannot format DateTime.");
1256       return;
1257     }
1258 
1259     timestamp_nanos.int32_val *= 1000;
1260 
1261     zval_dtor(&retval);
1262     zval_dtor(&function_name);
1263     zval_dtor(&format_string);
1264   }
1265 
1266   Message_setval(intern, "seconds", timestamp_seconds);
1267   Message_setval(intern, "nanos", timestamp_nanos);
1268 
1269   RETURN_NULL();
1270 }
1271 
PHP_METHOD(google_protobuf_Timestamp,toDateTime)1272 PHP_METHOD(google_protobuf_Timestamp, toDateTime) {
1273   Message* intern = (Message*)Z_OBJ_P(getThis());
1274   upb_msgval seconds = Message_getval(intern, "seconds");
1275   upb_msgval nanos = Message_getval(intern, "nanos");
1276 
1277   // Get formatted time string.
1278   char formatted_time[32];
1279   snprintf(formatted_time, sizeof(formatted_time), "%" PRId64 ".%06" PRId32,
1280            seconds.int64_val, nanos.int32_val / 1000);
1281 
1282   // Create Datetime object.
1283   zval datetime;
1284   zval function_name;
1285   zval format_string;
1286   zval formatted_time_php;
1287 
1288   ZVAL_STRING(&function_name, "date_create_from_format");
1289   ZVAL_STRING(&format_string, "U.u");
1290   ZVAL_STRING(&formatted_time_php, formatted_time);
1291 
1292   zval params[2] = {
1293     format_string,
1294     formatted_time_php,
1295   };
1296 
1297   if (call_user_function(EG(function_table), NULL, &function_name, &datetime, 2,
1298                          params) == FAILURE) {
1299     zend_error(E_ERROR, "Cannot create DateTime.");
1300     return;
1301   }
1302 
1303   zval_dtor(&function_name);
1304   zval_dtor(&format_string);
1305   zval_dtor(&formatted_time_php);
1306 
1307   ZVAL_OBJ(return_value, Z_OBJ(datetime));
1308 }
1309 
1310 #include "wkt.inc"
1311 
1312 /**
1313  * Message_ModuleInit()
1314  *
1315  * Called when the C extension is loaded to register all types.
1316  */
Message_ModuleInit()1317 void Message_ModuleInit() {
1318   zend_class_entry tmp_ce;
1319   zend_object_handlers *h = &message_object_handlers;
1320 
1321   INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\Message",
1322                    Message_methods);
1323 
1324   message_ce = zend_register_internal_class(&tmp_ce);
1325   message_ce->create_object = Message_create;
1326 
1327   memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
1328   h->dtor_obj = Message_dtor;
1329   h->compare_objects = Message_compare_objects;
1330   h->read_property = Message_read_property;
1331   h->write_property = Message_write_property;
1332   h->has_property = Message_has_property;
1333   h->unset_property = Message_unset_property;
1334   h->get_properties = Message_get_properties;
1335   h->get_property_ptr_ptr = Message_get_property_ptr_ptr;
1336 
1337   WellKnownTypes_ModuleInit();  /* From wkt.inc. */
1338 }
1339