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
40 #include "arena.h"
41 #include "array.h"
42 #include "convert.h"
43 #include "def.h"
44 #include "map.h"
45 #include "php-upb.h"
46 #include "protobuf.h"
47
48 // -----------------------------------------------------------------------------
49 // Message
50 // -----------------------------------------------------------------------------
51
52 typedef struct {
53 zend_object std;
54 zval arena;
55 const Descriptor* desc;
56 upb_msg *msg;
57 } Message;
58
59 zend_class_entry *message_ce;
60 static zend_object_handlers message_object_handlers;
61
62 // PHP Object Handlers /////////////////////////////////////////////////////////
63
64 /**
65 * Message_create()
66 *
67 * PHP class entry function to allocate and initialize a new Message object.
68 */
Message_create(zend_class_entry * class_type)69 static zend_object* Message_create(zend_class_entry *class_type) {
70 Message *intern = emalloc(sizeof(Message));
71 // XXX(haberman): verify whether we actually want to take this route.
72 class_type->default_properties_count = 0;
73 zend_object_std_init(&intern->std, class_type);
74 intern->std.handlers = &message_object_handlers;
75 Arena_Init(&intern->arena);
76 return &intern->std;
77 }
78
79 /**
80 * Message_dtor()
81 *
82 * Object handler to destroy a Message. This releases all resources associated
83 * with the message. Note that it is possible to access a destroyed object from
84 * PHP in rare cases.
85 */
Message_dtor(zend_object * obj)86 static void Message_dtor(zend_object* obj) {
87 Message* intern = (Message*)obj;
88 ObjCache_Delete(intern->msg);
89 zval_dtor(&intern->arena);
90 zend_object_std_dtor(&intern->std);
91 }
92
93 /**
94 * get_field()
95 *
96 * Helper function to look up a field given a member name (as a string).
97 */
get_field(Message * msg,PROTO_STR * member)98 static const upb_fielddef *get_field(Message *msg, PROTO_STR *member) {
99 const upb_msgdef *m = msg->desc->msgdef;
100 const upb_fielddef *f =
101 upb_msgdef_ntof(m, PROTO_STRVAL_P(member), PROTO_STRLEN_P(member));
102
103 if (!f) {
104 zend_throw_exception_ex(NULL, 0, "No such property %s.",
105 ZSTR_VAL(msg->desc->class_entry->name));
106 }
107
108 return f;
109 }
110
111 /**
112 * Message_has_property()
113 *
114 * Object handler for testing whether a property exists. Called when PHP code
115 * does any of:
116 *
117 * isset($message->foobar);
118 * property_exists($message->foobar);
119 *
120 * Note that all properties of generated messages are private, so this should
121 * only be possible to invoke from generated code, which has accessors like this
122 * (if the field has presence):
123 *
124 * public function hasOptionalInt32()
125 * {
126 * return isset($this->optional_int32);
127 * }
128 */
Message_has_property(PROTO_VAL * obj,PROTO_STR * member,int has_set_exists,void ** cache_slot)129 static int Message_has_property(PROTO_VAL *obj, PROTO_STR *member,
130 int has_set_exists,
131 void **cache_slot) {
132 Message* intern = PROTO_MSG_P(obj);
133 const upb_fielddef *f = get_field(intern, member);
134
135 if (!f) return 0;
136
137 if (!upb_fielddef_haspresence(f)) {
138 zend_throw_exception_ex(
139 NULL, 0,
140 "Cannot call isset() on field %s which does not have presence.",
141 ZSTR_VAL(intern->desc->class_entry->name));
142 return 0;
143 }
144
145 return upb_msg_has(intern->msg, f);
146 }
147
148 /**
149 * Message_unset_property()
150 *
151 * Object handler for unsetting a property. Called when PHP code calls:
152 * does any of:
153 *
154 * unset($message->foobar);
155 *
156 * Note that all properties of generated messages are private, so this should
157 * only be possible to invoke from generated code, which has accessors like this
158 * (if the field has presence):
159 *
160 * public function clearOptionalInt32()
161 * {
162 * unset($this->optional_int32);
163 * }
164 */
Message_unset_property(PROTO_VAL * obj,PROTO_STR * member,void ** cache_slot)165 static void Message_unset_property(PROTO_VAL *obj, PROTO_STR *member,
166 void **cache_slot) {
167 Message* intern = PROTO_MSG_P(obj);
168 const upb_fielddef *f = get_field(intern, member);
169
170 if (!f) return;
171
172 if (!upb_fielddef_haspresence(f)) {
173 zend_throw_exception_ex(
174 NULL, 0,
175 "Cannot call unset() on field %s which does not have presence.",
176 ZSTR_VAL(intern->desc->class_entry->name));
177 return;
178 }
179
180 upb_msg_clearfield(intern->msg, f);
181 }
182
183 /**
184 * Message_read_property()
185 *
186 * Object handler for reading a property in PHP. Called when PHP code does:
187 *
188 * $x = $message->foobar;
189 *
190 * Note that all properties of generated messages are private, so this should
191 * only be possible to invoke from generated code, which has accessors like:
192 *
193 * public function getOptionalInt32()
194 * {
195 * return $this->optional_int32;
196 * }
197 *
198 * We lookup the field and return the scalar, RepeatedField, or MapField for
199 * this field.
200 */
Message_read_property(PROTO_VAL * obj,PROTO_STR * member,int type,void ** cache_slot,zval * rv)201 static zval *Message_read_property(PROTO_VAL *obj, PROTO_STR *member,
202 int type, void **cache_slot, zval *rv) {
203 Message* intern = PROTO_MSG_P(obj);
204 const upb_fielddef *f = get_field(intern, member);
205 upb_arena *arena = Arena_Get(&intern->arena);
206
207 if (!f) return NULL;
208
209 if (upb_fielddef_ismap(f)) {
210 upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
211 MapField_GetPhpWrapper(rv, msgval.map, f, &intern->arena);
212 } else if (upb_fielddef_isseq(f)) {
213 upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena);
214 RepeatedField_GetPhpWrapper(rv, msgval.array, f, &intern->arena);
215 } else {
216 upb_msgval msgval = upb_msg_get(intern->msg, f);
217 const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
218 Convert_UpbToPhp(msgval, rv, upb_fielddef_type(f), subdesc, &intern->arena);
219 }
220
221 return rv;
222 }
223
224 /**
225 * Message_write_property()
226 *
227 * Object handler for writing a property in PHP. Called when PHP code does:
228 *
229 * $message->foobar = $x;
230 *
231 * Note that all properties of generated messages are private, so this should
232 * only be possible to invoke from generated code, which has accessors like:
233 *
234 * public function setOptionalInt32($var)
235 * {
236 * GPBUtil::checkInt32($var);
237 * $this->optional_int32 = $var;
238 *
239 * return $this;
240 * }
241 *
242 * The C extension version of checkInt32() doesn't actually check anything, so
243 * we perform all checking and conversion in this function.
244 */
Message_write_property(PROTO_VAL * obj,PROTO_STR * member,zval * val,void ** cache_slot)245 static PROTO_RETURN_VAL Message_write_property(
246 PROTO_VAL *obj, PROTO_STR *member, zval *val, void **cache_slot) {
247 Message* intern = PROTO_MSG_P(obj);
248 const upb_fielddef *f = get_field(intern, member);
249 upb_arena *arena = Arena_Get(&intern->arena);
250 upb_msgval msgval;
251
252 if (!f) goto error;
253
254 if (upb_fielddef_ismap(f)) {
255 msgval.map_val = MapField_GetUpbMap(val, f, arena);
256 if (!msgval.map_val) goto error;
257 } else if (upb_fielddef_isseq(f)) {
258 msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
259 if (!msgval.array_val) goto error;
260 } else {
261 upb_fieldtype_t type = upb_fielddef_type(f);
262 const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
263 bool ok = Convert_PhpToUpb(val, &msgval, type, subdesc, arena);
264 if (!ok) goto error;
265 }
266
267 upb_msg_set(intern->msg, f, msgval, arena);
268 #if PHP_VERSION_ID < 704000
269 return;
270 #else
271 return val;
272 #endif
273
274 error:
275 #if PHP_VERSION_ID < 704000
276 return;
277 #else
278 return &EG(error_zval);
279 #endif
280 }
281
282 /**
283 * Message_get_property_ptr_ptr()
284 *
285 * Object handler for the get_property_ptr_ptr event in PHP. This returns a
286 * reference to our internal properties. We don't support this, so we return
287 * NULL.
288 */
Message_get_property_ptr_ptr(PROTO_VAL * object,PROTO_STR * member,int type,void ** cache_slot)289 static zval *Message_get_property_ptr_ptr(PROTO_VAL *object, PROTO_STR *member,
290 int type,
291 void **cache_slot) {
292 return NULL; // We do not have a properties table.
293 }
294
295 /**
296 * Message_get_properties()
297 *
298 * Object handler for the get_properties event in PHP. This returns a HashTable
299 * of our internal properties. We don't support this, so we return NULL.
300 */
Message_get_properties(PROTO_VAL * object)301 static HashTable *Message_get_properties(PROTO_VAL *object) {
302 return NULL; // We don't offer direct references to our properties.
303 }
304
305 // C Functions from message.h. /////////////////////////////////////////////////
306
307 // These are documented in the header file.
308
Message_GetPhpWrapper(zval * val,const Descriptor * desc,upb_msg * msg,zval * arena)309 void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg,
310 zval *arena) {
311 if (!msg) {
312 ZVAL_NULL(val);
313 return;
314 }
315
316 if (!ObjCache_Get(msg, val)) {
317 Message *intern = emalloc(sizeof(Message));
318 // XXX(haberman): verify whether we actually want to take this route.
319 desc->class_entry->default_properties_count = 0;
320 zend_object_std_init(&intern->std, desc->class_entry);
321 intern->std.handlers = &message_object_handlers;
322 ZVAL_COPY(&intern->arena, arena);
323 intern->desc = desc;
324 intern->msg = msg;
325 ZVAL_OBJ(val, &intern->std);
326 ObjCache_Add(intern->msg, &intern->std);
327 }
328 }
329
Message_GetUpbMessage(zval * val,const Descriptor * desc,upb_arena * arena,upb_msg ** msg)330 bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena,
331 upb_msg **msg) {
332 PBPHP_ASSERT(desc);
333
334 if (Z_ISREF_P(val)) {
335 ZVAL_DEREF(val);
336 }
337
338 if (Z_TYPE_P(val) == IS_NULL) {
339 *msg = NULL;
340 return true;
341 }
342
343 if (Z_TYPE_P(val) == IS_OBJECT &&
344 instanceof_function(Z_OBJCE_P(val), desc->class_entry)) {
345 Message *intern = (Message*)Z_OBJ_P(val);
346 upb_arena_fuse(arena, Arena_Get(&intern->arena));
347 *msg = intern->msg;
348 return true;
349 } else {
350 zend_throw_exception_ex(NULL, 0, "Given value is not an instance of %s.",
351 ZSTR_VAL(desc->class_entry->name));
352 return false;
353 }
354 }
355
356 // Message PHP methods /////////////////////////////////////////////////////////
357
358 /**
359 * Message_InitFromPhp()
360 *
361 * Helper method to handle the initialization of a message from a PHP value, eg.
362 *
363 * $m = new TestMessage([
364 * 'optional_int32' => -42,
365 * 'optional_bool' => true,
366 * 'optional_string' => 'a',
367 * 'optional_enum' => TestEnum::ONE,
368 * 'optional_message' => new Sub([
369 * 'a' => 33
370 * ]),
371 * 'repeated_int32' => [-42, -52],
372 * 'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE],
373 * 'repeated_message' => [new Sub(['a' => 34]),
374 * new Sub(['a' => 35])],
375 * 'map_int32_int32' => [-62 => -62],
376 * 'map_int32_enum' => [1 => TestEnum::ONE],
377 * 'map_int32_message' => [1 => new Sub(['a' => 36])],
378 * ]);
379 *
380 * The initializer must be an array.
381 */
Message_InitFromPhp(upb_msg * msg,const upb_msgdef * m,zval * init,upb_arena * arena)382 bool Message_InitFromPhp(upb_msg *msg, const upb_msgdef *m, zval *init,
383 upb_arena *arena) {
384 HashTable* table = HASH_OF(init);
385 HashPosition pos;
386
387 if (Z_ISREF_P(init)) {
388 ZVAL_DEREF(init);
389 }
390
391 if (Z_TYPE_P(init) != IS_ARRAY) {
392 zend_throw_exception_ex(NULL, 0,
393 "Initializer for a message %s must be an array.",
394 upb_msgdef_fullname(m));
395 return false;
396 }
397
398 zend_hash_internal_pointer_reset_ex(table, &pos);
399
400 while (true) { // Iterate over key/value pairs.
401 zval key;
402 zval *val;
403 const upb_fielddef *f;
404 upb_msgval msgval;
405
406 zend_hash_get_current_key_zval_ex(table, &key, &pos);
407 val = zend_hash_get_current_data_ex(table, &pos);
408
409 if (!val) return true; // Finished iteration.
410
411 if (Z_ISREF_P(val)) {
412 ZVAL_DEREF(val);
413 }
414
415 f = upb_msgdef_ntof(m, Z_STRVAL_P(&key), Z_STRLEN_P(&key));
416
417 if (!f) {
418 zend_throw_exception_ex(NULL, 0,
419 "No such field %s", Z_STRVAL_P(&key));
420 return false;
421 }
422
423 if (upb_fielddef_ismap(f)) {
424 msgval.map_val = MapField_GetUpbMap(val, f, arena);
425 if (!msgval.map_val) return false;
426 } else if (upb_fielddef_isseq(f)) {
427 msgval.array_val = RepeatedField_GetUpbArray(val, f, arena);
428 if (!msgval.array_val) return false;
429 } else {
430 const Descriptor *desc = Descriptor_GetFromFieldDef(f);
431 upb_fieldtype_t type = upb_fielddef_type(f);
432 if (!Convert_PhpToUpbAutoWrap(val, &msgval, type, desc, arena)) {
433 return false;
434 }
435 }
436
437 upb_msg_set(msg, f, msgval, arena);
438 zend_hash_move_forward_ex(table, &pos);
439 zval_dtor(&key);
440 }
441 }
442
443 /**
444 * Message::__construct()
445 *
446 * Constructor for Message.
447 * @param array Map of initial values ['k' = val]
448 */
PHP_METHOD(Message,__construct)449 PHP_METHOD(Message, __construct) {
450 Message* intern = (Message*)Z_OBJ_P(getThis());
451 const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis()));
452 const upb_msgdef *msgdef = desc->msgdef;
453 upb_arena *arena = Arena_Get(&intern->arena);
454 zval *init_arr = NULL;
455
456 intern->desc = desc;
457 intern->msg = upb_msg_new(msgdef, arena);
458 ObjCache_Add(intern->msg, &intern->std);
459
460 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) {
461 return;
462 }
463
464 if (init_arr) {
465 Message_InitFromPhp(intern->msg, desc->msgdef, init_arr, arena);
466 }
467 }
468
469 /**
470 * Message::discardUnknownFields()
471 *
472 * Discards any unknown fields for this message or any submessages.
473 */
PHP_METHOD(Message,discardUnknownFields)474 PHP_METHOD(Message, discardUnknownFields) {
475 Message* intern = (Message*)Z_OBJ_P(getThis());
476 upb_msg_discardunknown(intern->msg, intern->desc->msgdef, 64);
477 }
478
479 /**
480 * Message::clear()
481 *
482 * Clears all fields of this message.
483 */
PHP_METHOD(Message,clear)484 PHP_METHOD(Message, clear) {
485 Message* intern = (Message*)Z_OBJ_P(getThis());
486 upb_msg_clear(intern->msg, intern->desc->msgdef);
487 }
488
489 /**
490 * Message::mergeFrom()
491 *
492 * Merges from the given message, which must be of the same class as us.
493 * @param object Message to merge from.
494 */
PHP_METHOD(Message,mergeFrom)495 PHP_METHOD(Message, mergeFrom) {
496 Message* intern = (Message*)Z_OBJ_P(getThis());
497 Message* from;
498 upb_arena *arena = Arena_Get(&intern->arena);
499 const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
500 zval* value;
501 char *pb;
502 size_t size;
503 bool ok;
504
505 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &value,
506 intern->desc->class_entry) == FAILURE) {
507 return;
508 }
509
510 from = (Message*)Z_OBJ_P(value);
511
512 // Should be guaranteed since we passed the class type to
513 // zend_parse_parameters().
514 PBPHP_ASSERT(from->desc == intern->desc);
515
516 // TODO(haberman): use a temp arena for this once we can make upb_decode()
517 // copy strings.
518 pb = upb_encode(from->msg, l, arena, &size);
519
520 if (!pb) {
521 zend_throw_exception_ex(NULL, 0, "Max nesting exceeded");
522 return;
523 }
524
525 ok = upb_decode(pb, size, intern->msg, l, arena);
526 PBPHP_ASSERT(ok);
527 }
528
529 /**
530 * Message::mergeFromString()
531 *
532 * Merges from the given string.
533 * @param string Binary protobuf data to merge.
534 */
PHP_METHOD(Message,mergeFromString)535 PHP_METHOD(Message, mergeFromString) {
536 Message* intern = (Message*)Z_OBJ_P(getThis());
537 char *data = NULL;
538 char *data_copy = NULL;
539 zend_long data_len;
540 const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
541 upb_arena *arena = Arena_Get(&intern->arena);
542
543 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &data_len) ==
544 FAILURE) {
545 return;
546 }
547
548 // TODO(haberman): avoid this copy when we can make the decoder copy.
549 data_copy = upb_arena_malloc(arena, data_len);
550 memcpy(data_copy, data, data_len);
551
552 if (!upb_decode(data_copy, data_len, intern->msg, l, arena)) {
553 zend_throw_exception_ex(NULL, 0, "Error occurred during parsing");
554 return;
555 }
556 }
557
558 /**
559 * Message::serializeToString()
560 *
561 * Serializes this message instance to protobuf data.
562 * @return string Serialized protobuf data.
563 */
PHP_METHOD(Message,serializeToString)564 PHP_METHOD(Message, serializeToString) {
565 Message* intern = (Message*)Z_OBJ_P(getThis());
566 const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef);
567 upb_arena *tmp_arena = upb_arena_new();
568 char *data;
569 size_t size;
570
571 data = upb_encode(intern->msg, l, tmp_arena, &size);
572
573 if (!data) {
574 zend_throw_exception_ex(NULL, 0, "Error occurred during serialization");
575 upb_arena_free(tmp_arena);
576 return;
577 }
578
579 RETVAL_STRINGL(data, size);
580 upb_arena_free(tmp_arena);
581 }
582
583 /**
584 * Message::mergeFromJsonString()
585 *
586 * Merges the JSON data parsed from the given string.
587 * @param string Serialized JSON data.
588 */
PHP_METHOD(Message,mergeFromJsonString)589 PHP_METHOD(Message, mergeFromJsonString) {
590 Message* intern = (Message*)Z_OBJ_P(getThis());
591 char *data = NULL;
592 char *data_copy = NULL;
593 zend_long data_len;
594 upb_arena *arena = Arena_Get(&intern->arena);
595 upb_status status;
596 zend_bool ignore_json_unknown = false;
597 int options = 0;
598
599 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &data, &data_len,
600 &ignore_json_unknown) == FAILURE) {
601 return;
602 }
603
604 // TODO(haberman): avoid this copy when we can make the decoder copy.
605 data_copy = upb_arena_malloc(arena, data_len + 1);
606 memcpy(data_copy, data, data_len);
607 data_copy[data_len] = '\0';
608
609 if (ignore_json_unknown) {
610 options |= UPB_JSONDEC_IGNOREUNKNOWN;
611 }
612
613 upb_status_clear(&status);
614 if (!upb_json_decode(data_copy, data_len, intern->msg, intern->desc->msgdef,
615 DescriptorPool_GetSymbolTable(), options, arena,
616 &status)) {
617 zend_throw_exception_ex(NULL, 0, "Error occurred during parsing: %s",
618 upb_status_errmsg(&status));
619 return;
620 }
621 }
622
623 /**
624 * Message::serializeToJsonString()
625 *
626 * Serializes this object to JSON.
627 * @return string Serialized JSON data.
628 */
PHP_METHOD(Message,serializeToJsonString)629 PHP_METHOD(Message, serializeToJsonString) {
630 Message* intern = (Message*)Z_OBJ_P(getThis());
631 size_t size;
632 int options = 0;
633 char buf[1024];
634 zend_bool preserve_proto_fieldnames = false;
635 upb_status status;
636
637 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b",
638 &preserve_proto_fieldnames) == FAILURE) {
639 return;
640 }
641
642 if (preserve_proto_fieldnames) {
643 options |= UPB_JSONENC_PROTONAMES;
644 }
645
646 upb_status_clear(&status);
647 size = upb_json_encode(intern->msg, intern->desc->msgdef,
648 DescriptorPool_GetSymbolTable(), options, buf,
649 sizeof(buf), &status);
650
651 if (!upb_ok(&status)) {
652 zend_throw_exception_ex(NULL, 0,
653 "Error occurred during JSON serialization: %s",
654 upb_status_errmsg(&status));
655 return;
656 }
657
658 if (size >= sizeof(buf)) {
659 char *buf2 = malloc(size + 1);
660 upb_json_encode(intern->msg, intern->desc->msgdef,
661 DescriptorPool_GetSymbolTable(), options, buf2, size + 1,
662 &status);
663 RETVAL_STRINGL(buf2, size);
664 free(buf2);
665 } else {
666 RETVAL_STRINGL(buf, size);
667 }
668 }
669
670 /**
671 * Message::readWrapperValue()
672 *
673 * Returns an unboxed value for the given field. This is called from generated
674 * methods for wrapper fields, eg.
675 *
676 * public function getDoubleValueUnwrapped()
677 * {
678 * return $this->readWrapperValue("double_value");
679 * }
680 *
681 * @return Unwrapped field value or null.
682 */
PHP_METHOD(Message,readWrapperValue)683 PHP_METHOD(Message, readWrapperValue) {
684 Message* intern = (Message*)Z_OBJ_P(getThis());
685 char* member;
686 const upb_fielddef *f;
687 zend_long size;
688
689 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &member, &size) == FAILURE) {
690 return;
691 }
692
693 f = upb_msgdef_ntof(intern->desc->msgdef, member, size);
694
695 if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) {
696 zend_throw_exception_ex(NULL, 0, "Message %s has no field %s",
697 upb_msgdef_fullname(intern->desc->msgdef), member);
698 return;
699 }
700
701 if (upb_msg_has(intern->msg, f)) {
702 const upb_msg *wrapper = upb_msg_get(intern->msg, f).msg_val;
703 const upb_msgdef *m = upb_fielddef_msgsubdef(f);
704 const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
705 const upb_fieldtype_t val_type = upb_fielddef_type(val_f);
706 upb_msgval msgval = upb_msg_get(wrapper, val_f);
707 zval ret;
708 Convert_UpbToPhp(msgval, &ret, val_type, NULL, &intern->arena);
709 RETURN_ZVAL(&ret, 1, 0);
710 } else {
711 RETURN_NULL();
712 }
713 }
714
715 /**
716 * Message::writeWrapperValue()
717 *
718 * Sets the given wrapper field to the given unboxed value. This is called from
719 * generated methods for wrapper fields, eg.
720 *
721 *
722 * public function setDoubleValueUnwrapped($var)
723 * {
724 * $this->writeWrapperValue("double_value", $var);
725 * return $this;
726 * }
727 *
728 * @param Unwrapped field value or null.
729 */
PHP_METHOD(Message,writeWrapperValue)730 PHP_METHOD(Message, writeWrapperValue) {
731 Message* intern = (Message*)Z_OBJ_P(getThis());
732 upb_arena *arena = Arena_Get(&intern->arena);
733 char* member;
734 const upb_fielddef *f;
735 upb_msgval msgval;
736 zend_long size;
737 zval* val;
738
739 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &member, &size, &val) ==
740 FAILURE) {
741 return;
742 }
743
744 f = upb_msgdef_ntof(intern->desc->msgdef, member, size);
745
746 if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) {
747 zend_throw_exception_ex(NULL, 0, "Message %s has no field %s",
748 upb_msgdef_fullname(intern->desc->msgdef), member);
749 return;
750 }
751
752 if (Z_ISREF_P(val)) {
753 ZVAL_DEREF(val);
754 }
755
756 if (Z_TYPE_P(val) == IS_NULL) {
757 upb_msg_clearfield(intern->msg, f);
758 } else {
759 const upb_msgdef *m = upb_fielddef_msgsubdef(f);
760 const upb_fielddef *val_f = upb_msgdef_itof(m, 1);
761 upb_fieldtype_t val_type = upb_fielddef_type(val_f);
762 upb_msg *wrapper;
763
764 if (!Convert_PhpToUpb(val, &msgval, val_type, NULL, arena)) {
765 return; // Error is already set.
766 }
767
768 wrapper = upb_msg_mutable(intern->msg, f, arena).msg;
769 upb_msg_set(wrapper, val_f, msgval, arena);
770 }
771 }
772
773 /**
774 * Message::whichOneof()
775 *
776 * Given a oneof name, returns the name of the field that is set for this oneof,
777 * or otherwise the empty string.
778 *
779 * @return string The field name in this oneof that is currently set.
780 */
PHP_METHOD(Message,whichOneof)781 PHP_METHOD(Message, whichOneof) {
782 Message* intern = (Message*)Z_OBJ_P(getThis());
783 const upb_oneofdef* oneof;
784 const upb_fielddef* field;
785 char* name;
786 zend_long len;
787
788 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &len) == FAILURE) {
789 return;
790 }
791
792 oneof = upb_msgdef_ntoo(intern->desc->msgdef, name, len);
793
794 if (!oneof) {
795 zend_throw_exception_ex(NULL, 0, "Message %s has no oneof %s",
796 upb_msgdef_fullname(intern->desc->msgdef), name);
797 return;
798 }
799
800 field = upb_msg_whichoneof(intern->msg, oneof);
801 RETURN_STRING(field ? upb_fielddef_name(field) : "");
802 }
803
804 /**
805 * Message::readOneof()
806 *
807 * Returns the contents of the given oneof field, given a field number. Called
808 * from generated code methods such as:
809 *
810 * public function getDoubleValueOneof()
811 * {
812 * return $this->readOneof(10);
813 * }
814 *
815 * @return object The oneof's field value.
816 */
PHP_METHOD(Message,readOneof)817 PHP_METHOD(Message, readOneof) {
818 Message* intern = (Message*)Z_OBJ_P(getThis());
819 zend_long field_num;
820 const upb_fielddef* f;
821 zval ret;
822
823 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &field_num) == FAILURE) {
824 return;
825 }
826
827 f = upb_msgdef_itof(intern->desc->msgdef, field_num);
828
829 if (!f || !upb_fielddef_realcontainingoneof(f)) {
830 php_error_docref(NULL, E_USER_ERROR,
831 "Internal error, no such oneof field %d\n",
832 (int)field_num);
833 }
834
835 {
836 upb_msgval msgval = upb_msg_get(intern->msg, f);
837 const Descriptor *subdesc = Descriptor_GetFromFieldDef(f);
838 Convert_UpbToPhp(msgval, &ret, upb_fielddef_type(f), subdesc,
839 &intern->arena);
840 }
841
842 RETURN_ZVAL(&ret, 1, 0);
843 }
844
845 /**
846 * Message::writeOneof()
847 *
848 * Sets the contents of the given oneof field, given a field number. Called
849 * from generated code methods such as:
850 *
851 * public function setDoubleValueOneof($var)
852 * {
853 * GPBUtil::checkMessage($var, \Google\Protobuf\DoubleValue::class);
854 * $this->writeOneof(10, $var);
855 *
856 * return $this;
857 * }
858 *
859 * The C extension version of GPBUtil::check*() does nothing, so we perform
860 * all type checking and conversion here.
861 *
862 * @param integer The field number we are setting.
863 * @param object The field value we want to set.
864 */
PHP_METHOD(Message,writeOneof)865 PHP_METHOD(Message, writeOneof) {
866 Message* intern = (Message*)Z_OBJ_P(getThis());
867 zend_long field_num;
868 const upb_fielddef* f;
869 upb_arena *arena = Arena_Get(&intern->arena);
870 upb_msgval msgval;
871 zval* val;
872
873 if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &field_num, &val) ==
874 FAILURE) {
875 return;
876 }
877
878 f = upb_msgdef_itof(intern->desc->msgdef, field_num);
879
880 if (!Convert_PhpToUpb(val, &msgval, upb_fielddef_type(f),
881 Descriptor_GetFromFieldDef(f), arena)) {
882 return;
883 }
884
885 upb_msg_set(intern->msg, f, msgval, arena);
886 }
887
888 ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0)
889 ZEND_END_ARG_INFO()
890
891 ZEND_BEGIN_ARG_INFO_EX(arginfo_mergeFrom, 0, 0, 1)
892 ZEND_ARG_INFO(0, data)
893 ZEND_END_ARG_INFO()
894
895 ZEND_BEGIN_ARG_INFO_EX(arginfo_read, 0, 0, 1)
896 ZEND_ARG_INFO(0, field)
897 ZEND_END_ARG_INFO()
898
899 ZEND_BEGIN_ARG_INFO_EX(arginfo_write, 0, 0, 2)
900 ZEND_ARG_INFO(0, field)
901 ZEND_ARG_INFO(0, value)
902 ZEND_END_ARG_INFO()
903
904 static zend_function_entry Message_methods[] = {
905 PHP_ME(Message, clear, arginfo_void, ZEND_ACC_PUBLIC)
906 PHP_ME(Message, discardUnknownFields, arginfo_void, ZEND_ACC_PUBLIC)
907 PHP_ME(Message, serializeToString, arginfo_void, ZEND_ACC_PUBLIC)
908 PHP_ME(Message, mergeFromString, arginfo_mergeFrom, ZEND_ACC_PUBLIC)
909 PHP_ME(Message, serializeToJsonString, arginfo_void, ZEND_ACC_PUBLIC)
910 PHP_ME(Message, mergeFromJsonString, arginfo_mergeFrom, ZEND_ACC_PUBLIC)
911 PHP_ME(Message, mergeFrom, arginfo_mergeFrom, ZEND_ACC_PUBLIC)
912 PHP_ME(Message, readWrapperValue, arginfo_read, ZEND_ACC_PROTECTED)
913 PHP_ME(Message, writeWrapperValue, arginfo_write, ZEND_ACC_PROTECTED)
914 PHP_ME(Message, readOneof, arginfo_read, ZEND_ACC_PROTECTED)
915 PHP_ME(Message, writeOneof, arginfo_write, ZEND_ACC_PROTECTED)
916 PHP_ME(Message, whichOneof, arginfo_read, ZEND_ACC_PROTECTED)
917 PHP_ME(Message, __construct, arginfo_void, ZEND_ACC_PROTECTED)
918 ZEND_FE_END
919 };
920
921 /**
922 * Message_ModuleInit()
923 *
924 * Called when the C extension is loaded to register all types.
925 */
Message_ModuleInit()926 void Message_ModuleInit() {
927 zend_class_entry tmp_ce;
928 zend_object_handlers *h = &message_object_handlers;
929
930 INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\Message",
931 Message_methods);
932
933 message_ce = zend_register_internal_class(&tmp_ce);
934 message_ce->create_object = Message_create;
935
936 memcpy(h, &std_object_handlers, sizeof(zend_object_handlers));
937 h->dtor_obj = Message_dtor;
938 h->read_property = Message_read_property;
939 h->write_property = Message_write_property;
940 h->has_property = Message_has_property;
941 h->unset_property = Message_unset_property;
942 h->get_properties = Message_get_properties;
943 h->get_property_ptr_ptr = Message_get_property_ptr_ptr;
944 }
945