Skip to content

Commit 1dea063

Browse files
committed
Implement reflection constant
Fixes phpGH-13570
1 parent bc59e79 commit 1dea063

13 files changed

+453
-5
lines changed

Zend/zend_constants.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len)
275275
return NULL;
276276
}
277277

278-
static zend_constant *zend_get_constant_impl(zend_string *name)
278+
ZEND_API zend_constant *zend_get_constant_ptr(zend_string *name)
279279
{
280280
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
281281
if (c) {
@@ -292,7 +292,7 @@ static zend_constant *zend_get_constant_impl(zend_string *name)
292292

293293
ZEND_API zval *zend_get_constant(zend_string *name)
294294
{
295-
zend_constant *c = zend_get_constant_impl(name);
295+
zend_constant *c = zend_get_constant_ptr(name);
296296
if (c) {
297297
return &c->value;
298298
}
@@ -521,7 +521,7 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
521521
}
522522
} else {
523523
if (cname) {
524-
c = zend_get_constant_impl(cname);
524+
c = zend_get_constant_ptr(cname);
525525
} else {
526526
c = zend_get_constant_str_impl(name, name_len);
527527
}

Zend/zend_constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ void zend_shutdown_constants(void);
7575
void zend_register_standard_constants(void);
7676
ZEND_API bool zend_verify_const_access(zend_class_constant *c, zend_class_entry *ce);
7777
ZEND_API zval *zend_get_constant(zend_string *name);
78+
ZEND_API zend_constant *zend_get_constant_ptr(zend_string *name);
7879
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len);
7980
ZEND_API zval *zend_get_constant_ex(zend_string *name, zend_class_entry *scope, uint32_t flags);
8081
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

Lines changed: 152 additions & 0 deletions
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) \
@@ -1250,6 +1251,19 @@ static void _zend_extension_string(smart_str *str, zend_extension *extension, ch
12501251
}
12511252
/* }}} */
12521253

1254+
static void _const_decl_string(smart_str *str, zend_constant *const_)
1255+
{
1256+
smart_str_append_printf(str, "Const [ ");
1257+
if (ZEND_CONSTANT_FLAGS(const_) & CONST_DEPRECATED) {
1258+
smart_str_append_printf(str, "<deprecated> ");
1259+
}
1260+
smart_str_append_printf(str, "%s = ", ZSTR_VAL(const_->name));
1261+
if (format_default_value(str, &const_->value) == FAILURE) {
1262+
return;
1263+
}
1264+
smart_str_appends(str, " ]\n");
1265+
}
1266+
12531267
/* {{{ _function_check_flag */
12541268
static void _function_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask)
12551269
{
@@ -7207,6 +7221,140 @@ static zval *_reflection_write_property(zend_object *object, zend_string *name,
72077221
}
72087222
/* }}} */
72097223

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

7457+
reflection_constant_ptr = register_class_ReflectionConstant(reflector_ptr);
7458+
reflection_constant_ptr->create_object = reflection_objects_new;
7459+
reflection_constant_ptr->default_object_handlers = &reflection_object_handlers;
7460+
73097461
REFLECTION_G(key_initialized) = 0;
73107462

73117463
return SUCCESS;

ext/reflection/php_reflection.stub.php

Lines changed: 23 additions & 0 deletions
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

Lines changed: 52 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
ReflectionConstant double construct call
3+
--FILE--
4+
<?php
5+
6+
const C1 = 42;
7+
const C2 = 43;
8+
9+
$r = new ReflectionConstant('C' . mt_rand(1, 1));
10+
var_dump($r);
11+
12+
$r->__construct('C' . mt_rand(2, 2));
13+
var_dump($r);
14+
15+
?>
16+
--EXPECT--
17+
object(ReflectionConstant)#1 (1) {
18+
["name"]=>
19+
string(2) "C1"
20+
}
21+
object(ReflectionConstant)#1 (1) {
22+
["name"]=>
23+
string(2) "C2"
24+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
var_dump(ReflectionConstant)
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
define('RT_CONST', 42);
9+
const CT_CONST = [
10+
'foo' => 'foo',
11+
'bar' => ['bar'],
12+
];
13+
14+
function dump($r) {
15+
var_dump($r);
16+
echo $r;
17+
}
18+
19+
dump(new ReflectionConstant('ZEND_CONSTANT_A'));
20+
dump(new ReflectionConstant('ZEND_TEST_DEPRECATED'));
21+
dump(new ReflectionConstant('RT_CONST'));
22+
dump(new ReflectionConstant('CT_CONST'));
23+
24+
?>
25+
--EXPECT--
26+
object(ReflectionConstant)#1 (1) {
27+
["name"]=>
28+
string(15) "ZEND_CONSTANT_A"
29+
}
30+
Const [ ZEND_CONSTANT_A = 'global' ]
31+
object(ReflectionConstant)#1 (1) {
32+
["name"]=>
33+
string(20) "ZEND_TEST_DEPRECATED"
34+
}
35+
Const [ <deprecated> ZEND_TEST_DEPRECATED = 42 ]
36+
object(ReflectionConstant)#1 (1) {
37+
["name"]=>
38+
string(8) "RT_CONST"
39+
}
40+
Const [ RT_CONST = 42 ]
41+
object(ReflectionConstant)#1 (1) {
42+
["name"]=>
43+
string(8) "CT_CONST"
44+
}
45+
Const [ CT_CONST = ['foo' => 'foo', 'bar' => ['bar']] ]

0 commit comments

Comments
 (0)