views:

335

answers:

8

I ask this question because i learned that in programming and designing, you must have a good reason for decisions. I am php learner and i am at a crossroad here, i am using simple incrementation to try to get what im asking across. I am certainly not here to start a debate about the pros/cons of referencing, but when it comes to php, which is the better programming practice:

function increment(&$param) {
      ++$param;
}

vs.

function increment($param){
 return ++$param;
}

$param = increment($param);
+1  A: 

By definition, incrementing a variable's value (at least in PHP) mutates the variable. It doesn't take a value, increase it by 1 and return it; rather it changes the variable holding that value.

So your first example would be the better way to go, as it's taking a reference (PHP doesn't really do pointers per se) of $param and post-incrementing it.

BoltClock
PHP's default is pass by value. So any interaction on a value passed into the function stays within that scope.
jlindenbaum
I thought PHP5 changed that?
Graphain
IIRC PHP5's default is pass by value on everything except objects, which are pass by reference.
jlindenbaum
@jlindenbaum no, objects are passed by value. Well, actually, what's passed by value are the object references (or in this case you could call them "smart pointers"; they're not references in the sense of the rest of this discussion), the object is never passed around, so it *seems* to be passed by reference, but it's actual pass-object-reference-by-value.
Artefacto
OP's first example specified to pass $param by reference, by the way.
BoltClock
+1  A: 

I just ran a couple quick tests with the following code:

<?php

function increment(&$param){
 $param++;
}
$param = 1;

$start = microtime();

for($i = 1; $i <= 1000000; $i++) {
    increment($param);
}

$end = microtime();

echo $end - $start;

?>

This returned, consistently around .42 to .43, where as the following code returned about .55 to .59

<?php

function increment($param){
 return $param++;
}
$param = 1;

$start = microtime();

for($i = 1; $i <= 1000000; $i++) {
    $param = increment($param);
}

$end = microtime();

echo $end - $start;

?>

So I would say that the references are quicker, but only in extreme cases.

John
Nice test but in PHP5 at least you should pretty much never use pointers for performance reasons.
Graphain
This test was run in PHP 5.3.1, but I may be wrong?
John
You should use `microtime(true)` to return the time as a float instead of a string. Arithmetic on the string value is unreliable.
Bill Karwin
Ah, good point. Just re-ran the test with microtime(true) and 1,000,000 tests instead of 100,000. The reference gets 4.9 - 5.1, while the returning function gets 5.5 - 5.7
John
your function doesn't do anything. It should be `return ++$param;`
nickf
I pasted the code from the initial question
John
A: 

I think your example is a little abstract.

There is no problem with using pointers but in most real-world cases you are probably modifying an object not an int in which case you don't need the reference (in PHP5 at least).

Graphain
+3  A: 

The best practice is not to write a function when an expression will do.

$param++;

is all you need.

Entendu
I believe he simply gave an example, and he does not plan on using that exact function.
John
@John: that's the major difference between programmers and philosophers: programmers solve specific tasks. without clarification it is the best answer for the asked question.
zerkms
@zerkms The OP explicitly said he's using it as an example: `i am using simple incrementation to try to get what im askin across`
deceze
@deceze: the example should be relevant to the issue. if example isn't representative - then it should be replaced with a better one. in the way the question being asked now - the best answer is not to create function at all.
zerkms
deceze
zerkms
@zerkms Et voilà, you provided a fitting answer for the question. Wasn't that hard, was it? ;o)
deceze
@deceze: all this thread i was saying the same thought in the different manner - the solution should rely on context. the best answer on the given context is current, that's why I upvoted it ;-)
zerkms
A: 

I'd say it would quite depend on what you're doing. If you're trying to interact on a large set of data without wanting an extra copy of it in memory - go ahead, pass by value (your second example). If you want to save the memory, and interact on an object directly - pass by reference (your first example)

jlindenbaum
Except... PHP uses copy-on-write
Artefacto
+6  A: 

First, references are not pointers.

I tried the code given by @John in his answer, but I got strange results. It turns out microtime() returns a string. Arithmetic is unreliable and I even got negative results on some runs. One should use microtime(true) to get the value as a float.

I added another test of no function call, just incrementing the variable:

<?php

$param = 1;

$start = microtime(true);

for($i = 1; $i <= 1000000; $i++) {
    $param++;
}

$end = microtime(true);

echo "increment: " . ($end - $start) . "\n";

The results on my machine, Macbook 2.4GHz running PHP 5.3.2.

  • function call with pass by reference: 2.14 sec.
  • function call with pass by value: 2.26 sec.
  • no function call, just bare increment: 0.42 sec.

So there seems to be a 5.3% performance advantage to passing by reference, but there is a 81% performance advantage to avoiding the function call completely.

I guess the example of incrementing an integer is arbitrary, and the OP is really asking about the general advantage of passing by reference. But I'm just offering this example to demonstrate that the function call alone incurs far more overhead than the method of passing parameters.

So if you're trying to decide how to micro-optimize parameter passing, you're being penny wise and pound foolish.

There are also other reasons why you should avoid references. Though they can simplify several algorithms, especially when you are manipulating two or more data structures that must have the same underlying data:

  • They make functions have side-effects. You should, in general, avoid functions with side-effects, as they make the program more unpredictable (as in "OK, how this did this value get here? did any of the functions modify its parameters?")
  • They cause bugs. If you make a variable a reference, you must remember to unset it before assigning it a value, unless you want to change the underlying value of the reference set. This happens frequently after you run a foreach loop by reference and then re-use the loop variable.
Bill Karwin
Amdahl's Law. When passings by reference is the slowest part of your application... worry about it. Otherwise, as you correctly say, you're being pound foolish.Chances are, if it's a PHP application, you'll have a database object that will be a lot slower than any pass by reference call you make.
jlindenbaum
+3  A: 

It depends on what the functions purpose is. If its express purpose is to modify the input, use references. If the purpose is to compute some data based on the input and not to alter the input, by all means use a regular return.

Take for example array_push:

int array_push(array &$array, mixed $var[, mixed $...])

The express purpose of this function is to modify an array. It's unlikely that you need both the original array and a copy of it including the pushed values.

array_push($array, 42); // most likely use case

// if you really need both versions, just do:
$old = $array;
array_push($array, 42);

If array_push didn't take references, you'd need to do this:

// array_push($array, $var[, $...])
$array = array_push($array, 42); // quite a waste to do this every time

On the other hand, a purely computational function like pow should not modify the original value:

number pow(number $base, number $exp)

You are probably more likely to use this function in a context where you want to keep the original number intact and just compute a result based on it. In this case it would be a nuisance if pow modified the original number.

$x = 123;
$y = pow($x, 42); // most likely use case

If pow took references, you'd need to do this:

// pow(&$base, $exp)
$x = 123;
$y = $x; // nuisance
pow($y, 42);
deceze
+2  A: 

The second version:

function increment($param){
 return $param++;
}

$param = increment($param);

Does nothing. increment() returns $param. Perhaps you meant ++$param or $param+1;

I mention this not to be pedantic, but so that if you compare timings, you are comparing the same function (it could be possible for PHP's optimizer to remove the function completely).

Justin Frankel
jebus, it's justin frankel!
nickf