Hello all,
First, I'm new here, so let me start by warmly thanking everyone
involved in PHP development. I've started programming 22 years using it
as my first programming language, and still enjoy using it a lot :).
I've read the rfc:howto and the first step is to get feedback from this
mailing list about the intended proposal, so here is a short description
of what I have in mind:
Currently PHP does not warn when user-defined functions are called with
too many arguments. Changing this could break a lot of existing code,
but it would also be a very good verification to catch potential bugs.
I have an idea allowing this feature without breaking any existing code:
using the void keyword in a function arguments list to strictly limit
the number of arguments a user-defined function can receive, stopping it
on the void, which could only be used once and in the last position.
Currently, void is a return-only type so it isn't used in arguments list
in existing code, so I believe the new behavior would only concern newly
written code that explicitly wants to take advantage of this feature.
A few examples using functions (the same would apply to class methods,
callbacks, closures, etc.) :
function foo (void) {}
foo(42); // warning: foo() expects exactly 0 arguments, 1 given
function bar ($a, $b, void) {}
bar(1, 2, 3); // warning: bar() expects exactly 2 arguments, 3 given
function baz ($a, $b=null, void) {}
baz(1, 2, 3); // warning: baz() expects at most 2 arguments, 3 given
I have no knowledge of the PHP internals: would that be feasible without
breaking things? And, as importantly, would it be a welcome change?
Cheers,
--
Pablo
Hello all,
First, I'm new here, so let me start by warmly thanking everyone
involved in PHP development. I've started programming 22 years using it
as my first programming language, and still enjoy using it a lot :).I've read the rfc:howto and the first step is to get feedback from this
mailing list about the intended proposal, so here is a short description
of what I have in mind:Currently PHP does not warn when user-defined functions are called with
too many arguments. Changing this could break a lot of existing code,
but it would also be a very good verification to catch potential bugs.I have an idea allowing this feature without breaking any existing code:
using the void keyword in a function arguments list to strictly limit
the number of arguments a user-defined function can receive, stopping it
on the void, which could only be used once and in the last position.Currently, void is a return-only type so it isn't used in arguments list
in existing code, so I believe the new behavior would only concern newly
written code that explicitly wants to take advantage of this feature.A few examples using functions (the same would apply to class methods,
callbacks, closures, etc.) :function foo (void) {} foo(42); // warning: foo() expects exactly 0 arguments, 1 given function bar ($a, $b, void) {} bar(1, 2, 3); // warning: bar() expects exactly 2 arguments, 3 given function baz ($a, $b=null, void) {} baz(1, 2, 3); // warning: baz() expects at most 2 arguments, 3 given
I have no knowledge of the PHP internals: would that be feasible without
breaking things? And, as importantly, would it be a welcome change?Cheers,
--
Pablo
Hi Pablo,
I like this concept, but instead of introducing a new syntax, have you
considered leveraging attributes in the same way that PHP 8.3
introduced #[Override]?
#[Nonvariadic]
function foo () {}
foo(42); // warning: foo() expects exactly 0 arguments, 1 given
I think the intent would be clearer and it would avoid introducing a new syntax.
- Mark
Le 04/04/2024 à 15:03, Mark Trapp a écrit :
Hello all,
First, I'm new here, so let me start by warmly thanking everyone
involved in PHP development. I've started programming 22 years using it
as my first programming language, and still enjoy using it a lot :).I've read the rfc:howto and the first step is to get feedback from this
mailing list about the intended proposal, so here is a short description
of what I have in mind:Currently PHP does not warn when user-defined functions are called with
too many arguments. Changing this could break a lot of existing code,
but it would also be a very good verification to catch potential bugs.I have an idea allowing this feature without breaking any existing code:
using the void keyword in a function arguments list to strictly limit
the number of arguments a user-defined function can receive, stopping it
on the void, which could only be used once and in the last position.Currently, void is a return-only type so it isn't used in arguments list
in existing code, so I believe the new behavior would only concern newly
written code that explicitly wants to take advantage of this feature.A few examples using functions (the same would apply to class methods,
callbacks, closures, etc.) :function foo (void) {} foo(42); // warning: foo() expects exactly 0 arguments, 1 given function bar ($a, $b, void) {} bar(1, 2, 3); // warning: bar() expects exactly 2 arguments, 3 given function baz ($a, $b=null, void) {} baz(1, 2, 3); // warning: baz() expects at most 2 arguments, 3 given
I have no knowledge of the PHP internals: would that be feasible without
breaking things? And, as importantly, would it be a welcome change?Cheers,
Hi Pablo,I like this concept, but instead of introducing a new syntax, have you
considered leveraging attributes in the same way that PHP 8.3
introduced #[Override]?#[Nonvariadic]
function foo () {}
foo(42); // warning: foo() expects exactly 0 arguments, 1 givenI think the intent would be clearer and it would avoid introducing a new syntax.
Hello Mark,
I never used attributes so I didn't even think of it, but that would
work too, yes :).
I would personally prefer the void syntax, but that's really because
attributes look odd to me due to lack of habits (and also because it
avoids an additional line of code… which is probably not a solid enough
reason when it comes to language design decisions).
Regards,
--
Pablo
I like this concept, but instead of introducing a new syntax, have you
considered leveraging attributes in the same way that PHP 8.3
introduced #[Override]?#[Nonvariadic]
function foo () {}
foo(42); // warning: foo() expects exactly 0 arguments, 1 givenI think the intent would be clearer and it would avoid introducing a new syntax.
I agree that using an attribute would be better for this case.
I would personally prefer the void syntax, but that's really because attributes look odd to me due to lack of habits (and also because it avoids an additional line of code… which is probably not a solid enough reason when it comes to language design decisions).
As a reader of the code, having the attribute is much more clear to me
than having something that looks like an argument.
Also, with attributes, the code is backwards-compatible, i.e., I can
add the attribute to my code without any errors or warnings even if I
don't use the latest version of PHP, and even in those cases, IDEs
could still help me.
Hi
I like this concept, but instead of introducing a new syntax, have you
considered leveraging attributes in the same way that PHP 8.3
introduced #[Override]?#[Nonvariadic]
function foo () {}
foo(42); // warning: foo() expects exactly 0 arguments, 1 givenI think the intent would be clearer and it would avoid introducing a new syntax.
I agree that using an attribute would be better for this case.
I disagree with an attribute being the right choice here. In contrast to
#[\Override], this proposal changes the behavior for the caller of the
function and likely also requires adjustments in child classes when
newly introduced in a parent class or interface.
Thus it is part of the function's API and thus should be part of the
regular signature and not the attribute-based metadata.
Best regards
Tim Düsterhus
Hi
I like this concept, but instead of introducing a new syntax, have you
considered leveraging attributes in the same way that PHP 8.3
introduced #[Override]?#[Nonvariadic]
function foo () {}
foo(42); // warning: foo() expects exactly 0 arguments, 1 givenI think the intent would be clearer and it would avoid introducing a new syntax.
I agree that using an attribute would be better for this case.
I disagree with an attribute being the right choice here. In contrast to
#[\Override], this proposal changes the behavior for the caller of the
function and likely also requires adjustments in child classes when
newly introduced in a parent class or interface.Thus it is part of the function's API and thus should be part of the
regular signature and not the attribute-based metadata.Best regards
Tim Düsterhus
I would think following the pattern of dynamic properties would make the most sense. Require an opt-in via attribute, with deprecation period. And possibly just remove the functionality at some point in the future.
The core issue here IMO isn't the behavior itself, but the inconsistency. C-implemented functions reject extra args. PHP-implemented functions ignore them (modulo variadics). This is... a total mess that has bitten me more than I want to admit. Normalizing it one way or the other would be a win.
--Larry Garfield
Le 4 avr. 2024 à 15:03, Mark Trapp mark@itafroma.com a écrit :
I like this concept, but instead of introducing a new syntax, have you
considered leveraging attributes in the same way that PHP 8.3
introduced #[Override]?#[Nonvariadic]
function foo () {}
foo(42); // warning: foo() expects exactly 0 arguments, 1 givenI think the intent would be clearer and it would avoid introducing a new syntax.
Indeed, except that I think it should be better to have by default the same behaviour between built-in functions and user-defined functions:
function foo() { }
foo(42);
// in PHP 8.x: Deprecated: foo() expects expects exactly 0 arguments, 1 given
// in PHP 9: Fatal error: foo() expects expects exactly 0 arguments, 1 given
and, in case you want a variadic function:
#[Variadic]
function foo() { }
Alternatively, we don’t really need to introduce a new attribute, because one can just write:
function foo(...$args) { }
—Claude
Le 04/04/2024 à 16:29, Claude Pache a écrit :
Le 4 avr. 2024 à 15:03, Mark Trapp mark@itafroma.com a écrit :
I like this concept, but instead of introducing a new syntax, have you
considered leveraging attributes in the same way that PHP 8.3
introduced #[Override]?#[Nonvariadic]
function foo () {}
foo(42); // warning: foo() expects exactly 0 arguments, 1 givenI think the intent would be clearer and it would avoid introducing a new syntax.
Indeed, except that I think it should be better to have by default the same behaviour between built-in functions and user-defined functions:
function foo() { } foo(42); // in PHP 8.x: Deprecated: foo() expects expects exactly 0 arguments, 1 given // in PHP 9: Fatal error: foo() expects expects exactly 0 arguments, 1 given
and, in case you want a variadic function:
#[Variadic] function foo() { }
Alternatively, we don’t really need to introduce a new attribute, because one can just write:
function foo(...$args) { }
—Claude
Hello Claude,
I strongly agree in theory, but this could break existing code, and
moreover such a proposal was already rejected:
https://wiki.php.net/rfc/strict_argcount
Regards,
--
Pablo
Hi
I strongly agree in theory, but this could break existing code, and
moreover such a proposal was already rejected:
https://wiki.php.net/rfc/strict_argcount
The RFC is 9 years old by now. My gut feeling is be that using an actual
variadic parameter for functions that are variadic is what people do,
because it makes the function signature much clearer. Actually variadic
parameters are available since PHP 5.6, which at the time of the
previous RFC was the newest version. Since then we had two major
releases, one of which (7.x) is already out of support.
I think it would be reasonable to consider deprecating passing extra
arguments to a non-variadic function.
Best regards
Tim Düsterhus
Le 04/04/2024 à 17:57, Tim Düsterhus a écrit :
Hi
I strongly agree in theory, but this could break existing code, and
moreover such a proposal was already rejected:
https://wiki.php.net/rfc/strict_argcountThe RFC is 9 years old by now. My gut feeling is be that using an
actual variadic parameter for functions that are variadic is what
people do, because it makes the function signature much clearer.
Actually variadic parameters are available since PHP 5.6, which at the
time of the previous RFC was the newest version. Since then we had two
major releases, one of which (7.x) is already out of support.I think it would be reasonable to consider deprecating passing extra
arguments to a non-variadic function.
Well, if there is a consensus to agree on that, it would be even better!
Cheers,
--
Pablo
I think it would be reasonable to consider deprecating passing extra
arguments to a non-variadic function.
This seems like the only reasonable thing to do, to me. If this were the
case, there should be no need for any new syntax, since we could simply
deny passing extra arguments in the next major, no?
Bilge
I think it would be reasonable to consider deprecating passing extra
arguments to a non-variadic function.This seems like the only reasonable thing to do, to me. If this were the
case, there should be no need for any new syntax, since we could simply
deny passing extra arguments in the next major, no?Bilge
I'd caution about being too hasty to deprecate and remove support for
this feature without understanding the underlying reasons why it has
been used historically, and without providing a better (not just
different) alternative for those use cases, lest an impossible upgrade
path is created.
While the variadic operator should be a 1:1 replacement for using
func_get_args()
, is it enough of a benefit to force potentially tens
of thousands of codebases to change when they both accomplish the same
thing? What bad thing happens if PHP continues to support both
methods?
There is already enough friction to upgrade between major versions.
Introducing an optional attribute or syntax allows codebases that care
about signature strictness get that safety, while avoiding a
potentially costly and onerous upgrade for the community.
- Mark
Le 04/04/2024 à 23:38, Mark Trapp a écrit :
I think it would be reasonable to consider deprecating passing extra
arguments to a non-variadic function.
This seems like the only reasonable thing to do, to me. If this were the
case, there should be no need for any new syntax, since we could simply
deny passing extra arguments in the next major, no?Bilge
I'd caution about being too hasty to deprecate and remove support for
this feature without understanding the underlying reasons why it has
been used historically, and without providing a better (not just
different) alternative for those use cases, lest an impossible upgrade
path is created.While the variadic operator should be a 1:1 replacement for using
func_get_args()
, is it enough of a benefit to force potentially tens
of thousands of codebases to change when they both accomplish the same
thing? What bad thing happens if PHP continues to support both
methods?There is already enough friction to upgrade between major versions.
Introducing an optional attribute or syntax allows codebases that care
about signature strictness get that safety, while avoiding a
potentially costly and onerous upgrade for the community.
- Mark
I agree with both, ie. I think it's reasonable to deprecate extra
variadic arguments, but also do it with caution: some old code, some
templating systems for example may use implicit variadic arguments to
pass values to templates.
There are tons of use case like this in old yet still running code.
But I would love it to be deprecated, raise warnings, and give some time
for people to fix (adding an explicit mixed ... $values
is enough to
fix broken code).
Cheers,
--
Pierre
I strongly agree in theory, but this could break existing code, and
moreover such a proposal was already rejected:
https://wiki.php.net/rfc/strict_argcountThe RFC is 9 years old by now. My gut feeling is be that using an actual
variadic parameter for functions that are variadic is what people do,
because it makes the function signature much clearer. Actually variadic
parameters are available since PHP 5.6, which at the time of the
previous RFC was the newest version. Since then we had two major
releases, one of which (7.x) is already out of support.I think it would be reasonable to consider deprecating passing extra
arguments to a non-variadic function.
IIRC one of the bigger downsides of this change are closure calls that
may provide arguments that the callee does not care about.
function filter($array, callable $c) {
$result = [];
foreach ($array as $key => $value) {
if ($c($value, $key)) {
$result[$key] = $value;
}
}
return $result;
}
var_dump(filter(['foo', '', 'bar'], function ($value) {
return strlen($value);
}));
// Internal functions already throw on superfluous args
var_dump(filter(['foo', '', 'bar'], 'strlen'));
The user may currently choose to omit the $key parameter of the
closure, as it is never used. In the future, this would throw. We may
decide to create an exemption for such calls, but I'm not sure
replacing one inconsistency with another is a good choice.
Ilija
Hi
I think it would be reasonable to consider deprecating passing extra
arguments to a non-variadic function.IIRC one of the bigger downsides of this change are closure calls that
may provide arguments that the callee does not care about.[…]
The user may currently choose to omit the $key parameter of the
closure, as it is never used. In the future, this would throw. We may
decide to create an exemption for such calls, but I'm not sure
replacing one inconsistency with another is a good choice.
I must admit, I find the
This RFC considers that anonymous functions are not intended to have a formal signature and should skip the exceeding argument count check.
bit of the previous RFC pretty reasonable to reduce the impact of the
deprecation: I expect this type of callback to primarily be implemented
as a single-use closure, which aren't really part of a package's API and
thus relaxed checks are fine for those.
I also don't primarily see deprecating passing of superfluous parameters
as removing an inconsistency. The more important bit for me is the
improved error checking, i.e. I'd prefer to resolve the inconsistency in
favor of the stricter checks, instead of relaxing the checks for
internal functions. The previous RFC showcases multiple examples where
it found bugs that likely were introduced during refactoring.
Best regards
Tim Düsterhus
I strongly agree in theory, but this could break existing code, and
moreover such a proposal was already rejected:
https://wiki.php.net/rfc/strict_argcountThe RFC is 9 years old by now. My gut feeling is be that using an actual
variadic parameter for functions that are variadic is what people do,
because it makes the function signature much clearer. Actually variadic
parameters are available since PHP 5.6, which at the time of the
previous RFC was the newest version. Since then we had two major
releases, one of which (7.x) is already out of support.I think it would be reasonable to consider deprecating passing extra
arguments to a non-variadic function.IIRC one of the bigger downsides of this change are closure calls that
may provide arguments that the callee does not care about.function filter($array, callable $c) { $result = []; foreach ($array as $key => $value) { if ($c($value, $key)) { $result[$key] = $value; } } return $result; } var_dump(filter(['foo', '', 'bar'], function ($value) { return strlen($value); })); // Internal functions already throw on superfluous args var_dump(filter(['foo', '', 'bar'], 'strlen'));
The user may currently choose to omit the $key parameter of the
closure, as it is never used. In the future, this would throw. We may
decide to create an exemption for such calls, but I'm not sure
replacing one inconsistency with another is a good choice.Ilija
This is unfortunately not reliable today, because of the difference between how internal functions and user-defined ones are handled. The code above will fatal if you use a callable that is defined in stdlib rather than in user-space. I have been bitten by this many times, and is why I ended up with double the functions in my FP library:
cf: https://github.com/Crell/fp/blob/master/src/array.php#L34
It's also been argued to me rather effectively that ignoring trailing values and optional arguments creates a whole bunch of exciting landmines, as you may pass an "extra" parameter to a function expecting it to be ignored, but it's actually part of the optional arguments so gets used. The standard example here is intval($value, $base=10). Basically no one uses the $base parameter, and most people forget it exists, but if you allow an optional value to get passed to that, especially if in weak typing mode, you could get hilariously wrong results. (For sufficiently buggy definitions of hilarious.)
The behavior difference between internal and user-defined functions is the root issue. One way or another it should be addressed, because the current behavior is a landmine I have stepped on many times.
--Larry Garfield
Hello all,
Le 05/04/2024 à 15:53, Larry Garfield a écrit :
I strongly agree in theory, but this could break existing code, and
moreover such a proposal was already rejected:
https://wiki.php.net/rfc/strict_argcount
The RFC is 9 years old by now. My gut feeling is be that using an actual
variadic parameter for functions that are variadic is what people do,
because it makes the function signature much clearer. Actually variadic
parameters are available since PHP 5.6, which at the time of the
previous RFC was the newest version. Since then we had two major
releases, one of which (7.x) is already out of support.I think it would be reasonable to consider deprecating passing extra
arguments to a non-variadic function.
IIRC one of the bigger downsides of this change are closure calls that
may provide arguments that the callee does not care about.function filter($array, callable $c) { $result = []; foreach ($array as $key => $value) { if ($c($value, $key)) { $result[$key] = $value; } } return $result; } var_dump(filter(['foo', '', 'bar'], function ($value) { return strlen($value); })); // Internal functions already throw on superfluous args var_dump(filter(['foo', '', 'bar'], 'strlen'));
The user may currently choose to omit the $key parameter of the
closure, as it is never used. In the future, this would throw. We may
decide to create an exemption for such calls, but I'm not sure
replacing one inconsistency with another is a good choice.Ilija
This is unfortunately not reliable today, because of the difference between how internal functions and user-defined ones are handled. The code above will fatal if you use a callable that is defined in stdlib rather than in user-space. I have been bitten by this many times, and is why I ended up with double the functions in my FP library:cf:https://github.com/Crell/fp/blob/master/src/array.php#L34
It's also been argued to me rather effectively that ignoring trailing values and optional arguments creates a whole bunch of exciting landmines, as you may pass an "extra" parameter to a function expecting it to be ignored, but it's actually part of the optional arguments so gets used. The standard example here is intval($value, $base=10). Basically no one uses the $base parameter, and most people forget it exists, but if you allow an optional value to get passed to that, especially if in weak typing mode, you could get hilariously wrong results. (For sufficiently buggy definitions of hilarious.)
The behavior difference between internal and user-defined functions is the root issue. One way or another it should be addressed, because the current behavior is a landmine I have stepped on many times.
--Larry Garfield
So what should be done to move forward with this?
Should the old RFC on strict argument count be revived?
Or should a new RFC proposal be written? If so, should it contain an
approval voting (where voters can select any number of candidates),
prior to the RFC proposal vote itself, to decide if the change should
be: strict argument count, using the void keyword to explicitely stop
the argument list, or using a #[Nonvariadic] attribute?
Best regards,
--
Pablo
Hi
So what should be done to move forward with this?
Should the old RFC on strict argument count be revived?
Or should a new RFC proposal be written? If so, should it contain an
It should be a new "v2" RFC. The old RFC already contains a vote, thus
editing it would change historic votes.
approval voting (where voters can select any number of candidates),
prior to the RFC proposal vote itself, to decide if the change should
be: strict argument count, using the void keyword to explicitely stop
the argument list, or using a #[Nonvariadic] attribute?
And RFC needs a clear primary vote. I believe the options you mentioned
are too different to usefully be voted on with a single primary vote + a
"tie breaker". I believe it should be a simple opinionated RFC. Should
it not pass, follow-up RFCs for the alternative options can be proposed
if it is desired.
Best regards
Tim Düsterhus
Hi Pablo,
Interesting proposal, but immediately I have two questions:
- Why is passing extra arguments a problem at all? Is generating a
warning a good idea? If you would like to introduce a new syntax to
enforce non-variadic functions, shouldn't it be a runtime error then?
Generating a warning makes it look like the new syntax still allows
for variadic arguments, but passing the extra arguments now is somehow
a bad thing to do. - How does this affect
func_get_args()
? Will the function stop
working or continue working as before?
Regards,
Kamil
Hello Kamil,
Le 04/04/2024 à 15:10, Kamil Tekiela a écrit :
Interesting proposal, but immediately I have two questions:
- Why is passing extra arguments a problem at all? Is generating a
warning a good idea? If you would like to introduce a new syntax to
enforce non-variadic functions, shouldn't it be a runtime error then?
Generating a warning makes it look like the new syntax still allows
for variadic arguments, but passing the extra arguments now is somehow
a bad thing to do.- How does this affect
func_get_args()
? Will the function stop
working or continue working as before?
-
Indeed, if implemented it should be an error rather than a warning,
you're right. -
I don't see why
func_get_args()
should work differently, but that
maybe my lack of knowledge of the internals of PHP. In any case I think
it would be better if it worked the same.
Regards,
--
Pablo
Le 4 avr. 2024 à 15:10, Kamil Tekiela tekiela246@gmail.com a écrit :
Why is passing extra arguments a problem at all?
In a parallel perfect and boring world, it is not a problem. In our world, disallowing extra arguments have two advantages:
-
catching bugs;
-
allowing to add new optional parameters to an existing function without fearing to break existing code.
—Claude