Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_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);

Expand Down
55 changes: 55 additions & 0 deletions Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -2247,6 +2247,61 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
}
/* }}} */

static void zend_do_traits_method_binding(zend_class_entry *ce, zend_class_entry **traits, HashTable **exclude_tables, zend_class_entry **aliases, bool verify_abstract, bool *contains_abstract_methods);
static void zend_do_traits_constant_binding(zend_class_entry *ce, zend_class_entry **traits);
static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_entry **traits);
static void zend_fixup_trait_method(zend_function *fn, zend_class_entry *ce);
Comment thread
khaledalam marked this conversation as resolved.
Outdated

ZEND_API void zend_class_use_traits(zend_class_entry *class_entry, int num_traits, ...) /* {{{ */
Comment thread
khaledalam marked this conversation as resolved.
Outdated
{
zend_class_entry *trait_entry;
va_list trait_list;
zend_class_entry **traits;
zend_function *fn;
bool contains_abstract_methods = false;

ZEND_ASSERT(class_entry->ce_flags & ZEND_ACC_LINKED);

if (num_traits == 0) {
Comment thread
khaledalam marked this conversation as resolved.
Outdated
return;
}

traits = safe_emalloc(num_traits, sizeof(zend_class_entry *), 0);

va_start(trait_list, num_traits);
for (int i = 0; i < num_traits; i++) {
trait_entry = va_arg(trait_list, zend_class_entry *);
if (UNEXPECTED(!(trait_entry->ce_flags & ZEND_ACC_TRAIT))) {
efree(traits);
zend_error_noreturn(E_ERROR, "Class %s cannot use %s - it is not a trait",
Comment thread
khaledalam marked this conversation as resolved.
Outdated
ZSTR_VAL(class_entry->name), ZSTR_VAL(trait_entry->name));
return;
Comment thread
khaledalam marked this conversation as resolved.
Outdated
}
traits[i] = trait_entry;
}
va_end(trait_list);

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();

if (contains_abstract_methods) {
Comment thread
khaledalam marked this conversation as resolved.
Outdated
zend_do_traits_method_binding(class_entry, traits, NULL, NULL, true, &contains_abstract_methods);
ZEND_HASH_MAP_FOREACH_PTR(&class_entry->function_table, fn) {
zend_fixup_trait_method(fn, class_entry);
} ZEND_HASH_FOREACH_END();
}

efree(traits);
}
/* }}} */

static void zend_do_implement_interfaces(zend_class_entry *ce, zend_class_entry **interfaces) /* {{{ */
{
uint32_t num_parent_interfaces = ce->parent ? ce->parent->num_interfaces : 0;
Expand Down
29 changes: 29 additions & 0 deletions build/gen_stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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[] */
Expand All @@ -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
Expand All @@ -3448,6 +3452,7 @@ public function __construct(
bool $isNotSerializable,
array $extends,
array $implements,
array $uses,
array $constInfos,
array $propertyInfos,
array $funcInfos,
Expand All @@ -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;
Expand All @@ -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());

Expand Down Expand Up @@ -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_traits(class_entry, " . count($traits) . ", " . implode(", ", $traits) . ");\n";
}

if ($this->alias) {
$code .= "\tzend_register_class_alias(\"" . str_replace("\\", "\\\\", $this->alias) . "\", class_entry);\n";
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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()}");
}
Expand All @@ -4455,6 +4480,7 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri
$propertyInfos,
$methodInfos,
$enumCaseInfos,
$traitUses,
$cond,
$this->getMinimumPhpVersionIdCompatibility(),
$this->isUndocumentable
Expand Down Expand Up @@ -5000,6 +5026,7 @@ function parseProperty(
* @param PropertyInfo[] $properties
* @param FuncInfo[] $methods
* @param EnumCaseInfo[] $enumCases
* @param Name[] $traitUses
*/
function parseClass(
Name $name,
Expand All @@ -5008,6 +5035,7 @@ function parseClass(
array $properties,
array $methods,
array $enumCases,
array $traitUses,
?string $cond,
?int $minimumPhpVersionIdCompatibility,
bool $isUndocumentable
Expand Down Expand Up @@ -5083,6 +5111,7 @@ function parseClass(
$isNotSerializable,
$extends,
$implements,
$traitUses,
$consts,
$properties,
$methods,
Expand Down
11 changes: 11 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ 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_attribute;
static zend_class_entry *zend_test_repeatable_attribute;
static zend_class_entry *zend_test_parameter_attribute;
Expand Down Expand Up @@ -1235,6 +1237,12 @@ static ZEND_METHOD(_ZendTestTrait, testMethod)
RETURN_TRUE;
}

static ZEND_METHOD(_ZendTestTraitForInternalClass, traitMethod)
{
ZEND_PARSE_PARAMETERS_NONE();
RETURN_LONG(789);
}

static ZEND_METHOD(ZendTestNS_Foo, method)
{
ZEND_PARSE_PARAMETERS_NONE();
Expand Down Expand Up @@ -1537,6 +1545,9 @@ PHP_MINIT_FUNCTION(zend_test)

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);

register_test_symbols(module_number);

zend_test_attribute = register_class_ZendTestAttribute();
Expand Down
18 changes: 18 additions & 0 deletions ext/zend_test/test.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@ interface _ZendTestInterface
public const DUMMY = 0;
}

trait _ZendTestTraitForInternalClass
{
/** @var int */
public const ZEND_TRAIT_CONST = 123;

public int $traitProp = 456;

public function traitMethod(): int
{
return 789;
Comment thread
khaledalam marked this conversation as resolved.
Outdated
}
}

class _ZendTestClassWithTrait
{
use _ZendTestTraitForInternalClass;
}

/** @alias _ZendTestClassAlias */
class _ZendTestClass implements _ZendTestInterface {
public const mixed TYPED_CLASS_CONST1 = [];
Expand Down
52 changes: 51 additions & 1 deletion ext/zend_test/test_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions ext/zend_test/tests/internal_class_trait.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
Test internal class using trait via zend_class_use_traits
--EXTENSIONS--
zend_test
--FILE--
<?php

var_dump(_ZendTestClassWithTrait::ZEND_TRAIT_CONST);

$obj = new _ZendTestClassWithTrait();
var_dump($obj->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
Loading