Diese Seite ist leider nicht auf Deutsch verfügbar.

Using objects and references in PHP4

by Johannes Tiefenbrunner

This article describes how to use references to avoid object duplication in PHP4.

Abstract

One main difficulty with objects in PHP4 is that there's no such thing as an object reference or object pointer in PHP. Things have changed in PHP5, but until PHP5 is widely used by most providers, we have to deal with PHP4's way of using objects. There are references ($a =& $b) in PHP4 but they are references to other variables, not pointers to objects! What's the difference?

In most object oriented programming languages a common way of using objects is this: You create an object and store a reference (pointer) to this object into a variable. You can then copy this stored reference to other variables and they all will reference (point to) the same object.

In PHP you create an object and usually store the whole object into a variable. You can then store a reference to this variable into other variables, but these other variables then do not refer to the object - they refer to the "mother variable"!

An Example in PHP:

$a = new MyClass(); // the whole newly created object is stored in $a
$b =& $a; // $b references $a, $b will show whatever $a is containing
// these are the same:
$a->someMethod();
$b->someMethod();
// they operate on the SAME object
// but:
$a = 100;
// oops - now $b suddenly also is 100 and the object has gone up in smoke
$b->someMethod(); // won't work anymore

So actually there's no such thing as an "object reference" in PHP - you can't really reference an object! You just can reference a variable and this variable may momentarily contain an object. The main drawback in this is, that all the references are highly dependent from the "mother variable" and this often leads to unexpected side effects.

But when obeying some rules PHPs references can be used like real object references. (In the following we therefore will use the term "object reference")

Some basic rules

If $a contains a reference to an object and you do a "$b = $a", then $b will contain a copy of the referenced object! If you want to "hand over" the reference to another variable (or e.g. put it into an array), always use "=&" instead of "=" - so always write:

  $someOtherVar =& $anObject;

If you assign a newly created object ("new myClass()") to a variable with "=", the variable will contain a copy of that object - thus the object in the variable and the object in the constructor method's "$this" are not the same. Therefore you should always store references to newly created objects with "=&" by writing:

  $newObjectRef =& new myClass();

When defining functions that take objects as arguments, these arguments should always by defined as "pass by reference":

  function takeObjekt(&$anObjectRef) {...}

When calling these functions, you don't have to use the "&" modifier - just pass some variable containing an object or object reference:

  takeObject($someObjectRef);

When defining functions that return objects, you should always return object references - define the function as returning a reference by writing:

  function &somefunction(...) { ... return $someObjectRef; }

You need an "&" before the function name but not in the return statement! But don't forget to use "=&" when calling the function to assign the functions return value to some variable - e.g.:

  $anObject =& somefunction();

Summary

Whenever a variable cointaining an object reference is passed (assigned to another variable, put into an array, passed as an argument to a function or returned by a function), pass it "by reference". You should use:

  • "=&" for assignments
  • "function &somefunction(...) {...}" when defining functions that return objects
  • "&$anObjectRef" for object arguments (parameters) in function definitions

Appendix

Deleting vars that hold references

Whenever you want to "delete" a variable containing an object or object reference, allways use the sequence:

   unset($var)
   $var = false;

Only doing an "unset($var)" would leave $var undefined, making it necessary to do a

   if (isset($var))...

every time you access it later (if you want to create clean code and avoid warnings). Only doing a "$var = false" would not only kill this vars contents but also ALL vars up the "reference chain"! Remember: $var might have been created/filled by doing a

   $var =& $otherVar;

And thus doing a $var = false also will put a "false" into $otherVar!

Global vars and references

Storing references to global variables inside a function is a problem, as a global var inside a function seems to be nothing but a local var containing a reference to the global. Confused? An example:

  function something()
  {
  	 global $someObjectRef;
	 
	 ...
	 $someObjectRef =& new someClass();
  }

If you do this and look at $someObjectRef from outside the function (after calling the "something()" function), you will find it as it was before calling the function (probably empty).

The thing is, by using the "global" keyword you create a local variable pointing to (containing a reference to) a global variable. But as soon as you store another references into this local variable (by doing the "$someObjectRef =& ..."), it's not pointing the the global anymore! You simply "redirect" your local var, not even touching the contents of the global. So, what to do?

To assign object refs to global vars inside a function, don't use the "global" keyword! Instead use the $GLOBALS dict, like this:

  function something()
  {
	 ...
	 $GLOBALS['someObjectRef'] =& new someClass();
  }

That way, after calling something() the global variable someObjectRef will contain a reference to the newly created object.

Object references and arrays

When accessing object references stored in arrays, the same rules as for single object references apply: also always use "=&" for assignments - e.g.:

   $anObjectRef =& $arrayOfObjecRefs[5];

If you copy an array containing object references, the copy will also contain references to the original objects. Thus it's not necessary to use array references to keep the contained object references alive (copying arrays containing object references does not "flatten" the contained references). But of course you can use array references if you want to...

But be carefull when doing loops with arrays that contain object refs. For example the following loop will NOT change the original objects but only copies of them (thanx to Nolan Hartkamp for pointing this out):

foreach ($aObjectRefs as $o) {
	$o->changeSomeValue();
}

The problem is, that the foreach loop accesses the array with:

	$o = $aObjectRefs[...];

and not with:

	$o =& $aObjectRefs[...];

Nolan considered the following loop structure (slightly modified by me):

foreach ( array_keys($aObjectRefs) as $index ) {
	$o =& $aObjectRefs[$index];
	$o->changeSomeValue();
}

Finaly some code examples

$newObject =& new someClass();
$someObject =& $arrayOfObjectRefs[$iIndex];

function &getObjectByID($iID)
{
	global $objectCache;

	if (!isset($objectCache[$iID])) $objectCache[$iID] =& new myClass();
	return $objectCache[$iID];
}
$someObject =& getObjectByID(42);

class someClass
{
	var $myLovelyObject;
	...
	function takeObject(&$anObject)
	{
		$this->myLovelyObject =& $anObject;
	}
	function alterYourObject()
	{
		$this->myLovelyObject->name = "Sally";
	}
}
$objectA = new someClass();
$objectA->takeObject($someOtherObject);
$objectA-> alterYourObject();
echo $someOtherObject->name; // will print "Sally";