summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rwxr-xr-xUPGRADING5
-rw-r--r--Zend/tests/dereference_001.phpt51
-rw-r--r--Zend/tests/dereference_002.phpt79
-rw-r--r--Zend/tests/dereference_003.phpt46
-rw-r--r--Zend/tests/dereference_004.phpt27
-rw-r--r--Zend/tests/dereference_005.phpt38
-rw-r--r--Zend/tests/dereference_006.phpt30
-rw-r--r--Zend/tests/dereference_007.phpt22
-rw-r--r--Zend/tests/dereference_008.phpt33
-rw-r--r--Zend/tests/dereference_009.phpt26
-rw-r--r--Zend/tests/dereference_010.phpt27
-rw-r--r--Zend/tests/dereference_011.phpt45
-rw-r--r--Zend/zend_language_parser.y24
14 files changed, 449 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index 2c721e81e2..1ea8ac4914 100644
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,7 @@
- Added an optimization which saves memory and emalloc/efree calls for empty
HashTables (Stas, Dmitry)
+- Added array dereferencing support. (Felipe)
- Added DTrace support. (David Soria Parra)
- Added Tokyo Cabinet abstract DB support to ext/dba. (Michael Maclean)
- Added Jenkins's one-at-a-time hash support to ext/hash. (Martin Jansen)
diff --git a/UPGRADING b/UPGRADING
index f5588964e6..b249da1341 100755
--- a/UPGRADING
+++ b/UPGRADING
@@ -185,7 +185,10 @@ UPGRADE NOTES - PHP X.Y
11. Syntax additions
====================
--
+- Array dereferencing.
+ e.g.
+ foo()[0]
+ $foo->bar()[0]
===================
12. Windows support
diff --git a/Zend/tests/dereference_001.phpt b/Zend/tests/dereference_001.phpt
new file mode 100644
index 0000000000..e09ad2992a
--- /dev/null
+++ b/Zend/tests/dereference_001.phpt
@@ -0,0 +1,51 @@
+--TEST--
+Testing array dereference
+--FILE--
+<?php
+error_reporting(E_ALL);
+
+function a() {
+ return array(1,array(5));
+}
+var_dump(a()[1][0]); // int(5)
+
+function b() {
+ return array();
+}
+var_dump(b()[0]); // Notice: Undefined offset: 0
+
+class foo {
+ public $y = 1;
+
+ public function test() {
+ return array(array(array('foobar')));
+ }
+}
+
+function c() {
+ return array(new foo);
+}
+var_dump(c()[0]->y); // int(1)
+
+function d() {
+ $obj = new foo;
+ return $obj->test();
+}
+var_dump(d()[0][0][0][3]); // string(1) "b"
+
+function e() {
+ $y = 'bar';
+ $x = array('a' => 'foo', 'b' => $y);
+ return $x;
+}
+var_dump(e()['b']); // string(3) "bar"
+
+?>
+--EXPECTF--
+int(5)
+
+Notice: Undefined offset: 0 in %s on line %d
+NULL
+int(1)
+string(1) "b"
+string(3) "bar"
diff --git a/Zend/tests/dereference_002.phpt b/Zend/tests/dereference_002.phpt
new file mode 100644
index 0000000000..022ff370d2
--- /dev/null
+++ b/Zend/tests/dereference_002.phpt
@@ -0,0 +1,79 @@
+--TEST--
+Testing array dereference on method calls
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+class foo {
+ public function bar() {
+ $x = array();
+ $x[] = 3;
+ $x[] = array(1, 5);
+ $x[] = new foo;
+ return $x;
+ }
+}
+
+$foo = new foo;
+
+var_dump($x = $foo->bar()[1]);
+var_dump($foo->bar()[1][1]);
+var_dump($x[0]);
+var_dump($x = $foo->bar()[2]);
+var_dump($x->bar());
+var_dump($x->bar()[0]);
+
+$x = array();
+$x[] = new foo;
+var_dump($x[0]->bar()[2]);
+var_dump($foo->bar()[2]->bar()[1]);
+var_dump($foo->bar()[2]->bar()[2]->bar()[1][0]);
+var_dump($foo->bar()[2]->bar()[2]->bar()[1][0][1]);
+var_dump($foo->bar()[2]->bar()[2]->bar()[4]);
+var_dump($foo->bar()[3]->bar());
+
+?>
+--EXPECTF--
+array(2) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(5)
+}
+int(5)
+int(1)
+object(foo)#2 (0) {
+}
+array(3) {
+ [0]=>
+ int(3)
+ [1]=>
+ array(2) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(5)
+ }
+ [2]=>
+ object(foo)#3 (0) {
+ }
+}
+int(3)
+object(foo)#3 (0) {
+}
+array(2) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(5)
+}
+int(1)
+NULL
+
+Notice: Undefined offset: 4 in %s on line %d
+NULL
+
+Notice: Undefined offset: 3 in %s on line %d
+
+Fatal error: Call to a member function bar() on a non-object in %s on line %d
diff --git a/Zend/tests/dereference_003.phpt b/Zend/tests/dereference_003.phpt
new file mode 100644
index 0000000000..3a54875964
--- /dev/null
+++ b/Zend/tests/dereference_003.phpt
@@ -0,0 +1,46 @@
+--TEST--
+Testing array dereference on method calls
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+class foo {
+ public $x = 2;
+ public function a() {
+ $x = array();
+ $x[] = new foo;
+ return $x;
+ }
+ public function b() {
+ return array(1.2, array(new self));
+ }
+ public function c() {
+ $a = array();
+ $b = &$a;
+ $b[] = true;
+ return $a;
+ }
+ public function d() {
+ return $this->b();
+ }
+}
+
+$foo = new foo;
+
+var_dump($foo->a()[0]->x);
+var_dump($foo->a()[0]);
+var_dump($foo->b()[1][0]->a()[0]->x);
+var_dump($foo->c()[0]);
+var_dump($foo->d()[0]);
+
+?>
+--EXPECTF--
+int(2)
+object(foo)#%d (1) {
+ ["x"]=>
+ int(2)
+}
+int(2)
+bool(true)
+float(1.2)
diff --git a/Zend/tests/dereference_004.phpt b/Zend/tests/dereference_004.phpt
new file mode 100644
index 0000000000..a77cf032ed
--- /dev/null
+++ b/Zend/tests/dereference_004.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Testing array dereference on __invoke() result
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+class foo {
+ public $x = array();
+ public function __construct() {
+ $h = array();
+ $h[] = new stdclass;
+ $this->x = $h;
+ }
+ public function __invoke() {
+ return $this->x;
+ }
+}
+
+
+$fo = new foo;
+var_dump($fo()[0]);
+
+?>
+--EXPECTF--
+object(stdClass)#%d (0) {
+}
diff --git a/Zend/tests/dereference_005.phpt b/Zend/tests/dereference_005.phpt
new file mode 100644
index 0000000000..cca87571cd
--- /dev/null
+++ b/Zend/tests/dereference_005.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Testing array dereference on object that implements ArrayAccess
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+class obj implements arrayaccess {
+ private $container = array();
+ public function __construct() {
+ $this->container = array(
+ "one" => 1,
+ "two" => 2,
+ "three" => 3,
+ );
+ }
+ public function offsetSet($offset, $value) {
+ $this->container[$offset] = $value;
+ }
+ public function offsetExists($offset) {
+ return isset($this->container[$offset]);
+ }
+ public function offsetUnset($offset) {
+ unset($this->container[$offset]);
+ }
+ public function offsetGet($offset) {
+ return isset($this->container[$offset]) ? $this->container[$offset] : null;
+ }
+}
+
+function x() {
+ return new obj;
+}
+var_dump(x()['two']);
+
+?>
+--EXPECT--
+int(2)
diff --git a/Zend/tests/dereference_006.phpt b/Zend/tests/dereference_006.phpt
new file mode 100644
index 0000000000..61a07353a4
--- /dev/null
+++ b/Zend/tests/dereference_006.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Testing array dereference and references
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+function &foo(&$foo) {
+ return $foo;
+}
+
+$a = array(1);
+foo($a)[0] = 2;
+var_dump($a);
+
+foo($a)[] = 3;
+var_dump($a);
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ int(2)
+}
+array(2) {
+ [0]=>
+ int(2)
+ [1]=>
+ int(3)
+}
diff --git a/Zend/tests/dereference_007.phpt b/Zend/tests/dereference_007.phpt
new file mode 100644
index 0000000000..05eca6fb48
--- /dev/null
+++ b/Zend/tests/dereference_007.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Trying to write on method return
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+class foo {
+ public $x = array();
+
+ public function b() {
+ return $this->x;
+ }
+}
+
+$foo = new foo;
+
+$foo->b()[0] = 1;
+
+?>
+--EXPECTF--
+Fatal error: Can't use method return value in write context in %s on line %d
diff --git a/Zend/tests/dereference_008.phpt b/Zend/tests/dereference_008.phpt
new file mode 100644
index 0000000000..01c828f0c8
--- /dev/null
+++ b/Zend/tests/dereference_008.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Testing array dereference with dynamic method name and references
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+class foo {
+ public $x = array(1);
+
+ public function &b() {
+ return $this->x;
+ }
+}
+
+$foo = new foo;
+
+$a = 'b';
+var_dump($foo->$a()[0]);
+
+$h = &$foo->$a();
+$h[] = 2;
+var_dump($foo->$a());
+
+?>
+--EXPECT--
+int(1)
+array(2) {
+ [0]=>
+ int(1)
+ [1]=>
+ int(2)
+}
diff --git a/Zend/tests/dereference_009.phpt b/Zend/tests/dereference_009.phpt
new file mode 100644
index 0000000000..e579c44bae
--- /dev/null
+++ b/Zend/tests/dereference_009.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Testing array dereference with references
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+$a = array();
+
+function &a() {
+ return $GLOBALS['a'];
+}
+
+var_dump($h =& a());
+$h[] = 1;
+var_dump(a()[0]);
+
+$h[] = array($h);
+var_dump(a()[1][0][0]);
+
+?>
+--EXPECT--
+array(0) {
+}
+int(1)
+int(1)
diff --git a/Zend/tests/dereference_010.phpt b/Zend/tests/dereference_010.phpt
new file mode 100644
index 0000000000..6acda77ba8
--- /dev/null
+++ b/Zend/tests/dereference_010.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Testing dereference in non-array values
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+function a() {
+ return 1;
+}
+
+$a = 1;
+var_dump($a[1]);
+var_dump(a()[1]);
+
+function b() {
+ return new stdClass;
+}
+
+var_dump(b()[1]);
+
+?>
+--EXPECTF--
+NULL
+NULL
+
+Fatal error: Cannot use object of type stdClass as array in %s on line %d
diff --git a/Zend/tests/dereference_011.phpt b/Zend/tests/dereference_011.phpt
new file mode 100644
index 0000000000..17dc8b346b
--- /dev/null
+++ b/Zend/tests/dereference_011.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Testing array dereference with chaining
+--FILE--
+<?php
+
+error_reporting(E_ALL);
+
+class foo {
+ public $arr;
+
+ public function &a() {
+ return $this->arr;
+ }
+}
+
+$foo = new foo;
+
+$h = &$foo->a();
+$h[] = 1;
+$h[] = $foo;
+var_dump($foo->a()[1]->arr[1]->a()[1]->arr[1]->arr[0]);
+var_dump($foo->a()[1]);
+var_dump($foo->a()[1]->arr[1]);
+
+?>
+--EXPECTF--
+int(1)
+object(foo)#%d (1) {
+ ["arr"]=>
+ &array(2) {
+ [0]=>
+ int(1)
+ [1]=>
+ *RECURSION*
+ }
+}
+object(foo)#%d (1) {
+ ["arr"]=>
+ &array(2) {
+ [0]=>
+ int(1)
+ [1]=>
+ *RECURSION*
+ }
+}
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index 8881aac6a7..ddda7f3aa3 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -930,11 +930,20 @@ variable_property:
T_OBJECT_OPERATOR object_property { zend_do_push_object(&$2 TSRMLS_CC); } method_or_not { $$.EA = $4.EA; }
;
-method_or_not:
+array_method_dereference:
+ array_method_dereference '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
+ | method '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
+;
+
+method:
'(' { zend_do_pop_object(&$1 TSRMLS_CC); zend_do_begin_method_call(&$1 TSRMLS_CC); }
function_call_parameter_list ')'
- { zend_do_end_function_call(&$1, &$$, &$3, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);
- zend_do_push_object(&$$ TSRMLS_CC); $$.EA = ZEND_PARSED_METHOD_CALL; }
+ { zend_do_end_function_call(&$1, &$$, &$3, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
+;
+
+method_or_not:
+ method { $$ = $1; zend_do_push_object(&$$ TSRMLS_CC); $$.EA = ZEND_PARSED_METHOD_CALL; }
+ | array_method_dereference { $$ = $1; zend_do_push_object(&$$ TSRMLS_CC); $$.EA = ZEND_PARSED_METHOD_CALL; }
| /* empty */ { $$.EA = ZEND_PARSED_MEMBER; }
;
@@ -953,8 +962,15 @@ variable_class_name:
reference_variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$=$1;; }
;
+array_function_dereference:
+ array_function_dereference '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
+ | function_call { zend_do_begin_variable_parse(TSRMLS_C); $$.EA = ZEND_PARSED_FUNCTION_CALL; }
+ '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$4 TSRMLS_CC); }
+;
+
base_variable_with_function_calls:
- base_variable { $$ = $1; }
+ base_variable { $$ = $1; }
+ | array_function_dereference { $$ = $1; }
| function_call { zend_do_begin_variable_parse(TSRMLS_C); $$ = $1; $$.EA = ZEND_PARSED_FUNCTION_CALL; }
;