Skip to content

Add support for deprecating internal constants #5072

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Zend/tests/const_deprecation.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Internal constant deprecation
--SKIPIF--
<?php
if (!extension_loaded('zend-test')) die('skip requires zend-test');
?>
--FILE--
<?php

var_dump(ZEND_TEST_DEPRECATED);
var_dump(constant('ZEND_TEST_DEPRECATED'));

const X = ZEND_TEST_DEPRECATED;
var_dump(X);

?>
--EXPECTF--
Deprecated: Constant ZEND_TEST_DEPRECATED is deprecated in %s on line %d
int(42)

Deprecated: Constant ZEND_TEST_DEPRECATED is deprecated in %s on line %d
int(42)

Deprecated: Constant ZEND_TEST_DEPRECATED is deprecated in %s on line %d
int(42)
29 changes: 20 additions & 9 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1409,15 +1409,27 @@ ZEND_API int zend_unmangle_property_name_ex(const zend_string *name, const char
}
/* }}} */

static zend_bool can_ct_eval_const(zend_constant *c) {
if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
return 0;
}
if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
&& !(CG(compiler_options) & ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION)
&& !((ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
&& (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
return 1;
}
if (Z_TYPE(c->value) < IS_OBJECT
&& !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) {
return 1;
}
return 0;
}

static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool is_fully_qualified) /* {{{ */
{
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
if (c && (
((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
&& !(CG(compiler_options) & ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION)
&& !((ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE) && (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE)))
|| (Z_TYPE(c->value) < IS_OBJECT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION))
)) {
if (c && can_ct_eval_const(c)) {
ZVAL_COPY_OR_DUP(zv, &c->value);
return 1;
}
Expand All @@ -1426,14 +1438,13 @@ static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool i
/* Substitute true, false and null (including unqualified usage in namespaces) */
const char *lookup_name = ZSTR_VAL(name);
size_t lookup_len = ZSTR_LEN(name);
zval *val;

if (!is_fully_qualified) {
zend_get_unqualified_name(name, &lookup_name, &lookup_len);
}

if ((val = zend_get_special_const(lookup_name, lookup_len))) {
ZVAL_COPY_VALUE(zv, val);
if ((c = zend_get_special_const(lookup_name, lookup_len))) {
ZVAL_COPY_VALUE(zv, &c->value);
return 1;
}

Expand Down
77 changes: 47 additions & 30 deletions Zend/zend_constants.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
#define RESET_CONSTANT_VISITED(zv) Z_ACCESS_FLAGS_P(zv) &= ~IS_CONSTANT_VISITED_MARK

/* Use for special null/true/false constants. */
static zval null_value, true_value, false_value;
static zend_constant *null_const, *true_const, *false_const;

void free_zend_constant(zval *zv)
{
Expand Down Expand Up @@ -138,9 +138,9 @@ void zend_register_standard_constants(void)
REGISTER_MAIN_BOOL_CONSTANT("FALSE", 0, CONST_PERSISTENT);
REGISTER_MAIN_NULL_CONSTANT("NULL", CONST_PERSISTENT);

ZVAL_NULL(&null_value);
ZVAL_TRUE(&true_value);
ZVAL_FALSE(&false_value);
true_const = zend_hash_str_find_ptr(EG(zend_constants), "TRUE", sizeof("TRUE")-1);
false_const = zend_hash_str_find_ptr(EG(zend_constants), "FALSE", sizeof("FALSE")-1);
null_const = zend_hash_str_find_ptr(EG(zend_constants), "NULL", sizeof("NULL")-1);
}


Expand Down Expand Up @@ -235,22 +235,22 @@ static zend_constant *zend_get_halt_offset_constant(const char *name, size_t nam
}
}

ZEND_API zval *_zend_get_special_const(const char *name, size_t len) /* {{{ */
ZEND_API zend_constant *_zend_get_special_const(const char *name, size_t len) /* {{{ */
{
if (len == 4) {
if ((name[0] == 'n' || name[0] == 'N') &&
(name[1] == 'u' || name[1] == 'U') &&
(name[2] == 'l' || name[2] == 'L') &&
(name[3] == 'l' || name[3] == 'L')
) {
return &null_value;
return null_const;
}
if ((name[0] == 't' || name[0] == 'T') &&
(name[1] == 'r' || name[1] == 'R') &&
(name[2] == 'u' || name[2] == 'U') &&
(name[3] == 'e' || name[3] == 'E')
) {
return &true_value;
return true_const;
}
} else {
if ((name[0] == 'f' || name[0] == 'F') &&
Expand All @@ -259,10 +259,10 @@ ZEND_API zval *_zend_get_special_const(const char *name, size_t len) /* {{{ */
(name[3] == 's' || name[3] == 'S') &&
(name[4] == 'e' || name[4] == 'E')
) {
return &false_value;
return false_const;
}
}
return 0;
return NULL;
}
/* }}} */

Expand All @@ -279,36 +279,54 @@ ZEND_API int zend_verify_const_access(zend_class_constant *c, zend_class_entry *
}
/* }}} */

ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len)
static zend_constant *zend_get_constant_str_impl(const char *name, size_t name_len)
{
zend_constant *c = zend_hash_str_find_ptr(EG(zend_constants), name, name_len);
if (c) {
return &c->value;
return c;
}

c = zend_get_halt_offset_constant(name, name_len);
if (c) {
return &c->value;
return c;
}

return zend_get_special_const(name, name_len);
}

ZEND_API zval *zend_get_constant(zend_string *name)
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len)
{
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
zend_constant *c = zend_get_constant_str_impl(name, name_len);
if (c) {
return &c->value;
}
return NULL;
}

