mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Add support for readonly classes (#7305)
RFC: https://wiki.php.net/rfc/readonly_classes
This commit is contained in:
parent
6beee1a5bb
commit
7850c10389
@ -0,0 +1,12 @@
|
||||
--TEST--
|
||||
The readonly class modifier can only be added once
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly readonly class Foo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Multiple readonly modifiers are not allowed in %s on line %d
|
@ -0,0 +1,20 @@
|
||||
--TEST--
|
||||
Readonly classes cannot use dynamic properties
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly class Foo
|
||||
{
|
||||
}
|
||||
|
||||
$foo = new Foo();
|
||||
|
||||
try {
|
||||
$foo->bar = 1;
|
||||
} catch (Error $exception) {
|
||||
echo $exception->getMessage() . "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Cannot create dynamic property Foo::$bar
|
@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
Readonly classes cannot apply the #[AllowDynamicProperties] attribute
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
#[AllowDynamicProperties]
|
||||
readonly class Foo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo in %s on line %d
|
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
The readonly and final class modifiers can be defined in the same time
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
final readonly class Foo
|
||||
{
|
||||
}
|
||||
|
||||
readonly class Bar extends Foo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Class Bar cannot extend final class Foo in %s on line %d
|
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
Non-readonly class cannot extend a readonly class
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly class Foo
|
||||
{
|
||||
}
|
||||
|
||||
class Bar extends Foo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Non-readonly class Bar cannot extend readonly class Foo in %s on line %d
|
@ -0,0 +1,16 @@
|
||||
--TEST--
|
||||
Readonly class cannot extend a non-readonly class
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo
|
||||
{
|
||||
}
|
||||
|
||||
readonly class Bar extends Foo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Readonly class Bar cannot extend non-readonly class Foo in %s on line %d
|
@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
Readonly class can extend a readonly class
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly class Foo
|
||||
{
|
||||
}
|
||||
|
||||
readonly class Bar extends Foo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
Normal properties of a readonly class must have type
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly class Foo
|
||||
{
|
||||
public $bar;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Readonly property Foo::$bar must have type in %s on line %d
|
@ -0,0 +1,15 @@
|
||||
--TEST--
|
||||
Promoted properties of a readonly class must have type
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly class Foo
|
||||
{
|
||||
public function __construct(
|
||||
private $bar
|
||||
) {}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Readonly property Foo::$bar must have type in %s on line %d
|
25
Zend/tests/readonly_classes/readonly_class_property1.phpt
Normal file
25
Zend/tests/readonly_classes/readonly_class_property1.phpt
Normal file
@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
Normal properties of a readonly class are implicitly declared as readonly
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly class Foo
|
||||
{
|
||||
public int $bar;
|
||||
|
||||
public function __construct() {
|
||||
$this->bar = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$foo = new Foo();
|
||||
|
||||
try {
|
||||
$foo->bar = 2;
|
||||
} catch (Error $exception) {
|
||||
echo $exception->getMessage() . "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Cannot modify readonly property Foo::$bar
|
23
Zend/tests/readonly_classes/readonly_class_property2.phpt
Normal file
23
Zend/tests/readonly_classes/readonly_class_property2.phpt
Normal file
@ -0,0 +1,23 @@
|
||||
--TEST--
|
||||
Promoted properties of a readonly class are implicitly declared as readonly
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly class Foo
|
||||
{
|
||||
public function __construct(
|
||||
public int $bar
|
||||
) {}
|
||||
}
|
||||
|
||||
$foo = new Foo(1);
|
||||
|
||||
try {
|
||||
$foo->bar = 2;
|
||||
} catch (Error $exception) {
|
||||
echo $exception->getMessage() . "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Cannot modify readonly property Foo::$bar
|
13
Zend/tests/readonly_classes/readonly_class_property3.phpt
Normal file
13
Zend/tests/readonly_classes/readonly_class_property3.phpt
Normal file
@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
Declaring static property for a readonly class is forbidden
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly class Foo
|
||||
{
|
||||
public static int $bar;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Readonly class Foo cannot declare static properties in %s on line %d
|
12
Zend/tests/readonly_classes/readonly_enum.phpt
Normal file
12
Zend/tests/readonly_classes/readonly_enum.phpt
Normal file
@ -0,0 +1,12 @@
|
||||
--TEST--
|
||||
Enums cannot be readonly
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly enum Foo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected token "enum", expecting "abstract" or "final" or "readonly" or "class" in %s on line %d
|
12
Zend/tests/readonly_classes/readonly_interface.phpt
Normal file
12
Zend/tests/readonly_classes/readonly_interface.phpt
Normal file
@ -0,0 +1,12 @@
|
||||
--TEST--
|
||||
Interfaces cannot be readonly
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly interface Foo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected token "interface", expecting "abstract" or "final" or "readonly" or "class" in %s on line %d
|
12
Zend/tests/readonly_classes/readonly_trait.phpt
Normal file
12
Zend/tests/readonly_classes/readonly_trait.phpt
Normal file
@ -0,0 +1,12 @@
|
||||
--TEST--
|
||||
Traits cannot be readonly
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly trait Foo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Parse error: syntax error, unexpected token "trait", expecting "abstract" or "final" or "readonly" or "class" in %s on line %d
|
@ -1715,6 +1715,9 @@ tail_call:
|
||||
if (decl->flags & ZEND_ACC_FINAL) {
|
||||
smart_str_appends(str, "final ");
|
||||
}
|
||||
if (decl->flags & ZEND_ACC_READONLY_CLASS) {
|
||||
smart_str_appends(str, "readonly ");
|
||||
}
|
||||
smart_str_appends(str, "class ");
|
||||
}
|
||||
smart_str_appendl(str, ZSTR_VAL(decl->name), ZSTR_LEN(decl->name));
|
||||
|
@ -71,6 +71,11 @@ static void validate_allow_dynamic_properties(
|
||||
if (scope->ce_flags & ZEND_ACC_INTERFACE) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to interface");
|
||||
}
|
||||
if (scope->ce_flags & ZEND_ACC_READONLY_CLASS) {
|
||||
zend_error_noreturn(E_ERROR, "Cannot apply #[AllowDynamicProperties] to readonly class %s",
|
||||
ZSTR_VAL(scope->name)
|
||||
);
|
||||
}
|
||||
scope->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
|
||||
}
|
||||
|
||||
|
@ -797,6 +797,10 @@ uint32_t zend_add_class_modifier(uint32_t flags, uint32_t new_flag) /* {{{ */
|
||||
zend_throw_exception(zend_ce_compile_error, "Multiple final modifiers are not allowed", 0);
|
||||
return 0;
|
||||
}
|
||||
if ((flags & ZEND_ACC_READONLY_CLASS) && (new_flag & ZEND_ACC_READONLY_CLASS)) {
|
||||
zend_throw_exception(zend_ce_compile_error, "Multiple readonly modifiers are not allowed", 0);
|
||||
return 0;
|
||||
}
|
||||
if ((new_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS) && (new_flags & ZEND_ACC_FINAL)) {
|
||||
zend_throw_exception(zend_ce_compile_error,
|
||||
"Cannot use the final modifier on an abstract class", 0);
|
||||
@ -6673,6 +6677,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
|
||||
if (property_flags) {
|
||||
zend_op_array *op_array = CG(active_op_array);
|
||||
zend_class_entry *scope = op_array->scope;
|
||||
|
||||
bool is_ctor =
|
||||
scope && zend_is_constructor(op_array->function_name);
|
||||
if (!is_ctor) {
|
||||
@ -6699,6 +6704,10 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
|
||||
ZSTR_VAL(scope->name), ZSTR_VAL(name), ZSTR_VAL(str));
|
||||
}
|
||||
|
||||
if (!(property_flags & ZEND_ACC_READONLY) && (scope->ce_flags & ZEND_ACC_READONLY_CLASS)) {
|
||||
property_flags |= ZEND_ACC_READONLY;
|
||||
}
|
||||
|
||||
/* Recompile the type, as it has different memory management requirements. */
|
||||
zend_type type = ZEND_TYPE_INIT_NONE(0);
|
||||
if (type_ast) {
|
||||
@ -7241,6 +7250,12 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Properties cannot be declared abstract");
|
||||
}
|
||||
|
||||
if ((ce->ce_flags & ZEND_ACC_READONLY_CLASS) && (flags & ZEND_ACC_STATIC)) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Readonly class %s cannot declare static properties",
|
||||
ZSTR_VAL(ce->name)
|
||||
);
|
||||
}
|
||||
|
||||
for (i = 0; i < children; ++i) {
|
||||
zend_property_info *info;
|
||||
zend_ast *prop_ast = list->child[i];
|
||||
@ -7306,6 +7321,10 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
|
||||
ZVAL_UNDEF(&value_zv);
|
||||
}
|
||||
|
||||
if ((ce->ce_flags & ZEND_ACC_READONLY_CLASS)) {
|
||||
flags |= ZEND_ACC_READONLY;
|
||||
}
|
||||
|
||||
if (flags & ZEND_ACC_READONLY) {
|
||||
if (!ZEND_TYPE_IS_SET(type)) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "Readonly property %s::$%s must have type",
|
||||
|
@ -240,7 +240,7 @@ typedef struct _zend_oparray_context {
|
||||
/* or IS_CONSTANT_VISITED_MARK | | | */
|
||||
#define ZEND_CLASS_CONST_IS_CASE (1 << 6) /* | | | X */
|
||||
/* | | | */
|
||||
/* Class Flags (unused: 16,21,30,31) | | | */
|
||||
/* Class Flags (unused: 21,30,31) | | | */
|
||||
/* =========== | | | */
|
||||
/* | | | */
|
||||
/* Special class types | | | */
|
||||
@ -273,6 +273,9 @@ typedef struct _zend_oparray_context {
|
||||
/* without triggering a deprecation warning | | | */
|
||||
#define ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES (1 << 15) /* X | | | */
|
||||
/* | | | */
|
||||
/* Readonly class | | | */
|
||||
#define ZEND_ACC_READONLY_CLASS (1 << 16) /* X | | | */
|
||||
/* | | | */
|
||||
/* Parent class is resolved (CE). | | | */
|
||||
#define ZEND_ACC_RESOLVED_PARENT (1 << 17) /* X | | | */
|
||||
/* | | | */
|
||||
|
@ -1430,6 +1430,13 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
|
||||
}
|
||||
}
|
||||
|
||||
if (UNEXPECTED((ce->ce_flags & ZEND_ACC_READONLY_CLASS) != (parent_ce->ce_flags & ZEND_ACC_READONLY_CLASS))) {
|
||||
zend_error_noreturn(E_COMPILE_ERROR, "%s class %s cannot extend %s class %s",
|
||||
ce->ce_flags & ZEND_ACC_READONLY_CLASS ? "Readonly" : "Non-readonly", ZSTR_VAL(ce->name),
|
||||
parent_ce->ce_flags & ZEND_ACC_READONLY_CLASS ? "readonly" : "non-readonly", ZSTR_VAL(parent_ce->name)
|
||||
);
|
||||
}
|
||||
|
||||
if (ce->parent_name) {
|
||||
zend_string_release_ex(ce->parent_name, 0);
|
||||
}
|
||||
|
@ -595,6 +595,7 @@ class_modifiers:
|
||||
class_modifier:
|
||||
T_ABSTRACT { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; }
|
||||
| T_FINAL { $$ = ZEND_ACC_FINAL; }
|
||||
| T_READONLY { $$ = ZEND_ACC_READONLY_CLASS|ZEND_ACC_NO_DYNAMIC_PROPERTIES; }
|
||||
;
|
||||
|
||||
trait_declaration_statement:
|
||||
|
@ -1797,6 +1797,10 @@ class ClassInfo {
|
||||
$flags[] = "ZEND_ACC_ABSTRACT";
|
||||
}
|
||||
|
||||
if ($this->flags & Class_::MODIFIER_READONLY) {
|
||||
$flags[] = "ZEND_ACC_READONLY_CLASS";
|
||||
}
|
||||
|
||||
if ($this->isDeprecated) {
|
||||
$flags[] = "ZEND_ACC_DEPRECATED";
|
||||
}
|
||||
|
@ -348,6 +348,9 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char
|
||||
if (ce->ce_flags & ZEND_ACC_FINAL) {
|
||||
smart_str_append_printf(str, "final ");
|
||||
}
|
||||
if (ce->ce_flags & ZEND_ACC_READONLY_CLASS) {
|
||||
smart_str_append_printf(str, "readonly ");
|
||||
}
|
||||
smart_str_append_printf(str, "class ");
|
||||
}
|
||||
smart_str_append_printf(str, "%s", ZSTR_VAL(ce->name));
|
||||
@ -4863,6 +4866,12 @@ ZEND_METHOD(ReflectionClass, isFinal)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* Returns whether this class is readonly */
|
||||
ZEND_METHOD(ReflectionClass, isReadOnly)
|
||||
{
|
||||
_class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_READONLY_CLASS);
|
||||
}
|
||||
|
||||
/* {{{ Returns whether this class is abstract */
|
||||
ZEND_METHOD(ReflectionClass, isAbstract)
|
||||
{
|
||||
@ -4875,8 +4884,7 @@ ZEND_METHOD(ReflectionClass, getModifiers)
|
||||
{
|
||||
reflection_object *intern;
|
||||
zend_class_entry *ce;
|
||||
uint32_t keep_flags = ZEND_ACC_FINAL
|
||||
| ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
|
||||
uint32_t keep_flags = ZEND_ACC_FINAL | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS | ZEND_ACC_READONLY_CLASS;
|
||||
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
@ -7123,6 +7131,7 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
|
||||
REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_IMPLICIT_ABSTRACT", ZEND_ACC_IMPLICIT_ABSTRACT_CLASS);
|
||||
REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_EXPLICIT_ABSTRACT", ZEND_ACC_EXPLICIT_ABSTRACT_CLASS);
|
||||
REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_FINAL", ZEND_ACC_FINAL);
|
||||
REGISTER_REFLECTION_CLASS_CONST_LONG(class, "IS_READONLY", ZEND_ACC_READONLY_CLASS);
|
||||
|
||||
reflection_object_ptr = register_class_ReflectionObject(reflection_class_ptr);
|
||||
reflection_object_ptr->create_object = reflection_objects_new;
|
||||
|
@ -318,6 +318,8 @@ class ReflectionClass implements Reflector
|
||||
/** @tentative-return-type */
|
||||
public function isFinal(): bool {}
|
||||
|
||||
public function isReadOnly(): bool {}
|
||||
|
||||
/** @tentative-return-type */
|
||||
public function getModifiers(): int {}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: c9656b23db965e890e73d0064005b981ee1991cf */
|
||||
* Stub hash: 4191864554b030bea40306c0d30090a8e2c76ab2 */
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
|
||||
@ -260,6 +260,8 @@ ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_ReflectionClass_isFinal arginfo_class_ReflectionFunctionAbstract_inNamespace
|
||||
|
||||
#define arginfo_class_ReflectionClass_isReadOnly arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType
|
||||
|
||||
#define arginfo_class_ReflectionClass_getModifiers arginfo_class_ReflectionFunctionAbstract_getNumberOfParameters
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionClass_isInstance, 0, 1, _IS_BOOL, 0)
|
||||
@ -697,6 +699,7 @@ ZEND_METHOD(ReflectionClass, isTrait);
|
||||
ZEND_METHOD(ReflectionClass, isEnum);
|
||||
ZEND_METHOD(ReflectionClass, isAbstract);
|
||||
ZEND_METHOD(ReflectionClass, isFinal);
|
||||
ZEND_METHOD(ReflectionClass, isReadOnly);
|
||||
ZEND_METHOD(ReflectionClass, getModifiers);
|
||||
ZEND_METHOD(ReflectionClass, isInstance);
|
||||
ZEND_METHOD(ReflectionClass, newInstance);
|
||||
@ -963,6 +966,7 @@ static const zend_function_entry class_ReflectionClass_methods[] = {
|
||||
ZEND_ME(ReflectionClass, isEnum, arginfo_class_ReflectionClass_isEnum, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(ReflectionClass, isAbstract, arginfo_class_ReflectionClass_isAbstract, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(ReflectionClass, isFinal, arginfo_class_ReflectionClass_isFinal, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(ReflectionClass, isReadOnly, arginfo_class_ReflectionClass_isReadOnly, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(ReflectionClass, getModifiers, arginfo_class_ReflectionClass_getModifiers, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(ReflectionClass, isInstance, arginfo_class_ReflectionClass_isInstance, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(ReflectionClass, newInstance, arginfo_class_ReflectionClass_newInstance, ZEND_ACC_PUBLIC)
|
||||
|
@ -9,15 +9,17 @@ abstract class A {}
|
||||
class B extends A {}
|
||||
class C {}
|
||||
final class D {}
|
||||
readonly class E {}
|
||||
interface I {}
|
||||
|
||||
$classes = array("A", "B", "C", "D", "I");
|
||||
$classes = array("A", "B", "C", "D", "E", "I");
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$rc = new ReflectionClass($class);
|
||||
var_dump($rc->isFinal());
|
||||
var_dump($rc->isInterface());
|
||||
var_dump($rc->isAbstract());
|
||||
var_dump($rc->isReadOnly());
|
||||
var_dump($rc->getModifiers());
|
||||
}
|
||||
?>
|
||||
@ -25,20 +27,30 @@ foreach ($classes as $class) {
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(true)
|
||||
bool(false)
|
||||
int(64)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
int(0)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
int(0)
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
int(32)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(true)
|
||||
int(65536)
|
||||
bool(false)
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(false)
|
||||
int(0)
|
||||
|
@ -11,10 +11,11 @@ echo $rc;
|
||||
--EXPECT--
|
||||
Class [ <internal:Reflection> class ReflectionClass implements Stringable, Reflector ] {
|
||||
|
||||
- Constants [3] {
|
||||
- Constants [4] {
|
||||
Constant [ public int IS_IMPLICIT_ABSTRACT ] { 16 }
|
||||
Constant [ public int IS_EXPLICIT_ABSTRACT ] { 64 }
|
||||
Constant [ public int IS_FINAL ] { 32 }
|
||||
Constant [ public int IS_READONLY ] { 65536 }
|
||||
}
|
||||
|
||||
- Static properties [0] {
|
||||
@ -27,7 +28,7 @@ Class [ <internal:Reflection> class ReflectionClass implements Stringable, Refle
|
||||
Property [ public string $name ]
|
||||
}
|
||||
|
||||
- Methods [55] {
|
||||
- Methods [56] {
|
||||
Method [ <internal:Reflection> private method __clone ] {
|
||||
|
||||
- Parameters [0] {
|
||||
@ -284,6 +285,13 @@ Class [ <internal:Reflection> class ReflectionClass implements Stringable, Refle
|
||||
- Tentative return [ bool ]
|
||||
}
|
||||
|
||||
Method [ <internal:Reflection> public method isReadOnly ] {
|
||||
|
||||
- Parameters [0] {
|
||||
}
|
||||
- Return [ bool ]
|
||||
}
|
||||
|
||||
Method [ <internal:Reflection> public method getModifiers ] {
|
||||
|
||||
- Parameters [0] {
|
||||
|
34
ext/reflection/tests/ReflectionClass_toString_005.phpt
Normal file
34
ext/reflection/tests/ReflectionClass_toString_005.phpt
Normal file
@ -0,0 +1,34 @@
|
||||
--TEST--
|
||||
Using ReflectionClass::__toString() on readonly classes
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
readonly class Foo {
|
||||
public int $bar;
|
||||
public readonly int $baz;
|
||||
}
|
||||
|
||||
echo new ReflectionClass(Foo::class);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Class [ <user> readonly class Foo ] {
|
||||
@@ %s 3-6
|
||||
|
||||
- Constants [0] {
|
||||
}
|
||||
|
||||
- Static properties [0] {
|
||||
}
|
||||
|
||||
- Static methods [0] {
|
||||
}
|
||||
|
||||
- Properties [2] {
|
||||
Property [ public readonly int $bar ]
|
||||
Property [ public readonly int $baz ]
|
||||
}
|
||||
|
||||
- Methods [0] {
|
||||
}
|
||||
}
|
25
ext/reflection/tests/readonly_class.phpt
Normal file
25
ext/reflection/tests/readonly_class.phpt
Normal file
@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
Readonly class reflection
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class Foo {
|
||||
}
|
||||
|
||||
readonly class Bar {
|
||||
}
|
||||
|
||||
$foo = new ReflectionClass(Foo::class);
|
||||
var_dump($foo->isReadOnly());
|
||||
var_dump(($foo->getModifiers() & ReflectionClass::IS_READONLY) != 0);
|
||||
|
||||
$bar = new ReflectionClass(Bar::class);
|
||||
var_dump($bar->isReadOnly());
|
||||
var_dump(($bar->getModifiers() & ReflectionClass::IS_READONLY) != 0);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(false)
|
||||
bool(false)
|
||||
bool(true)
|
||||
bool(true)
|
Loading…
Reference in New Issue
Block a user