Inércia Sensorial

29 de December de 2005

PHP named parameters alternative

Filed under: PHP — inerte @ 13:46

Looks like PHP won’t get named parameters on version 6. And, well, I am not the only one that want it 🙂 . It’s a feature of Python that I love. Why? Consider the following situation:

You’re making a function/method to return a string with HTML code for an input field.

1
2
3
4
function inputField($name, $maxlength = 50, $size = 20)
{
    return "<input type=\"text\" name=\"$name\" size=\"$size\" maxlength=\"$maxlength\" /> ";
}

Later on, you need to put a label attribute on your inputText()’s. You can change every function call to pass this parameter or making it optional at the function definition, which is probably desired because it’s easier and faster than changing dozens of lines of code (and coming up with label names):

1
2
3
4
5
6
7
8
9
function inputField($name, $maxlength = 50, $size = 20, $label = '')
{
    $html = "<input type=\"text" name=\"$name\" size=\"$size\" maxlength=\"$maxlength\"";
    if ($label != '') {
        $html .= " label=\"$label\"";
    }
    $html .= " /> ";
    return $html;
}

Sometime later, you need to put javascript events on your input. You can change the function definition again, and it works. But then you’ll need to add a value attribute, or disabled and the many other possible input attributes. That’s impractical on a daily basis, and we’re just talking about input=”text”, not mentioning selects, textareas, etc.

AND you have to remember the exact order of the parameters. You will write code that looks like this:

1
2
3
4
5
6
7
8
9
10
function inputField($name, $maxlength = 50, $size = 20, $label = '', $value = '', $javascript = '', $disabled = '')
{
    // bunch of if's here
}
 
echo inputField($name, 50, 20, '', 'a_value', 'disabled');
 
echo inputField($name, 50, 20, $name, 'a_value', 'onclick=js()', '');
 
echo inputField($name, 50, 20, '', '', '', 'disabled');

These are not pretty function calls at all 🙁

Named parameters could be a solution. You just need to test if the parameter is present, and use it. But PHP doesn’t support them… There are alternatives, like using associative arrays or eval()’ing a string.

But here’s a cleaner, more readable, and expansible way to do it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class named
{
}
 
$named = new named();
 
function foo()
{
    global $named;
 
    echo "foo() called\n";
 
    if (isset($named->var)) {
        echo "\$var->named passed as a parameter with $named->var as value\n";
    }
}
 
echo foo();
 
echo foo($named->var = "test");
 
/*
Outputs:
 
foo() called
foo() called
$var->named passed as a parameter with test as value
*/

Below is the inputText() function rewritten to use this technique. But to make it more useful we will need to perform a clean-up on $named after we use it, because otherwise all its attributes will be carried between function calls (that’s the effect of global $named), and we don’t want that 🙂

For this example, since every inputText() needs to have a name attribute, I am making it a required parameter at the function definition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class named
{
    public function clean()
    {
        foreach ($this as $key => $value) {
            unset($this->$key);
        }
    }
}
 
$named = new named();
 
function inputText($name)
{
    global $named;
 
    $html = "<input type=\"text\" name=\"$name\" ";
 
    if (isset($named->maxlength)) {
        $html .= "maxlength=\"$named->maxlength\" ";
    }
 
    if (isset($named->disabled)) {
        $html .= "disabled=\"$named->disabled\" ";
    }
 
    $html .= " />\n";
 
    $named->clean();
 
    return $html;
}
 
echo inputText('input_name');
 
echo inputText('input_name', $named->maxlength = 5);
 
echo inputText('input_name', $named->disabled = "disabled");
 
echo inputText('input_name', $named->disabled = "disabled", $named->maxlength = 5);
 
// Order doesn't matter.
echo inputText('input_name', $named->maxlength = 5, $named->disabled = "disabled");
 
/*
Outputs:
<input type="text" name="input_name"  />
<input type="text" name="input_name" maxlength="5"  />
<input type="text" name="input_name" disabled="disabled"  />
<input type="text" name="input_name" maxlength="5" disabled="disabled"  />
<input type="text" name="input_name" maxlength="5" disabled="disabled"  />
*/

All we needed were two more lines of code (global $named and $named->clean())

The above class could be further customized with some Overloading, providing for example default values, or even reacting to method calls. Heck, since I am learning what’s new in PHP 5 for just a couple days, I bet there’s a way to automate the clean() method, but couldn’t come up with a way to do it, for now.

Hope you liked 🙂

5 Comments »

  1. Hi Julio,

    Thanks for posting the comment and pointing me to your workaround for named parameters.

    It’s definitely a clever solution for the lack of a feature we’d both like to see. You could get the same effect from making a set of variables global in the function, but by using a class you can set that one as global and declare new variables within it at runtime. smart! Also, the danger of clashing with other global variables is minimized by only have one. In my strongly-typed and non-variable-length-parameter-list world (i.e. Java) this solution wasn’t obvious. On the downside, my first reaction to seeing a global struct/class instance being used in a function is that this code is obviously not thread-safe. But hey, since PHP doesn’t really support multithreading, that point is moot.

    In the cases that I outlined in my post, the usefulness came from a combo of default and named parameters. To further extend your solution, you could create member variables with default values in the class for whatever parameters you wanted to have a default. The clean() class function could reset these default values rather than unsetting them. Also, you wouldn’t have to check if defaulted parameters were set or not, since they would either be set or default. I would be a bit wary of using this solution for any large application though, since one class to handle many default parameters would probably get a bit unwieldy to understand and maintain (unless you kept all the setting of defaults in the functions themselves). Also, having to include the ‘$’, ‘->’ and ‘=’ is obvious enough to me, but it’d be nicer if it was just a ‘=>’ or ‘=’ for users who need to write calls to display functions using my WordPress plugin.

    Thanks again for the idea. It’s obviously more verbose in implementation than if PHP supported the feature itself, but its an option nonetheless.

    Comment by Adam Kramer — 30 de December de 2005 @ 01:13

  2. Your approach is plain ridiculous…

    Global variable.
    Coupling all your functions with a class.
    Function calls is still verbose and bigger than using an associative array in the first place.

    More simple approach using an associative array…

    test(array('var2' => 'bob', 'var3' => 32));

    function test(array $parameter = array())

    {

    $var1 = 10; $var2 = 'string'; $var3 = null;

    extract($parameter);
    // continue…
    }

    Comment by Psi — 27 de December de 2008 @ 05:36

  3. But I tried to do without using associative arrays, which I even said in the middle of the text. The point wasn't to make it pretty. Just a hack to show how to do it….

    Comment by inerte — 27 de December de 2008 @ 11:09

  4. But you said that it is “a cleaner, more readable, and expansible way to do it.” Are you crazy? Declaring a separate object just to hold the parameters is “cleaner” and “more readable?” Creating a global variable for each function is “expansible?” Using a global variable to pass in function parameters kills re-entrancy, thread-safety, and any sense of reusability of code. You may as well be using GOTO and FORTRAN 66. But wait, even FORTRAN 66 used this revolutionary thing called “the stack” that allowed wonderful things like recursion and re-entrancy of code. Oh, but I forgot. Your way is a “a cleaner, more readable, and expansible way to do it.” My bad.

    Comment by First-year CS student — 15 de April de 2009 @ 01:41

  5. This is probably a bit late to comment, but … http://www.hotscripts.com/blog/php-parameter-sk

    Comment by PHP Ninja — 07 de January de 2010 @ 21:33

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress