summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2018-06-23 20:51:49 +0200
committerNikita Popov <nikita.ppv@gmail.com>2018-07-16 19:16:55 +0200
commit3588d8af129489eda3e3fdb9612b09a4da16dcfd (patch)
tree180d52b1c0e33acd97f98cdd4c9d2f839bdd0fa2
parenta632ecd22433f8f4b763586407bbc00472feba84 (diff)
downloadphp-git-3588d8af129489eda3e3fdb9612b09a4da16dcfd.tar.gz
Deprecate case-insensitive constants
RFC: https://wiki.php.net/rfc/case_insensitive_constant_deprecation
-rw-r--r--NEWS2
-rw-r--r--UPGRADING7
-rw-r--r--Zend/tests/bug46304.phpt15
-rw-r--r--Zend/tests/case_insensitive_constant_deprecation.phpt127
-rw-r--r--Zend/zend_builtin_functions.c9
-rw-r--r--Zend/zend_constants.c92
-rw-r--r--Zend/zend_constants.h6
-rw-r--r--Zend/zend_vm_def.h13
-rw-r--r--Zend/zend_vm_execute.h13
9 files changed, 263 insertions, 21 deletions
diff --git a/NEWS b/NEWS
index 3fc05e06e3..665b7f9c05 100644
--- 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).
diff --git a/UPGRADING b/UPGRADING
index 160a1d3549..abd2ca7f20 100644
--- 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.
diff --git a/Zend/tests/bug46304.phpt b/Zend/tests/bug46304.phpt
index e2e031116d..9898838907 100644
--- a/Zend/tests/bug46304.phpt
+++ b/Zend/tests/bug46304.phpt
@@ -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
index 0000000000..e7a92c8c4c
--- /dev/null
+++ b/Zend/tests/case_insensitive_constant_deprecation.phpt
@@ -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)
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index 57e5d77380..7b6cebff5e 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -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;
diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c
index fe1d9e235b..8b68858ad4 100644
--- a/Zend/zend_constants.c
+++ b/Zend/zend_constants.c
@@ -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;
}
diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h
index 98484debe2..e06d91ee14 100644
--- a/Zend/zend_constants.h
+++ b/Zend/zend_constants.h
@@ -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
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 898bb1229a..3d0fdc0182 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -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 {
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 499838f1cc..c5598d6da7 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -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);
}