Skip to content

Commit 0833fd4

Browse files
dstogovhikari-no-yume
authored andcommitted
Allow arrays with define(), to match const syntax support
1 parent c94f62d commit 0833fd4

File tree

7 files changed

+182
-7
lines changed

7 files changed

+182
-7
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
. Fixed bug #55415 (php_info produces invalid anchor names). (Kalle, Johannes)
3232
. Added ?? operator. (Andrea)
3333
. Added \u{xxxxx} Unicode Codepoint Escape Syntax. (Andrea)
34+
. Fixed oversight where define() did not support arrays yet const syntax did. (Andrea, Dmitry)
3435

3536
- Date:
3637
. Fixed day_of_week function as it could sometimes return negative values

UPGRADING

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ PHP X.Y UPGRADE NOTES
7272
. Closure::call() method added
7373
. Added \u{xxxxxx} Unicode Codepoint Escape Syntax for double-quoted strings
7474
and heredocs.
75+
. define() now supports arrays as constant values, fixing an oversight where define() did not support arrays yet const syntax did. (Andrea, Dmitry)
7576

7677
- Standard
7778
. intdiv() function for integer division added.

Zend/tests/008.phpt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,9 @@ bool(true)
4141

4242
Notice: Constant test const already defined in %s on line %d
4343
bool(false)
44+
bool(true)
4445

45-
Warning: Constants may only evaluate to scalar values in %s on line %d
46-
bool(false)
47-
48-
Warning: Constants may only evaluate to scalar values in %s on line %d
46+
Warning: Constants may only evaluate to scalar values or arrays in %s on line %d
4947
bool(false)
5048
int(1)
5149
int(2)

Zend/tests/bug37811.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ var_dump(Baz);
2121
--EXPECTF--
2222
string(3) "Foo"
2323

24-
Warning: Constants may only evaluate to scalar values in %sbug37811.php on line %d
24+
Warning: Constants may only evaluate to scalar values or arrays in %sbug37811.php on line %d
2525

2626
Notice: Use of undefined constant Baz - assumed 'Baz' in %sbug37811.php on line %d
2727
string(3) "Baz"

Zend/tests/constant_arrays.phpt

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
--TEST--
2+
Constant arrays
3+
--FILE--
4+
<?php
5+
6+
define('FOOBAR', [1, 2, 3, ['foo' => 'bar']]);
7+
const FOO_BAR = [1, 2, 3, ['foo' => 'bar']];
8+
9+
$x = FOOBAR;
10+
$x[0] = 7;
11+
var_dump($x, FOOBAR);
12+
13+
$x = FOO_BAR;
14+
$x[0] = 7;
15+
var_dump($x, FOO_BAR);
16+
17+
// ensure references are removed
18+
$x = 7;
19+
$y = [&$x];
20+
define('QUX', $y);
21+
$y[0] = 3;
22+
var_dump($x, $y, QUX);
23+
24+
// ensure objects not allowed in arrays
25+
var_dump(define('ELEPHPANT', [new StdClass]));
26+
27+
// ensure recursion doesn't crash
28+
$recursive = [];
29+
$recursive[0] = &$recursive;
30+
var_dump(define('RECURSION', $recursive));
31+
32+
--EXPECTF--
33+
array(4) {
34+
[0]=>
35+
int(7)
36+
[1]=>
37+
int(2)
38+
[2]=>
39+
int(3)
40+
[3]=>
41+
array(1) {
42+
["foo"]=>
43+
string(3) "bar"
44+
}
45+
}
46+
array(4) {
47+
[0]=>
48+
int(1)
49+
[1]=>
50+
int(2)
51+
[2]=>
52+
int(3)
53+
[3]=>
54+
array(1) {
55+
["foo"]=>
56+
string(3) "bar"
57+
}
58+
}
59+
array(4) {
60+
[0]=>
61+
int(7)
62+
[1]=>
63+
int(2)
64+
[2]=>
65+
int(3)
66+
[3]=>
67+
array(1) {
68+
["foo"]=>
69+
string(3) "bar"
70+
}
71+
}
72+
array(4) {
73+
[0]=>
74+
int(1)
75+
[1]=>
76+
int(2)
77+
[2]=>
78+
int(3)
79+
[3]=>
80+
array(1) {
81+
["foo"]=>
82+
string(3) "bar"
83+
}
84+
}
85+
int(3)
86+
array(1) {
87+
[0]=>
88+
int(3)
89+
}
90+
array(1) {
91+
[0]=>
92+
int(7)
93+
}
94+
95+
Warning: Constants may only evaluate to scalar values or arrays in %s on line %d
96+
bool(false)
97+
98+
Warning: Constants cannot be recursive arrays in %s on line %d
99+
bool(false)

