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, ×tamp_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, ×tamp_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