Skip to content

Commit e23440e

Browse files
committed
Implement reflection constant
Fixes GH-13570 Closes GH-13669
1 parent 7a10b97 commit e23440e

15 files changed

+495
-31
lines changed

UPGRADING

+1
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ PHP 8.4 UPGRADE NOTES
252252
experience.
253253
. ReflectionClassConstant::__toString() and ReflectionProperty::__toString()
254254
now returns the attached doc comments.
255+
. ReflectionConstant was introduced.
255256

256257
- Standard:
257258
. stream_bucket_make_writeable() and stream_bucket_new() will now return a

Zend/zend_constants.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len)
268268
return NULL;
269269
}
270270

271-
static zend_constant *zend_get_constant_impl(zend_string *name)
271+
ZEND_API zend_constant *zend_get_constant_ptr(zend_string *name)
272272
{
273273
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
274274
if (c) {
@@ -285,7 +285,7 @@ static zend_constant *zend_get_constant_impl(zend_string *name)
285285

286286
ZEND_API zval *zend_get_constant(zend_string *name)
287287
{
288-
zend_constant *c = zend_get_constant_impl(name);
288+
zend_constant *c = zend_get_constant_ptr(name);
289289
if (c) {
290290
return &c->value;
291291
}
@@ -514,7 +514,7 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
514514
}
515515
} else {
516516
if (cname) {
517-
c = zend_get_constant_impl(cname);
517+
c = zend_get_constant_ptr(cname);
518518
} else {
519519
c = zend_get_constant_str_impl(name, name_len);
520520
}

Zend/zend_constants.h

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ void zend_startup_constants(void);
7474
void zend_register_standard_constants(void);
7575
ZEND_API bool zend_verify_const_access(zend_class_constant *c, zend_class_entry *ce);
7676
ZEND_API zval *zend_get_constant(zend_string *name);
77+
ZEND_API zend_constant *zend_get_constant_ptr(zend_string *name);
7778
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len);
7879
ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, uint32_t flags);
7980
ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *constant_name, zend_class_entry *scope, uint32_t flags);

ext/reflection/php_reflection.c

+174-7
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ PHPAPI zend_class_entry *reflection_enum_ptr;
9797
PHPAPI zend_class_entry *reflection_enum_unit_case_ptr;
9898
PHPAPI zend_class_entry *reflection_enum_backed_case_ptr;
9999
PHPAPI zend_class_entry *reflection_fiber_ptr;
100+
PHPAPI zend_class_entry *reflection_constant_ptr;
100101

101102
/* Exception throwing macro */
102103
#define _DO_THROW(msg) \
@@ -538,20 +539,48 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, char
538539
static void _const_string(smart_str *str, char *name, zval *value, char *indent)
539540
{
540541
const char *type = zend_zval_type_name(value);
542+
uint32_t flags = Z_CONSTANT_FLAGS_P(value);
543+
544+
smart_str_appends(str, indent);
545+
smart_str_appends(str, "Constant [ ");
546+
547+
if (flags & (CONST_PERSISTENT|CONST_NO_FILE_CACHE|CONST_DEPRECATED)) {
548+
bool first = true;
549+
smart_str_appends(str, "<");
550+
551+
#define DUMP_CONST_FLAG(flag, output) \
552+
do { \
553+
if (flags & flag) { \
554+
if (!first) smart_str_appends(str, ", "); \
555+
smart_str_appends(str, output); \
556+
first = false; \
557+
} \
558+
} while (0)
559+
DUMP_CONST_FLAG(CONST_PERSISTENT, "persistent");
560+
DUMP_CONST_FLAG(CONST_NO_FILE_CACHE, "no_file_cache");
561+
DUMP_CONST_FLAG(CONST_DEPRECATED, "deprecated");
562+
#undef DUMP_CONST_FLAG
563+
564+
smart_str_appends(str, "> ");
565+
}
566+
567+
smart_str_appends(str, type);
568+
smart_str_appendc(str, ' ');
569+
smart_str_appends(str, name);
570+
smart_str_appends(str, " ] { ");
541571

542572
if (Z_TYPE_P(value) == IS_ARRAY) {
543-
smart_str_append_printf(str, "%s Constant [ %s %s ] { Array }\n",
544-
indent, type, name);
573+
smart_str_append(str, ZSTR_KNOWN(ZEND_STR_ARRAY_CAPITALIZED));
545574
} else if (Z_TYPE_P(value) == IS_STRING) {
546-
smart_str_append_printf(str, "%s Constant [ %s %s ] { %s }\n",
547-
indent, type, name, Z_STRVAL_P(value));
575+
smart_str_appends(str, Z_STRVAL_P(value));
548576
} else {
549577
zend_string *tmp_value_str;
550578
zend_string *value_str = zval_get_tmp_string(value, &tmp_value_str);
551-
smart_str_append_printf(str, "%s Constant [ %s %s ] { %s }\n",
552-
indent, type, name, ZSTR_VAL(value_str));
579+
smart_str_append(str, value_str);
553580
zend_tmp_string_release(tmp_value_str);
554581
}
582+
583+
smart_str_appends(str, " }\n");
555584
}
556585
/* }}} */
557586