Zend/tests/constants_002.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ var_dump(foo);
1111

1212
?>
1313
--EXPECTF--
14-
Warning: Constants may only evaluate to scalar values in %s on line %d
14+
Warning: Constants may only evaluate to scalar values or arrays in %s on line %d
1515

1616
Notice: Use of undefined constant foo - assumed 'foo' in %s on line %d
1717
string(%d) "foo"

Zend/zend_builtin_functions.c

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,71 @@ ZEND_FUNCTION(error_reporting)
688688
}
689689
/* }}} */
690690

691+
static int validate_constant_array(HashTable *ht) /* {{{ */
692+
{
693+
int ret = 1;
694+
zval *val;
695+
696+
ht->u.v.nApplyCount++;
697+
ZEND_HASH_FOREACH_VAL_IND(ht, val) {
698+
ZVAL_DEREF(val);
699+
if (Z_REFCOUNTED_P(val)) {
700+
if (Z_TYPE_P(val) == IS_ARRAY) {
701+
if (!Z_IMMUTABLE_P(val)) {
702+
if (Z_ARRVAL_P(val)->u.v.nApplyCount > 0) {
703+
zend_error(E_WARNING, "Constants cannot be recursive arrays");
704+
ret = 0;
705+
break;
706+
} else if (!validate_constant_array(Z_ARRVAL_P(val))) {
707+
ret = 0;
708+
break;
709+
}
710+
}
711+
} else if (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_RESOURCE) {
712+
zend_error(E_WARNING, "Constants may only evaluate to scalar values or arrays");
713+
ret = 0;
714+
break;
715+
}
716+
}
717+
} ZEND_HASH_FOREACH_END();
718+
ht->u.v.nApplyCount--;
719+
return ret;
720+
}
721+
/* }}} */
722+
723+
static void copy_constant_array(zval *dst, zval *src) /* {{{ */
724+
{
725+
zend_string *key;
726+
zend_ulong idx;
727+
zval *new_val, *val;
728+
729+
array_init_size(dst, zend_hash_num_elements(Z_ARRVAL_P(src)));
730+
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(src), idx, key, val) {
731+
/* constant arrays can't contain references */
732+
if (Z_ISREF_P(val)) {
733+
if (Z_REFCOUNT_P(val) == 1) {
734+
ZVAL_UNREF(val);
735+
} else {
736+
Z_DELREF_P(val);
737+
val = Z_REFVAL_P(val);
738+
}
739+
}
740+
if (key) {
741+
new_val = zend_hash_add_new(Z_ARRVAL_P(dst), key, val);
742+
} else {
743+
new_val = zend_hash_index_add_new(Z_ARRVAL_P(dst), idx, val);
744+
}
745+
if (Z_TYPE_P(val) == IS_ARRAY) {
746+
if (!Z_IMMUTABLE_P(val)) {
747+
copy_constant_array(new_val, val);
748+
}
749+
} else if (Z_REFCOUNTED_P(val)) {
750+
Z_ADDREF_P(val);
751+
}
752+
} ZEND_HASH_FOREACH_END();
753+
}
754+
/* }}} */
755+
691756
/* {{{ proto bool define(string constant_name, mixed value, boolean case_insensitive=false)
692757
Define a new constant */
693758
ZEND_FUNCTION(define)
@@ -733,6 +798,16 @@ ZEND_FUNCTION(define)
733798
case IS_RESOURCE:
734799
case IS_NULL:
735800
break;
801+
case IS_ARRAY:
802+
if (!Z_IMMUTABLE_P(val)) {
803+
if (!validate_constant_array(Z_ARRVAL_P(val))) {
804+
RETURN_FALSE;
805+
} else {
806+
copy_constant_array(&c.value, val);
807+
goto register_constant;
808+
}
809+
}
810+
break;
736811
case IS_OBJECT:
737812
if (Z_TYPE(val_free) == IS_UNDEF) {
738813
if (Z_OBJ_HT_P(val)->get) {
@@ -749,13 +824,14 @@ ZEND_FUNCTION(define)
749824
}
750825
/* no break */
751826
default:
752-
zend_error(E_WARNING,"Constants may only evaluate to scalar values");
827+
zend_error(E_WARNING, "Constants may only evaluate to scalar values or arrays");
753828
zval_ptr_dtor(&val_free);
754829
RETURN_FALSE;
755830
}
756831

757832
ZVAL_DUP(&c.value, val);
758833
zval_ptr_dtor(&val_free);
834+
register_constant:
759835
c.flags = case_sensitive; /* non persistent */
760836
c.name = zend_string_copy(name);
761837
c.module_number = PHP_USER_CONSTANT;

0 commit comments

Comments
 (0)