]> granicus.if.org Git - php/commitdiff
Deprecate case-insensitive constants
authorNikita Popov <nikita.ppv@gmail.com>
Sat, 23 Jun 2018 18:51:49 +0000 (20:51 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Mon, 16 Jul 2018 17:16:55 +0000 (19:16 +0200)
RFC: https://wiki.php.net/rfc/case_insensitive_constant_deprecation

NEWS
UPGRADING
Zend/tests/bug46304.phpt
Zend/tests/case_insensitive_constant_deprecation.phpt [new file with mode: 0644]
Zend/zend_builtin_functions.c
Zend/zend_constants.c
Zend/zend_constants.h
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/NEWS b/NEWS
index 3fc05e06e30f365564ef932d4dc5d69b3a13fe78..665b7f9c057ded0355873fb60ea4fdc95925e0a8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,8 @@ PHP                                                                        NEWS
     arguments). (cmb)
   . Fixed bug #76392 (Error relocating sapi/cli/php: unsupported relocation
     type 37). (Peter Kokot)
+  . The declaration and use of case-insensitive constants has been deprecated.
+    (Nikita)
 
 - Filter:
   . Added the 'add_slashes' sanitization mode (FILTER_SANITIZE_ADD_SLASHES). 
index 160a1d3549268288950bebd94a5dcf86f038711a..abd2ca7f20c12dec0de46063be9a837a398b066a 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -258,6 +258,13 @@ FPM:
 4. Deprecated Functionality
 ========================================
 
+Core:
+  . The declaration of case-insensitive constants has been deprecate. Passing
+    true as the third argument to define() will now generate a deprecation
+    warning. The use of case-insensitive constants with a case that differs from
+    the declaration is also deprecated.
+    (RFC: https://wiki.php.net/rfc/case_insensitive_constant_deprecation)
+
 GD:
   . image2wbmp() has been deprecated.
 
index e2e031116d2e113d0bc53c778564355d8770d941..98988389070f6b853b0b2d91e1b2d5e1f91053eb 100644 (file)
@@ -40,6 +40,11 @@ print ns1\ns2\coNSt6 . "\n";
 print NS1\ns2\coNSt1 . "\n";
 ?>
 --EXPECTF--
+Deprecated: define(): Declaration of case-insensitive constants is deprecated in %s on line 6
+
+Deprecated: define(): Declaration of case-insensitive constants is deprecated in %s on line 7
+
+Deprecated: define(): Declaration of case-insensitive constants is deprecated in %s on line 8
 value1
 value1
 value1
@@ -52,13 +57,23 @@ value3
 value4
 value4
 value4
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "NS1\ns2\const4" in %s on line 25
 value4
 value5
 value5
 value5
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "ns1\ns2\const5" in %s on line 30
 value5
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "ns1\NS2\coNSt6" in %s on line 32
 value6
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "ns1\NS2\coNSt6" in %s on line 33
 value6
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "ns1\NS2\coNSt6" in %s on line 34
 value6
 value6
 
diff --git a/Zend/tests/case_insensitive_constant_deprecation.phpt b/Zend/tests/case_insensitive_constant_deprecation.phpt
new file mode 100644 (file)
index 0000000..e7a92c8
--- /dev/null
@@ -0,0 +1,127 @@
+--TEST--
+Case-insensitive constants are deprecated
+--FILE--
+<?php
+
+namespace {
+    define('FOO', 42, true); // Deprecated
+    define('NS\FOO', 24, true); // Deprecated
+
+    var_dump(FOO); // Ok
+    var_dump(foo); // Deprecated
+
+    var_dump(NS\FOO); // Ok
+    var_dump(ns\FOO); // Ok
+    var_dump(ns\foo); // Deprecated
+
+    var_dump(defined('FOO')); // Ok
+    var_dump(defined('foo')); // Ok
+    var_dump(defined('NS\FOO')); // Ok
+    var_dump(defined('ns\FOO')); // Ok
+    var_dump(defined('ns\foo')); // Ok
+
+    var_dump(constant('FOO')); // Ok
+    var_dump(constant('foo')); // Deprecated
+    var_dump(constant('NS\FOO')); // Ok
+    var_dump(constant('ns\FOO')); // Ok
+    var_dump(constant('ns\foo')); // Deprecated
+}
+
+namespace NS {
+    var_dump(FOO); // Ok
+    var_dump(foo); // Deprecated
+}
+
+namespace ns {
+    var_dump(FOO); // Ok
+    var_dump(foo); // Deprecated
+}
+
+namespace Other {
+    var_dump(FOO); // Ok
+    var_dump(foo); // Deprecated
+
+    var_dump(defined('FOO')); // Ok
+    var_dump(defined('foo')); // Ok
+    var_dump(defined('NS\FOO')); // Ok
+    var_dump(defined('ns\FOO')); // Ok
+    var_dump(defined('ns\foo')); // Ok
+
+    var_dump(constant('FOO')); // Ok
+    var_dump(constant('foo')); // Deprecated
+    var_dump(constant('NS\FOO')); // Ok
+    var_dump(constant('ns\FOO')); // Ok
+    var_dump(constant('ns\foo')); // Deprecated
+
+    const C1 = FOO; // Ok
+    var_dump(C1);
+    const C2 = foo; // Deprecated
+    var_dump(C2);
+    const C3 = 1 + FOO; // Ok
+    var_dump(C3);
+    const C4 = 1 + foo; // Deprecated
+    var_dump(C4);
+}
+
+?>
+--EXPECTF--
+Deprecated: define(): Declaration of case-insensitive constants is deprecated in %s on line 4
+
+Deprecated: define(): Declaration of case-insensitive constants is deprecated in %s on line 5
+int(42)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "FOO" in %s on line 8
+int(42)
+int(24)
+int(24)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "NS\FOO" in %s on line 12
+int(24)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+int(42)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "FOO" in %s on line 21
+int(42)
+int(24)
+int(24)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "NS\FOO" in %s on line 24
+int(24)
+int(24)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "NS\FOO" in %s on line 29
+int(24)
+int(24)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "NS\FOO" in %s on line 34
+int(24)
+int(42)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "FOO" in %s on line 39
+int(42)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+bool(true)
+int(42)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "FOO" in %s on line 48
+int(42)
+int(24)
+int(24)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "NS\FOO" in %s on line 51
+int(24)
+int(42)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "FOO" in %s on line 55
+int(42)
+int(43)
+
+Deprecated: Case-insensitive constants are deprecated. The correct casing for this constant is "FOO" in %s on line 59
+int(43)
index 57e5d7738000f0b1da99f8cb7e0cc7016bc386b8..7b6cebff5e58590cd469340f46e602cebcaefee7 100644 (file)
@@ -854,7 +854,6 @@ ZEND_FUNCTION(define)
                case_sensitive = 0;
        }
 
