Learning something new.
By Toby Allen
Tuesday, October 15, 2002
I've been programming for a long time - about 10 years. I know thats not as long as some of you out there, but its a lot longer than most of you out there. We all like to learn new stuff, languages, protocols, methodologies. Sometimes we learn all new stuff, other times its just a new way of doing things. Sometimes we think we know what were doing, just doing it in a new way, then it all goes haywire.
This happened me recently with some PHP stuff I'm doing. I've recently learned PHP and find it absolutely great, bye bye ASP. I've done delphi so I have a fairly clear understanding of objects and object oriented programming and I've been programming for a while so I know the difference between passing a variable by reference and by value. I was doing some stuff with objects and suddenly I hit a bizarre problem. Turns out I was making too many assumptions.
In PHP variables are always passed as a copy unless you specify otherwise (similar to lots of other languages). This is generally easier, faster and less complicated for everyone involved, and rarely causes a problem. If you want a variable by reference for some reason you specify it. However because I am used to using Delphi I expected objects to be essentially passed by reference (see note). This bit of code stumped me.
|
class Order { function basicView($id = 0) { $this->checkid($id); $this->GetProductRow($this->ID); $this->html .= "<Table>"; $productid = $this->itemrow['ProductID']; $funcpointer = array($this, "_arraywalk_doViewTableRow"); array_walk($this->itemrow, $funcpointer); $this->html .= "</Table>"; return $this->html; }
function _arraywalk_doViewTableRow ( $Value, $Key){ global $Ahtml; $arraynumber = is_integer($Key); // only add values when we have the associative key.if (! $arraynumber) {if ( $Key === 'ExtraCode') {} else $this->html .= "<TR><TD ><B>$Key<B></TD><TD >$Value</TD></TR>\n";} } } |
Dont worry about most of the code, what matters is the code that is larger than the rest.
$funcpointer = array($this, "_arraywalk_doViewTableRow");
array_walk($this->itemrow, $funcpointer);
The first line is just to create the function pointer, which you have to do if the function you want to call is a method of a class. So basically, I'm just calling array_walk for an array and want it to use a method in the current class. It didnt work. I was returning the generated html by adding to the $this->html variable of the class, I could print out the variable from inside the function, and see that it was generating the html alright, but when it returned, the html variable mysteriously returned to an empty string.
It was using a copy of $this!
The way I had written the call, it made a copy of the object, did the array walk and then returned, but the copy wasnt kept, and the $html variable that had been modified was the one in the copy, not the one in my object. To fix it you must pass the $this variable by reference.
$funcpointer = array(&$this, "_arraywalk_doViewTableRow");
array_walk($this->itemrow, $funcpointer);
Now it does what its supposed to.
I know that this functionality is exactly as described in the use of by reference variables in the php manual, but it caught me off guard because I'm used to things working differently. If this article helps prevent one person spending 2 hours debugging, all will not have been lost.
Thanks for listening.
Note:
In delphi an object variable is really a typed pointer so if you pass it to a function, you are passing the pointer which can only really ever point to the actual object, even though the pointer is passed by value in the function call the object is points to is not.