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