Skip to content

Commit 1875320

Browse files
Add #[ClassAlias]
1 parent 5f52f92 commit 1875320

10 files changed

+209
-1
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Alias name must be valid
3+
--FILE--
4+
<?php
5+
6+
#[ClassAlias('never')]
7+
class Demo {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Cannot use "never" as a class alias as it is reserved in %s on line %d
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Parameter must be a string
3+
--FILE--
4+
<?php
5+
6+
#[ClassAlias([])]
7+
class Demo {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: ClassAlias::__construct(): Argument #1 ($alias) must be of type string, array given in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
Parameter is required
3+
--FILE--
4+
<?php
5+
6+
#[ClassAlias]
7+
class Demo {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Uncaught ArgumentCountError: ClassAlias::__construct() expects exactly 1 argument, 0 given in %s:%d
12+
Stack trace:
13+
#0 %s(%d): ClassAlias->__construct()
14+
#1 {main}
15+
thrown in %s on line %d
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Cannot redeclare an existing class
3+
--FILE--
4+
<?php
5+
6+
#[ClassAlias('Attribute')]
7+
class Demo {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Unable to declare alias 'Attribute' for 'Demo' in %s on line %d
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Attribute can be repeated
3+
--FILE--
4+
<?php
5+
6+
#[ClassAlias('First')]
7+
#[ClassAlias('Second')]
8+
class Demo {}
9+
10+
var_dump( class_exists( 'First' ) );
11+
var_dump( class_exists( 'Second' ) );
12+
13+
$obj1 = new First();
14+
var_dump( $obj1 );
15+
16+
$obj2 = new Second();
17+
var_dump( $obj2 );
18+
?>
19+
--EXPECTF--
20+
bool(true)
21+
bool(true)
22+
object(Demo)#%d (0) {
23+
}
24+
object(Demo)#%d (0) {
25+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Can only be used on classes
3+
--FILE--
4+
<?php
5+
6+
#[ClassAlias]
7+
function demo() {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Attribute "ClassAlias" cannot target function (allowed targets: class) in %s on line %d
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Working usage
3+
--FILE--
4+
<?php
5+
6+
#[ClassAlias('Other')]
7+
class Demo {}
8+
9+
var_dump( class_exists( 'Other' ) );
10+
11+
$obj = new Other();
12+
var_dump( $obj );
13+
?>
14+
--EXPECTF--
15+
bool(true)
16+
object(Demo)#%d (0) {
17+
}

Zend/zend_attributes.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ ZEND_API zend_class_entry *zend_ce_sensitive_parameter_value;
3232
ZEND_API zend_class_entry *zend_ce_override;
3333
ZEND_API zend_class_entry *zend_ce_deprecated;
3434
ZEND_API zend_class_entry *zend_ce_nodiscard;
35+
ZEND_API zend_class_entry *zend_ce_class_alias;
3536

3637
static zend_object_handlers attributes_object_handlers_sensitive_parameter_value;
3738

@@ -95,6 +96,46 @@ static void validate_allow_dynamic_properties(
9596
scope->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
9697
}
9798

99+
static void validate_class_alias(
100+
zend_attribute *attr, uint32_t target, zend_class_entry *scope)
101+
{
102+
zval alias_obj;
103+
ZVAL_UNDEF(&alias_obj);
104+
zend_result result = zend_get_attribute_object(
105+
&alias_obj,
106+
zend_ce_class_alias,
107+
attr,
108+
scope,
109+
scope->info.user.filename
110+
);
111+
if (result == FAILURE) {
112+
ZEND_ASSERT(EG(exception));
113+
return;
114+
}
115+
116+
zval *alias_name = zend_read_property(
117+
zend_ce_class_alias,
118+
Z_OBJ(alias_obj),
119+
ZEND_STRL("alias"),
120+
false,
121+
NULL
122+
);
123+
result = zend_register_class_alias_ex(
124+
Z_STRVAL_P(alias_name),
125+
Z_STRLEN_P(alias_name),
126+
scope,
127+
false
128+
);
129+
if (result == FAILURE) {
130+
zend_error_noreturn(E_ERROR, "Unable to declare alias '%s' for '%s'",
131+
Z_STRVAL_P(alias_name),
132+
ZSTR_VAL(scope->name)
133+
);
134+
}
135+
zval_ptr_dtor(alias_name);
136+
zval_ptr_dtor(&alias_obj);
137+
}
138+
98139
ZEND_METHOD(Attribute, __construct)
99140
{
100141
zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
@@ -217,6 +258,24 @@ ZEND_METHOD(NoDiscard, __construct)
217258
}
218259
}
219260

261+
ZEND_METHOD(ClassAlias, __construct)
262+
{
263+
zend_string *alias = NULL;
264+
265+
ZEND_PARSE_PARAMETERS_START(1, 1)
266+
Z_PARAM_STR(alias)
267+
ZEND_PARSE_PARAMETERS_END();
268+
269+
zval value;
270+
ZVAL_STR(&value, alias);
271+
zend_update_property(zend_ce_class_alias, Z_OBJ_P(ZEND_THIS), ZEND_STRL("alias"), &value);
272+
273+
/* The assignment might fail due to 'readonly'. */
274+
if (UNEXPECTED(EG(exception))) {
275+
RETURN_THROWS();
276+
}
277+
}
278+
220279
static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
221280
{
222281
if (attributes) {
@@ -548,6 +607,10 @@ void zend_register_attribute_ce(void)
548607

549608
zend_ce_nodiscard = register_class_NoDiscard();
550609
attr = zend_mark_internal_attribute(zend_ce_nodiscard);
610+
611+
zend_ce_class_alias = register_class_ClassAlias();
612+
attr = zend_mark_internal_attribute(zend_ce_class_alias);
613+
attr->validator = validate_class_alias;
551614
}
552615

553616
void zend_attributes_shutdown(void)

Zend/zend_attributes.stub.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,14 @@ final class NoDiscard
9797

9898
public function __construct(?string $message = null) {}
9999
}
100+
101+
/**
102+
* @strict-properties
103+
*/
104+
#[Attribute(Attribute::TARGET_CLASS|Attribute::IS_REPEATABLE)]
105+
final class ClassAlias
106+
{
107+
public readonly string $alias;
108+
109+
public function __construct(string $alias) {}
110+
}

Zend/zend_attributes_arginfo.h

Lines changed: 34 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)