-       /* class constant, check if there is name and make sure class is valid & exists */
        if (zend_memnstr(ZSTR_VAL(name), "::", sizeof("::") - 1, ZSTR_VAL(name) + ZSTR_LEN(name))) {
                zend_error(E_WARNING, "Class constants cannot be defined or redefined");
                RETURN_FALSE;
@@ -905,7 +904,13 @@ repeat:
 
        ZVAL_COPY(&c.value, val);
        zval_ptr_dtor(&val_free);
+
 register_constant:
+       if (non_cs) {
+               zend_error(E_DEPRECATED,
+                       "define(): Declaration of case-insensitive constants is deprecated");
+       }
+
        c.flags = case_sensitive; /* non persistent */
        c.name = zend_string_copy(name);
        c.module_number = PHP_USER_CONSTANT;
@@ -928,7 +933,7 @@ ZEND_FUNCTION(defined)
                Z_PARAM_STR(name)
        ZEND_PARSE_PARAMETERS_END();
 
-       if (zend_get_constant_ex(name, zend_get_executed_scope(), ZEND_FETCH_CLASS_SILENT)) {
+       if (zend_get_constant_ex(name, zend_get_executed_scope(), ZEND_FETCH_CLASS_SILENT | ZEND_GET_CONSTANT_NO_DEPRECATION_CHECK)) {
                RETURN_TRUE;
        } else {
                RETURN_FALSE;
index fe1d9e235b459c0475354d77da4e8a7705f71554..8b68858ad4c45401751dc3b3b20fdf0d178e377c 100644 (file)
@@ -250,7 +250,7 @@ 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 inline zend_constant *zend_get_constant_str_impl(const char *name, size_t name_len)
 {
        zend_constant *c;
        ALLOCA_FLAG(use_heap)
@@ -268,10 +268,16 @@ ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len)
                free_alloca(lcname, use_heap);
        }
 
+       return c;
+}
+
+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);
        return c ? &c->value : NULL;
 }
 
-ZEND_API zval *zend_get_constant(zend_string *name)
+static inline zend_constant *zend_get_constant_impl(zend_string *name)
 {
     zval *zv;
        zend_constant *c;
@@ -291,9 +297,32 @@ ZEND_API zval *zend_get_constant(zend_string *name)
                        c = zend_get_special_constant(ZSTR_VAL(name), ZSTR_LEN(name));
                }
                free_alloca(lcname, use_heap);
