debuggable

 
Contact Us
 

Composing Methods: Remove Assignments to Parameters

Posted on 6/7/07 by Tim Koschützki

When your code assigns to a parameter in a function/method, use a temporary variable instead.

Motivation

With assigning to a parameter I mean that when you pass in an object foo, you do not make it refer to a different object. Changing a parameter object is perfectly cool and I do that myself all the time. Just do not make it reference another object, like:

function aFunction($foo) {
  $foo->modify();
  $foo = new SomeOtherClass;
}

The reason this is so awkward is the confusion between pass by value and pass by reference in PHP. Whereas in PHP4 everything was
passed in by value as default, PHP5 passes objects around by reference. In PHP4 when you assign the parameter another object this will not be reflected in the calling routine, which can lead to quite nasty bugs if you forget to insert the by-reference operator (&).

In PHP5 this is not so much of a problem as it passes objects around by reference anyways. So if, in PHP5, you assign to a parameter you must have a good reason anyways, because you will change the parameter object also in the calling routine with your assignment. However, I find that in PHP5 there is another area of confusion making this refactoring worthwhile for both PHP4 and PHP5. That area of confusion is within the body of the code itself. It is much clearer to use only the parameter to represent what has been passed in, because that is consistent usage and therefore a good coding practice.

It will make your code much more readable and prevents by-reference confusion and therefore big problems in the future. In PHP5, assigning to an parameter object within a method will not be reflected outside the method. So I suggest you just don't do it anyways. There seems to be no point in it anyways.

Think of hairloss. ;] Haha, I seem to be bringing up hairloss all the time in connection with bug fixing. :]

Mechanics

  • Create a temporary variable for the parameter.
  • Replace all references to the parameter, made after the assignment, to the temporary variable.
  • Change the assignment to assign to the temporary variable.
  • If you are on PHP5 or PHP4 (using the by-reference operator), look in the calling method to see if the parameter is used again afterward and try to understand what is changed where in your code and document that. Make sure you only return one value from the method. If more than one call-by-reference parameter is assigned to and returned, try to use a data clump or Extract Method.

Code Example

Let's start with the following routine:

function price($inputVal, $quantity, $anotherFactor) {
  if ($inputVal > 50) $inputVal -= 30;
  if ($quantity > 20) $inputVal -= 5;
  if ($anotherFactor> 1000) $inputVal -= 2;
  return $inputVal;
}

echo price(50, 10, 1200);

Replacing with a temp leads to:

function price($inputVal, $quantity, $anotherFactor) {
  $result = $inputVal;
 
  if ($inputVal > 50) $result -= 30;
  if ($quantity > 20) $result -= 5;
  if ($anotherFactor> 1000) $result -= 2;
  return $result ;
}

echo price(50, 10, 1200);

An easy refactoring, yet quite cool. I do it all the time. Will you, too?

 
&nsbp;

You can skip to the end and add a comment.

[...] another part of his “Composing Methods” series, Tim Koschuetzki posts about removing assignments to parameters today - working with a temporary variable inside a method rather [...]

Bruce Weirdan said on Jul 07, 2007:

Unlike what you've said PHP5 *does not* pass objects by reference by default. Assignment to function/method parameter will not be reflected in the outer scope:
[weirdan@server ~]$ /usr/local/bin/php -r 'function func($q) { $q = new stdClass; } $e = new stdClass; $e->q="asd"; var_dump("before", $e); func($e); var_dump("after", $e);'

string(6) "before"

object(stdClass)#1 (1) {

["q"]=>

string(3) "asd"

}

string(5) "after"

object(stdClass)#1 (1) {

["q"]=>

string(3) "asd"

}

Bruce Weirdan said on Jul 07, 2007:

oh, forgot to mention the php version:
[weirdan@server ~]$ /usr/local/bin/php -v

PHP 5.2.3 (cli) (built: Jun 21 2007 12:29:33)

Tim Koschuetzki said on Jul 07, 2007:
class a {
  public $val;

  public function __construct() {

    $this->val = 1;

  }

  public function modify() {

    $this->val++;

  }

}



function func($q) {

  $q->modify();

}



$e = new a;





echo $e->val;

func($e);



echo $e->val;



func($e);



echo $e->val;



func($e);



echo $e->val;

outputs: 1234

Maybe php forbids assigning new objects to parameter object variables? Interesting.

Bruce Weirdan said on Jul 07, 2007:

That I would call 'passing a pointer' in c++ terms. You can change the pointer inside your function and it will be referencing another data, but this change won't be reflected in the outer scope. But if you change *data* referenced by that pointer, the change would be visible outside (that's [in rough terms] is what you did in your code). And PHP does not forbid assigning anything to parameters, the assignment won't have any effect outside the function and that's all.

Tim Koschuetzki said on Jul 08, 2007:

Hrm nice. :] I will change the article accordingly.

Thanks for letting me know!

php blog said on Jul 19, 2007:

Yes, second example better :)

Hodicska Gergely  said on Jul 29, 2007:

"It will make your code much more readable and prevents by-reference confusion and therefore big problems in the future. In PHP5, assigning to an parameter object within a method will not be reflected outside the method."

This is the normal behavior (if you are familiar with the internal representation of variables in PHP). A variable has two parts, an entry in a symbol table (the name of the variable), which points to a zval (C) struct (this holds the content of the variable). When you call a function/method, PHP create a local symbol table. The parameters of the function exist in this local symbol table.

function bar($obj) {
$obj = new Obj2();

}

// PHP creates an entry in the global symbol table, which points to the zval struct, which holds the object

$foo = new Obj();

// When you call the function an entry is created in an local symbol table, which points to the same zval struct.

bar($foo);

// At this line the entry in the local symbol table points to the new zval struct which holds the newly created object. But the "foo" entry points to the original zval struct.
$obj = new Obj2();

deltawing1  said on Jun 25, 2008:

Heheh. I used to use temp variables like this all the time until my peers advised me to stop doing it ...

This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.