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