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