dep-protobuf/php/ext/google/protobuf/def.c
Tony Liao 1b27892b8b Support custom JSON enum names in PHP
This CL adds support for the Edition 2026 custom JSON enum name feature (pb.enumvalue.json) in PHP Protobuf.

Note that we don't need extension caching similar to C++ or Java because our PHP code generator collects custom JSON names at protoc compile time and passes them to DescriptorPool::internalAddGeneratedFile(), populating EnumValueDescriptor and EnumDescriptor during descriptor registration.

Tested with PHP unit tests across pure-PHP and C-extension modes.

PiperOrigin-RevId: 963411589
2026-08-12 06:45:04 -07:00

1191 lines
36 KiB
C

// 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::isRequired()
*
* Returns true if this field is a required field.
*/
PHP_METHOD(FieldDescriptor, isRequired) {
FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis());
RETURN_BOOL(upb_FieldDef_IsRequired(intern->fielddef));
}
/*
* FieldDescriptor::isRepeated()
*
* Returns true if this field is a repeated field.
*/
PHP_METHOD(FieldDescriptor, isRepeated) {
FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis());
RETURN_BOOL(upb_FieldDef_IsRepeated(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::hasPresence()
*
* Returns true if this field tracks presence, ie. does the field
* distinguish between "unset" and "present with default value."
*
* This includes required, optional, and oneof fields. It excludes maps,
* repeated fields, and singular proto3 fields without "optional".
*
* For fields where hasPresence() == true, the return value of
* msg.hasField() is semantically meaningful.
*
*/
PHP_METHOD(FieldDescriptor, hasPresence) {
FieldDescriptor* intern = (FieldDescriptor*)Z_OBJ_P(getThis());
RETURN_BOOL(upb_FieldDef_HasPresence(intern->fielddef));
}
/*
* 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, isRequired, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, isRepeated, 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, hasPresence, 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,
size_t 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;
zval* custom_json_names = NULL;
upb_Arena* arena;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|bz", &data, &data_len,
&use_nested_submsg,
&custom_json_names) != SUCCESS) {
return;
}
// custom_json_names is passed by generated PHP metadata for pure-PHP mode,
// but is unused in the C extension because upb parses enum JSON names
// directly from the descriptor data.
(void)custom_json_names;
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, 1)
ZEND_ARG_INFO(0, data)
ZEND_ARG_INFO(0, use_nested_submsg)
ZEND_ARG_INFO(0, custom_json_names)
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
}