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