diff --git a/Zend/tests/arginfo_zpp_mismatch.inc b/Zend/tests/arginfo_zpp_mismatch.inc index 2eb8905f5d4a1..aa362a2fac4ca 100644 --- a/Zend/tests/arginfo_zpp_mismatch.inc +++ b/Zend/tests/arginfo_zpp_mismatch.inc @@ -15,6 +15,7 @@ function skipFunction($function): bool { || $function === 'zend_test_array_return' || $function === 'zend_test_crash' || $function === 'zend_leak_bytes' + || $function === 'zend_test_use_internal_traits_not_trait' /* mess with output */ || (is_string($function) && str_starts_with($function, 'ob_')) || $function === 'output_add_rewrite_var' diff --git a/Zend/zend_API.h b/Zend/zend_API.h index c1ccbf13666a5..3aa1a19f399ba 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -392,6 +392,7 @@ ZEND_API zend_class_entry *zend_register_internal_class_ex(const zend_class_entr ZEND_API zend_class_entry *zend_register_internal_class_with_flags(const zend_class_entry *class_entry, zend_class_entry *parent_ce, uint32_t flags); ZEND_API zend_class_entry *zend_register_internal_interface(const zend_class_entry *orig_class_entry); ZEND_API void zend_class_implements(zend_class_entry *class_entry, int num_interfaces, ...); +ZEND_API void zend_class_use_internal_traits(zend_class_entry *class_entry, int num_traits, ...); ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_len, zend_class_entry *ce, bool persistent); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index ce576def24ddd..b3cc35396bd38 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -2402,7 +2402,11 @@ static void zend_add_trait_method(zend_class_entry *ce, zend_string *name, zend_ } } - if (UNEXPECTED(fn->type == ZEND_INTERNAL_FUNCTION)) { + if (ce->type == ZEND_INTERNAL_CLASS) { + ZEND_ASSERT(fn->type == ZEND_INTERNAL_FUNCTION); + new_fn = (zend_function*)(uintptr_t)malloc(sizeof(zend_internal_function)); + memcpy(new_fn, fn, sizeof(zend_internal_function)); + } else if (UNEXPECTED(fn->type == ZEND_INTERNAL_FUNCTION)) { new_fn = zend_arena_alloc(&CG(arena), sizeof(zend_internal_function)); memcpy(new_fn, fn, sizeof(zend_internal_function)); new_fn->common.fn_flags |= ZEND_ACC_ARENA_ALLOCATED; @@ -2823,7 +2827,11 @@ static void zend_do_traits_constant_binding(zend_class_entry *ce, zend_class_ent if (do_trait_constant_check(ce, constant, constant_name, traits, i)) { zend_class_constant *ct = NULL; - ct = zend_arena_alloc(&CG(arena),sizeof(zend_class_constant)); + if (ce->type == ZEND_INTERNAL_CLASS) { + ct = malloc(sizeof(zend_class_constant)); + } else { + ct = zend_arena_alloc(&CG(arena),sizeof(zend_class_constant)); + } memcpy(ct, constant, sizeof(zend_class_constant)); constant = ct; @@ -3004,6 +3012,56 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent } /* }}} */ +ZEND_API void zend_class_use_internal_traits(zend_class_entry *class_entry, int num_traits, ...) /* {{{ */ +{ + zend_class_entry *trait_entry; + va_list trait_list; + zend_class_entry **traits; + zend_function *fn; + + ZEND_ASSERT(class_entry->ce_flags & ZEND_ACC_LINKED); + ZEND_ASSERT(num_traits >= 0); + + if (UNEXPECTED(num_traits == 0)) { + return; + } + + traits = safe_emalloc(num_traits, sizeof(zend_class_entry *), 0); + class_entry->trait_names = safe_pemalloc(num_traits, sizeof(zend_class_name), 0, 1); + class_entry->num_traits = num_traits; + + va_start(trait_list, num_traits); + for (int i = 0; i < num_traits; i++) { + trait_entry = va_arg(trait_list, zend_class_entry *); + class_entry->trait_names[i].name = zend_string_copy(trait_entry->name); + class_entry->trait_names[i].lc_name = zend_string_tolower_ex(zend_string_copy(trait_entry->name), 1); + + if (UNEXPECTED(!(trait_entry->ce_flags & ZEND_ACC_TRAIT))) { + efree(traits); + zend_error_noreturn(E_COMPILE_ERROR, "Class %s cannot use %s - it is not a trait", + ZSTR_VAL(class_entry->name), ZSTR_VAL(trait_entry->name)); + } + traits[i] = trait_entry; + } + va_end(trait_list); + + bool contains_abstract_methods = false; + zend_do_traits_method_binding(class_entry, traits, NULL, NULL, false, &contains_abstract_methods); + + zend_do_traits_constant_binding(class_entry, traits); + + zend_do_traits_property_binding(class_entry, traits); + + ZEND_HASH_MAP_FOREACH_PTR(&class_entry->function_table, fn) { + zend_fixup_trait_method(fn, class_entry); + } ZEND_HASH_FOREACH_END(); + + efree(traits); + + /* TODO: Verify abstract trait method implementation requirements are enforced. */ +} +/* }}} */ + #define MAX_ABSTRACT_INFO_CNT 3 #define MAX_ABSTRACT_INFO_FMT "%s%s%s%s" #define DISPLAY_ABSTRACT_FN(idx) \ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index d874f566dc87d..065016351737c 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -486,7 +486,7 @@ ZEND_API void destroy_zend_class(zval *zv) zend_string_release_ex(ce->name, 1); ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) { - if (fn->common.scope == ce) { + if (fn->common.scope == ce && !(fn->common.fn_flags & ZEND_ACC_TRAIT_CLONE)) { zend_free_internal_arg_info(&fn->internal_function, true); if (fn->common.attributes) { @@ -536,6 +536,13 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->attributes) { zend_hash_release(ce->attributes); } + if (ce->num_traits > 0) { + for (uint32_t i = 0; i < ce->num_traits; i++) { + zend_string_release(ce->trait_names[i].name); + zend_string_release(ce->trait_names[i].lc_name); + } + free(ce->trait_names); + } free(ce); break; } diff --git a/build/gen_stub.php b/build/gen_stub.php index 9cd9b9a18768c..4e9f050945b44 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -13,6 +13,7 @@ use PhpParser\Node\Stmt\Enum_; use PhpParser\Node\Stmt\Interface_; use PhpParser\Node\Stmt\Trait_; +use PhpParser\Node\Stmt\TraitUse; use PhpParser\PrettyPrinter\Standard; use PhpParser\PrettyPrinterAbstract; @@ -3414,6 +3415,8 @@ class ClassInfo { private /* readonly */ array $extends; /** @var Name[] */ private /* readonly */ array $implements; + /** @var Name[] */ + private /* readonly */ array $uses; /** @var ConstInfo[] */ public /* readonly */ array $constInfos; /** @var PropertyInfo[] */ @@ -3430,6 +3433,7 @@ class ClassInfo { * @param AttributeInfo[] $attributes * @param Name[] $extends * @param Name[] $implements + * @param Name[] $uses * @param ConstInfo[] $constInfos * @param PropertyInfo[] $propertyInfos * @param FuncInfo[] $funcInfos @@ -3448,6 +3452,7 @@ public function __construct( bool $isNotSerializable, array $extends, array $implements, + array $uses, array $constInfos, array $propertyInfos, array $funcInfos, @@ -3468,6 +3473,7 @@ public function __construct( $this->isNotSerializable = $isNotSerializable; $this->extends = $extends; $this->implements = $implements; + $this->uses = $uses; $this->constInfos = $constInfos; $this->propertyInfos = $propertyInfos; $this->funcInfos = $funcInfos; @@ -3487,6 +3493,9 @@ public function getRegistration(array $allConstInfos): string foreach ($this->implements as $implements) { $params[] = "zend_class_entry *class_entry_" . implode("_", $implements->getParts()); } + foreach ($this->uses as $use) { + $params[] = "zend_class_entry *class_entry_" . implode("_", $use->getParts()); + } $escapedName = implode("_", $this->name->getParts()); @@ -3584,6 +3593,17 @@ function (Name $item) { $code .= "\tzend_class_implements(class_entry, " . count($implements) . ", " . implode(", ", $implements) . ");\n"; } + $traits = array_map( + function (Name $item) { + return "class_entry_" . implode("_", $item->getParts()); + }, + $this->uses + ); + + if (!empty($traits)) { + $code .= "\tzend_class_use_internal_traits(class_entry, " . count($traits) . ", " . implode(", ", $traits) . ");\n"; + } + if ($this->alias) { $code .= "\tzend_register_class_alias(\"" . str_replace("\\", "\\\\", $this->alias) . "\", class_entry);\n"; } @@ -4385,6 +4405,7 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri $propertyInfos = []; $methodInfos = []; $enumCaseInfos = []; + $traitUses = []; foreach ($stmt->stmts as $classStmt) { $cond = self::handlePreprocessorConditions($conds, $classStmt); if ($classStmt instanceof Stmt\Nop) { @@ -4443,6 +4464,10 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri } else if ($classStmt instanceof Stmt\EnumCase) { $enumCaseInfos[] = new EnumCaseInfo( $classStmt->name->toString(), $classStmt->expr); + } else if ($classStmt instanceof TraitUse) { + foreach ($classStmt->traits as $trait) { + $traitUses[] = $trait; + } } else { throw new Exception("Not implemented {$classStmt->getType()}"); } @@ -4455,6 +4480,7 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri $propertyInfos, $methodInfos, $enumCaseInfos, + $traitUses, $cond, $this->getMinimumPhpVersionIdCompatibility(), $this->isUndocumentable @@ -5000,6 +5026,7 @@ function parseProperty( * @param PropertyInfo[] $properties * @param FuncInfo[] $methods * @param EnumCaseInfo[] $enumCases + * @param Name[] $traitUses */ function parseClass( Name $name, @@ -5008,6 +5035,7 @@ function parseClass( array $properties, array $methods, array $enumCases, + array $traitUses, ?string $cond, ?int $minimumPhpVersionIdCompatibility, bool $isUndocumentable @@ -5083,6 +5111,7 @@ function parseClass( $isNotSerializable, $extends, $implements, + $traitUses, $consts, $properties, $methods, diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 31a14f219acbf..6a240780873d7 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -57,6 +57,10 @@ static zend_class_entry *zend_test_child_class; static zend_class_entry *zend_test_gen_stub_flag_compatibility_test; static zend_class_entry *zend_attribute_test_class; static zend_class_entry *zend_test_trait; +static zend_class_entry *zend_test_trait_for_internal_class; +static zend_class_entry *zend_test_class_with_trait; +static zend_class_entry *zend_test_not_a_trait_for_internal_traits; +static zend_class_entry *zend_test_internal_traits_driver; static zend_class_entry *zend_test_attribute; static zend_class_entry *zend_test_repeatable_attribute; static zend_class_entry *zend_test_parameter_attribute; @@ -1235,6 +1239,26 @@ static ZEND_METHOD(_ZendTestTrait, testMethod) RETURN_TRUE; } +static ZEND_METHOD(_ZendTestTraitForInternalClass, traitMethod) +{ + ZEND_PARSE_PARAMETERS_NONE(); + RETURN_LONG(789); +} + +static ZEND_FUNCTION(zend_test_use_internal_traits_zero) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_class_use_internal_traits(zend_test_internal_traits_driver, 0); +} + +static ZEND_FUNCTION(zend_test_use_internal_traits_not_trait) +{ + ZEND_PARSE_PARAMETERS_NONE(); + + zend_class_use_internal_traits(zend_test_internal_traits_driver, 1, zend_test_not_a_trait_for_internal_traits); +} + static ZEND_METHOD(ZendTestNS_Foo, method) { ZEND_PARSE_PARAMETERS_NONE(); @@ -1536,6 +1560,10 @@ PHP_MINIT_FUNCTION(zend_test) zend_attribute_test_class = register_class_ZendAttributeTest(); zend_test_trait = register_class__ZendTestTrait(); + zend_test_trait_for_internal_class = register_class__ZendTestTraitForInternalClass(); + zend_test_class_with_trait = register_class__ZendTestClassWithTrait(zend_test_trait_for_internal_class); + zend_test_not_a_trait_for_internal_traits = register_class__ZendTestNotATraitForInternalTraits(); + zend_test_internal_traits_driver = register_class__ZendTestInternalTraitsDriver(); register_test_symbols(module_number); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index e93362cebe07f..19e283ff51421 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -34,6 +34,28 @@ interface _ZendTestInterface public const DUMMY = 0; } + trait _ZendTestTraitForInternalClass + { + /** @var int */ + public const ZEND_TRAIT_CONST = 123; + + public int $traitProp = 456; + + public function traitMethod(): int {} + } + + class _ZendTestClassWithTrait + { + use _ZendTestTraitForInternalClass; + } + + class _ZendTestNotATraitForInternalTraits {} + + class _ZendTestInternalTraitsDriver {} + + function zend_test_use_internal_traits_zero(): void {} + function zend_test_use_internal_traits_not_trait(): void {} + /** @alias _ZendTestClassAlias */ class _ZendTestClass implements _ZendTestInterface { public const mixed TYPED_CLASS_CONST1 = []; diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index e7d09d7da5f3b..ee7c99ca19d54 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,10 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 25b63d5be5822cf0b717150dde07625cdd503c24 */ + * Stub hash: f98ede068cef2b4a2fe4a423d18fed1748c22b29 */ + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_use_internal_traits_zero, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() + +#define arginfo_zend_test_use_internal_traits_not_trait arginfo_zend_test_use_internal_traits_zero ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, IS_NEVER, 0) ZEND_END_ARG_INFO() @@ -10,8 +15,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_nullable_array_return, 0, 0, IS_ARRAY, 1) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_void_return, 0, 0, IS_VOID, 0) -ZEND_END_ARG_INFO() +#define arginfo_zend_test_void_return arginfo_zend_test_use_internal_traits_zero ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_compile_string, 0, 3, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, source_string, IS_STRING, 0) @@ -23,16 +27,16 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_deprecated, 0, 0, IS_V ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg, IS_MIXED, 0, "null") ZEND_END_ARG_INFO() -#define arginfo_zend_test_deprecated_attr arginfo_zend_test_void_return +#define arginfo_zend_test_deprecated_attr arginfo_zend_test_use_internal_traits_zero ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_nodiscard, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_zend_test_deprecated_nodiscard arginfo_zend_test_nodiscard -#define arginfo_zend_test_aliased arginfo_zend_test_void_return +#define arginfo_zend_test_aliased arginfo_zend_test_use_internal_traits_zero -#define arginfo_zend_test_deprecated_aliased arginfo_zend_test_void_return +#define arginfo_zend_test_deprecated_aliased arginfo_zend_test_use_internal_traits_zero ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_create_unterminated_string, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0) @@ -99,7 +103,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_parameter_with_attribu ZEND_ARG_TYPE_INFO(0, parameter, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_zend_test_attribute_with_named_argument arginfo_zend_test_void_return +#define arginfo_zend_test_attribute_with_named_argument arginfo_zend_test_use_internal_traits_zero ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_get_current_func_name, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -188,7 +192,7 @@ ZEND_END_ARG_INFO() #define arginfo_zend_test_compile_to_ast arginfo_zend_create_unterminated_string -#define arginfo_zend_test_gh18756 arginfo_zend_test_void_return +#define arginfo_zend_test_gh18756 arginfo_zend_test_use_internal_traits_zero #define arginfo_zend_test_opcache_preloading arginfo_zend_test_is_pcre_bundled @@ -197,23 +201,25 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_uri_parser, 0, 2, IS_A ZEND_ARG_TYPE_INFO(0, parser, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_zend_test_gh19792 arginfo_zend_test_void_return +#define arginfo_zend_test_gh19792 arginfo_zend_test_use_internal_traits_zero #define arginfo_ZendTestNS2_namespaced_func arginfo_zend_test_is_pcre_bundled -#define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_void_return +#define arginfo_ZendTestNS2_namespaced_deprecated_func arginfo_zend_test_use_internal_traits_zero -#define arginfo_ZendTestNS2_namespaced_aliased_func arginfo_zend_test_void_return +#define arginfo_ZendTestNS2_namespaced_aliased_func arginfo_zend_test_use_internal_traits_zero -#define arginfo_ZendTestNS2_namespaced_deprecated_aliased_func arginfo_zend_test_void_return +#define arginfo_ZendTestNS2_namespaced_deprecated_aliased_func arginfo_zend_test_use_internal_traits_zero #define arginfo_ZendTestNS2_ZendSubNS_namespaced_func arginfo_zend_test_is_pcre_bundled -#define arginfo_ZendTestNS2_ZendSubNS_namespaced_deprecated_func arginfo_zend_test_void_return +#define arginfo_ZendTestNS2_ZendSubNS_namespaced_deprecated_func arginfo_zend_test_use_internal_traits_zero -#define arginfo_ZendTestNS2_ZendSubNS_namespaced_aliased_func arginfo_zend_test_void_return +#define arginfo_ZendTestNS2_ZendSubNS_namespaced_aliased_func arginfo_zend_test_use_internal_traits_zero -#define arginfo_ZendTestNS2_ZendSubNS_namespaced_deprecated_aliased_func arginfo_zend_test_void_return +#define arginfo_ZendTestNS2_ZendSubNS_namespaced_deprecated_aliased_func arginfo_zend_test_use_internal_traits_zero + +#define arginfo_class__ZendTestTraitForInternalClass_traitMethod arginfo_zend_test_nodiscard #define arginfo_class__ZendTestClass_is_object arginfo_zend_test_nodiscard @@ -263,9 +269,9 @@ ZEND_END_ARG_INFO() #define arginfo_class_ZendTestChildClassWithMethodWithParameterAttribute_override arginfo_zend_test_parameter_with_attribute -#define arginfo_class_ZendTestForbidDynamicCall_call arginfo_zend_test_void_return +#define arginfo_class_ZendTestForbidDynamicCall_call arginfo_zend_test_use_internal_traits_zero -#define arginfo_class_ZendTestForbidDynamicCall_callStatic arginfo_zend_test_void_return +#define arginfo_class_ZendTestForbidDynamicCall_callStatic arginfo_zend_test_use_internal_traits_zero #if (PHP_VERSION_ID >= 80100) ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ZendTestNS_Foo_method, 0, 0, IS_LONG, 0) @@ -280,10 +286,12 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ZendTestNS_NotUnlikelyCompileError_method, 0, 0, ZendTestNS\\\116otUnlikelyCompileError, 1) ZEND_END_ARG_INFO() -#define arginfo_class_ZendTestNS2_Foo_method arginfo_zend_test_void_return +#define arginfo_class_ZendTestNS2_Foo_method arginfo_zend_test_use_internal_traits_zero -#define arginfo_class_ZendTestNS2_ZendSubNS_Foo_method arginfo_zend_test_void_return +#define arginfo_class_ZendTestNS2_ZendSubNS_Foo_method arginfo_zend_test_use_internal_traits_zero +static ZEND_FUNCTION(zend_test_use_internal_traits_zero); +static ZEND_FUNCTION(zend_test_use_internal_traits_not_trait); static ZEND_FUNCTION(zend_trigger_bailout); static ZEND_FUNCTION(zend_test_array_return); static ZEND_FUNCTION(zend_test_nullable_array_return); @@ -348,6 +356,7 @@ static ZEND_FUNCTION(ZendTestNS2_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_namespaced_deprecated_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_func); static ZEND_FUNCTION(ZendTestNS2_ZendSubNS_namespaced_deprecated_func); +static ZEND_METHOD(_ZendTestTraitForInternalClass, traitMethod); static ZEND_METHOD(_ZendTestClass, is_object); static ZEND_METHOD(_ZendTestClass, __toString); static ZEND_METHOD(_ZendTestClass, returnsStatic); @@ -374,6 +383,8 @@ static ZEND_METHOD(ZendTestNS2_Foo, method); static ZEND_METHOD(ZendTestNS2_ZendSubNS_Foo, method); static const zend_function_entry ext_functions[] = { + ZEND_FE(zend_test_use_internal_traits_zero, arginfo_zend_test_use_internal_traits_zero) + ZEND_FE(zend_test_use_internal_traits_not_trait, arginfo_zend_test_use_internal_traits_not_trait) ZEND_FE(zend_trigger_bailout, arginfo_zend_trigger_bailout) ZEND_FE(zend_test_array_return, arginfo_zend_test_array_return) #if (PHP_VERSION_ID >= 80400) @@ -527,6 +538,11 @@ static const zend_function_entry ext_functions[] = { ZEND_FE_END }; +static const zend_function_entry class__ZendTestTraitForInternalClass_methods[] = { + ZEND_ME(_ZendTestTraitForInternalClass, traitMethod, arginfo_class__ZendTestTraitForInternalClass_traitMethod, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static const zend_function_entry class__ZendTestClass_methods[] = { ZEND_ME(_ZendTestClass, is_object, arginfo_class__ZendTestClass_is_object, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(_ZendTestClass, __toString, arginfo_class__ZendTestClass___toString, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED) @@ -697,6 +713,76 @@ static zend_class_entry *register_class__ZendTestInterface(void) return class_entry; } +static zend_class_entry *register_class__ZendTestTraitForInternalClass(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "_ZendTestTraitForInternalClass", class__ZendTestTraitForInternalClass_methods); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_TRAIT); +#else + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_TRAIT; +#endif + + zval const_ZEND_TRAIT_CONST_value; + ZVAL_LONG(&const_ZEND_TRAIT_CONST_value, 123); + zend_string *const_ZEND_TRAIT_CONST_name = zend_string_init_interned("ZEND_TRAIT_CONST", sizeof("ZEND_TRAIT_CONST") - 1, true); + zend_declare_class_constant_ex(class_entry, const_ZEND_TRAIT_CONST_name, &const_ZEND_TRAIT_CONST_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release_ex(const_ZEND_TRAIT_CONST_name, true); + + zval property_traitProp_default_value; + ZVAL_LONG(&property_traitProp_default_value, 456); + zend_string *property_traitProp_name = zend_string_init("traitProp", sizeof("traitProp") - 1, true); + zend_declare_typed_property(class_entry, property_traitProp_name, &property_traitProp_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release_ex(property_traitProp_name, true); + + return class_entry; +} + +static zend_class_entry *register_class__ZendTestClassWithTrait(zend_class_entry *class_entry__ZendTestTraitForInternalClass) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "_ZendTestClassWithTrait", NULL); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else + class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif + zend_class_use_internal_traits(class_entry, 1, class_entry__ZendTestTraitForInternalClass); + + return class_entry; +} + +static zend_class_entry *register_class__ZendTestNotATraitForInternalTraits(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "_ZendTestNotATraitForInternalTraits", NULL); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else + class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif + + return class_entry; +} + +static zend_class_entry *register_class__ZendTestInternalTraitsDriver(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "_ZendTestInternalTraitsDriver", NULL); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); +#else + class_entry = zend_register_internal_class_ex(&ce, NULL); +#endif + + return class_entry; +} + static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_entry__ZendTestInterface) { zend_class_entry ce, *class_entry; diff --git a/ext/zend_test/tests/internal_class_trait.phpt b/ext/zend_test/tests/internal_class_trait.phpt new file mode 100644 index 0000000000000..471b4d2a5a7af --- /dev/null +++ b/ext/zend_test/tests/internal_class_trait.phpt @@ -0,0 +1,31 @@ +--TEST-- +Test internal class using trait via zend_class_use_internal_traits +--EXTENSIONS-- +zend_test +--FILE-- +traitProp); + +var_dump($obj->traitMethod()); + +var_dump(method_exists(_ZendTestClassWithTrait::class, 'traitMethod')); + +$rc = new ReflectionClass(_ZendTestClassWithTrait::class); +$traits = $rc->getTraitNames(); +var_dump(count($traits)); +var_dump(in_array('_ZendTestTraitForInternalClass', $traits)); + +echo "Done\n"; +?> +--EXPECT-- +int(123) +int(456) +int(789) +bool(true) +int(1) +bool(true) +Done diff --git a/ext/zend_test/tests/internal_class_trait_not_trait.phpt b/ext/zend_test/tests/internal_class_trait_not_trait.phpt new file mode 100644 index 0000000000000..c5350bd499988 --- /dev/null +++ b/ext/zend_test/tests/internal_class_trait_not_trait.phpt @@ -0,0 +1,13 @@ +--TEST-- +zend_class_use_internal_traits with non-trait class produces E_COMPILE_ERROR +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECTF-- +Fatal error: Class %s cannot use %s - it is not a trait in %s on line %d diff --git a/ext/zend_test/tests/internal_class_trait_zero_traits.phpt b/ext/zend_test/tests/internal_class_trait_zero_traits.phpt new file mode 100644 index 0000000000000..d44d7a446e225 --- /dev/null +++ b/ext/zend_test/tests/internal_class_trait_zero_traits.phpt @@ -0,0 +1,19 @@ +--TEST-- +zend_class_use_internal_traits with num_traits=0 (no-op behavior) +--EXTENSIONS-- +zend_test +--FILE-- +getTraitNames())); + +echo "Done\n"; +?> +--EXPECT-- +int(0) +Done