static zend_constant *zend_get_constant_impl(zend_string *name)
{
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
if (c) {
return c;
}

c = zend_get_halt_offset_constant(ZSTR_VAL(name), ZSTR_LEN(name));
if (c) {
return &c->value;
return c;
}

return zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
}

ZEND_API zval *zend_get_constant(zend_string *name)
{
zend_constant *c = zend_get_constant_impl(name);
if (c) {
return &c->value;
}
return NULL;
}

ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, uint32_t flags)
{
zend_constant *c;
Expand Down Expand Up @@ -402,7 +420,6 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
}

/* non-class constant */
zval *value;
if ((colon = zend_memrchr(name, '\\', name_len)) != NULL) {
/* compound constant name */
int prefix_len = colon - name;
Expand All @@ -423,28 +440,28 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
c = zend_hash_str_find_ptr(EG(zend_constants), lcname, lcname_len);
free_alloca(lcname, use_heap);

if (c) {
return &c->value;
}

if (flags & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
/* name requires runtime resolution, need to check non-namespaced name */
value = zend_get_constant_str(constant_name, const_name_len);
} else {
value = NULL;
if (!c) {
if (flags & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
/* name requires runtime resolution, need to check non-namespaced name */
c = zend_get_constant_str_impl(constant_name, const_name_len);
}
}
} else {
if (cname) {
value = zend_get_constant(cname);
c = zend_get_constant_impl(cname);
} else {
value = zend_get_constant_str(name, name_len);
c = zend_get_constant_str_impl(name, name_len);
}
}

if (!value && !(flags & ZEND_FETCH_CLASS_SILENT)) {
zend_throw_error(NULL, "Undefined constant '%s'", name);
if (!(flags & ZEND_FETCH_CLASS_SILENT)) {
if (!c) {
zend_throw_error(NULL, "Undefined constant '%s'", name);
} else if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
zend_error(E_DEPRECATED, "Constant %s is deprecated", name);
}
}
return value;
return &c->value;
}

static void* zend_hash_add_constant(HashTable *ht, zend_string *key, zend_constant *c)
Expand Down
6 changes: 4 additions & 2 deletions Zend/zend_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#define CONST_CS 0 /* No longer used -- always case sensitive */
#define CONST_PERSISTENT (1<<0) /* Persistent */
#define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */
#define CONST_DEPRECATED (1<<2) /* Deprecated */

#define PHP_USER_CONSTANT 0x7fffff /* a constant defined in user space */

Expand Down Expand Up @@ -86,9 +87,10 @@ ZEND_API int zend_register_constant(zend_constant *c);
void zend_copy_constants(HashTable *target, HashTable *sourc);
#endif

ZEND_API zval *_zend_get_special_const(const char *name, size_t name_len);
ZEND_API zend_constant *_zend_get_special_const(const char *name, size_t name_len);

static zend_always_inline zval *zend_get_special_const(const char *name, size_t name_len) {
static zend_always_inline zend_constant *zend_get_special_const(
const char *name, size_t name_len) {
if (name_len == 4 || name_len == 5) {
return _zend_get_special_const(name, name_len);
}
Expand Down
4 changes: 4 additions & 0 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -4301,6 +4301,10 @@ static zend_always_inline int _zend_quick_get_constant(

if (!check_defined_only) {
ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value);
if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
zend_error(E_DEPRECATED, "Constant %s is deprecated", ZSTR_VAL(c->name));
return SUCCESS;
}
}

CACHE_PTR(opline->extended_value, c);
Expand Down
8 changes: 4 additions & 4 deletions ext/opcache/Optimizer/block_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
/* Checks if a constant (like "true") may be replaced by its value */
int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
{
zval *zv;
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
if (c) {
if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
&& !(ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED)
&& (!(ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
|| !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
ZVAL_COPY_VALUE(result, &c->value);
Expand All @@ -50,9 +50,9 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
}

/* Special constants null/true/false can always be substituted. */
zv = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
if (zv) {
ZVAL_COPY_VALUE(result, zv);
c = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
if (c) {
ZVAL_COPY_VALUE(result, &c->value);
return 1;
}
return 0;
Expand Down
2 changes: 2 additions & 0 deletions ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ PHP_MINIT_FUNCTION(zend_test)
zend_declare_property_null(zend_test_trait, "testProp", sizeof("testProp")-1, ZEND_ACC_PUBLIC);

zend_register_class_alias("_ZendTestClassAlias", zend_test_class);

REGISTER_LONG_CONSTANT("ZEND_TEST_DEPRECATED", 42, CONST_PERSISTENT | CONST_DEPRECATED);
return SUCCESS;
}

Expand Down