Both sides previous revisionPrevious revisionNext revision | Previous revision |
rfc:variadics [2013/08/28 15:58] – nikic | rfc:variadics [2017/09/22 13:28] (current) – external edit 127.0.0.1 |
---|
* Date: 2013-08-27 | * Date: 2013-08-27 |
* Author: Nikita Popov <nikic@php.net> | * Author: Nikita Popov <nikic@php.net> |
* Status: Under Discussion | * Status: Implemented in PHP 5.6 ([[https://github.com/php/php-src/commit/0d7a6388663b76ebed6585ac92dfca5ef65fa7af|git:0d7a6388]]) |
* Proposed for: PHP 5.6 | |
* Patch: https://github.com/php/php-src/pull/421 | * Patch: https://github.com/php/php-src/pull/421 |
| * Mailing list discussion: http://markmail.org/message/uhewgv7zaagkgzdv |
| |
===== Proposal ===== | ===== Proposal ===== |
| |
The ''%%...$params%%'' syntax indicates that this is a variadic function and that all arguments after ''$query'' should be put into the ''$params'' array. Using the new syntax both of the issues mentioned above are solved. | The ''%%...$params%%'' syntax indicates that this is a variadic function and that all arguments after ''$query'' should be put into the ''$params'' array. Using the new syntax both of the issues mentioned above are solved. |
| |
| ==== Population of variadic parameter ==== |
| |
| The following example shows how the variadic parameter ''%%...$params%%'' is populated depending on the number of passed arguments: |
| |
| <code php> |
| function fn($reqParam, $optParam = null, ...$params) { |
| var_dump($reqParam, $optParam, $params); |
| } |
| |
| fn(1); // 1, null, [] |
| fn(1, 2); // 1, 2, [] |
| fn(1, 2, 3); // 1, 2, [3] |
| fn(1, 2, 3, 4); // 1, 2, [3, 4] |
| fn(1, 2, 3, 4, 5); // 1, 2, [3, 4, 5] |
| </code> |
| |
| ''$params'' will be an empty array if the number of passed arguments is smaller than the number of declared parameters. Any further arguments will be added to the ''$params'' array (in the order in which they were passed). The ''$params'' array is using continuous zero-based indices. |
| |
==== By-reference capture ==== | ==== By-reference capture ==== |
<code php> | <code php> |
class MySQL implements DB { | class MySQL implements DB { |
public function prepare($query, &... $params) { | public function prepare($query, &...$params) { |
$stmt = $this->pdo->prepare($query); | $stmt = $this->pdo->prepare($query); |
foreach ($params as $i => &$param) { | foreach ($params as $i => &$param) { |
| |
<code php> | <code php> |
function array_merge(array... $arrays) { /* ... */ } | function array_merge(array ...$arrays) { /* ... */ } |
</code> | </code> |
| |
PHP would make sure that all arguments are actually arrays. This also works for all other typehints like ''%%callable... $callbacks%%'' or ''%%Route... $routes%%''. | PHP would make sure that all arguments are actually arrays. This also works for all other typehints like ''%%callable ...$callbacks%%'' or ''%%Route ...$routes%%''. |
| |
==== Prototype checks ==== | ==== Prototype checks ==== |
public function query($query, array ...$params) | public function query($query, array ...$params) |
public function query($query, callable $extraParam = null, array ...$params) | public function query($query, callable $extraParam = null, array ...$params) |
| </code> |
| |
| ==== Syntactic restrictions ==== |
| |
| There may be only one variadic parameter and it needs to be the last parameter of the function. A variadic parameter may not have a default value. |
| |
| As such all of the following are invalid: |
| |
| <code php> |
| function fn(...$args, $arg) |
| function fn(...$args1, ...$args2) |
| function fn($arg, ...$args = []) |
</code> | </code> |
| |
* ''%%function fn($arg, &...$args)%%'': Do the capture by reference | * ''%%function fn($arg, &...$args)%%'': Do the capture by reference |
* ''%%function fn($arg, array ...$args)%%'': Enforce that all variadic arguments are arrays (or some other typehint) | * ''%%function fn($arg, array ...$args)%%'': Enforce that all variadic arguments are arrays (or some other typehint) |
* ''%%function fn($arg, array& ...$args)%%'': Combine both - variadic arguments are arrays that are captured by-reference | * ''%%function fn($arg, array &...$args)%%'': Combine both - variadic arguments are arrays that are captured by-reference |
| |
The advantages of the syntax are: | The advantages of the syntax are: |
* Types can be checked with a typehint (rather than a manual loop) | * Types can be checked with a typehint (rather than a manual loop) |
* Variadic prototypes can be enforce in interfaces / by inheritance | * Variadic prototypes can be enforce in interfaces / by inheritance |
| |
| ===== Backwards compatibility ===== |
| |
| ==== Userland ==== |
| |
| This change does not break backwards compatibility for userland code. |
| |
| In particular, this RFC does not propose to deprecate or remove the ''func_get_args()'' family of functions, at least not any time soon. |
| |
===== Impact on internals ===== | ==== Internal ==== |
| |
The ''pass_rest_by_ref'' argument of ''ZEND_BEGIN_ARG_INFO'' and ''ZEND_BEING_ARG_INFO_EX'' is no longer used. Instead functions can declare a variadic argument in the arginfo using ''ZEND_ARG_VARIADIC_INFO''. | The ''pass_rest_by_ref'' argument of ''ZEND_BEGIN_ARG_INFO'' and ''ZEND_BEING_ARG_INFO_EX'' is no longer used. Instead functions can declare a variadic argument in the arginfo using ''ZEND_ARG_VARIADIC_INFO''. |
| |
For example, this is how the arginfo for ''sscanf'' changed: | For example, this is how the arginfo for ''sscanf()'' changed: |
| |
<code c> | <code c> |
It would theoretically be possible to retain support for ''pass_rest_by_ref'' (by automatically generating a variadic arg), but as this is an exceedingly rarely used feature I don't think this is necessary. All usages of it in php-src have been replaced. | It would theoretically be possible to retain support for ''pass_rest_by_ref'' (by automatically generating a variadic arg), but as this is an exceedingly rarely used feature I don't think this is necessary. All usages of it in php-src have been replaced. |
| |
Apart from this the change should be transparent from an internals point of view. Macros like ''ARG_MUST_BE_SENT_BY_REF'' | Apart from this the change should be transparent from an internals point of view. Macros like ''ARG_MUST_BE_SENT_BY_REF'' continue to work. |
continue to work. | |
| |
===== Backwards Compatability ===== | |
| |
This change does not break backwards compatability for userland code. | |
| |
===== Discussion ===== | ===== Discussion ===== |
| |
* ''%%$args...%%''. With ref-modifier (''%%&$args...%%'') this does not show well that the individual arguments are references, rather than ''$args'' itself. With typehint (''%%array $args...%%'') it also looks like the typehint applies to ''$args'' itself rather than all variadic arguments. | * ''%%$args...%%''. With ref-modifier (''%%&$args...%%'') this does not show well that the individual arguments are references, rather than ''$args'' itself. With typehint (''%%array $args...%%'') it also looks like the typehint applies to ''$args'' itself rather than all variadic arguments. |
* ''*$args''. This is the syntax that both Ruby and Python use. For PHP this does not work well because ''*$'' is a weird combination. It gets worse with a by-reference capture: ''&*$args''. This looks like a random sequences of special characters. | * ''*$args''. This is the syntax that both Ruby and Python use. For PHP this does not work well because ''*$'' is a weird combination. It gets worse with a by-reference capture: ''&*$args''. This looks like a random sequences of special characters. Combined with a typehint the syntax looks a lot like a pointer: ''Foo *$args''. |
* ''params $args''. This is what C# does. This would require making ''params'' a keyword. Furthermore this doesn't have any nice way to declare typehints. In C# this is done using ''params type[] args'', but PHP doesn't have ''type[]'' hints and introducing them only here doesn't seem right. | * ''params $args''. This is what C# does. This would require making ''params'' a keyword. Furthermore this doesn't have any nice way to declare typehints. In C# this is done using ''params type[] args'', but PHP doesn't have ''type[]'' hints and introducing them only here doesn't seem right. |
| |
The proposed syntax is also used by Java. | The proposed syntax is also used by Java and will be used in [[http://wiki.ecmascript.org/doku.php?id=harmony:rest_parameters|Javascript (ECMAScript Harmony proposal)]]. Go and C++ also employ a similar syntax. |
| |
===== Patch ===== | ===== Patch ===== |
| |
Patch available in PR#421: https://github.com/php/php-src/pull/421 | Patch available in PR#421: https://github.com/php/php-src/pull/421 |
| |
| ===== Vote ===== |
| |
| The vote started on 16.09.2013 and ended on 23.09.2013. There were 36 votes in favor and one against, as such the necessary two-third majority is met and this feature is **accepted**. |
| |
| <doodle title="Should the proposed variadic-function syntax be added in PHP 5.6 (master)?" auth="nikic" voteType="single" closed="true"> |
| * Yes |
| * No |
| </doodle> |
| |
| ===== Argument unpacking ===== |
| |
| The [[rfc:argument_unpacking|argument unpacking RFC]] introduces the following related syntax: |
| |
| <code php> |
| $db->query($query, ...$params); |
| </code> |