-               return c ? &c->value : NULL;
+               return c;
+       } else {
+               return (zend_constant *) Z_PTR_P(zv);
+       }
+}
+
+ZEND_API zval *zend_get_constant(zend_string *name)
+{
+       zend_constant *c = zend_get_constant_impl(name);
+       return c ? &c->value : NULL;
+}
+
+static zend_bool is_access_deprecated(const zend_constant *c, const char *access_name) {
+       const char *ns_sep = zend_memrchr(ZSTR_VAL(c->name), '\\', ZSTR_LEN(c->name));
+       if (ns_sep) {
+               /* Namespaces are always case-insensitive. Only compare shortname. */
+               size_t shortname_offset = ns_sep - ZSTR_VAL(c->name) + 1;
+               size_t shortname_len = ZSTR_LEN(c->name) - shortname_offset;
+               return memcmp(
+                       access_name + shortname_offset,
+                       ZSTR_VAL(c->name) + shortname_offset,
+                       shortname_len
+               ) != 0;
        } else {
-               return &((zend_constant*)Z_PTR_P(zv))->value;
+               /* No namespace, compare whole name */
+               return memcmp(access_name, ZSTR_VAL(c->name), ZSTR_LEN(c->name)) != 0;
        }
 }
 
@@ -415,26 +444,45 @@ failure:
                        }
                }
                free_alloca(lcname, use_heap);
-               if (c) {
-                       return &c->value;
+
+               if (!c) {
+                       if (!(flags & IS_CONSTANT_UNQUALIFIED)) {
+                               return NULL;
+                       }
+
+                       /* name requires runtime resolution, need to check non-namespaced name */
+                       c = zend_get_constant_str_impl(constant_name, const_name_len);
+                       name = constant_name;
                }
-               /* name requires runtime resolution, need to check non-namespaced name */
-               if ((flags & IS_CONSTANT_UNQUALIFIED) != 0) {
-                       return zend_get_constant_str(constant_name, const_name_len);
+       } else {
+               if (cname) {
+                       c = zend_get_constant_impl(cname);
+               } else {
+                       c = zend_get_constant_str_impl(name, name_len);
                }
+       }
+
+       if (!c) {
                return NULL;
        }
 
-       if (cname) {
-               return zend_get_constant(cname);
-       } else {
-               return zend_get_constant_str(name, name_len);
+       if (!(flags & ZEND_GET_CONSTANT_NO_DEPRECATION_CHECK)) {
+               if (!(c->flags & (CONST_CS|CONST_CT_SUBST)) && is_access_deprecated(c, name)) {
+                       zend_error(E_DEPRECATED,
+                               "Case-insensitive constants are deprecated. "
+                               "The correct casing for this constant is \"%s\"",
+                               ZSTR_VAL(c->name));
+               }
        }
+
+       return &c->value;
 }
 
-ZEND_API zend_constant* ZEND_FASTCALL zend_quick_get_constant(const zval *key, uint32_t flags)
+ZEND_API zend_constant* ZEND_FASTCALL zend_quick_get_constant(
+               const zval *key, uint32_t flags, zend_bool *is_deprecated)
 {
        zval *zv;
+       const zval *orig_key = key;
        zend_constant *c = NULL;
 
        zv = zend_hash_find_ex(EG(zend_constants), Z_STR_P(key), 1);
@@ -461,6 +509,22 @@ ZEND_API zend_constant* ZEND_FASTCALL zend_quick_get_constant(const zval *key, u
                        }
                }
        }
+
+       if (!c) {
+               return NULL;
+       }
+
+       if (is_deprecated) {
+               if (c->flags & (CONST_CS|CONST_CT_SUBST)) {
+                       /* Constant is case-sensitive or true/false/null */
+                       *is_deprecated = 0;
+               } else {
+                       zend_bool ns_fallback = key >= orig_key + 2;
+                       const zval *access_key = ns_fallback ? orig_key + 2 : orig_key - 1;
+                       *is_deprecated = is_access_deprecated(c, Z_STRVAL_P(access_key));
+               }
+       }
+
        return c;
 }
 
index 98484debe2a199131a68db9c578e63acc7f33409..e06d91ee14c03127ff876cce2afdcf156eefc312 100644 (file)
@@ -31,6 +31,9 @@
 
 #define        PHP_USER_CONSTANT INT_MAX       /* a constant defined in user space */
 
