Yeah, it's been a busy day today. But I'm quite pleased with the PHP stuff I've learned. This is another quick one (OK, I'll stipulate now: a lot of these PHP ones will be short, so I'll stop saying that), and harkens back to something I talked about re CFML back in 2012: "Complex data-types in CF, and how they're not copied by reference".
PHP has a concrete and code-controllable notion of references; not just something that happens under the hood like in CFML.
I'll not go into what references are. You can read the PHP docs regarding references: "What References Are", or the language-neutral Wikipedia page: "Reference (computer science)".
CFML
Let's start with CFML though. In CFML we don't have any control over whether a piece of data is passed by value or passed by reference (I use that term incorrectly, but for expedience. Read the blog article I link to above for an explanation). Simple types and arrays are passed by value in CFML (Railo passes arrays by reference, that said), and other complex types are passed by reference.Here's some example code which demonstrates CFML behaviour:
// references.cfm
original = "tahi,rua,toru";
copy = original;
original &= ",wha";
copy &= ",rima";
writeOutput("original: #original#<br>copy: #copy#<br>");
writeOutput("<hr>");
original = {"red"="whero", "orange"="karaka", "yellow"="kowhai"};
reference = original;
original["green"] = "kakariki";
original["blue"] = "kikorangi";
writeDump(original);
writeDump(reference);
writeOutput("<hr>");
reference = "and now for something completely different";
writeDump([original,reference]);
writeOutput("<hr>");
This outputs:
original: tahi,rua,toru,wha
reference: tahi,rua,toru,rima
struct | |
---|---|
blue | kikorangi |
green | kakariki |
orange | karaka |
red | whero |
yellow | kowhai |
struct | |
---|---|
blue | kikorangi |
green | kakariki |
orange | karaka |
red | whero |
yellow | kowhai |
array | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 |
| ||||||||||||
2 | and now for something completely different |
Here the assignment operator copies the value of the string
original
, and makes a duplicate of it as copy
. They are two difference strings, so appending to one does not impact the other.On the other hand when dealing with complex objects like a structure, the assignment operator creates a new variable which has a new reference pointing to the original. So it's not a "copy by reference" situation, but it's a similar "copy of reference" situation (I dunno the official term for this approach). The ramification is that adjustments made to the
original
are reflected in the reference
to it, and vice versa. However if one gives the reference
a new value completely, it does not change the original
, because it overwrites that variable's reference with a new one to the new data.If that's not clear, that other blog article I link to above goes through it thoroughly.
PHP
PHP has the ability to specify how assignments are made. One can do this:$reference = &$original;
The ampersand indicates to make a new reference to the same data. The reference is the same, there's just a new variable pointing to that reference. In CFML's case there's a new variable, a new reference, just the new reference points to the same data as the original reference.
Here's some PHP code showing references in action. It's an approximation of the CFML code above.
// references.php
require "../../debug/dBug.php";
$original = "tahi,rua,toru";
$copy = $original;
$reference = &$original;
$original .= ",wha";
$reference .= ",rima";
$copy .= ",ono";
echo "original: $original<br>reference: $reference<br>copy: $copy<br>";
echo "<hr>";
$original = ["red"=>"whero", "orange"=>"karaka", "yellow"=>"kowhai"];
$reference = &$original;
$copy = $original;
$original["green"] = "kakariki";
$original["blue"] = "kikorangi";
$copy["indigo"] = "poropango";
new dBug($original);
new dBug($reference);
new dBug($copy);
echo "<hr>";
$reference = "and now for something completely different";
echo "original: $original<br>reference: $reference<br>";
new dBug($copy);
echo "<hr>";
And the output:
original: tahi,rua,toru,wha,rima
reference: tahi,rua,toru,wha,rima
copy: tahi,rua,toru,ono
$original (array) | |
red | whero |
orange | karaka |
yellow | kowhai |
green | kakariki |
blue | kikorangi |
$reference (array) | |
red | whero |
orange | karaka |
yellow | kowhai |
green | kakariki |
blue | kikorangi |
$copy (array) | |
red | whero |
orange | karaka |
yellow | kowhai |
indigo | poropango |
original: and now for something completely different
reference: and now for something completely different
$copy (array) | |
red | whero |
orange | karaka |
yellow | kowhai |
indigo | poropango |
There's a few things to note:
- the
$reference
string points to exactly the same string in memory as the$original
one. So change one: it's reflected in the other. - however the
$copy
string is completely different:changes to it apply only to it, and not the$original
. - PHP will copy an array (bear in mind PHP doesn't have structs, it's just got "associative arrays"... that's a story for another day though) by value if you don't tell it otherwise. as demonstrated by the
$copy
. - Making a reference to an array now seems to behave like CFML does.
- However if one does not do it with a reference, it makes a new copy, so changes to the copy are specific to the copy.
- Until one gives that reference an entirely new value... the new value goes into memory pointed to by both the
$original
and$reference
references, so original takes that new value. This demonstrates the difference between proper "assign by reference" and that which CFML does.
So why would one want to do this? one does it mostly with function arguments, eg:
function increment(&$x)
{
$x++;
}
$i = 0;
echo "\$i before: $i<br>";
increment($i);
echo "\$i after: $i<br>";
This outputs:
$i before: 0
$i after: 1
Here
increment()
takes a reference to $i
as its $x
argument value, so when $x
is incremented... so is $i
. This is a pretty dumb use case for using references though. They come into their own when dealing with large bits of data: we don't want to be copying huge objects / arrays / strings about the place when passing 'em to functions... usually just a reference is fine. This saves processing overhead and memory. One just needs to be mindful that when a function takes a reference, the value referenced could be changed by the function. So one needs to tread with caution.I think this would be a good feature for CFML, and I have added an enhancement request which is being considered for uptake: 3638235. I had not raised it for Railo, but I have now: RAILO-3209.
--
Adam