PHP Parameter Skipping and Named Parameters – Finally?


Well, sort of; it’s not 100% native, but it works, and requires only 1 simple cut-and-pasted line added per function to implement.  This article will detail how to do so, as well as the gist of its inner workings.  It’s certainly easy to grep up complaints and feature requests for both parameter skipping and named parameters – two oft-wanted language features.  Stanislav Malyshev, a core PHP developer says on his “PHP 10.0″ blog that he misses these features in August, 2009 (http://php100.wordpress.com/2009/08/21/syntax-i-miss-in-php/).  Unfortunately, as we’ll document below, not everyone agrees.

Many other high-level languages support these features as well.  No matter.  We’ll examine a nice little workaround approach and implement it.  Let’s first peruse an example of a hypothetical function that may benefit from these features:

function getUsers($id=null, $group_id=1, $username = ”, $order=’order_fld’, $order_asc=1) {
return $users_matching_all_criteria;
}

As to whether 1 public function/method with so many parameters is good practice is beyond the scope of this article – it’s just a (somewhat contrived) example!  However, one may take the approach of lightly wrapping such a function with special dedicated getters, i.e.:

function getUserByUsername($username) {
return getUsers(null, 1, $username);
}

This is a valid approach.  However, it represents a bunch of meaningless mechanical coding work.  We must have something more productive to do.  Calling the function directly is, of course, even less desirable:

getUsers(null, 1, ‘bob’);

And this will inevitably result in many “off-by-1” errors in parameter passing, as well as hard-coding defaults.

So wouldn’t it be nice if there were a better way — an easy way to specify the setting via a named list or skip over parameters with ease?

Unfortunately, there is no totally native way to do this.  Both of the below code snippets or similar might work in another language, but – alas – not PHP:

getUsers(, , $username); // Hypothetical; this does not work!

or –

getUsers(‘username’ => $username); // Hypothetical; this does not work!

Such a syntax would preserve the defaults implicitly, while allowing the programmer to modify those parameters that matter to the task at hand.  PHP IDEs that implement implicit code completion could still indicate the defaults based on the function prototype.  As another perk, it permits the programmer to retroactively change a default without locating all references to that function.  If this is not desired, simply do as one must in PHP currently anyway – hard-code the default value in a particular function call.

Many have proposed solutions that involve the use of hashed arrays as 1 argument, but this involves a lot of repetitive work, and the function no longer works via a list-based syntax unless a lot of code for both types of parameter syntaxes is written.  This will also disable the implicit code completion support of various PHP IDEs.  We happen to use Zend Studio for development, and we find that particular feature enhances our productivity.

We will present an implementation of the above features with a 1 line snippet (an include() file) as the first line of every applicable function like so:

(Note: No changes are made to the prototype, and implicit code completion with the various PHP IDEs will even document the function calls!)

function getUsers($id=null, $group_id=1, $username = ”, $order=’order_fld’, $order_asc=1) {

include(par.php); // hook in the Par code

… code …

}

One may then call the function like so:

getUsers(_, _, $username); // Note the underscores.

or –

getUsers(PAR( array(‘username’ => $username) ));

or even mix named and skipped parameters —

getUsers(_. PAR( array(‘username’ => $username) ) );

or even –

getUsers(_. PAR(‘username’, $username));

(Note: If “_” presents a problem, another safer constant may be chosen.  PHP itself allows extended characters 128..255 to be used in the constant and variable grammars.  We briefly examined using “¬¨ (not symbol),” but this is very difficult to key on Windows machines; and just plain annoying on Macs.)

As for native support in PHP 6, don’t count on it:

A PHP “Developer Minutes” says there is no “… real need for named parameters.” And it states further that that they “… do not want to add it” (http://www.php.net/~derick/meeting-notes.html#named-parameters).  The “PHP Todo Backlog” (http://wiki.php.net/todo/backlog#dropped_items) lists named parameters under “Dropped Items.”  Skipped parameters are not mentioned at all in either, but as aforementioned, Stanislav has mentioned he’d like to see them.

Our non-native syntax is not even terribly unwieldy, and it’s unlikely to break much, as it’s on a per-function basis.  If the PHP team decides to introduce the features natively, it will not conflict for a few reasons.  A constant is used instead of an empty parameter – the most likely native syntax for an implementation of parameter skipping.  The PAR object is even less of a problem and wraps all named parameters.  “par.php” handles and abstracts it all, and is itself a combination of reflection and elegant hackery.  It is all blissfully encapsulated inside one simple include call.  So what gives – why an include?

Well, include is a pseudo-closure of sorts.  Anything within the include retains the scope of the parent without specifying the particular variables as with PHP’s closure support. It is of this form:

$returnval = include(‘FILE_FUNC_NAME’);

All parameters in the current scope are “passed” by reference to the “function” within FILE_FUNC_NAME.  The speed-penalty is on the order of half a millisecond per call – half of which seems to be from the include, and the other from the reflection API.  Then penalty, of course, is only present for those functions where it is implemented.  APC seems to speed it up as well, though we didn’t benchmark as much this way.

It’s simple on the outside, and trivial to implement on a per-function basis.  As far as we can tell, PHP 5.3’s closures cannot easily accomplish the same due to their requirement of explicitly stating relevant variables in “use.” This implementation does not require PHP 5.3, and works with any version of PHP with support for reflection.  You may download the applicable code and see more examples of implementation at the below URL:
http://seoegghead.com/software/PHP-parameter-skipping-and-named-parameters.seo
Limitations:
So far reference parameters are not implemented for PAR(), and there may be limitations with objects or it may create a copy where you might not expect.  we just rarely use reference parameter passing in our design patterns.  In PHP, references are a tool of convenience, not performance.  Feel free to fix or contribute, and we’ll credit your contribution.  Enjoy!

Author: SEO Egghead

SEO Egghead is a web development firm dedicated to creating custom, search-engine-optimized web site applications.

Related Posts:

2 Responses to “PHP Parameter Skipping and Named Parameters – Finally?”

  1. Jay Says:

    Wow That’s a damn good idea.

  2. Jaimie Sirovich Says:

    Note that this broke in PHP 5.3. The quickest way to fix it is to use a stack backtrace instead of func_get/num_args(). I have no idea why the PHP devs did this, as I looked at the code, and it was a deliberate check that disabled these function calls from an include within a function.

    I’ll release a new version that uses reflection, but for now this is the only fix.

Leave a Comment