+/* Flag for zend_get_constant_ex(). Must not class with ZEND_FETCH_CLASS_* flags. */
+#define ZEND_GET_CONSTANT_NO_DEPRECATION_CHECK 0x1000
+
 typedef struct _zend_constant {
        zval value;
        zend_string *name;
@@ -79,7 +82,8 @@ ZEND_API int zend_register_constant(zend_constant *c);
 #ifdef ZTS
 void zend_copy_constants(HashTable *target, HashTable *sourc);
 #endif
-ZEND_API zend_constant* ZEND_FASTCALL zend_quick_get_constant(const zval *key, uint32_t flags);
+ZEND_API zend_constant* ZEND_FASTCALL zend_quick_get_constant(
+               const zval *key, uint32_t flags, zend_bool *is_deprecated);
 END_EXTERN_C()
 
 #define ZEND_CONSTANT_DTOR free_zend_constant
index 898bb1229a09f186477bca8e3fa308c6a47cd4f8..3d0fdc01829ac2ee9f6e21b547ae858320a1ea3a 100644 (file)
@@ -5045,11 +5045,12 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, UNUSED|CONST_FETCH, CONST, CACHE_SLOT)
 {
        USE_OPLINE
        zend_constant *c;
+       zend_bool is_deprecated;
 
        c = CACHED_PTR(opline->extended_value);
        if (EXPECTED(c != NULL) && EXPECTED(!IS_SPECIAL_CACHE_VAL(c))) {
                /* pass */
-       } else if (UNEXPECTED((c = zend_quick_get_constant(RT_CONSTANT(opline, opline->op2) + 1, opline->op1.num)) == NULL)) {
+       } else if (UNEXPECTED((c = zend_quick_get_constant(RT_CONSTANT(opline, opline->op2) + 1, opline->op1.num, &is_deprecated)) == NULL)) {
                SAVE_OPLINE();
 
                if ((opline->op1.num & IS_CONSTANT_UNQUALIFIED) != 0) {
@@ -5070,6 +5071,14 @@ ZEND_VM_HANDLER(99, ZEND_FETCH_CONSTANT, UNUSED|CONST_FETCH, CONST, CACHE_SLOT)
                        ZVAL_UNDEF(EX_VAR(opline->result.var));
                        HANDLE_EXCEPTION();
                }
+       } else if (is_deprecated) {
+               SAVE_OPLINE();
+               zend_error(E_DEPRECATED,
+                       "Case-insensitive constants are deprecated. "
+                       "The correct casing for this constant is \"%s\"",
+                       ZSTR_VAL(c->name));
+               ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value);
+               ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
        } else {
                CACHE_PTR(opline->extended_value, c);
        }
@@ -7611,7 +7620,7 @@ ZEND_VM_HANDLER(122, ZEND_DEFINED, CONST, ANY, CACHE_SLOT)
                                break;
                        }
                }
-               if ((c = zend_quick_get_constant(RT_CONSTANT(opline, opline->op1), 0)) == NULL) {
+               if ((c = zend_quick_get_constant(RT_CONSTANT(opline, opline->op1), 0, NULL)) == NULL) {
                        CACHE_PTR(opline->extended_value, ENCODE_SPECIAL_CACHE_NUM(zend_hash_num_elements(EG(zend_constants))));
                        result = 0;
                } else {
index 499838f1cc4a5c9647f293e82c7ca1d090886a3b..c5598d6da7d89dc0faa1c6900e19def209ed1271 100644 (file)
@@ -3883,7 +3883,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DEFINED_SPEC_CONST_HANDLER(ZEN
                                break;
                        }
                }
-               if ((c = zend_quick_get_constant(RT_CONSTANT(opline, opline->op1), 0)) == NULL) {
+               if ((c = zend_quick_get_constant(RT_CONSTANT(opline, opline->op1), 0, NULL)) == NULL) {
                        CACHE_PTR(opline->extended_value, ENCODE_SPECIAL_CACHE_NUM(zend_hash_num_elements(EG(zend_constants))));
                        result = 0;
                } else {
@@ -31965,11 +31965,12 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CON
 {
        USE_OPLINE
        zend_constant *c;
+       zend_bool is_deprecated;
 
        c = CACHED_PTR(opline->extended_value);
        if (EXPECTED(c != NULL) && EXPECTED(!IS_SPECIAL_CACHE_VAL(c))) {
                /* pass */
-       } else if (UNEXPECTED((c = zend_quick_get_constant(RT_CONSTANT(opline, opline->op2) + 1, opline->op1.num)) == NULL)) {
+       } else if (UNEXPECTED((c = zend_quick_get_constant(RT_CONSTANT(opline, opline->op2) + 1, opline->op1.num, &is_deprecated)) == NULL)) {
                SAVE_OPLINE();
 
                if ((opline->op1.num & IS_CONSTANT_UNQUALIFIED) != 0) {
@@ -31990,6 +31991,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CON
                        ZVAL_UNDEF(EX_VAR(opline->result.var));
                        HANDLE_EXCEPTION();
                }
+       } else if (is_deprecated) {
+               SAVE_OPLINE();
+               zend_error(E_DEPRECATED,
+                       "Case-insensitive constants are deprecated. "
+                       "The correct casing for this constant is \"%s\"",
+                       ZSTR_VAL(c->name));
+               ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value);
+               ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
        } else {
                CACHE_PTR(opline->extended_value, c);
        }