]> granicus.if.org Git - php/commitdiff
Add support for deprecating constants
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 10 Jan 2020 10:34:05 +0000 (11:34 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Fri, 17 Jan 2020 09:05:06 +0000 (10:05 +0100)
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.

Zend/tests/const_deprecation.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_constants.c
Zend/zend_constants.h
Zend/zend_execute.c
ext/opcache/Optimizer/block_pass.c
ext/zend_test/test.c

diff --git a/Zend/tests/const_deprecation.phpt b/Zend/tests/const_deprecation.phpt
new file mode 100644 (file)
index 0000000..b7cf99d
--- /dev/null
@@ -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)
index 0f01d1a4e5e4f569174b98f82d5c7948696d088f..2a82513016bc673e2742a1bcbba0bc62b3e677e6 100644 (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;
                }
 
index 84e7891aba2507354686cbfd1c487c660e1d984e..0885525676bff5f9c340902978b2c72b4522528a 100644 (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_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;
@@ -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)
index 75be4c32b268c3dbb2f71315ab8b30641d7aef10..9b5a63a0260d9af82ff752c7857962326c0c06db 100644 (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);
        }
index cbd3a06fad1dbc7de0cd8e1cabaf9e49b9405ea0..deb89bc7e3d97f68cc5fa64846d3227cc3001053 100644 (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);
index fa570c9301b18f707c73fba815ea71fdc574c6f0..bd1946e960b08e4cb9a1b327e6dfea1c9a4c7782 100644 (file)
 /* 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;
index dc8a44cd95690cd47783c08b3ad4a1a5fa5cb0a0..64db2a380e52d873f14e0bf509b816cc86a34f7d 100644 (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;
 }