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!



