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