| // Protocol Buffers - Google's data interchange format |
| // Copyright 2008 Google Inc. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd |
| |
| #include "def.h" |
| |
| #include <php.h> |
| |
| // This is not self-contained: it must be after other Zend includes. |
| #include <Zend/zend_exceptions.h> |
| |
| #include "names.h" |
| #include "php-upb.h" |
| #include "protobuf.h" |
| |
| static void CheckUpbStatus(const upb_Status* status, const char* msg) { |
| if (!upb_Status_IsOk(status)) { |
| zend_error(E_ERROR, "%s: %s\n", msg, upb_Status_ErrorMessage(status)); |
| } |
| } |
| |
| static void FieldDescriptor_FromFieldDef(zval* val, const upb_FieldDef* f); |
| |
| // We use this for objects that should not be created directly from PHP. |
| static zend_object* CreateHandler_ReturnNull(zend_class_entry* class_type) { |
| return NULL; // Nobody should call this. |
| } |
| |
| // clang-format off |
| ZEND_BEGIN_ARG_INFO_EX(arginfo_getByIndex, 0, 0, 1) |
| ZEND_ARG_INFO(0, index) |
| ZEND_END_ARG_INFO() |
| // clang-format on |
| |
| // ----------------------------------------------------------------------------- |
| // EnumValueDescriptor |
| // ----------------------------------------------------------------------------- |
| |
| typedef struct { |
| zend_object std; |
| const char* name; |
| int32_t number; |
| } EnumValueDescriptor; |
| |
| zend_class_entry* EnumValueDescriptor_class_entry; |
| static zend_object_handlers EnumValueDescriptor_object_handlers; |
| |
| /* |
| * EnumValueDescriptor_Make() |
| * |
| * Function to create an EnumValueDescriptor object from C. |
| */ |
| static void EnumValueDescriptor_Make(zval* val, const char* name, |
| int32_t number) { |
| EnumValueDescriptor* intern = emalloc(sizeof(EnumValueDescriptor)); |
| zend_object_std_init(&intern->std, EnumValueDescriptor_class_entry); |
| intern->std.handlers = &EnumValueDescriptor_object_handlers; |
| intern->name = name; |
| intern->number = number; |
| // Skip object_properties_init(), we don't allow derived classes. |
| ZVAL_OBJ(val, &intern->std); |
| } |
| |
| /* |
| * EnumValueDescriptor::getName() |
| * |
| * Returns the name for this enum value. |
| */ |
| PHP_METHOD(EnumValueDescriptor, getName) { |
| EnumValueDescriptor* intern = (EnumValueDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_STRING(intern->name); |
| } |
| |
| /* |
| * EnumValueDescriptor::getNumber() |
| * |
| * Returns the number for this enum value. |
| */ |
| PHP_METHOD(EnumValueDescriptor, getNumber) { |
| EnumValueDescriptor* intern = (EnumValueDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_LONG(intern->number); |
| } |
| |
| // clang-format off |
| static zend_function_entry EnumValueDescriptor_methods[] = { |
| PHP_ME(EnumValueDescriptor, getName, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(EnumValueDescriptor, getNumber, arginfo_void, ZEND_ACC_PUBLIC) |
| ZEND_FE_END |
| }; |
| // clang-format on |
| |
| // ----------------------------------------------------------------------------- |
| // EnumDescriptor |
| // ----------------------------------------------------------------------------- |
| |
| typedef struct { |
| zend_object std; |
| const upb_EnumDef* enumdef; |
| void* cache_key; |
| } EnumDescriptor; |
| |
| zend_class_entry* EnumDescriptor_class_entry; |
| static zend_object_handlers EnumDescriptor_object_handlers; |
| |
| static void EnumDescriptor_destructor(zend_object* obj) { |
| EnumDescriptor* intern = (EnumDescriptor*)obj; |
| ObjCache_Delete(intern->cache_key); |
| } |
| |
| // Caller owns a ref on the returned zval. |
| static void EnumDescriptor_FromClassEntry(zval* val, zend_class_entry* ce) { |
| // To differentiate enums from classes, we pointer-tag the class entry. |
| void* key = (void*)((uintptr_t)ce | 1); |
| PBPHP_ASSERT(key != ce); |
| |
| if (ce == NULL) { |
| ZVAL_NULL(val); |
| return; |
| } |
| |
| if (!ObjCache_Get(key, val)) { |
| const upb_EnumDef* e = NameMap_GetEnum(ce); |
| if (!e) { |
| ZVAL_NULL(val); |
| return; |
| } |
| EnumDescriptor* ret = emalloc(sizeof(EnumDescriptor)); |
| zend_object_std_init(&ret->std, EnumDescriptor_class_entry); |
| ret->std.handlers = &EnumDescriptor_object_handlers; |
| ret->enumdef = e; |
| ret->cache_key = key; |
| ObjCache_Add(key, &ret->std); |
| ZVAL_OBJ(val, &ret->std); |
| } |
| } |
| |
| // Caller owns a ref on the returned zval. |
| static void EnumDescriptor_FromEnumDef(zval* val, const upb_EnumDef* m) { |
| if (!m) { |
| ZVAL_NULL(val); |
| } else { |
| char* classname = |
| GetPhpClassname(upb_EnumDef_File(m), upb_EnumDef_FullName(m), false); |
| zend_string* str = zend_string_init(classname, strlen(classname), 0); |
| zend_class_entry* ce = zend_lookup_class(str); // May autoload the class. |
| |
| zend_string_release(str); |
| |
| if (!ce) { |
| zend_error(E_ERROR, "Couldn't load generated class %s", classname); |
| } |
| |
| free(classname); |
| EnumDescriptor_FromClassEntry(val, ce); |
| } |
| } |
| |
| /* |
| * EnumDescriptor::getValue() |
| * |
| * Returns an EnumValueDescriptor for this index. Note: we are not looking |
| * up by numeric enum value, but by the index in the list of enum values. |
| */ |
| PHP_METHOD(EnumDescriptor, getValue) { |
| EnumDescriptor* intern = (EnumDescriptor*)Z_OBJ_P(getThis()); |
| zend_long index; |
| zval ret; |
| |
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { |
| zend_error(E_USER_ERROR, "Expect integer for index.\n"); |
| return; |
| } |
| |
| if (index < 0 || index >= upb_EnumDef_ValueCount(intern->enumdef)) { |
| zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index); |
| return; |
| } |
| |
| const upb_EnumValueDef* ev = upb_EnumDef_Value(intern->enumdef, index); |
| EnumValueDescriptor_Make(&ret, upb_EnumValueDef_Name(ev), |
| upb_EnumValueDef_Number(ev)); |
| RETURN_COPY_VALUE(&ret); |
| } |
| |
| /* |
| * EnumDescriptor::getValueCount() |
| * |
| * Returns the number of values in this enum. |
| */ |
| PHP_METHOD(EnumDescriptor, getValueCount) { |
| EnumDescriptor* intern = (EnumDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_LONG(upb_EnumDef_ValueCount(intern->enumdef)); |
| } |
| |
| /* |
| * EnumDescriptor::getPublicDescriptor() |
| * |
| * Returns this EnumDescriptor. Unlike the pure-PHP descriptor, we do not |
| * have two separate EnumDescriptor classes. We use a single class for both |
| * the public and private descriptor. |
| */ |
| PHP_METHOD(EnumDescriptor, getPublicDescriptor) { RETURN_COPY(getThis()); } |
| |
| // clang-format off |
| static zend_function_entry EnumDescriptor_methods[] = { |
| PHP_ME(EnumDescriptor, getPublicDescriptor, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(EnumDescriptor, getValueCount, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(EnumDescriptor, getValue, arginfo_getByIndex, ZEND_ACC_PUBLIC) |
| ZEND_FE_END |
| }; |
| // clang-format on |
| |
| // ----------------------------------------------------------------------------- |
| // Oneof |
| // ----------------------------------------------------------------------------- |
| |
| typedef struct { |
| zend_object std; |
| const upb_OneofDef* oneofdef; |
| } OneofDescriptor; |
| |
| zend_class_entry* OneofDescriptor_class_entry; |
| static zend_object_handlers OneofDescriptor_object_handlers; |
| |
| static void OneofDescriptor_destructor(zend_object* obj) { |
| OneofDescriptor* intern = (OneofDescriptor*)obj; |
| ObjCache_Delete(intern->oneofdef); |
| } |
| |
| static void OneofDescriptor_FromOneofDef(zval* val, const upb_OneofDef* o) { |
| if (o == NULL) { |
| ZVAL_NULL(val); |
| return; |
| } |
| |
| if (!ObjCache_Get(o, val)) { |
| OneofDescriptor* ret = emalloc(sizeof(OneofDescriptor)); |
| zend_object_std_init(&ret->std, OneofDescriptor_class_entry); |
| ret->std.handlers = &OneofDescriptor_object_handlers; |
| ret->oneofdef = o; |
| ObjCache_Add(o, &ret->std); |
| ZVAL_OBJ(val, &ret->std); |
| } |
| } |
| |
| /* |
| * OneofDescriptor::getName() |
| * |
| * Returns the name of this oneof. |
| */ |
| PHP_METHOD(OneofDescriptor, getName) { |
| OneofDescriptor* intern = (OneofDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_STRING(upb_OneofDef_Name(intern->oneofdef)); |
| } |
| |
| /* |
| * OneofDescriptor::getField() |
| * |
| * Returns a field from this oneof. The given index must be in the range |
| * [0, getFieldCount() - 1]. |
| */ |
| PHP_METHOD(OneofDescriptor, getField) { |
| OneofDescriptor* intern = (OneofDescriptor*)Z_OBJ_P(getThis()); |
| zend_long index; |
| zval ret; |
| |
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { |
| zend_error(E_USER_ERROR, "Expect integer for index.\n"); |
| return; |
| } |
| |
| if (index < 0 || index >= upb_OneofDef_FieldCount(intern->oneofdef)) { |
| zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index); |
| return; |
| } |
| |
| const upb_FieldDef* field = upb_OneofDef_Field(intern->oneofdef, index); |
| FieldDescriptor_FromFieldDef(&ret, field); |
| RETURN_COPY_VALUE(&ret); |
| } |
| |
| /* |
| * OneofDescriptor::getFieldCount() |
| * |
| * Returns the number of fields in this oneof. |
| */ |
| PHP_METHOD(OneofDescriptor, getFieldCount) { |
| OneofDescriptor* intern = (OneofDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_LONG(upb_OneofDef_FieldCount(intern->oneofdef)); |
| } |
| |
| // clang-format off |
| static zend_function_entry OneofDescriptor_methods[] = { |
| PHP_ME(OneofDescriptor, getName, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(OneofDescriptor, getField, arginfo_getByIndex, ZEND_ACC_PUBLIC) |
| PHP_ME(OneofDescriptor, getFieldCount, arginfo_void, ZEND_ACC_PUBLIC) |
| ZEND_FE_END |
| }; |
| // clang-format on |
| |
| // ----------------------------------------------------------------------------- |
| // FieldDescriptor |
| // ----------------------------------------------------------------------------- |
| |
| typedef struct { |
| zend_object std; |
| const upb_FieldDef* fielddef; |
| } FieldDescriptor; |
| |
| zend_class_entry* FieldDescriptor_class_entry; |
| static zend_object_handlers FieldDescriptor_object_handlers; |
| |
| static void FieldDescriptor_destructor(zend_object* obj) { |
| FieldDescriptor* intern = (FieldDescriptor*)obj; |
| ObjCache_Delete(intern->fielddef); |
| } |
| |
| // Caller owns a ref on the returned zval. |
| static void FieldDescriptor_FromFieldDef(zval* val, const upb_FieldDef* f) { |
| if (f == NULL) { |
| ZVAL_NULL(val); |
| return; |
| } |
| |
| if (!ObjCache_Get(f, val)) { |
| FieldDescriptor* ret = emalloc(sizeof(FieldDescriptor)); |
| zend_object_std_init(&ret->std, FieldDescriptor_class_entry); |
| ret->std.handlers = &FieldDescriptor_object_handlers; |
| ret->fielddef = f; |
| ObjCache_Add(f, &ret->std); |
| ZVAL_OBJ(val, &ret->std); |
| } |
| } |
| |
| upb_CType to_fieldtype(upb_FieldType type) { |
| switch (type) { |
| #define CASE(descriptor_type, type) \ |
| case kUpb_FieldType_##descriptor_type: \ |
| return kUpb_CType_##type; |
| |
| CASE(Float, Float); |
| CASE(Double, Double); |
| CASE(Bool, Bool); |
| CASE(String, String); |
| CASE(Bytes, Bytes); |
| CASE(Message, Message); |
| CASE(Group, Message); |
| CASE(Enum, Enum); |
| CASE(Int32, Int32); |
| CASE(Int64, Int64); |
| CASE(UInt32, UInt32); |
| CASE(UInt64, UInt64); |
| CASE(SInt32, Int32); |
| CASE(SInt64, Int64); |
| CASE(Fixed32, UInt32); |
| CASE(Fixed64, UInt64); |
| CASE(SFixed32, Int32); |
| CASE(SFixed64, Int64); |
| |
| #undef CONVERT |
| } |
| |
| zend_error(E_ERROR, "Unknown field type."); |
| return 0; |
| } |
| |
| /* |
| * FieldDescriptor::getName() |
| * |
| * Returns the name of this field. |
| */ |
| PHP_METHOD(FieldDescriptor, getName) { |
| FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_STRING(upb_FieldDef_Name(intern->fielddef)); |
| } |
| |
| /* |
| * FieldDescriptor::getNumber() |
| * |
| * Returns the number of this field. |
| */ |
| PHP_METHOD(FieldDescriptor, getNumber) { |
| FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_LONG(upb_FieldDef_Number(intern->fielddef)); |
| } |
| |
| /* |
| * FieldDescriptor::getLabel() |
| * |
| * Returns the label of this field as an integer. |
| */ |
| PHP_METHOD(FieldDescriptor, getLabel) { |
| FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_LONG(upb_FieldDef_Label(intern->fielddef)); |
| } |
| |
| /* |
| * FieldDescriptor::getType() |
| * |
| * Returns the type of this field as an integer. |
| */ |
| PHP_METHOD(FieldDescriptor, getType) { |
| FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_LONG(upb_FieldDef_Type(intern->fielddef)); |
| } |
| |
| /* |
| * FieldDescriptor::isMap() |
| * |
| * Returns true if this field is a map. |
| */ |
| PHP_METHOD(FieldDescriptor, isMap) { |
| FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis()); |
| RETURN_BOOL(upb_FieldDef_IsMap(intern->fielddef)); |
| } |
| |
| /* |
| * FieldDescriptor::getEnumType() |
| * |
| * Returns the EnumDescriptor for this field, which must be an enum. |
| */ |
| PHP_METHOD(FieldDescriptor, getEnumType) { |
| FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis()); |
| const upb_EnumDef* e = upb_FieldDef_EnumSubDef(intern->fielddef); |
| zval ret; |
| |
| if (!e) { |
| zend_throw_exception_ex(NULL, 0, |
| "Cannot get enum type for non-enum field '%s'", |
| upb_FieldDef_Name(intern->fielddef)); |
| return; |
| } |
| |
| EnumDescriptor_FromEnumDef(&ret, e); |
| RETURN_COPY_VALUE(&ret); |
| } |
| |
| /* |
| * FieldDescriptor::getContainingOneof() |
| * |
| * Returns the OneofDescriptor for this field, or null if it is not inside |
| * a oneof. |
| */ |
| PHP_METHOD(FieldDescriptor, getContainingOneof) { |
| FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis()); |
| const upb_OneofDef* o = upb_FieldDef_ContainingOneof(intern->fielddef); |
| zval ret; |
| |
| if (!o) { |
| RETURN_NULL(); |
| } |
| |
| OneofDescriptor_FromOneofDef(&ret, o); |
| RETURN_COPY_VALUE(&ret); |
| } |
| |
| /* |
| * FieldDescriptor::getRealContainingOneof() |
| * |
| * Returns the non-synthetic OneofDescriptor for this field, or null if it is |
| * not inside a oneof. |
| */ |
| PHP_METHOD(FieldDescriptor, getRealContainingOneof) { |
| FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis()); |
| const upb_OneofDef* o = upb_FieldDef_RealContainingOneof(intern->fielddef); |
| zval ret; |
| |
| if (!o) { |
| RETURN_NULL(); |
| } |
| |
| OneofDescriptor_FromOneofDef(&ret, o); |
| RETURN_COPY_VALUE(&ret); |
| } |
| |
| /* |
| * FieldDescriptor::getMessageType() |
| * |
| * Returns the Descriptor for this field, which must be a message. |
| */ |
| PHP_METHOD(FieldDescriptor, getMessageType) { |
| FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis()); |
| Descriptor* desc = Descriptor_GetFromFieldDef(intern->fielddef); |
| |
| if (!desc) { |
| zend_throw_exception_ex( |
| NULL, 0, "Cannot get message type for non-message field '%s'", |
| upb_FieldDef_Name(intern->fielddef)); |
| return; |
| } |
| |
| RETURN_OBJ_COPY(&desc->std); |
| } |
| |
| // clang-format off |
| static zend_function_entry FieldDescriptor_methods[] = { |
| PHP_ME(FieldDescriptor, getName, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(FieldDescriptor, getNumber, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(FieldDescriptor, getLabel, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(FieldDescriptor, getType, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(FieldDescriptor, isMap, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(FieldDescriptor, getEnumType, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(FieldDescriptor, getContainingOneof, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(FieldDescriptor, getRealContainingOneof, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(FieldDescriptor, getMessageType, arginfo_void, ZEND_ACC_PUBLIC) |
| ZEND_FE_END |
| }; |
| // clang-format on |
| |
| // ----------------------------------------------------------------------------- |
| // Descriptor |
| // ----------------------------------------------------------------------------- |
| |
| zend_class_entry* Descriptor_class_entry; |
| static zend_object_handlers Descriptor_object_handlers; |
| |
| static void Descriptor_destructor(zend_object* obj) { |
| // We don't really need to do anything here, we don't allow this to be |
| // collected before the end of the request. |
| } |
| |
| static zend_class_entry* Descriptor_GetGeneratedClass(const upb_MessageDef* m) { |
| for (int i = 0; i < 2; ++i) { |
| char* classname = GetPhpClassname(upb_MessageDef_File(m), |
| upb_MessageDef_FullName(m), (bool)i); |
| zend_string* str = zend_string_init(classname, strlen(classname), 0); |
| zend_class_entry* ce = zend_lookup_class(str); // May autoload the class. |
| |
| zend_string_release(str); |
| free(classname); |
| |
| if (ce) { |
| return ce; |
| } |
| } |
| |
| char* classname = GetPhpClassname(upb_MessageDef_File(m), |
| upb_MessageDef_FullName(m), false); |
| zend_error(E_ERROR, "Couldn't load generated class %s", classname); |
| return NULL; |
| } |
| |
| void Descriptor_FromMessageDef(zval* val, const upb_MessageDef* m) { |
| if (m == NULL) { |
| ZVAL_NULL(val); |
| return; |
| } |
| |
| if (!ObjCache_Get(m, val)) { |
| zend_class_entry* ce = NULL; |
| if (!upb_MessageDef_IsMapEntry(m)) { // Map entries don't have a class. |
| ce = Descriptor_GetGeneratedClass(m); |
| if (!ce) { |
| ZVAL_NULL(val); |
| return; |
| } |
| } |
| Descriptor* ret = emalloc(sizeof(Descriptor)); |
| zend_object_std_init(&ret->std, Descriptor_class_entry); |
| ret->std.handlers = &Descriptor_object_handlers; |
| ret->class_entry = ce; |
| ret->msgdef = m; |
| ObjCache_Add(m, &ret->std); |
| Descriptors_Add(&ret->std); |
| ZVAL_OBJ(val, &ret->std); |
| } |
| } |
| |
| static void Descriptor_FromClassEntry(zval* val, zend_class_entry* ce) { |
| if (ce) { |
| Descriptor_FromMessageDef(val, NameMap_GetMessage(ce)); |
| } else { |
| ZVAL_NULL(val); |
| } |
| } |
| |
| static Descriptor* Descriptor_GetFromZval(zval* val) { |
| if (Z_TYPE_P(val) == IS_NULL) { |
| return NULL; |
| } else { |
| zend_object* ret = Z_OBJ_P(val); |
| zval_ptr_dtor(val); |
| return (Descriptor*)ret; |
| } |
| } |
| |
| // C Functions from def.h ////////////////////////////////////////////////////// |
| |
| // These are documented in the header file. |
| |
| Descriptor* Descriptor_GetFromClassEntry(zend_class_entry* ce) { |
| zval desc; |
| Descriptor_FromClassEntry(&desc, ce); |
| return Descriptor_GetFromZval(&desc); |
| } |
| |
| Descriptor* Descriptor_GetFromMessageDef(const upb_MessageDef* m) { |
| zval desc; |
| Descriptor_FromMessageDef(&desc, m); |
| return Descriptor_GetFromZval(&desc); |
| } |
| |
| Descriptor* Descriptor_GetFromFieldDef(const upb_FieldDef* f) { |
| return Descriptor_GetFromMessageDef(upb_FieldDef_MessageSubDef(f)); |
| } |
| |
| /* |
| * Descriptor::getPublicDescriptor() |
| * |
| * Returns this EnumDescriptor. Unlike the pure-PHP descriptor, we do not |
| * have two separate EnumDescriptor classes. We use a single class for both |
| * the public and private descriptor. |
| */ |
| PHP_METHOD(Descriptor, getPublicDescriptor) { RETURN_COPY(getThis()); } |
| |
| /* |
| * Descriptor::getFullName() |
| * |
| * Returns the full name for this message type. |
| */ |
| PHP_METHOD(Descriptor, getFullName) { |
| Descriptor* intern = (Descriptor*)Z_OBJ_P(getThis()); |
| RETURN_STRING(upb_MessageDef_FullName(intern->msgdef)); |
| } |
| |
| /* |
| * Descriptor::getField() |
| * |
| * Returns a FieldDescriptor for the given index, which must be in the range |
| * [0, getFieldCount()-1]. |
| */ |
| PHP_METHOD(Descriptor, getField) { |
| Descriptor* intern = (Descriptor*)Z_OBJ_P(getThis()); |
| int count = upb_MessageDef_FieldCount(intern->msgdef); |
| zval ret; |
| zend_long index; |
| |
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { |
| zend_error(E_USER_ERROR, "Expect integer for index.\n"); |
| return; |
| } |
| |
| if (index < 0 || index >= count) { |
| zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index); |
| return; |
| } |
| |
| FieldDescriptor_FromFieldDef(&ret, |
| upb_MessageDef_Field(intern->msgdef, index)); |
| RETURN_COPY_VALUE(&ret); |
| } |
| |
| /* |
| * Descriptor::getFieldCount() |
| * |
| * Returns the number of fields in this message. |
| */ |
| PHP_METHOD(Descriptor, getFieldCount) { |
| Descriptor* intern = (Descriptor*)Z_OBJ_P(getThis()); |
| RETURN_LONG(upb_MessageDef_FieldCount(intern->msgdef)); |
| } |
| |
| /* |
| * Descriptor::getOneofDecl() |
| * |
| * Returns a OneofDescriptor for the given index, which must be in the range |
| * [0, getOneofDeclCount()]. |
| */ |
| PHP_METHOD(Descriptor, getOneofDecl) { |
| Descriptor* intern = (Descriptor*)Z_OBJ_P(getThis()); |
| zend_long index; |
| zval ret; |
| |
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { |
| zend_error(E_USER_ERROR, "Expect integer for index.\n"); |
| return; |
| } |
| |
| if (index < 0 || index >= upb_MessageDef_OneofCount(intern->msgdef)) { |
| zend_error(E_USER_ERROR, "Cannot get element at %ld.\n", index); |
| return; |
| } |
| |
| OneofDescriptor_FromOneofDef(&ret, |
| upb_MessageDef_Oneof(intern->msgdef, index)); |
| RETURN_COPY_VALUE(&ret); |
| } |
| |
| /* |
| * Descriptor::getOneofDeclCount() |
| * |
| * Returns the number of oneofs in this message. |
| */ |
| PHP_METHOD(Descriptor, getOneofDeclCount) { |
| Descriptor* intern = (Descriptor*)Z_OBJ_P(getThis()); |
| RETURN_LONG(upb_MessageDef_OneofCount(intern->msgdef)); |
| } |
| |
| /* |
| * Descriptor::getClass() |
| * |
| * Returns the name of the PHP class for this message. |
| */ |
| PHP_METHOD(Descriptor, getClass) { |
| Descriptor* intern = (Descriptor*)Z_OBJ_P(getThis()); |
| const char* classname = ZSTR_VAL(intern->class_entry->name); |
| RETURN_STRING(classname); |
| } |
| |
| // clang-format off |
| static zend_function_entry Descriptor_methods[] = { |
| PHP_ME(Descriptor, getClass, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(Descriptor, getFullName, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(Descriptor, getField, arginfo_getByIndex, ZEND_ACC_PUBLIC) |
| PHP_ME(Descriptor, getFieldCount, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(Descriptor, getOneofDecl, arginfo_getByIndex, ZEND_ACC_PUBLIC) |
| PHP_ME(Descriptor, getOneofDeclCount, arginfo_void, ZEND_ACC_PUBLIC) |
| PHP_ME(Descriptor, getPublicDescriptor, arginfo_void, ZEND_ACC_PUBLIC) |
| ZEND_FE_END |
| }; |
| // clang-format on |
| |
| // ----------------------------------------------------------------------------- |
| // DescriptorPool |
| // ----------------------------------------------------------------------------- |
| |
| typedef struct DescriptorPool { |
| zend_object std; |
| upb_DefPool* symtab; |
| } DescriptorPool; |
| |
| zend_class_entry* DescriptorPool_class_entry; |
| static zend_object_handlers DescriptorPool_object_handlers; |
| |
| static DescriptorPool* GetPool(const zval* this_ptr) { |
| return (DescriptorPool*)Z_OBJ_P(this_ptr); |
| } |
| |
| /** |
| * Object handler to free an DescriptorPool. |
| */ |
| static void DescriptorPool_destructor(zend_object* obj) { |
| DescriptorPool* intern = (DescriptorPool*)obj; |
| |
| // We can't free our underlying symtab here, because user code may create |
| // messages from destructors that will refer to it. The symtab will be freed |
| // by our RSHUTDOWN() handler in protobuf.c |
| |
| zend_object_std_dtor(&intern->std); |
| } |
| |
| void DescriptorPool_CreateWithSymbolTable(zval* zv, upb_DefPool* symtab) { |
| DescriptorPool* intern = emalloc(sizeof(DescriptorPool)); |
| zend_object_std_init(&intern->std, DescriptorPool_class_entry); |
| intern->std.handlers = &DescriptorPool_object_handlers; |
| intern->symtab = symtab; |
| |
| ZVAL_OBJ(zv, &intern->std); |
| } |
| |
| upb_DefPool* DescriptorPool_GetSymbolTable() { return get_global_symtab(); } |
| |
| /* |
| * DescriptorPool::getGeneratedPool() |
| * |
| * Returns the generated DescriptorPool. |
| */ |
| PHP_METHOD(DescriptorPool, getGeneratedPool) { |
| DescriptorPool_CreateWithSymbolTable(return_value, get_global_symtab()); |
| } |
| |
| /* |
| * DescriptorPool::getDescriptorByClassName() |
| * |
| * Returns a Descriptor object for the given PHP class name. |
| */ |
| PHP_METHOD(DescriptorPool, getDescriptorByClassName) { |
| char* classname = NULL; |
| zend_long classname_len; |
| zend_class_entry* ce; |
| zend_string* str; |
| zval ret; |
| |
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &classname, &classname_len) == |
| FAILURE) { |
| return; |
| } |
| |
| str = zend_string_init(classname, strlen(classname), 0); |
| ce = zend_lookup_class(str); // May autoload the class. |
| zend_string_release(str); |
| |
| if (!ce) { |
| RETURN_NULL(); |
| } |
| |
| Descriptor_FromClassEntry(&ret, ce); |
| RETURN_COPY_VALUE(&ret); |
| } |
| |
| /* |
| * DescriptorPool::getEnumDescriptorByClassName() |
| * |
| * Returns a EnumDescriptor object for the given PHP class name. |
| */ |
| PHP_METHOD(DescriptorPool, getEnumDescriptorByClassName) { |
| char* classname = NULL; |
| zend_long classname_len; |
| zend_class_entry* ce; |
| zend_string* str; |
| zval ret; |
| |
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &classname, &classname_len) == |
| FAILURE) { |
| return; |
| } |
| |
| str = zend_string_init(classname, strlen(classname), 0); |
| ce = zend_lookup_class(str); // May autoload the class. |
| zend_string_release(str); |
| |
| if (!ce) { |
| RETURN_NULL(); |
| } |
| |
| EnumDescriptor_FromClassEntry(&ret, ce); |
| RETURN_COPY_VALUE(&ret); |
| } |
| |
| /* |
| * DescriptorPool::getEnumDescriptorByProtoName() |
| * |
| * Returns a Descriptor object for the given protobuf message name. |
| */ |
| PHP_METHOD(DescriptorPool, getDescriptorByProtoName) { |
| DescriptorPool* intern = GetPool(getThis()); |
| char* protoname = NULL; |
| zend_long protoname_len; |
| const upb_MessageDef* m; |
| |
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &protoname, &protoname_len) == |
| FAILURE) { |
| return; |
| } |
| |
| if (*protoname == '.') protoname++; |
| |
| m = upb_DefPool_FindMessageByName(intern->symtab, protoname); |
| |
| if (m) { |
| RETURN_OBJ_COPY(&Descriptor_GetFromMessageDef(m)->std); |
| } else { |
| RETURN_NULL(); |
| } |
| } |
| |
| /* |
| * depends_on_descriptor() |
| * |
| * Returns true if this FileDescriptorProto depends on descriptor.proto. |
| */ |
| bool depends_on_descriptor(const google_protobuf_FileDescriptorProto* file) { |
| const upb_StringView* deps; |
| upb_StringView name = |
| upb_StringView_FromString("google/protobuf/descriptor.proto"); |
| size_t i, n; |
| |
| deps = google_protobuf_FileDescriptorProto_dependency(file, &n); |
| for (i = 0; i < n; i++) { |
| if (upb_StringView_IsEqual(deps[i], name)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static void add_message_name_mappings(const upb_MessageDef* message) { |
| NameMap_AddMessage(message); |
| int msg_n = upb_MessageDef_NestedMessageCount(message); |
| for (int i = 0; i < msg_n; i++) { |
| add_message_name_mappings(upb_MessageDef_NestedMessage(message, i)); |
| } |
| int enum_n = upb_MessageDef_NestedEnumCount(message); |
| for (int i = 0; i < enum_n; i++) { |
| NameMap_AddEnum(upb_MessageDef_NestedEnum(message, i)); |
| } |
| } |
| |
| /* |
| * add_name_mappings() |
| * |
| * Adds the messages and enums in this file to the NameMap. |
| */ |
| static void add_name_mappings(const upb_FileDef* file) { |
| for (int i = 0; i < upb_FileDef_TopLevelMessageCount(file); i++) { |
| add_message_name_mappings(upb_FileDef_TopLevelMessage(file, i)); |
| } |
| |
| for (int i = 0; i < upb_FileDef_TopLevelEnumCount(file); i++) { |
| NameMap_AddEnum(upb_FileDef_TopLevelEnum(file, i)); |
| } |
| } |
| |
| static void add_descriptor(upb_DefPool* symtab, |
| const google_protobuf_FileDescriptorProto* file) { |
| upb_StringView name = google_protobuf_FileDescriptorProto_name(file); |
| upb_Status status; |
| const upb_FileDef* file_def; |
| upb_Status_Clear(&status); |
| |
| if (upb_DefPool_FindFileByNameWithSize(symtab, name.data, name.size)) { |
| // Already added. |
| // TODO: Re-enable this warning when aggregate metadata is |
| // deprecated. |
| // zend_error(E_USER_WARNING, |
| // "proto descriptor was previously loaded (included in multiple |
| // " "metadata bundles?): " UPB_STRINGVIEW_FORMAT, |
| // UPB_STRINGVIEW_ARGS(name)); |
| return; |
| } |
| |
| // The PHP code generator currently special-cases descriptor.proto. It |
| // doesn't add it as a dependency even if the proto file actually does |
| // depend on it. |
| if (depends_on_descriptor(file)) { |
| google_protobuf_FileDescriptorProto_getmsgdef(symtab); |
| } |
| |
| file_def = upb_DefPool_AddFile(symtab, file, &status); |
| CheckUpbStatus(&status, "Unable to load descriptor"); |
| add_name_mappings(file_def); |
| } |
| |
| /* |
| * add_descriptor() |
| * |
| * Adds the given descriptor data to this DescriptorPool. |
| */ |
| static void add_descriptor_set(upb_DefPool* symtab, const char* data, |
| int data_len, upb_Arena* arena) { |
| size_t i, n; |
| google_protobuf_FileDescriptorSet* set; |
| const google_protobuf_FileDescriptorProto* const* files; |
| |
| set = google_protobuf_FileDescriptorSet_parse(data, data_len, arena); |
| |
| if (!set) { |
| zend_error(E_ERROR, "Failed to parse binary descriptor\n"); |
| return; |
| } |
| |
| files = google_protobuf_FileDescriptorSet_file(set, &n); |
| |
| for (i = 0; i < n; i++) { |
| const google_protobuf_FileDescriptorProto* file = files[i]; |
| add_descriptor(symtab, file); |
| } |
| } |
| |
| bool DescriptorPool_HasFile(const char* filename) { |
| return upb_DefPool_FindFileByName(get_global_symtab(), filename) != NULL; |
| } |
| |
| void DescriptorPool_AddDescriptor(const char* filename, const char* data, |
| int size) { |
| upb_Arena* arena = upb_Arena_New(); |
| const google_protobuf_FileDescriptorProto* file = |
| google_protobuf_FileDescriptorProto_parse(data, size, arena); |
| |
| if (!file) { |
| zend_error(E_ERROR, "Failed to parse binary descriptor for %s\n", filename); |
| return; |
| } |
| |
| add_descriptor(get_global_symtab(), file); |
| upb_Arena_Free(arena); |
| } |
| |
| /* |
| * DescriptorPool::internalAddGeneratedFile() |
| * |
| * Adds the given descriptor data to this DescriptorPool. |
| */ |
| PHP_METHOD(DescriptorPool, internalAddGeneratedFile) { |
| DescriptorPool* intern = GetPool(getThis()); |
| char* data = NULL; |
| zend_long data_len; |
| zend_bool use_nested_submsg = false; |
| upb_Arena* arena; |
| |
| if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &data, &data_len, |
| &use_nested_submsg) != SUCCESS) { |
| return; |
| } |
| |
| arena = upb_Arena_New(); |
| add_descriptor_set(intern->symtab, data, data_len, arena); |
| upb_Arena_Free(arena); |
| } |
| |
| // clang-format off |
| ZEND_BEGIN_ARG_INFO_EX(arginfo_lookupByName, 0, 0, 1) |
| ZEND_ARG_INFO(0, name) |
| ZEND_END_ARG_INFO() |
| |
| ZEND_BEGIN_ARG_INFO_EX(arginfo_addgeneratedfile, 0, 0, 2) |
| ZEND_ARG_INFO(0, data) |
| ZEND_ARG_INFO(0, data_len) |
| ZEND_END_ARG_INFO() |
| |
| static zend_function_entry DescriptorPool_methods[] = { |
| PHP_ME(DescriptorPool, getGeneratedPool, arginfo_void, |
| ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
| PHP_ME(DescriptorPool, getDescriptorByClassName, arginfo_lookupByName, ZEND_ACC_PUBLIC) |
| PHP_ME(DescriptorPool, getDescriptorByProtoName, arginfo_lookupByName, ZEND_ACC_PUBLIC) |
| PHP_ME(DescriptorPool, getEnumDescriptorByClassName, arginfo_lookupByName, ZEND_ACC_PUBLIC) |
| PHP_ME(DescriptorPool, internalAddGeneratedFile, arginfo_addgeneratedfile, ZEND_ACC_PUBLIC) |
| ZEND_FE_END |
| }; |
| // clang-format on |
| |
| // ----------------------------------------------------------------------------- |
| // InternalDescriptorPool |
| // ----------------------------------------------------------------------------- |
| |
| // For the C extension, Google\Protobuf\Internal\DescriptorPool is not a |
| // separate instantiable object, it just returns a |
| // Google\Protobuf\DescriptorPool. |
| |
| zend_class_entry* InternalDescriptorPool_class_entry; |
| |
| /* |
| * InternalDescriptorPool::getGeneratedPool() |
| * |
| * Returns the generated DescriptorPool. Note that this is identical to |
| * DescriptorPool::getGeneratedPool(), and in fact returns a DescriptorPool |
| * instance. |
| */ |
| PHP_METHOD(InternalDescriptorPool, getGeneratedPool) { |
| DescriptorPool_CreateWithSymbolTable(return_value, get_global_symtab()); |
| } |
| |
| // clang-format off |
| static zend_function_entry InternalDescriptorPool_methods[] = { |
| PHP_ME(InternalDescriptorPool, getGeneratedPool, arginfo_void, |
| ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
| ZEND_FE_END |
| }; |
| // clang-format on |
| |
| // ----------------------------------------------------------------------------- |
| // GPBType |
| // ----------------------------------------------------------------------------- |
| |
| zend_class_entry* gpb_type_type; |
| |
| static zend_function_entry gpb_type_methods[] = {ZEND_FE_END}; |
| |
| // ----------------------------------------------------------------------------- |
| // Module Init |
| // ----------------------------------------------------------------------------- |
| |
| void Def_ModuleInit() { |
| zend_class_entry tmp_ce; |
| zend_object_handlers* h; |
| |
| INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\OneofDescriptor", |
| OneofDescriptor_methods); |
| OneofDescriptor_class_entry = zend_register_internal_class(&tmp_ce); |
| OneofDescriptor_class_entry->ce_flags |= ZEND_ACC_FINAL; |
| OneofDescriptor_class_entry->create_object = CreateHandler_ReturnNull; |
| h = &OneofDescriptor_object_handlers; |
| memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
| h->dtor_obj = &OneofDescriptor_destructor; |
| |
| INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\EnumValueDescriptor", |
| EnumValueDescriptor_methods); |
| EnumValueDescriptor_class_entry = zend_register_internal_class(&tmp_ce); |
| EnumValueDescriptor_class_entry->ce_flags |= ZEND_ACC_FINAL; |
| EnumValueDescriptor_class_entry->create_object = CreateHandler_ReturnNull; |
| h = &EnumValueDescriptor_object_handlers; |
| memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
| |
| INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\EnumDescriptor", |
| EnumDescriptor_methods); |
| EnumDescriptor_class_entry = zend_register_internal_class(&tmp_ce); |
| EnumDescriptor_class_entry->ce_flags |= ZEND_ACC_FINAL; |
| EnumDescriptor_class_entry->create_object = CreateHandler_ReturnNull; |
| h = &EnumDescriptor_object_handlers; |
| memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
| h->dtor_obj = &EnumDescriptor_destructor; |
| |
| INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Descriptor", Descriptor_methods); |
| |
| Descriptor_class_entry = zend_register_internal_class(&tmp_ce); |
| Descriptor_class_entry->ce_flags |= ZEND_ACC_FINAL; |
| Descriptor_class_entry->create_object = CreateHandler_ReturnNull; |
| h = &Descriptor_object_handlers; |
| memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
| h->dtor_obj = Descriptor_destructor; |
| |
| INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\FieldDescriptor", |
| FieldDescriptor_methods); |
| FieldDescriptor_class_entry = zend_register_internal_class(&tmp_ce); |
| FieldDescriptor_class_entry->ce_flags |= ZEND_ACC_FINAL; |
| FieldDescriptor_class_entry->create_object = CreateHandler_ReturnNull; |
| h = &FieldDescriptor_object_handlers; |
| memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
| h->dtor_obj = &FieldDescriptor_destructor; |
| |
| INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\DescriptorPool", |
| DescriptorPool_methods); |
| DescriptorPool_class_entry = zend_register_internal_class(&tmp_ce); |
| DescriptorPool_class_entry->ce_flags |= ZEND_ACC_FINAL; |
| DescriptorPool_class_entry->create_object = CreateHandler_ReturnNull; |
| h = &DescriptorPool_object_handlers; |
| memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
| h->dtor_obj = DescriptorPool_destructor; |
| |
| INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\DescriptorPool", |
| InternalDescriptorPool_methods); |
| InternalDescriptorPool_class_entry = zend_register_internal_class(&tmp_ce); |
| |
| // GPBType. |
| #define STR(str) (str), strlen(str) |
| zend_class_entry class_type; |
| INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBType", |
| gpb_type_methods); |
| gpb_type_type = zend_register_internal_class(&class_type); |
| zend_declare_class_constant_long(gpb_type_type, STR("DOUBLE"), 1); |
| zend_declare_class_constant_long(gpb_type_type, STR("FLOAT"), 2); |
| zend_declare_class_constant_long(gpb_type_type, STR("INT64"), 3); |
| zend_declare_class_constant_long(gpb_type_type, STR("UINT64"), 4); |
| zend_declare_class_constant_long(gpb_type_type, STR("INT32"), 5); |
| zend_declare_class_constant_long(gpb_type_type, STR("FIXED64"), 6); |
| zend_declare_class_constant_long(gpb_type_type, STR("FIXED32"), 7); |
| zend_declare_class_constant_long(gpb_type_type, STR("BOOL"), 8); |
| zend_declare_class_constant_long(gpb_type_type, STR("STRING"), 9); |
| zend_declare_class_constant_long(gpb_type_type, STR("GROUP"), 10); |
| zend_declare_class_constant_long(gpb_type_type, STR("MESSAGE"), 11); |
| zend_declare_class_constant_long(gpb_type_type, STR("BYTES"), 12); |
| zend_declare_class_constant_long(gpb_type_type, STR("UINT32"), 13); |
| zend_declare_class_constant_long(gpb_type_type, STR("ENUM"), 14); |
| zend_declare_class_constant_long(gpb_type_type, STR("SFIXED32"), 15); |
| zend_declare_class_constant_long(gpb_type_type, STR("SFIXED64"), 16); |
| zend_declare_class_constant_long(gpb_type_type, STR("SINT32"), 17); |
| zend_declare_class_constant_long(gpb_type_type, STR("SINT64"), 18); |
| #undef STR |
| } |