mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Add support for internal enums
This adds support for internal enums with the same basic approach as userland enums. Enum values are stored as CONSTANT_AST and objects created during constant updating at runtime. This means that we need to use mutable_data for internal enums. This just adds basic support and APIs, it does not include the stubs integration from #7212. Closes GH-7302.
This commit is contained in:
parent
ff8e04ac30
commit
a374230c15
11 changed files with 250 additions and 12 deletions
58
Zend/tests/enum/internal_enums.phpt
Normal file
58
Zend/tests/enum/internal_enums.phpt
Normal file
|
@ -0,0 +1,58 @@
|
|||
--TEST--
|
||||
Internal enums
|
||||
--EXTENSIONS--
|
||||
zend_test
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
var_dump($bar = ZendTestUnitEnum::Bar);
|
||||
var_dump($bar === ZendTestUnitEnum::Bar);
|
||||
var_dump($bar instanceof UnitEnum);
|
||||
|
||||
var_dump($foo = zend_get_unit_enum());
|
||||
var_dump($foo === ZendTestUnitEnum::Foo);
|
||||
|
||||
var_dump(ZendTestUnitEnum::cases());
|
||||
echo "\n";
|
||||
|
||||
var_dump($foo = ZendTestStringEnum::Foo);
|
||||
var_dump($foo instanceof BackedEnum);
|
||||
var_dump(ZendTestStringEnum::Foo->value);
|
||||
var_dump($bar = ZendTestStringEnum::from("Test2"));
|
||||
var_dump($bar === ZendTestStringEnum::Bar);
|
||||
var_dump(ZendTestStringEnum::tryFrom("Test3"));
|
||||
var_dump(ZendTestStringEnum::cases());
|
||||
|
||||
var_dump($s = serialize($foo));
|
||||
var_dump(unserialize($s));
|
||||
var_dump(unserialize($s) === $foo);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
enum(ZendTestUnitEnum::Bar)
|
||||
bool(true)
|
||||
bool(true)
|
||||
enum(ZendTestUnitEnum::Foo)
|
||||
bool(true)
|
||||
array(2) {
|
||||
[0]=>
|
||||
enum(ZendTestUnitEnum::Foo)
|
||||
[1]=>
|
||||
enum(ZendTestUnitEnum::Bar)
|
||||
}
|
||||
|
||||
enum(ZendTestStringEnum::Foo)
|
||||
bool(true)
|
||||
string(5) "Test1"
|
||||
enum(ZendTestStringEnum::Bar)
|
||||
bool(true)
|
||||
NULL
|
||||
array(2) {
|
||||
[0]=>
|
||||
enum(ZendTestStringEnum::Foo)
|
||||
[1]=>
|
||||
enum(ZendTestStringEnum::Bar)
|
||||
}
|
||||
string(30) "E:22:"ZendTestStringEnum:Foo";"
|
||||
enum(ZendTestStringEnum::Foo)
|
||||
bool(true)
|
|
@ -1298,7 +1298,6 @@ static zend_class_mutable_data *zend_allocate_mutable_data(zend_class_entry *cla
|
|||
{
|
||||
zend_class_mutable_data *mutable_data;
|
||||
|
||||
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE);
|
||||
ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL);
|
||||
ZEND_ASSERT(ZEND_MAP_PTR_GET_IMM(class_type->mutable_data) == NULL);
|
||||
|
||||
|
@ -1331,7 +1330,6 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_
|
|||
_zend_hash_append_ptr(constants_table, key, c);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
ZEND_ASSERT(class_type->ce_flags & ZEND_ACC_IMMUTABLE);
|
||||
ZEND_ASSERT(ZEND_MAP_PTR(class_type->mutable_data) != NULL);
|
||||
|
||||
mutable_data = ZEND_MAP_PTR_GET_IMM(class_type->mutable_data);
|
||||
|
@ -4365,6 +4363,9 @@ ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *c
|
|||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
|
||||
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
|
||||
if (ce->type == ZEND_INTERNAL_CLASS && !ZEND_MAP_PTR(ce->mutable_data)) {
|
||||
ZEND_MAP_PTR_NEW(ce->mutable_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (!zend_hash_add_ptr(&ce->constants_table, name, c)) {
|
||||
|
|
|
@ -416,8 +416,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type);
|
|||
ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type);
|
||||
|
||||
static zend_always_inline HashTable *zend_class_constants_table(zend_class_entry *ce) {
|
||||
if ((ce->ce_flags & ZEND_ACC_HAS_AST_CONSTANTS)
|
||||
&& (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
if ((ce->ce_flags & ZEND_ACC_HAS_AST_CONSTANTS) && ZEND_MAP_PTR(ce->mutable_data)) {
|
||||
zend_class_mutable_data *mutable_data =
|
||||
(zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
|
||||
if (mutable_data && mutable_data->constants_table) {
|
||||
|
@ -431,8 +430,7 @@ static zend_always_inline HashTable *zend_class_constants_table(zend_class_entry
|
|||
}
|
||||
|
||||
static zend_always_inline zval *zend_class_default_properties_table(zend_class_entry *ce) {
|
||||
if ((ce->ce_flags & ZEND_ACC_HAS_AST_PROPERTIES)
|
||||
&& (ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
if ((ce->ce_flags & ZEND_ACC_HAS_AST_PROPERTIES) && ZEND_MAP_PTR(ce->mutable_data)) {
|
||||
zend_class_mutable_data *mutable_data =
|
||||
(zend_class_mutable_data*)ZEND_MAP_PTR_GET_IMM(ce->mutable_data);
|
||||
return mutable_data->default_properties_table;
|
||||
|
|
|
@ -38,10 +38,6 @@ static inline void *zend_ast_realloc(void *old, size_t old_size, size_t new_size
|
|||
return new;
|
||||
}
|
||||
|
||||
static inline size_t zend_ast_size(uint32_t children) {
|
||||
return sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children;
|
||||
}
|
||||
|
||||
static inline size_t zend_ast_list_size(uint32_t children) {
|
||||
return sizeof(zend_ast_list) - sizeof(zend_ast *) + sizeof(zend_ast *) * children;
|
||||
}
|
||||
|
|
|
@ -307,6 +307,10 @@ ZEND_API void ZEND_FASTCALL zend_ast_ref_destroy(zend_ast_ref *ast);
|
|||
typedef void (*zend_ast_apply_func)(zend_ast **ast_ptr, void *context);
|
||||
ZEND_API void zend_ast_apply(zend_ast *ast, zend_ast_apply_func fn, void *context);
|
||||
|
||||
static zend_always_inline size_t zend_ast_size(uint32_t children) {
|
||||
return sizeof(zend_ast) - sizeof(zend_ast *) + sizeof(zend_ast *) * children;
|
||||
}
|
||||
|
||||
static zend_always_inline bool zend_ast_is_special(zend_ast *ast) {
|
||||
return (ast->kind >> ZEND_AST_SPECIAL_SHIFT) & 1;
|
||||
}
|
||||
|
|
137
Zend/zend_enum.c
137
Zend/zend_enum.c
|
@ -371,3 +371,140 @@ void zend_enum_register_props(zend_class_entry *ce)
|
|||
zend_declare_typed_property(ce, ZSTR_KNOWN(ZEND_STR_VALUE), &value_default_value, ZEND_ACC_PUBLIC, NULL, value_type);
|
||||
}
|
||||
}
|
||||
|
||||
static const zend_function_entry unit_enum_methods[] = {
|
||||
ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
static const zend_function_entry backed_enum_methods[] = {
|
||||
ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
|
||||
ZEND_NAMED_ME(from, zend_enum_from_func, arginfo_class_BackedEnum_from, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
|
||||
ZEND_NAMED_ME(tryFrom, zend_enum_try_from_func, arginfo_class_BackedEnum_tryFrom, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
ZEND_API zend_class_entry *zend_register_internal_enum(
|
||||
const char *name, zend_uchar type, zend_function_entry *functions)
|
||||
{
|
||||
ZEND_ASSERT(type == IS_UNDEF || type == IS_LONG || type == IS_STRING);
|
||||
|
||||
zend_class_entry tmp_ce;
|
||||
INIT_CLASS_ENTRY_EX(tmp_ce, name, strlen(name), functions);
|
||||
|
||||
zend_class_entry *ce = zend_register_internal_class(&tmp_ce);
|
||||
ce->ce_flags |= ZEND_ACC_ENUM;
|
||||
ce->enum_backing_type = type;
|
||||
if (type != IS_UNDEF) {
|
||||
ce->backed_enum_table = pemalloc(sizeof(HashTable), 1);
|
||||
zend_hash_init(ce->backed_enum_table, 0, NULL, ZVAL_PTR_DTOR, 1);
|
||||
}
|
||||
|
||||
zend_enum_register_props(ce);
|
||||
if (type == IS_UNDEF) {
|
||||
zend_register_functions(
|
||||
ce, unit_enum_methods, &ce->function_table, EG(current_module)->type);
|
||||
zend_class_implements(ce, 1, zend_ce_unit_enum);
|
||||
} else {
|
||||
zend_register_functions(
|
||||
ce, backed_enum_methods, &ce->function_table, EG(current_module)->type);
|
||||
zend_class_implements(ce, 1, zend_ce_backed_enum);
|
||||
}
|
||||
|
||||
return ce;
|
||||
}
|
||||
|
||||
static zend_ast_ref *create_enum_case_ast(
|
||||
zend_string *class_name, zend_string *case_name, zval *value) {
|
||||
// TODO: Use custom node type for enum cases?
|
||||
size_t num_children = value ? 3 : 2;
|
||||
size_t size = sizeof(zend_ast_ref) + zend_ast_size(num_children)
|
||||
+ num_children * sizeof(zend_ast_zval);
|
||||
char *p = pemalloc(size, 1);
|
||||
zend_ast_ref *ref = (zend_ast_ref *) p; p += sizeof(zend_ast_ref);
|
||||
GC_SET_REFCOUNT(ref, 1);
|
||||
GC_TYPE_INFO(ref) = GC_CONSTANT_AST | GC_PERSISTENT | GC_IMMUTABLE;
|
||||
|
||||
zend_ast *ast = (zend_ast *) p; p += zend_ast_size(3);
|
||||
ast->kind = ZEND_AST_CONST_ENUM_INIT;
|
||||
ast->attr = 0;
|
||||
ast->lineno = 0;
|
||||
|
||||
ast->child[0] = (zend_ast *) p; p += sizeof(zend_ast_zval);
|
||||
ast->child[0]->kind = ZEND_AST_ZVAL;
|
||||
ast->child[0]->attr = 0;
|
||||
ZEND_ASSERT(ZSTR_IS_INTERNED(class_name));
|
||||
ZVAL_STR(zend_ast_get_zval(ast->child[0]), class_name);
|
||||
|
||||
ast->child[1] = (zend_ast *) p; p += sizeof(zend_ast_zval);
|
||||
ast->child[1]->kind = ZEND_AST_ZVAL;
|
||||
ast->child[1]->attr = 0;
|
||||
ZEND_ASSERT(ZSTR_IS_INTERNED(case_name));
|
||||
ZVAL_STR(zend_ast_get_zval(ast->child[1]), case_name);
|
||||
|
||||
if (value) {
|
||||
ast->child[2] = (zend_ast *) p; p += sizeof(zend_ast_zval);
|
||||
ast->child[2]->kind = ZEND_AST_ZVAL;
|
||||
ast->child[2]->attr = 0;
|
||||
ZEND_ASSERT(!Z_REFCOUNTED_P(value));
|
||||
ZVAL_COPY_VALUE(zend_ast_get_zval(ast->child[2]), value);
|
||||
} else {
|
||||
ast->child[2] = NULL;
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, zval *value)
|
||||
{
|
||||
if (value) {
|
||||
ZEND_ASSERT(ce->enum_backing_type == Z_TYPE_P(value));
|
||||
if (Z_TYPE_P(value) == IS_STRING && !ZSTR_IS_INTERNED(Z_STR_P(value))) {
|
||||
zval_make_interned_string(value);
|
||||
}
|
||||
|
||||
zval case_name_zv;
|
||||
ZVAL_STR(&case_name_zv, case_name);
|
||||
if (Z_TYPE_P(value) == IS_LONG) {
|
||||
zend_hash_index_add_new(ce->backed_enum_table, Z_LVAL_P(value), &case_name_zv);
|
||||
} else {
|
||||
zend_hash_add_new(ce->backed_enum_table, Z_STR_P(value), &case_name_zv);
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(ce->enum_backing_type == IS_UNDEF);
|
||||
}
|
||||
|
||||
zval ast_zv;
|
||||
Z_TYPE_INFO(ast_zv) = IS_CONSTANT_AST;
|
||||
Z_AST(ast_zv) = create_enum_case_ast(ce->name, case_name, value);
|
||||
zend_class_constant *c = zend_declare_class_constant_ex(
|
||||
ce, case_name, &ast_zv, ZEND_ACC_PUBLIC, NULL);
|
||||
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE;
|
||||
}
|
||||
|
||||
ZEND_API void zend_enum_add_case_cstr(zend_class_entry *ce, const char *name, zval *value)
|
||||
{
|
||||
zend_string *name_str = zend_string_init_interned(name, strlen(name), 1);
|
||||
zend_enum_add_case(ce, name_str, value);
|
||||
zend_string_release(name_str);
|
||||
}
|
||||
|
||||
ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name) {
|
||||
zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), name);
|
||||
ZEND_ASSERT(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE);
|
||||
|
||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
|
||||
if (zval_update_constant_ex(&c->value, c->ce) == FAILURE) {
|
||||
ZEND_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
ZEND_ASSERT(Z_TYPE(c->value) == IS_OBJECT);
|
||||
return Z_OBJ(c->value);
|
||||
}
|
||||
|
||||
ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name) {
|
||||
zend_string *name_str = zend_string_init(name, strlen(name), 0);
|
||||
zend_object *result = zend_enum_get_case(ce, name_str);
|
||||
zend_string_release(name_str);
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,13 @@ void zend_verify_enum(zend_class_entry *ce);
|
|||
void zend_enum_register_funcs(zend_class_entry *ce);
|
||||
void zend_enum_register_props(zend_class_entry *ce);
|
||||
|
||||
ZEND_API zend_class_entry *zend_register_internal_enum(
|
||||
const char *name, zend_uchar type, zend_function_entry *functions);
|
||||
ZEND_API void zend_enum_add_case(zend_class_entry *ce, zend_string *case_name, zval *value);
|
||||
ZEND_API void zend_enum_add_case_cstr(zend_class_entry *ce, const char *name, zval *value);
|
||||
ZEND_API zend_object *zend_enum_get_case(zend_class_entry *ce, zend_string *name);
|
||||
ZEND_API zend_object *zend_enum_get_case_cstr(zend_class_entry *ce, const char *name);
|
||||
|
||||
static zend_always_inline zval *zend_enum_fetch_case_name(zend_object *zobj)
|
||||
{
|
||||
ZEND_ASSERT(zobj->ce->ce_flags & ZEND_ACC_ENUM);
|
||||
|
|
|
@ -397,6 +397,9 @@ ZEND_API void destroy_zend_class(zval *zv)
|
|||
}
|
||||
break;
|
||||
case ZEND_INTERNAL_CLASS:
|
||||
if (ce->backed_enum_table) {
|
||||
zend_hash_release(ce->backed_enum_table);
|
||||
}
|
||||
if (ce->default_properties_table) {
|
||||
zval *p = ce->default_properties_table;
|
||||
zval *end = p + ce->default_properties_count;
|
||||
|
@ -442,7 +445,14 @@ ZEND_API void destroy_zend_class(zval *zv)
|
|||
|
||||
ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
|
||||
if (c->ce == ce) {
|
||||
zval_internal_ptr_dtor(&c->value);
|
||||
if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
|
||||
/* We marked this as IMMUTABLE, but do need to free it when the
|
||||
* class is destroyed. */
|
||||
ZEND_ASSERT(Z_ASTVAL(c->value)->kind == ZEND_AST_CONST_ENUM_INIT);
|
||||
free(Z_AST(c->value));
|
||||
} else {
|
||||
zval_internal_ptr_dtor(&c->value);
|
||||
}
|
||||
if (c->doc_comment) {
|
||||
zend_string_release_ex(c->doc_comment, 1);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "observer.h"
|
||||
#include "fiber.h"
|
||||
#include "zend_attributes.h"
|
||||
#include "zend_enum.h"
|
||||
#include "Zend/Optimizer/zend_optimizer.h"
|
||||
|
||||
ZEND_DECLARE_MODULE_GLOBALS(zend_test)
|
||||
|
@ -38,6 +39,8 @@ static zend_class_entry *zend_test_attribute;
|
|||
static zend_class_entry *zend_test_ns_foo_class;
|
||||
static zend_class_entry *zend_test_ns2_foo_class;
|
||||
static zend_class_entry *zend_test_ns2_ns_foo_class;
|
||||
static zend_class_entry *zend_test_unit_enum;
|
||||
static zend_class_entry *zend_test_string_enum;
|
||||
static zend_object_handlers zend_test_class_handlers;
|
||||
|
||||
static ZEND_FUNCTION(zend_test_func)
|
||||
|
@ -227,6 +230,13 @@ static ZEND_FUNCTION(zend_iterable)
|
|||
ZEND_PARSE_PARAMETERS_END();
|
||||
}
|
||||
|
||||
static ZEND_FUNCTION(zend_get_unit_enum)
|
||||
{
|
||||
ZEND_PARSE_PARAMETERS_NONE();
|
||||
|
||||
RETURN_OBJ_COPY(zend_enum_get_case_cstr(zend_test_unit_enum, "Foo"));
|
||||
}
|
||||
|
||||
static ZEND_FUNCTION(namespaced_func)
|
||||
{
|
||||
ZEND_PARSE_PARAMETERS_NONE();
|
||||
|
@ -384,6 +394,17 @@ PHP_MINIT_FUNCTION(zend_test)
|
|||
zend_test_ns2_foo_class = register_class_ZendTestNS2_Foo();
|
||||
zend_test_ns2_ns_foo_class = register_class_ZendTestNS2_ZendSubNS_Foo();
|
||||
|
||||
zend_test_unit_enum = zend_register_internal_enum("ZendTestUnitEnum", IS_UNDEF, NULL);
|
||||
zend_enum_add_case_cstr(zend_test_unit_enum, "Foo", NULL);
|
||||
zend_enum_add_case_cstr(zend_test_unit_enum, "Bar", NULL);
|
||||
|
||||
zval val;
|
||||
zend_test_string_enum = zend_register_internal_enum("ZendTestStringEnum", IS_STRING, NULL);
|
||||
ZVAL_PSTRINGL(&val, "Test1", sizeof("Test1")-1);
|
||||
zend_enum_add_case_cstr(zend_test_string_enum, "Foo", &val);
|
||||
ZVAL_PSTRINGL(&val, "Test2", sizeof("Test2")-1);
|
||||
zend_enum_add_case_cstr(zend_test_string_enum, "Bar", &val);
|
||||
|
||||
// Loading via dl() not supported with the observer API
|
||||
if (type != MODULE_TEMPORARY) {
|
||||
REGISTER_INI_ENTRIES();
|
||||
|
|
|
@ -74,6 +74,7 @@ namespace {
|
|||
|
||||
function zend_iterable(iterable $arg1, ?iterable $arg2 = null): void {}
|
||||
|
||||
function zend_get_unit_enum(): ZendTestUnitEnum {}
|
||||
}
|
||||
|
||||
namespace ZendTestNS {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 2a1f8ff8205507259ba19bd379a07b390bc525cd */
|
||||
* Stub hash: 93bb8b9120e510e8c3afc29dc0a5d47cb6b5f10e */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
@ -51,6 +51,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_iterable, 0, 1, IS_VOID, 0)
|
|||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_ITERABLE, 1, "null")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_zend_get_unit_enum, 0, 0, ZendTestUnitEnum, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
|
@ -91,6 +94,7 @@ static ZEND_FUNCTION(zend_string_or_object_or_null);
|
|||
static ZEND_FUNCTION(zend_string_or_stdclass);
|
||||
static ZEND_FUNCTION(zend_string_or_stdclass_or_null);
|
||||
static ZEND_FUNCTION(zend_iterable);
|
||||
static ZEND_FUNCTION(zend_get_unit_enum);
|
||||
static ZEND_FUNCTION(namespaced_func);
|
||||
static ZEND_METHOD(_ZendTestClass, is_object);
|
||||
static ZEND_METHOD(_ZendTestClass, __toString);
|
||||
|
@ -117,6 +121,7 @@ static const zend_function_entry ext_functions[] = {
|
|||
ZEND_FE(zend_string_or_stdclass, arginfo_zend_string_or_stdclass)
|
||||
ZEND_FE(zend_string_or_stdclass_or_null, arginfo_zend_string_or_stdclass_or_null)
|
||||
ZEND_FE(zend_iterable, arginfo_zend_iterable)
|
||||
ZEND_FE(zend_get_unit_enum, arginfo_zend_get_unit_enum)
|
||||
ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue