Skip to content

Commit

Permalink
Implement variadic function syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
nikic committed Sep 26, 2013
1 parent 6daa04a commit 0d7a638
Show file tree
Hide file tree
Showing 43 changed files with 2,908 additions and 2,278 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ PHP NEWS
. Reduced POST data memory usage by 200-300%. Removed INI setting
always_populate_raw_post_data and the $HTTP_RAW_POST_DATA global
variable. (Mike)
. Implemented dedicated syntax for variadic functions
(RFC: https://wiki.php.net/rfc/variadics). (Nikita)

- cURL:
. Implemented FR #65646 (re-enable CURLOPT_FOLLOWLOCATION with open_basedir
Expand Down
6 changes: 4 additions & 2 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ PHP X.Y UPGRADE NOTES
2. New Features
========================================

- Core:
The php://input stream is now re-usable and can be used concurrently with
- Added dedicated syntax for variadic functions.
(https://wiki.php.net/rfc/variadics)

- The php://input stream is now re-usable and can be used concurrently with
enable_post_data_reading=0.

========================================
Expand Down
42 changes: 42 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ UPGRADE NOTES - PHP X.Y
a. Addition of do_operation and compare object handlers
b. return_value_ptr now always available, RETVAL_ZVAL_FAST macros
c. POST data handling
d. Arginfo changes

2. Build system changes
a. Unix build system changes
Expand Down Expand Up @@ -68,6 +69,47 @@ UPGRADE NOTES - PHP X.Y
The recommended way to access raw POST data is to open and use a php://input
stream wrapper. It is safe to be used concurrently and more than once.

d. Arginfo changes

The pass_rest_by_reference argument of the ZEND_BEGIN_ARG_INFO and
ZEND_BEGIN_ARG_INFO_EX() is no longer used. The value passed to it is ignored.

Instead a variadic argument is created using ZEND_ARG_VARIADIC_INFO():

ZEND_ARG_VARIADIC_INFO(0, name) /* pass rest by value */
ZEND_ARG_VARIADIC_INFO(1, name) /* pass rest by reference */
ZEND_ARG_VARIADIC_INFO(ZEND_SEND_PREFER_REF, name)
/* pass rest by prefer-ref */

ZEND_ARG_VARIADIC_INFO() should only be used for the last argument.

The following changes were applied to the zend_arg_info struct:

typedef struct _zend_arg_info {
const char *class_name;
zend_uint class_name_len;
zend_uchar type_hint;
+ zend_uchar pass_by_reference;
zend_bool allow_null;
- zend_bool pass_by_reference;
+ zend_bool is_variadic;
} zend_arg_info;

The following changes were applied to the zend_internal_function_info struct:

typedef struct _zend_internal_function_info {
zend_uint required_num_args;
zend_uchar _type_hint;
zend_bool return_reference;
- zend_bool pass_rest_by_reference;
+ zend_bool _allow_null;
+ zend_bool _is_variadic;
} zend_internal_function_info;

The CHECK_ARG_SEND_TYPE(), ARG_MUST_BE_SENT_BY_REF(),
ARG_SHOULD_BE_SENT_BY_REF() and ARG_MAY_BE_SENT_BY_REF() macros now assume
that the argument passed to them is a zend_function* and that it is non-NULL.

========================
2. Build system changes
========================
Expand Down
17 changes: 17 additions & 0 deletions Zend/tests/variadic/adding_additional_optional_parameter.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
It's possible to add additional optional arguments with matching signature
--FILE--
<?php

interface DB {
public function query($query, string ...$params);
}

class MySQL implements DB {
public function query($query, string $extraParam = null, string ...$params) { }
}

?>
===DONE===
--EXPECT--
===DONE===
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
Additional optional parameters must have a matching prototype
--FILE--
<?php

interface DB {
public function query($query, string ...$params);
}

class MySQL implements DB {
public function query($query, int $extraParam = null, string ...$params) { }
}

?>
--EXPECTF--
Fatal error: Declaration of MySQL::query() must be compatible with DB::query($query, string ...$params) in %s on line %d
57 changes: 57 additions & 0 deletions Zend/tests/variadic/basic.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
--TEST--
Basic variadic function
--FILE--
<?php

function test1(... $args) {
var_dump($args);
}

test1();
test1(1);
test1(1, 2, 3);

function test2($arg1, $arg2, ...$args) {
var_dump($arg1, $arg2, $args);
}

test2(1, 2);
test2(1, 2, 3);
test2(1, 2, 3, 4, 5);

?>
--EXPECT--
array(0) {
}
array(1) {
[0]=>
int(1)
}
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
int(1)
int(2)
array(0) {
}
int(1)
int(2)
array(1) {
[0]=>
int(3)
}
int(1)
int(2)
array(3) {
[0]=>
int(3)
[1]=>
int(4)
[2]=>
int(5)
}
24 changes: 24 additions & 0 deletions Zend/tests/variadic/by_ref.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Variadic arguments with by-reference passing
--FILE--
<?php

function test(&... $args) {
$i = 0;
foreach ($args as &$arg) {
$arg = $i++;
}
}

test();
test($a);
var_dump($a);
test($b, $c, $d);
var_dump($b, $c, $d);

?>
--EXPECT--
int(0)
int(0)
int(1)
int(2)
12 changes: 12 additions & 0 deletions Zend/tests/variadic/by_ref_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
By-ref variadics enforce the reference
--FILE--
<?php

function test(&... $args) { }

test(1);

?>
--EXPECTF--
Fatal error: Only variables can be passed by reference in %s on line %d
10 changes: 10 additions & 0 deletions Zend/tests/variadic/no_default_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Variadic argument cannot have a default value
--FILE--
<?php

function test(...$args = 123) {}

?>
--EXPECTF--
Fatal error: Variadic parameter cannot have a default value in %s on line %d
16 changes: 16 additions & 0 deletions Zend/tests/variadic/non_variadic_implements_variadic_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
It's not possible to turn a variadic function into a non-variadic one
--FILE--
<?php

interface DB {
public function query($query, ...$params);
}

class MySQL implements DB {
public function query($query, $params) { }
}

?>
--EXPECTF--
Fatal error: Declaration of MySQL::query() must be compatible with DB::query($query, ...$params) in %s on line %d
10 changes: 10 additions & 0 deletions Zend/tests/variadic/only_last_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Only the last argument can be variadic
--FILE--
<?php

function test($foo, ...$bar, $baz) {}

?>
--EXPECTF--
Fatal error: Only the last parameter can be variadic in %s on line %d
49 changes: 49 additions & 0 deletions Zend/tests/variadic/optional_params.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--TEST--
Optional parameter before variadic parameter
--FILE--
<?php

function fn($reqParam, $optParam = null, ...$params) {
var_dump($reqParam, $optParam, $params);
}

fn(1);
fn(1, 2);
fn(1, 2, 3);
fn(1, 2, 3, 4);
fn(1, 2, 3, 4, 5);

?>
--EXPECT--
int(1)
NULL
array(0) {
}
int(1)
int(2)
array(0) {
}
int(1)
int(2)
array(1) {
[0]=>
int(3)
}
int(1)
int(2)
array(2) {
[0]=>
int(3)
[1]=>
int(4)
}
int(1)
int(2)
array(3) {
[0]=>
int(3)
[1]=>
int(4)
[2]=>
int(5)
}
20 changes: 20 additions & 0 deletions Zend/tests/variadic/removing_parameter_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
It's not possible to remove required parameter before a variadic parameter
--FILE--
<?php

/* Theoretically this should be valid because it weakens the constraint, but
* PHP does not allow this (for non-variadics), so I'm not allowing it here, too,
* to stay consistent. */

interface DB {
public function query($query, ...$params);
}

class MySQL implements DB {
public function query(...$params) { }
}

?>
--EXPECTF--
Fatal error: Declaration of MySQL::query() must be compatible with DB::query($query, ...$params) in %s on line %d
36 changes: 36 additions & 0 deletions Zend/tests/variadic/typehint_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
Variadic arguments enforce typehints
--FILE--
<?php

function test(array... $args) {
var_dump($args);
}

test();
test([0], [1], [2]);
test([0], [1], 2);

?>
--EXPECTF--
array(0) {
}
array(3) {
[0]=>
array(1) {
[0]=>
int(0)
}
[1]=>
array(1) {
[0]=>
int(1)
}
[2]=>
array(1) {
[0]=>
int(2)
}
}

Catchable fatal error: Argument 3 passed to test() must be of the type array, integer given, called in %s on line %d
33 changes: 33 additions & 0 deletions Zend/tests/variadic/typehint_suppressed_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
Error suppression for typehints on variadic arguments works
--FILE--
<?php

function test(array... $args) {
var_dump($args);
}

set_error_handler(function($errno, $errstr) {
var_dump($errstr);
return true;
});

test([0], [1], 2);

?>
--EXPECTF--
string(%d) "Argument 3 passed to test() must be of the type array, integer given, called in %s on line %d and defined"
array(3) {
[0]=>
array(1) {
[0]=>
int(0)
}
[1]=>
array(1) {
[0]=>
int(1)
}
[2]=>
int(2)
}
Loading

0 comments on commit 0d7a638

Please sign in to comment.