What a difference a character makes

Introduction

If you’ve not read my ‘The first step’ posts (all three – one, two and three) and don’t understand PHP, then please do before continuing. The code presented will be on the same setup and the post will reference this.

Passing variables into functions can be done in one of two ways:

  • By value
  • By reference

In this post I will explain both and compare the effect on an array of numbers.

Disclaimers

Firefox® is a registered trademark of the Mozilla Foundation – www.mozilla.org/en-US/foundation/trademarks/policy.

Ubuntu® is a registered trademark of Canonical Ltd – ubuntu.com/legal/intellectual-property-policy.

“Raspberry Pi” is a trademark of the Raspberry Pi Foundation – www.raspberrypi.org/trademark-rules.

Apache and Apache HTTP Server are trademarks of The Apache Software Foundation – www.apache.org.

Other names / logos can be trademarks of their respective owners. Please review their website for details.

I am independent from the organisations listed above and am in no way writing for or endorsed by them.

All example ‘code’ presented is written by myself and is GPLv3 Licensed, please feel free to copy from the ‘July_2020’ folder of github.com/gjb2048/code.

If the code does not work

If you decide to try the example code and it goes wrong, then you need to look in the error log file. On the Pi, this will be located in the ‘/var/log/apache2’ folder as ‘error.log’ and you can use the command ‘tail’ to see the most recent events as the file is appended to. In other systems, hunt around for ‘error.log’.

Pass by value

This tends to be the most common. This is when a variable you have defined is passed into a function by making a copy of it, such as:

<!doctype html>
<html>
    <head>
        <title>Pass by value</title>
        <style>
            body {
                font-family: sans-serif;
            }
        </style>
    </head>
    <body>
        <h1>Pass by value</h1>
<?php
$anumber = 41;
function inc_echo_me($value) {
    $value++;
    echo '<p>'.$value.'</p>';
}
inc_echo_me($anumber);
echo '<p>'.$anumber.'</p>';

?>
    </body>
</html>

giving:

where you’ll notice that even though the number has been increased in the function, the variable that was passed in has not. This is because it was ‘copied’ when passing in.

Pass by reference

The other way of passing a variable is by ‘reference’, this is done by adding the ampersand character ‘&’ to the beginning of the variable in the parameter list of the function ‘inc_echo_me’, such as:

<!doctype html>
<html>
    <head>
        <title>Pass by reference</title>
        <style>
            body {
                font-family: sans-serif;
            }
        </style>
    </head>
    <body>
        <h1>Pass by reference</h1>
<?php
$anumber = 41;
function inc_echo_me(&$value) {
    $value++;
    echo '<p>'.$value.'</p>';
}
inc_echo_me($anumber);
echo '<p>'.$anumber.'</p>';

?>
    </body>
</html>

which gives:

so a ‘reference’ to the variable is being passed (copied) in to the function and not the actual value. The ‘reference’ can be thought of as the ‘address in memory’ of the variable, and indeed in other languages such as ‘C’ the ‘&’ character means ‘address of’. This is why the value of the variable in the global scope is changed by the ‘inc_echo_me’ function because it is being changed and not its copy.

Passing arrays

So far we have just passed an integer which takes up a relatively small amount of memory. This is assumed as the ‘sizeof’ function in PHP states the number of items in an array and not the actual size in memory it does in other languages such as C (www.php.net/manual/en/function.sizeof.php). But indeed, what about an array? That can be much larger. So what if we have an array that contains the powers of 2 between 2^1 and 2^16, thus sixteen integers? So:

$store = array();
for($i = 1; $i <= 16; $i++) {
    $store[$i] = pow(2, $i);
}

being ‘2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536’. If we were to pass in this array to a function that subtracted 2 from it? Then with ‘pass by value’ we would expect:

  • The source array to be copied.
  • The source array not to be changed.
  • The copied array to be changed.

With ‘pass by reference’ we would expect:

  • The source array not to be copied but a reference to it passed in.
  • The source array changed.

