diff options
-rw-r--r-- | NEWS | 1 | ||||
-rwxr-xr-x | UPGRADING | 5 | ||||
-rw-r--r-- | Zend/tests/dereference_001.phpt | 51 | ||||
-rw-r--r-- | Zend/tests/dereference_002.phpt | 79 | ||||
-rw-r--r-- | Zend/tests/dereference_003.phpt | 46 | ||||
-rw-r--r-- | Zend/tests/dereference_004.phpt | 27 | ||||
-rw-r--r-- | Zend/tests/dereference_005.phpt | 38 | ||||
-rw-r--r-- | Zend/tests/dereference_006.phpt | 30 | ||||
-rw-r--r-- | Zend/tests/dereference_007.phpt | 22 | ||||
-rw-r--r-- | Zend/tests/dereference_008.phpt | 33 | ||||
-rw-r--r-- | Zend/tests/dereference_009.phpt | 26 | ||||
-rw-r--r-- | Zend/tests/dereference_010.phpt | 27 | ||||
-rw-r--r-- | Zend/tests/dereference_011.phpt | 45 | ||||
-rw-r--r-- | Zend/zend_language_parser.y | 24 |
14 files changed, 449 insertions, 5 deletions
@@ -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) @@ -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; } ; |