Skip to content

Instantly share code, notes, and snippets.

@dstogov
Created December 20, 2014 10:09
Show Gist options
  • Save dstogov/3956efd7bbf924cfa0f8 to your computer and use it in GitHub Desktop.
Save dstogov/3956efd7bbf924cfa0f8 to your computer and use it in GitHub Desktop.
diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c
index c05e797..f956f49 100644
--- a/Zend/zend_builtin_functions.c
+++ b/Zend/zend_builtin_functions.c
@@ -688,6 +688,71 @@ ZEND_FUNCTION(error_reporting)
}
/* }}} */
+static int validate_constant_array(HashTable *ht) /* {{{ */
+{
+ int ret = 1;
+ zval *val;
+
+ ht->u.v.nApplyCount++;
+ ZEND_HASH_FOREACH_VAL_IND(ht, val) {
+ ZVAL_DEREF(val);
+ if (Z_REFCOUNTED_P(val)) {
+ if (Z_TYPE_P(val) == IS_ARRAY) {
+ if (!Z_IMMUTABLE_P(val)) {
+ if (Z_ARRVAL_P(val)->u.v.nApplyCount > 0) {
+ zend_error(E_WARNING, "Constants cannot be recursive arrays");
+ ret = 0;
+ break;
+ } else if (!validate_constant_array(Z_ARRVAL_P(val))) {
+ ret = 0;
+ break;
+ }
+ }
+ } else if (Z_TYPE_P(val) != IS_STRING && Z_TYPE_P(val) != IS_RESOURCE) {
+ zend_error(E_WARNING, "Constants may only evaluate to scalar values or arrays");
+ ret = 0;
+ break;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ ht->u.v.nApplyCount--;
+ return ret;
+}
+/* }}} */
+
+static void copy_constant_array(zval *dst, zval *src) /* {{{ */
+{
+ zend_string *key;
+ zend_ulong idx;
+ zval *new_val, *val;
+
+ array_init_size(dst, zend_hash_num_elements(Z_ARRVAL_P(src)));
+ ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(src), idx, key, val) {
+ /* constant arrays can't contain references */
+ if (Z_ISREF_P(val)) {
+ if (Z_REFCOUNT_P(val) == 1) {
+ ZVAL_UNREF(val);
+ } else {
+ Z_DELREF_P(val);
+ val = Z_REFVAL_P(val);
+ }
+ }
+ if (key) {
+ new_val = zend_hash_add_new(Z_ARRVAL_P(dst), key, val);
+ } else {
+ new_val = zend_hash_index_add_new(Z_ARRVAL_P(dst), idx, val);
+ }
+ if (Z_TYPE_P(val) == IS_ARRAY) {
+ if (!Z_IMMUTABLE_P(val)) {
+ copy_constant_array(new_val, val);
+ }
+ } else if (Z_REFCOUNTED_P(val)) {
+ Z_ADDREF_P(val);
+ }
+ } ZEND_HASH_FOREACH_END();
+}
+/* }}} */
+
/* {{{ proto bool define(string constant_name, mixed value, boolean case_insensitive=false)
Define a new constant */
ZEND_FUNCTION(define)
@@ -733,6 +798,16 @@ repeat:
case IS_RESOURCE:
case IS_NULL:
break;
+ case IS_ARRAY:
+ if (!Z_IMMUTABLE_P(val)) {
+ if (!validate_constant_array(Z_ARRVAL_P(val))) {
+ RETURN_FALSE;
+ } else {
+ copy_constant_array(&c.value, val);
+ goto register_constant;
+ }
+ }
+ break;
case IS_OBJECT:
if (Z_TYPE(val_free) == IS_UNDEF) {
if (Z_OBJ_HT_P(val)->get) {
@@ -749,13 +824,14 @@ repeat:
}
/* no break */
default:
- zend_error(E_WARNING,"Constants may only evaluate to scalar values");
+ zend_error(E_WARNING, "Constants may only evaluate to scalar values or arrays");
zval_ptr_dtor(&val_free);
RETURN_FALSE;
}
ZVAL_DUP(&c.value, val);
zval_ptr_dtor(&val_free);
+register_constant:
c.flags = case_sensitive; /* non persistent */
c.name = zend_string_copy(name);
c.module_number = PHP_USER_CONSTANT;
diff --git a/Zend/tests/constant_arrays.phpt b/Zend/tests/constant_arrays.phpt
new file mode 100644
index 0000000..834c126
--- /dev/null
+++ b/Zend/tests/constant_arrays.phpt
@@ -0,0 +1,99 @@
+--TEST--
+Constant arrays
+--FILE--
+<?php
+
+define('FOOBAR', [1, 2, 3, ['foo' => 'bar']]);
+const FOO_BAR = [1, 2, 3, ['foo' => 'bar']];
+
+$x = FOOBAR;
+$x[0] = 7;
+var_dump($x, FOOBAR);
+
+$x = FOO_BAR;
+$x[0] = 7;
+var_dump($x, FOO_BAR);
+
+// ensure references are removed
+$x = 7;
+$y = [&$x];
+define('QUX', $y);
+$y[0] = 3;
+var_dump($x, $y, QUX);
+
+// ensure objects not allowed in arrays
+var_dump(define('ELEPHPANT', [new StdClass]));
+
+// ensure recursion doesn't crash
+$recursive = [];
+$recursive[0] = &$recursive;
+var_dump(define('RECURSION', $recursive));
+
+--EXPECTF--
+array(4) {
+ [0]=>
+ int(7)
+ [1]=>
+ int(2)
+ [2]=>
+ int(3)
+ [3]=>
+ array(1) {
+ ["foo"]=>
+ string(3) "bar"
+ }
+}
+array(4) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+ [2]=>
+ int(3)
+ [3]=>
+ array(1) {
+ ["foo"]=>
+ string(3) "bar"
+ }
+}
+array(4) {
+ [0]=>
+ int(7)
+ [1]=>
+ int(2)
+ [2]=>
+ int(3)
+ [3]=>
+ array(1) {
+ ["foo"]=>
+ string(3) "bar"
+ }
+}
+array(4) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+ [2]=>
+ int(3)
+ [3]=>
+ array(1) {
+ ["foo"]=>
+ string(3) "bar"
+ }
+}
+int(3)
+array(1) {
+ [0]=>
+ int(3)
+}
+array(1) {
+ [0]=>
+ int(7)
+}
+
+Warning: Constants may only evaluate to scalar values or arrays in %s on line %d
+bool(false)
+
+Warning: Constants cannot be recursive arrays in %s on line %d
+bool(false)
diff --git a/Zend/tests/008.phpt b/Zend/tests/008.phpt
index 4f58e80..3e990fb 100644
--- a/Zend/tests/008.phpt
+++ b/Zend/tests/008.phpt
@@ -41,11 +41,9 @@ bool(true)
Notice: Constant test const already defined in %s on line %d
bool(false)
+bool(true)
-Warning: Constants may only evaluate to scalar values in %s on line %d
-bool(false)
-
-Warning: Constants may only evaluate to scalar values in %s on line %d
+Warning: Constants may only evaluate to scalar values or arrays in %s on line %d
bool(false)
int(1)
int(2)
diff --git a/Zend/tests/bug37811.phpt b/Zend/tests/bug37811.phpt
index 70c4c90..f224f9b 100644
--- a/Zend/tests/bug37811.phpt
+++ b/Zend/tests/bug37811.phpt
@@ -21,7 +21,7 @@ var_dump(Baz);
--EXPECTF--
string(3) "Foo"
-Warning: Constants may only evaluate to scalar values in %sbug37811.php on line %d
+Warning: Constants may only evaluate to scalar values or arrays in %sbug37811.php on line %d
Notice: Use of undefined constant Baz - assumed 'Baz' in %sbug37811.php on line %d
string(3) "Baz"
diff --git a/Zend/tests/constants_002.phpt b/Zend/tests/constants_002.phpt
index 5acca98..d590bf4 100644
--- a/Zend/tests/constants_002.phpt
+++ b/Zend/tests/constants_002.phpt
@@ -11,7 +11,7 @@ var_dump(foo);
?>
--EXPECTF--
-Warning: Constants may only evaluate to scalar values in %s on line %d
+Warning: Constants may only evaluate to scalar values or arrays in %s on line %d
Notice: Use of undefined constant foo - assumed 'foo' in %s on line %d
string(%d) "foo"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment