5

I have a function:

$query = "SELECT * from lol";
database_query( $query );

considering the $query will never be changed inside the database_query function, is it good practice to use a pointer to $query so the function doesn't need to assign more memory to a new iteration of the value passed in?

function database_query( &$query ){
    //do stuff that does not affect $query
}
Jimmyt1988
  • 20,466
  • 41
  • 133
  • 233
  • This recent question may be of interest to you: [Why are references rarely used in PHP?](http://programmers.stackexchange.com/q/188765/4978) –  Feb 28 '13 at 17:26

3 Answers3

6

No, don't do this. PHP will only create another copy of the string if the value of the non-pass-by-reference parameter is changed inside of the function ("copy on write"). There is no reason to give people reading your code the wrong impression of what your function is doing by making the parameter a reference.

Also, references are not pointers.

  • This is not correct. PHP will not do a copy on write for a reference variable. – Benny Hill Dec 12 '12 at 16:21
  • @BennyHill: I wasn't referring to reference variables when talking about copy on write. I've clarified this for you. –  Dec 12 '12 at 17:33
  • @BennyHill When your application is bigger than a one-file-example-script you can never be sure how many different references points to the same value. You will likely trigger a copy-on-write from a value from somewhere high up above in the stacktrace. – KingCrunch Dec 12 '12 at 21:25
  • If you use a reference you know for sure that you WON'T trigger a copy on write. – Benny Hill Dec 12 '12 at 22:01
1

The value of $query is a string. Strings don't have internal mutable state -- the only way to "change" a variable that contains a string in PHP is to assign a new string to it (or pass it by reference to a function which then assigns a new string to it, etc.). Thus, when you do $foo = $query;, any reasonable implementation would simply copy the pointer to the internal character array, not copy the actual characters, since they cannot change. So even if you didn't know that PHP has copy-on-write, you can conclude that passing a string by value is not expensive.

newacct
  • 119,665
  • 29
  • 163
  • 224
-2

This turns out to be a very interesting question, I've spent the last hour and a half reading about PHP and how it handles references (thank you Tim Cooper for the links that got me started).

To answer your question, yes - it's good practice to use a reference like that when you call a function. By using a reference you will use fewer resources - there is no "copy on write" for a reference variable. Here's some proof:

<?php
function noref_nowrite($var_a){
    echo '<h3>NOT Using a Reference/Not Changing Data</h3>';
    echo '<p>'. xdebug_debug_zval('var_a') .'</p>';
    echo '<p>'. debug_zval_dump($var_a) .'</p>';
    echo '<p>$var_a = '. $var_a .' and $GLOBALS[a] = '. $GLOBALS['a'] .'</p>';
}
function noref_write($var_a){
    $var_a++;
    echo '<h3>NOT Using a Reference/Changing Data</h3>';
    echo '<p>'. xdebug_debug_zval('var_a') .'</p>';
    echo '<p>'. debug_zval_dump($var_a) .'</p>';
    echo '<p>$var_a = '. $var_a .' and $GLOBALS[a] = '. $GLOBALS['a'] .'</p>';
}

function ref_nowrite(&$var_a){
    echo '<h3>Using a Reference/Not Changing Data</h3>';
    echo '<p>'. xdebug_debug_zval('var_a') .'</p>';
    echo '<p>'. debug_zval_dump($var_a) .'</p>';
    echo '<p>$var_a = '. $var_a .' and $GLOBALS[a] = '. $GLOBALS['a'] .'</p>';
}
function ref_write(&$var_a){
    $var_a++;
    echo '<h3>Using a Reference/Changing Data</h3>';
    echo '<p>'. xdebug_debug_zval('var_a') .'</p>';
    echo '<p>'. debug_zval_dump($var_a) .'</p>';
    echo '<p>$var_a = '. $var_a .' and $GLOBALS[a] = '. $GLOBALS['a'] .'</p>';
}

$a = 5;
noref_nowrite($a);
noref_write($a);
ref_nowrite($a);
ref_write($a);
?>

If you copy/paste the above code into a PHP page and execute it you will see this:

NOT Using a Reference/Not Changing Data
var_a: (refcount=3, is_ref=0)=5
long(5) refcount(4)

$var_a = 5 and $GLOBALS[a] = 5


NOT Using a Reference/Changing Data
var_a: (refcount=1, is_ref=0)=6
long(6) refcount(2)
$var_a = 6 and $GLOBALS[a] = 5

Using a Reference/Not Changing Data
var_a: (refcount=3, is_ref=1)=5
long(5) refcount(1)
$var_a = 5 and $GLOBALS[a] = 5

Using a Reference/Changing Data
var_a: (refcount=3, is_ref=1)=6
long(6) refcount(1)
$var_a = 6 and $GLOBALS[a] = 6

So what we have here is four basic tests. I create a global variable ($a) and assign it the value of 5.

When I call the noref_nowrite function we see that XDebug counts 3 references while PHP's built in function counts 4. Interestingly, PHP optimizes this so internally it's really like calling the ref_nowrite function because PHP makes $var_a a reference to $GLOBALS['a'].

When I call the noref_write function we see that the refcount drops to 1 (or 2 if you look at PHP's built-in function). Why? Because this is where the "copy on write" issue takes place. Up until we incremented $var_a PHP was internally using $var_a as a reference to $a but when we changed the value we forced PHP to make a copy of the variable so it could be incremented. So at that point $var_a was no longer a reference to $a, instead it was changed to reference it's own data.

The ref_nowrite function shows ambiguous results. Looking at it alone we can't prove anything. However the ref_write function shows us that XDebug says we're dealing with a reference variable (is_ref=1) and most importantly we see that after we increment $var_a the value of our global variable $a has also changed - this means $var_a and $GLOBALS['a'] are definitely pointing at the same place in memory. Which means changing $var_a did NOT trigger a "copy on write" situation - and it shouldn't since we're dealing with a reference.

Play around with this to convince yourself and here's some more reading:

Detecting whether a PHP variable is a reference / referenced (I thought ircmaxell had a well thought out answer)

http://us2.php.net/debug-zval-dump

XDebug documentation: http://xdebug.org/docs/display

PHP What References Do: http://us3.php.net/manual/en/language.references.whatdo.php

PHP Reference Counting Basics: http://us3.php.net/manual/en/features.gc.refcounting-basics.php

Community
  • 1
  • 1
Benny Hill
  • 6,191
  • 4
  • 39
  • 59
  • 2
    I'm not really sure what the objective of this was, since the question wasn't whether or not PHP performs copy on write with reference variables, which it clearly does not, but whether or not it is good practice to pass an argument as reference if the value is not being changed. Your own test shows that PHP does make a copy of the variable data when it is not modified, therefore the variable should NOT be passed as a reference. This is just creating ambiguous code. –  Dec 12 '12 at 17:31
  • 1
    Just don't pass something as reference, that is not intended to get passed as reference, if you don't want the specific behaviour. First of all it's (in this case: ultra-)micro optimization (and I assume the engine can do it better) and therefore you just spent 90 minutes to find something, that has no benefit. Beside this the question was about "good practice" and this is definitely not. – KingCrunch Dec 12 '12 at 21:20