Skip to content

Commit 156156c

Browse files
committed
Add support for final constants
1 parent fea437a commit 156156c

File tree

11 files changed

+115
-14
lines changed

11 files changed

+115
-14
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Class constants support the final modifier
3+
--FILE--
4+
<?php
5+
6+
class Foo
7+
{
8+
final const A = "foo";
9+
final public const B = "foo";
10+
}
11+
12+
?>
13+
--EXPECT--
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Final class constants cannot be overridden
3+
--FILE--
4+
<?php
5+
6+
class Foo
7+
{
8+
final const A = "foo";
9+
}
10+
11+
class Bar extends Foo
12+
{
13+
const A = "bar";
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Bar::A cannot override final constant Foo::A in %s on line %d
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
Private class constants cannot be final
3+
--FILE--
4+
<?php
5+
6+
class Foo
7+
{
8+
private final const A = "foo";
9+
}
10+
11+
?>
12+
--EXPECTF--
13+
Fatal error: Private constant Foo::A cannot be final as it is never overridden in %s on line %d

Zend/tests/errmsg_038.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ class test {
1010
echo "Done\n";
1111
?>
1212
--EXPECTF--
13-
Fatal error: Cannot declare property test::$var final, the final modifier is allowed only for methods and classes in %s on line %d
13+
Fatal error: Cannot declare property test::$var final, the final modifier is allowed only for methods, classes, and class constants in %s on line %d

Zend/zend_compile.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7212,7 +7212,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z
72127212

72137213
if (flags & ZEND_ACC_FINAL) {
72147214
zend_error_noreturn(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, "
7215-
"the final modifier is allowed only for methods and classes",
7215+
"the final modifier is allowed only for methods, classes, and class constants",
72167216
ZSTR_VAL(ce->name), ZSTR_VAL(name));
72177217
}
72187218

@@ -7300,10 +7300,17 @@ void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_ast *attr
73007300
zend_string *doc_comment = doc_comment_ast ? zend_string_copy(zend_ast_get_str(doc_comment_ast)) : NULL;
73017301
zval value_zv;
73027302

7303-
if (UNEXPECTED(flags & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_FINAL))) {
7303+
if (UNEXPECTED(flags & (ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT))) {
73047304
zend_check_const_and_trait_alias_attr(flags, "constant");
73057305
}
73067306

7307+
if (UNEXPECTED((flags & ZEND_ACC_PRIVATE) && (flags & ZEND_ACC_FINAL))) {
7308+
zend_error_noreturn(
7309+
E_COMPILE_ERROR, "Private constant %s::%s cannot be final as it is never overridden",
7310+
ZSTR_VAL(ce->name), ZSTR_VAL(name)
7311+
);
7312+
}
7313+
73077314
zend_const_expr_to_zval(&value_zv, value_ast_ptr);
73087315
c = zend_declare_class_constant_ex(ce, name, &value_zv, flags, doc_comment);
73097316

Zend/zend_inheritance.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,13 @@ static void do_inherit_class_constant(zend_string *name, zend_class_constant *pa
12041204
zend_error_noreturn(E_COMPILE_ERROR, "Access level to %s::%s must be %s (as in class %s)%s",
12051205
ZSTR_VAL(ce->name), ZSTR_VAL(name), zend_visibility_string(ZEND_CLASS_CONST_FLAGS(parent_const)), ZSTR_VAL(parent_const->ce->name), (ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PUBLIC) ? "" : " or weaker");
12061206
}
1207+
1208+
if (UNEXPECTED((ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_FINAL))) {
1209+
zend_error_noreturn(
1210+
E_COMPILE_ERROR, "%s::%s cannot override final constant %s::%s",
1211+
ZSTR_VAL(ce->name), ZSTR_VAL(name), ZSTR_VAL(parent_const->ce->name), ZSTR_VAL(name)
1212+
);
1213+
}
12071214
} else if (!(ZEND_CLASS_CONST_FLAGS(parent_const) & ZEND_ACC_PRIVATE)) {
12081215
if (Z_TYPE(parent_const->value) == IS_CONSTANT_AST) {
12091216
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;

ext/reflection/php_reflection.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3784,6 +3784,12 @@ ZEND_METHOD(ReflectionClassConstant, isProtected)
37843784
}
37853785
/* }}} */
37863786

3787+
/* Returns whether this constant is final */
3788+
ZEND_METHOD(ReflectionClassConstant, isFinal)
3789+
{
3790+
_class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_FINAL);
3791+
}
3792+
37873793
/* {{{ Returns a bitfield of the access modifiers for this constant */
37883794
ZEND_METHOD(ReflectionClassConstant, getModifiers)
37893795
{

ext/reflection/php_reflection.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,8 @@ public function isPrivate(): bool {}
475475
/** @tentative-return-type */
476476
public function isProtected(): bool {}
477477

478+
public function isFinal(): bool {}
479+
478480
/** @tentative-return-type */
479481
public function getModifiers(): int {}
480482

ext/reflection/php_reflection_arginfo.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: e66c459f457f71cb677a93652364ab7e81be8b0e */
2+
* Stub hash: 7b88d88091244509d767a5146507ba0df17967e8 */
33

44
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0)
55
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
@@ -396,6 +396,8 @@ ZEND_END_ARG_INFO()
396396

397397
#define arginfo_class_ReflectionClassConstant_isProtected arginfo_class_ReflectionFunctionAbstract_inNamespace
398398

399+
#define arginfo_class_ReflectionClassConstant_isFinal arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
400+
399401
#define arginfo_class_ReflectionClassConstant_getModifiers arginfo_class_ReflectionFunctionAbstract_getNumberOfParameters
400402

401403
#define arginfo_class_ReflectionClassConstant_getDeclaringClass arginfo_class_ReflectionMethod_getDeclaringClass
@@ -734,6 +736,7 @@ ZEND_METHOD(ReflectionClassConstant, getValue);
734736
ZEND_METHOD(ReflectionClassConstant, isPublic);
735737
ZEND_METHOD(ReflectionClassConstant, isPrivate);
736738
ZEND_METHOD(ReflectionClassConstant, isProtected);
739+
ZEND_METHOD(ReflectionClassConstant, isFinal);
737740
ZEND_METHOD(ReflectionClassConstant, getModifiers);
738741
ZEND_METHOD(ReflectionClassConstant, getDeclaringClass);
739742
ZEND_METHOD(ReflectionClassConstant, getDocComment);
@@ -1012,6 +1015,7 @@ static const zend_function_entry class_ReflectionClassConstant_methods[] = {
10121015
ZEND_ME(ReflectionClassConstant, isPublic, arginfo_class_ReflectionClassConstant_isPublic, ZEND_ACC_PUBLIC)
10131016
ZEND_ME(ReflectionClassConstant, isPrivate, arginfo_class_ReflectionClassConstant_isPrivate, ZEND_ACC_PUBLIC)
10141017
ZEND_ME(ReflectionClassConstant, isProtected, arginfo_class_ReflectionClassConstant_isProtected, ZEND_ACC_PUBLIC)
1018+
ZEND_ME(ReflectionClassConstant, isFinal, arginfo_class_ReflectionClassConstant_isFinal, ZEND_ACC_PUBLIC)
10151019
ZEND_ME(ReflectionClassConstant, getModifiers, arginfo_class_ReflectionClassConstant_getModifiers, ZEND_ACC_PUBLIC)
10161020
ZEND_ME(ReflectionClassConstant, getDeclaringClass, arginfo_class_ReflectionClassConstant_getDeclaringClass, ZEND_ACC_PUBLIC)
10171021
ZEND_ME(ReflectionClassConstant, getDocComment, arginfo_class_ReflectionClassConstant_getDocComment, ZEND_ACC_PUBLIC)

ext/reflection/tests/ReflectionClassConstant_basic1.phpt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ function reflectClassConstant($base, $constant) {
2020
var_dump($constInfo->isPrivate());
2121
echo "isProtected():\n";
2222
var_dump($constInfo->isProtected());
23+
echo "isFinal():\n";
24+
var_dump($constInfo->isFinal());
2325
echo "getModifiers():\n";
2426
var_dump($constInfo->getModifiers());
2527
echo "getDeclaringClass():\n";
@@ -34,12 +36,14 @@ class TestClass {
3436
/** Another doc comment */
3537
protected const PROT = 4;
3638
private const PRIV = "keepOut";
39+
public final const FINAL = "foo";
3740
}
3841
$instance = new TestClass();
3942

4043
reflectClassConstant("TestClass", "PUB");
4144
reflectClassConstant("TestClass", "PROT");
4245
reflectClassConstant("TestClass", "PRIV");
46+
reflectClassConstant("TestClass", "FINAL");
4347
reflectClassConstant($instance, "PRIV");
4448
reflectClassConstant($instance, "BAD_CONST");
4549

@@ -61,6 +65,8 @@ isPrivate():
6165
bool(false)
6266
isProtected():
6367
bool(false)
68+
isFinal():
69+
bool(false)
6470
getModifiers():
6571
int(1)
6672
getDeclaringClass():
@@ -88,6 +94,8 @@ isPrivate():
8894
bool(false)
8995
isProtected():
9096
bool(true)
97+
isFinal():
98+
bool(false)
9199
getModifiers():
92100
int(2)
93101
getDeclaringClass():
@@ -115,6 +123,8 @@ isPrivate():
115123
bool(true)
116124
isProtected():
117125
bool(false)
126+
isFinal():
127+
bool(false)
118128
getModifiers():
119129
int(4)
120130
getDeclaringClass():
@@ -125,6 +135,35 @@ object(ReflectionClass)#3 (1) {
125135
getDocComment():
126136
bool(false)
127137

138+
**********************************
139+
**********************************
140+
Reflecting on class constant TestClass::FINAL
141+
142+
__toString():
143+
string(41) "Constant [ public string FINAL ] { foo }
144+
"
145+
getName():
146+
string(5) "FINAL"
147+
getValue():
148+
string(3) "foo"
149+
isPublic():
150+
bool(true)
151+
isPrivate():
152+
bool(false)
153+
isProtected():
154+
bool(false)
155+
isFinal():
156+
bool(true)
157+
getModifiers():
158+
int(1)
159+
getDeclaringClass():
160+
object(ReflectionClass)#3 (1) {
161+
["name"]=>
162+
string(9) "TestClass"
163+
}
164+
getDocComment():
165+
bool(false)
166+
128167
**********************************
129168
**********************************
130169
Reflecting on class constant TestClass::PRIV
@@ -142,6 +181,8 @@ isPrivate():
142181
bool(true)
143182
isProtected():
144183
bool(false)
184+
isFinal():
185+
bool(false)
145186
getModifiers():
146187
int(4)
147188
getDeclaringClass():

0 commit comments

Comments
 (0)