Skip to content

Commit 9ec1ee5

Browse files
committed
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.
1 parent ae706e5 commit 9ec1ee5

File tree

7 files changed

+106
-45
lines changed

7 files changed

+106
-45
lines changed

Zend/tests/const_deprecation.phpt

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Internal constant deprecation
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('zend-test')) die('skip requires zend-test');
6+
?>
7+
--FILE--
8+
<?php
9+
10+
var_dump(ZEND_TEST_DEPRECATED);
11+
var_dump(constant('ZEND_TEST_DEPRECATED'));
12+
13+
const X = ZEND_TEST_DEPRECATED;
14+
var_dump(X);
15+
16+
?>
17+
--EXPECTF--
18+
Deprecated: Constant ZEND_TEST_DEPRECATED is deprecated in %s on line %d
19+
int(42)
20+
21+
Deprecated: Constant ZEND_TEST_DEPRECATED is deprecated in %s on line %d
22+
int(42)
23+
24+
Deprecated: Constant ZEND_TEST_DEPRECATED is deprecated in %s on line %d
25+
int(42)

Zend/zend_compile.c

+20-9
Original file line numberDiff line numberDiff line change
@@ -1401,15 +1401,27 @@ ZEND_API int zend_unmangle_property_name_ex(const zend_string *name, const char
14011401
}
14021402
/* }}} */
14031403

