Add support for deprecating constants

Internal constants can be marked as CONST_DEPRECATED, in which
case accessing them will throw a deprecation warning.

For now this is only supported on global constants, not class
constants. Complain to me if you need to deprecate a class
constant...

Closes GH-5072.
This commit is contained in:
Nikita Popov 2020-01-10 11:34:05 +01:00
parent ae706e51e1
commit 9ec1ee5976
7 changed files with 106 additions and 45 deletions

View File

@ -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)

View File

@ -1401,15 +1401,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;
}
@ -1418,14 +1430,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;
}

View File

@ -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)
{
@ -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);
}
@ -235,7 +235,7 @@ 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') &&
@ -243,14 +243,14 @@ ZEND_API zval *_zend_get_special_const(const char *name, size_t len) /* {{{ */
(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') &&
@ -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;
}
/* }}} */
@ -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_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->value;
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;
@ -402,7 +420,6 @@ failure:
}
/* non-class constant */
zval *value;
if ((colon = zend_memrchr(name, '\\', name_len)) != NULL) {
/* compound constant name */
int prefix_len = colon - name;
@ -423,28 +440,28 @@ failure:
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)

View File

@ -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 */
@ -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);
}

View File

@ -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);

View File

@ -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);
@ -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;

View File

@ -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;
}