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