1404+
static zend_bool can_ct_eval_const(zend_constant *c) {
1405+
if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
1406+
return 0;
1407+
}
1408+
if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
1409+
&& !(CG(compiler_options) & ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION)
1410+
&& !((ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
1411+
&& (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
1412+
return 1;
1413+
}
1414+
if (Z_TYPE(c->value) < IS_OBJECT
1415+
&& !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) {
1416+
return 1;
1417+
}
1418+
return 0;
1419+
}
1420+
14041421
static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool is_fully_qualified) /* {{{ */
14051422
{
14061423
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
1407-
if (c && (
1408-
((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
1409-
&& !(CG(compiler_options) & ZEND_COMPILE_NO_PERSISTENT_CONSTANT_SUBSTITUTION)
1410-
&& !((ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE) && (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE)))
1411-
|| (Z_TYPE(c->value) < IS_OBJECT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION))
1412-
)) {
1424+
if (c && can_ct_eval_const(c)) {
14131425
ZVAL_COPY_OR_DUP(zv, &c->value);
14141426
return 1;
14151427
}
@@ -1418,14 +1430,13 @@ static zend_bool zend_try_ct_eval_const(zval *zv, zend_string *name, zend_bool i
14181430
/* Substitute true, false and null (including unqualified usage in namespaces) */
14191431
const char *lookup_name = ZSTR_VAL(name);
14201432
size_t lookup_len = ZSTR_LEN(name);
1421-
zval *val;
14221433

14231434
if (!is_fully_qualified) {
14241435
zend_get_unqualified_name(name, &lookup_name, &lookup_len);
14251436
}
14261437

1427-
if ((val = zend_get_special_const(lookup_name, lookup_len))) {
1428-
ZVAL_COPY_VALUE(zv, val);
1438+
if ((c = zend_get_special_const(lookup_name, lookup_len))) {
1439+
ZVAL_COPY_VALUE(zv, &c->value);
14291440
return 1;
14301441
}
14311442

Zend/zend_constants.c

+47-30
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
#define RESET_CONSTANT_VISITED(zv) Z_ACCESS_FLAGS_P(zv) &= ~IS_CONSTANT_VISITED_MARK
3535

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

3939
void free_zend_constant(zval *zv)
4040
{
@@ -138,9 +138,9 @@ void zend_register_standard_constants(void)
138138
REGISTER_MAIN_BOOL_CONSTANT("FALSE", 0, CONST_PERSISTENT);
139139
REGISTER_MAIN_NULL_CONSTANT("NULL", CONST_PERSISTENT);
140140

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

146146

@@ -235,22 +235,22 @@ static zend_constant *zend_get_halt_offset_constant(const char *name, size_t nam
235235
}
236236
}
237237

238-
ZEND_API zval *_zend_get_special_const(const char *name, size_t len) /* {{{ */
238+
ZEND_API zend_constant *_zend_get_special_const(const char *name, size_t len) /* {{{ */
239239
{
240240
if (len == 4) {
241241
if ((name[0] == 'n' || name[0] == 'N') &&
242242
(name[1] == 'u' || name[1] == 'U') &&
243243
(name[2] == 'l' || name[2] == 'L') &&
244244
(name[3] == 'l' || name[3] == 'L')
245245
) {
246-
return &null_value;
246+
return null_const;
247247
}
248248
if ((name[0] == 't' || name[0] == 'T') &&
249249
(name[1] == 'r' || name[1] == 'R') &&
250250
(name[2] == 'u' || name[2] == 'U') &&
251251
(name[3] == 'e' || name[3] == 'E')
252252
) {
253-
return &true_value;
253+
return true_const;
254254
}
255255
} else {
256256
if ((name[0] == 'f' || name[0] == 'F') &&
@@ -259,10 +259,10 @@ ZEND_API zval *_zend_get_special_const(const char *name, size_t len) /* {{{ */
259259
(name[3] == 's' || name[3] == 'S') &&
260260
(name[4] == 'e' || name[4] == 'E')
261261
) {
262-
return &false_value;
262+
return false_const;
263263
}
264264
}
265-
return 0;
265+
return NULL;
266266
}
267267
/* }}} */
268268

@@ -279,36 +279,54 @@ ZEND_API int zend_verify_const_access(zend_class_constant *c, zend_class_entry *
279279
}
280280
/* }}} */
281281

282-
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len)
282+
static zend_constant *zend_get_constant_str_impl(const char *name, size_t name_len)
283283
{
284284
zend_constant *c = zend_hash_str_find_ptr(EG(zend_constants), name, name_len);
285285
if (c) {
286-
return &c->value;
286+
return c;
287287
}
288288

289289
c = zend_get_halt_offset_constant(name, name_len);
290290
if (c) {
291-
return &c->value;
291+
return c;
292292
}
293293

294294
return zend_get_special_const(name, name_len);
295295
}
296296

297-
ZEND_API zval *zend_get_constant(zend_string *name)
297+
ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len)
298298
{
299-
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
299+
zend_constant *c = zend_get_constant_str_impl(name, name_len);
300300
if (c) {
301301
return &c->value;
302302
}
303+
return NULL;
304+
}
305+
306+
static zend_constant *zend_get_constant_impl(zend_string *name)
307+
{
308+
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
309+
if (c) {
310+
return c;
311+
}
303312

304313
c = zend_get_halt_offset_constant(ZSTR_VAL(name), ZSTR_LEN(name));
305314
if (c) {
306-
return &c->value;
315+
return c;
307316
}
308317

309318
return zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
310319
}
311320

321+
ZEND_API zval *zend_get_constant(zend_string *name)
322+
{
323+
zend_constant *c = zend_get_constant_impl(name);
324+
if (c) {
325+
return &c->value;
326+
}
327+
return NULL;
328+
}
329+
312330
ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope, uint32_t flags)
313331
{
314332
zend_constant *c;
@@ -402,7 +420,6 @@ ZEND_API zval *zend_get_constant_ex(zend_string *cname, zend_class_entry *scope,
402420
}
403421

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

426-
if (c) {
427-
return &c->value;
428-
}
429-
430-
if (flags & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
431-
/* name requires runtime resolution, need to check non-namespaced name */
432-
value = zend_get_constant_str(constant_name, const_name_len);
433-
} else {
434-
value = NULL;
443+
if (!c) {
444+
if (flags & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
445+
/* name requires runtime resolution, need to check non-namespaced name */
446+
c = zend_get_constant_str_impl(constant_name, const_name_len);
447+
}
435448
}
436449
} else {
437450
if (cname) {
438-
value = zend_get_constant(cname);
451+
c = zend_get_constant_impl(cname);
439452
} else {
440-
value = zend_get_constant_str(name, name_len);
453+
c = zend_get_constant_str_impl(name, name_len);
441454
}
442455
}
443456

444-
if (!value && !(flags & ZEND_FETCH_CLASS_SILENT)) {
445-
zend_throw_error(NULL, "Undefined constant '%s'", name);
457+
if (!(flags & ZEND_FETCH_CLASS_SILENT)) {
458+
if (!c) {
459+
zend_throw_error(NULL, "Undefined constant '%s'", name);
460+
} else if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
461+
zend_error(E_DEPRECATED, "Constant %s is deprecated", name);
462+
}
446463
}
447-
return value;
464+
return &c->value;
448465
}
449466

450467
static void* zend_hash_add_constant(HashTable *ht, zend_string *key, zend_constant *c)

Zend/zend_constants.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#define CONST_CS 0 /* No longer used -- always case sensitive */
2626
#define CONST_PERSISTENT (1<<0) /* Persistent */
2727
#define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */
28+
#define CONST_DEPRECATED (1<<2) /* Deprecated */
2829

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

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

89-
ZEND_API zval *_zend_get_special_const(const char *name, size_t name_len);
90+
ZEND_API zend_constant *_zend_get_special_const(const char *name, size_t name_len);
9091

91-
static zend_always_inline zval *zend_get_special_const(const char *name, size_t name_len) {
92+
static zend_always_inline zend_constant *zend_get_special_const(
93+
const char *name, size_t name_len) {
9294
if (name_len == 4 || name_len == 5) {
9395
return _zend_get_special_const(name, name_len);
9496
}

Zend/zend_execute.c

+4
Original file line numberDiff line numberDiff line change
@@ -4301,6 +4301,10 @@ static zend_always_inline int _zend_quick_get_constant(
43014301

43024302
if (!check_defined_only) {
43034303
ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value);
4304+
if (ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED) {
4305+
zend_error(E_DEPRECATED, "Constant %s is deprecated", ZSTR_VAL(c->name));
4306+
return SUCCESS;
4307+
}
43044308
}
43054309

43064310
CACHE_PTR(opline->extended_value, c);

ext/opcache/Optimizer/block_pass.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
/* Checks if a constant (like "true") may be replaced by its value */
3434
int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy)
3535
{
36-
zval *zv;
3736
zend_constant *c = zend_hash_find_ptr(EG(zend_constants), name);
3837
if (c) {
3938
if ((ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT)
39+
&& !(ZEND_CONSTANT_FLAGS(c) & CONST_DEPRECATED)
4040
&& (!(ZEND_CONSTANT_FLAGS(c) & CONST_NO_FILE_CACHE)
4141
|| !(CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE))) {
4242
ZVAL_COPY_VALUE(result, &c->value);
@@ -50,9 +50,9 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int
5050
}
5151

5252
/* Special constants null/true/false can always be substituted. */
53-
zv = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
54-
if (zv) {
55-
ZVAL_COPY_VALUE(result, zv);
53+
c = zend_get_special_const(ZSTR_VAL(name), ZSTR_LEN(name));
54+
if (c) {
55+
ZVAL_COPY_VALUE(result, &c->value);
5656
return 1;
5757
}
5858
return 0;

ext/zend_test/test.c

+2
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ PHP_MINIT_FUNCTION(zend_test)
301301
zend_declare_property_null(zend_test_trait, "testProp", sizeof("testProp")-1, ZEND_ACC_PUBLIC);
302302

303303
zend_register_class_alias("_ZendTestClassAlias", zend_test_class);
304+
305+
REGISTER_LONG_CONSTANT("ZEND_TEST_DEPRECATED", 42, CONST_PERSISTENT | CONST_DEPRECATED);
304306
return SUCCESS;
305307
}
306308

0 commit comments

Comments
 (0)