But how can we tell this? Well there is the function ‘memory_get_usage()’ (https://www.php.net/manual/en/function.memory-get-usage.php), which when combined with storing the past value of the amount of memory in use every time it is called then we can tell the change in the amount between calls. So with the program:

<!doctype html>
<html>
    <head>
        <title>Pass by value verses by reference - array</title>
        <style>
            body {
                font-family: sans-serif;
            }
        </style>
    </head>
    <body>
        <h1>Pass by value verses by reference - array</h1>
        <table>
            <thead><tr><th>Stage</th><th>Data</th><th>Information</th></tr></thead>
            <tbody>
<?php
function display_array(&$array, $prefix) {
    echo '<tr><td>'.$prefix.':</td><td>'.implode(', ', $array).'</td><td></td></tr>';
}

function sumarray_passbyreference(&$sumthis) {
    foreach($sumthis as $key => $value) {
        $sumthis[$key] = $value - 2;
    }
    display_array($sumthis, '$store at end of pass by reference');
    memoryusage('In sumarray_passbyreference');
}

function sumarray_passbyvalue($sumthis) {
    foreach($sumthis as $key => $value) {
        $sumthis[$key] = $value - 2;
    }
    display_array($sumthis, '$store at end of pass by value');
    memoryusage('In sumarray_passbyvalue');
}

function memoryusage($extra) {
    static $lastcurrent = 0;
    static $lastpeak = 0;
    $current = memory_get_usage();
    $peak = memory_get_peak_usage();
    $currentdiff = $current - $lastcurrent;
    $peakdiff = $peak - $lastpeak;
    echo '<tr><td>Memory usage ('.$extra.') - '.date("H:i:s").'</td><td></td><td>Current: '.$current.'('.$currentdiff.') and peak: '.$peak.'('.$peakdiff.')</td></tr>';
    $lastcurrent = $current;
    $lastpeak = $peak;
}

memoryusage('Start');
$store = array();
for($i = 1; $i <= 16; $i++) {
    $store[$i] = pow(2, $i);
}
memoryusage('$store');

display_array($store, '$store at start');
memoryusage('Calling sumarray_passbyvalue');
sumarray_passbyvalue($store);
display_array($store, '$store after sumarray_passbyvalue');
memoryusage('Calling sumarray_passbyreference');
sumarray_passbyreference($store);
display_array($store, '$store after sumarray_passbyreference');
memoryusage('Second call to sumarray_passbyvalue');
sumarray_passbyvalue($store);
display_array($store, '$store after second sumarray_passbyvalue');
memoryusage('Second call to  sumarray_passbyreference');
sumarray_passbyreference($store);
display_array($store, '$store after second sumarray_passbyreference');
memoryusage('End');
?>
            </tbody>
        </table>
    </body>
</html>

we get:

where we can see exactly what we are expecting. You can also see the change in amount of memory from ‘memory_get_usage()’ with the difference in brackets.

Thus if you’re careful with your code when one of the parameters is an array then it can be deemed to be better to pass by reference than by value and save all of that copying.

But, what if we used a class instead of an array to contain the values? So with:

<!doctype html>
<html>
    <head>
        <title>Pass by value verses by reference - class</title>
        <style>
            body {
                font-family: sans-serif;
            }
        </style>
    </head>
    <body>
        <h1>Pass by value verses by reference - class</h1>
        <table>
            <thead><tr><th>Stage</th><th>Data</th><th>Information</th></tr></thead>
            <tbody>
<?php
function display_class(&$class, $prefix) {
    $classasarray = array();
    for($i = 1; $i <= 16; $i++) {
        $classattr = 'val'.$i;
        $classasarray[] = $class->$classattr;
    }
    echo '<tr><td>'.$prefix.':</td><td>'.$class->name.': '.implode(', ', $classasarray).'</td><td></td></tr>';
}

function sumclassval_passbyreference(&$sumthis) {
    for($i = 1; $i <= 16; $i++) {
        $classattr = 'val'.$i;
        $sumthis->$classattr = $sumthis->$classattr - 2;
    }
    display_class($sumthis, '$store at end of pass by reference');
    memoryusage('In sumarray_passbyreference');
}

function sumclassval_passbyvalue($sumthis) {
    for($i = 1; $i <= 16; $i++) {
        $classattr = 'val'.$i;
        $sumthis->$classattr = $sumthis->$classattr - 2;
    }
    display_class($sumthis, '$store at end of pass by value');
    memoryusage('In sumarray_passbyvalue');
}

function memoryusage($extra) {
    static $lastcurrent = 0;
    static $lastpeak = 0;
    $current = memory_get_usage();
    $peak = memory_get_peak_usage();
    $currentdiff = $current - $lastcurrent;
    $peakdiff = $peak - $lastpeak;
    echo '<tr><td>Memory usage ('.$extra.') - '.date("H:i:s").'</td><td></td><td>Current: '.$current.'('.$currentdiff.') and peak: '.$peak.'('.$peakdiff.')</td></tr>';
    $lastcurrent = $current;
    $lastpeak = $peak;
}

memoryusage('Start');
class myStore {
    public $name;
    public $val1, $val2, $val3, $val4, $val5, $val6, $val7, $val8,
        $val9, $val10, $val11, $val12, $val13, $val14, $val15, $val16;

    public function __construct() {
        $this->name = 'myStore';
        for($i = 1; $i <= 16; $i++) {
            $classattr = 'val'.$i;
            $this->$classattr = pow(2, $i);
        }
    }
}
$store = new myStore;

memoryusage('$store');

display_class($store, '$store at start');
memoryusage('Calling sumclassval_passbyvalue');
sumclassval_passbyvalue($store);
display_class($store, '$store after sumclassval_passbyvalue');
memoryusage('Calling sumclassval_passbyreference');
sumclassval_passbyreference($store);
display_class($store, '$store after sumclassval_passbyreference');
memoryusage('Second call to sumclassval_passbyvalue');
sumclassval_passbyvalue($store);
display_class($store, '$store after second sumclassval_passbyvalue');
memoryusage('Second call to  sumclassval_passbyreference');
sumclassval_passbyreference($store);
display_class($store, '$store after second sumclassval_passbyreference');
memoryusage('End');
?>
            </tbody>
        </table>
    </body>
</html>

we get:

which appears to demonstrate that the object instantiated from the class is being ‘shallow copied’ instead of ‘deep’ (en.wikipedia.org/wiki/Object_copying) and thus our ‘store’ is being changed even when passing by value.

Conclusion

All I have done in this post is discuss the impact of one single character being ‘&’ on the behaviour of a program! Thus when it comes to programming, the ‘devil is in the detail’ (en.wikipedia.org/wiki/The_devil_is_in_the_detail) and you have to be so careful about what you write.

Gareth Barnard
Latest posts by Gareth Barnard (see all)
blank

Gareth Barnard

Gareth is a developer of numerous Moodle Themes including Essential (the most popular Moodle Theme ever), Shoelace, and other modules such as course formats.

One thought on “What a difference a character makes

Add a reply or comment...