@@ -1059,7 +1088,7 @@ static void _extension_string(smart_str *str, zend_module_entry *module, char *i
10591088

10601089
ZEND_HASH_MAP_FOREACH_PTR(EG(zend_constants), constant) {
10611090
if (ZEND_CONSTANT_MODULE_NUMBER(constant) == module->module_number) {
1062-
_const_string(&str_constants, ZSTR_VAL(constant->name), &constant->value, indent);
1091+
_const_string(&str_constants, ZSTR_VAL(constant->name), &constant->value, " ");
10631092
num_constants++;
10641093
}
10651094
} ZEND_HASH_FOREACH_END();
@@ -7207,6 +7236,140 @@ static zval *_reflection_write_property(zend_object *object, zend_string *name,
72077236
}
72087237
/* }}} */
72097238

7239+
ZEND_METHOD(ReflectionConstant, __construct)
7240+
{
7241+
zend_string *name;
7242+
7243+
zval *object = ZEND_THIS;
7244+
reflection_object *intern = Z_REFLECTION_P(object);
7245+
7246+
ZEND_PARSE_PARAMETERS_START(1, 1)
7247+
Z_PARAM_STR(name)
7248+
ZEND_PARSE_PARAMETERS_END();
7249+
7250+
/* Build name with lowercased ns. */
7251+
bool backslash_prefixed = ZSTR_VAL(name)[0] == '\\';
7252+
char *source = ZSTR_VAL(name) + backslash_prefixed;
7253+
size_t source_len = ZSTR_LEN(name) - backslash_prefixed;
7254+
zend_string *lc_name = zend_string_alloc(source_len, /* persistent */ false);
7255+
const char *ns_end = zend_memrchr(source, '\\', source_len);
7256+
size_t ns_len = 0;
7257+
if (ns_end) {
7258+
ns_len = ns_end - ZSTR_VAL(name);
7259+
zend_str_tolower_copy(ZSTR_VAL(lc_name), source, ns_len);
7260+
}
7261+
memcpy(ZSTR_VAL(lc_name) + ns_len, source + ns_len, source_len - ns_len);
7262+
7263+
zend_constant *const_ = zend_get_constant_ptr(lc_name);
7264+
zend_string_release_ex(lc_name, /* persistent */ false);
7265+
if (!const_) {
7266+
zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant \"%s\" does not exist", ZSTR_VAL(name));
7267+
RETURN_THROWS();
7268+
}
7269+
7270+
intern->ptr = const_;
7271+
intern->ref_type = REF_TYPE_OTHER;
7272+
7273+
zval *name_zv = reflection_prop_name(object);
7274+
zval_ptr_dtor(name_zv);
7275+
ZVAL_STR_COPY(name_zv, name);
7276+
}
7277+
7278+
ZEND_METHOD(ReflectionConstant, getName)
7279+
{
7280+
reflection_object *intern;
7281+
zend_constant *const_;
7282+
7283+
if (zend_parse_parameters_none() == FAILURE) {
7284+
RETURN_THROWS();
7285+
}
7286+
7287+
GET_REFLECTION_OBJECT_PTR(const_);
7288+
RETURN_STR_COPY(const_->name);
7289+
}
7290+
7291+
ZEND_METHOD(ReflectionConstant, getNamespaceName)
7292+
{
7293+
reflection_object *intern;
7294+
zend_constant *const_;
7295+
7296+
if (zend_parse_parameters_none() == FAILURE) {
7297+
RETURN_THROWS();
7298+
}
7299+
7300+
GET_REFLECTION_OBJECT_PTR(const_);
7301+
7302+
const char *backslash = zend_memrchr(ZSTR_VAL(const_->name), '\\', ZSTR_LEN(const_->name));
7303+
if (backslash) {
7304+
size_t length = backslash - ZSTR_VAL(const_->name);
7305+
RETURN_STRINGL(ZSTR_VAL(const_->name), length);
7306+
} else {
7307+
RETURN_EMPTY_STRING();
7308+
}
7309+
}
7310+
7311+
ZEND_METHOD(ReflectionConstant, getShortName)
7312+
{
7313+
reflection_object *intern;
7314+
zend_constant *const_;
7315+
7316+
if (zend_parse_parameters_none() == FAILURE) {
7317+
RETURN_THROWS();
7318+
}
7319+
7320+
GET_REFLECTION_OBJECT_PTR(const_);
7321+
7322+
const char *backslash = zend_memrchr(ZSTR_VAL(const_->name), '\\', ZSTR_LEN(const_->name));
7323+
if (backslash) {
7324+
size_t prefix = backslash - ZSTR_VAL(const_->name) + 1;
7325+
size_t length = ZSTR_LEN(const_->name) - prefix;
7326+
RETURN_STRINGL(ZSTR_VAL(const_->name) + prefix, length);
7327+
} else {
7328+
RETURN_STR_COPY(const_->name);
7329+
}
7330+
}
7331+
7332+
ZEND_METHOD(ReflectionConstant, getValue)
7333+
{
7334+
reflection_object *intern;
7335+
zend_constant *const_;
7336+
7337+
if (zend_parse_parameters_none() == FAILURE) {
7338+
RETURN_THROWS();
7339+
}
7340+
7341+
GET_REFLECTION_OBJECT_PTR(const_);
7342+
RETURN_COPY(&const_->value);
7343+
}
7344+
7345+
ZEND_METHOD(ReflectionConstant, isDeprecated)
7346+
{
7347+
reflection_object *intern;
7348+
zend_constant *const_;
7349+
7350+
if (zend_parse_parameters_none() == FAILURE) {
7351+
RETURN_THROWS();
7352+
}
7353+
7354+
GET_REFLECTION_OBJECT_PTR(const_);
7355+
RETURN_BOOL(ZEND_CONSTANT_FLAGS(const_) & CONST_DEPRECATED);
7356+
}
7357+
7358+
ZEND_METHOD(ReflectionConstant, __toString)
7359+
{
7360+
reflection_object *intern;
7361+
zend_constant *const_;
7362+
smart_str str = {0};
7363+
7364+
if (zend_parse_parameters_none() == FAILURE) {
7365+
RETURN_THROWS();
7366+
}
7367+
7368+
GET_REFLECTION_OBJECT_PTR(const_);
7369+
_const_string(&str, ZSTR_VAL(const_->name), &const_->value, "");
7370+
RETURN_STR(smart_str_extract(&str));
7371+
}
7372+
72107373
PHP_MINIT_FUNCTION(reflection) /* {{{ */
72117374
{
72127375
memcpy(&reflection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
@@ -7306,6 +7469,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
73067469
reflection_fiber_ptr->create_object = reflection_objects_new;
73077470
reflection_fiber_ptr->default_object_handlers = &reflection_object_handlers;
73087471

7472+
reflection_constant_ptr = register_class_ReflectionConstant(reflector_ptr);
7473+
reflection_constant_ptr->create_object = reflection_objects_new;
7474+
reflection_constant_ptr->default_object_handlers = &reflection_object_handlers;
7475+
73097476
REFLECTION_G(key_initialized) = 0;
73107477

73117478
return SUCCESS;

ext/reflection/php_reflection.stub.php

+23
Original file line numberDiff line numberDiff line change
@@ -826,3 +826,26 @@ public function getCallable(): callable {}
826826

827827
public function getTrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT): array {}
828828
}
829+
830+
/**
831+
* @strict-properties
832+
* @not-serializable
833+
*/
834+
final class ReflectionConstant implements Reflector
835+
{
836+
public string $name;
837+
838+
public function __construct(string $name) {}
839+
840+
public function getName(): string {}
841+
842+
public function getNamespaceName(): string {}
843+
844+
public function getShortName(): string {}
845+
846+
public function getValue(): mixed {}
847+
848+
public function isDeprecated(): bool {}
849+
850+
public function __toString(): string {}
851+
}

ext/reflection/php_reflection_arginfo.h

+52-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)