Why strict argument count on function calls will make PHP better, saner, easier…

Márcio Almada
4 min readFeb 22, 2015

--

This is about my latest RFC: https://wiki.php.net/rfc/strict_argcount

The RFC proposes that PHP should emit a warning when a function is called with an exceeding number of arguments, according to function signatures. Example:

What about func_get_args()? Are you crazy?

Glad you asked! The implementation is aware of the native variable-lengh argument list API and won’t emit any warning if the called function or method is variadic or implemented with:

And, according to doctors, I have just enough sanity to contribute to PHP.

How it works?

Easy breeze. Go read the RFC, all technical details are there.

Or take a look at the pull request with the proposed patch if you really like to read C code full of esoteric macros (click the first commit).

How could this be useful?

Most of the times, to catch bugs. I’ve seen current silent PHP behavior bite people more than enough to know this would be a good fix. Consider the following fictitious use case:

A little story about code and people

Meet Bob! Bob created his first API to handle users of his application:

“Why Bob is not using an interface?” You ask. Keep in mind this story also applies to functional APIs and PHP has no interfaces for stand alone functions

Everything is fine. The API is good enough (yay PHP) and is being used by all Bob’s coworkers in many parts of the code base, just like that:

Then somebody decided that users should not have a nickname anymore, so Bob has to update the API, without break all the application, of course. Here is the patch:

So Bob runs all the unit tests and… all green!

Despite UserRepository::createUser() is now being called with an extra argument on all the code base, PHP won’t warn about it. It’s not PHP being bad, it’s a language feature!

No alarms…

PHP will happily swallow the unnecessary $nickname argument on every method call. No one will never notice this little detail. Bob made unit tests, anyway. What can go wrong? Pfffff.

Meet Mike! Bob got promoted and Mike is maintaining the API now. He thinks that it could be easier to inform the user role during creation, so the code is updated. Git diff:

Can you spot the issue? See the gist below:

No, Bob and Mike are not fools. And no warnings informed them about the wrong argument count on UserRepository::createUser() calls after first API change.

The create_user.php endpoint is now a bug. The worst kind of bug. A silent bug. No alarms, but certainly unpleasant surprises.

So what’s the story if PHP had a strict argument count check?

[…] So Bob runs all the unit tests and… oops!

Fix, fix, fix... Bob killed some potential bugs that would grow giant some day without recurring to fancy code analyzers. Good job!

Will this be a huge catastrophic BC break?

No.

The BC break is localized. To emit the proposed warning, a function:

Must be invoked with an exceeding amount of arguments AND must not be variadic AND must not make use of the PHP variable-length argument list API.

  • People taking advantage of the current PHP behavior, the programmers not the users becoming “admin”, can fix the warning really quick. Keep in mind PHP has variadic functions since version 5.6!
  • The warning is great to catch bugs. As good as the warnings we already have when functions are called with missing arguments.
  • The warning is helpful for development. Legacy applications won’t break because of it.
  • Internal PHP functions already have a strict argument count. Try to run strlen() with more than 1 argument:
<?php
strlen("foo", "bar");
PHP warning: strlen() expects exactly 1 parameter, 2 given...

What about other languages out there?

Python:

def fn(a, b): return 1
fn(1, 2, 3)
>>> TypeError: fn() takes exactly 2 arguments (3 given)

Ruby:

def fn(a, b) return true end
fn(1, 2, 3)
>>> ArgumentError: wrong number of arguments (3 for 2)
from (irb):1:in `fn'...

Go:

func fn() bool { return true }
func main() {
fn(1, 2);
}
prog.go:3: too many arguments in call to fn

What about func_num_args()? You forgot func_num_args().

It turns out that func_num_args(); is not directly associated with variadic implementations, it’s just accidentally associated with it. Real world example:

https://github.com/zendframework/zf2/blob/master/library/Zend/Cache/Storage/Adapter/Memcached.php#L218

That’s why the usage of this function won’t mark any implementation as sensitive to dynamic argument lists.

PS: Thanks to Marc Bennewitz for pointing this out on the mailing lists.

TL;DR

Go read the RFC: https://wiki.php.net/rfc/strict_argcount

Thanks!

PS: Bob still got promoted on the alternative story line. The chaos theory wasn’t part of the simulation.

--

--

Márcio Almada

I have just enough sanity to contribute to PHP and FOSS. Build it, ship it. ™