Accelerate your eCommerce ambitions with adeptCommerce Suite

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

We specialize in eCommerce and content management web sites that not only render information beautifully to the human, but also satisfy the "third browser" - the search engine. To us, search engines are people too.

image description image description image description image description

PHP Skipped and Named Parameters

No Native PHP Parameter Skipping and Named Parameters? No Problem ...

NEED A GREAT WEB SITE? NEED IT TO BE SEARCH-ENGINE-FRIENDLY?

SEO Egghead is a web development firm dedicated to creating custom, search engine optimized web site applications. We specialize in eCommerce and content management web sites that not only render information beautifully to the human, but also satisfy the "third browser" — the search engine. To us, search engines are people too. Click here to talk to us. We'd love to help!
X
And it requires only 1 simple cut-and-pasted line added per function to implement. This information page will detail how to do so. Both parameter skipping and named parameters are 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 see later, the PHP core developers vetoed this feature in PHP 6.

Let's propose an elegant workaround and implement it. 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;
}

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 repetitive 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 all defaults without respecifying, and allow the programmer to modify only 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 a result, 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 each time it is implemented, and the function no longer works via a list-based syntax unless even more code to support both types of parameter syntaxes is written. This will also cripple 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.

So let's 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:

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!

Tell an amigo:
Sphinn Digg Reddit del.icio.us StumbleUpon Facebook