search for in the  
<Object InterfacesObject Iteration>
Last updated: Thu, 19 May 2005

Overloading

Both method calls and member accesses can be overloaded via the __call, __get and __set methods. These methods will only be triggered when your object or inherited object doesn't contain the member or method you're trying to access. All overloading methods must be defined as public.

Member overloading

void __set ( string name, mixed value )

mixed __get ( string name )

Class members can be overloaded to run custom code defined in your class by defining these specially named methods. The $name parameter used is the name of the variable that should be set or retrieved. The __set() method's $value parameter specifies the value that the object should set the $name.

Example 19-18. overloading with __get and __set example

<?php
class Setter
{
   public
$n;
   private
$x = array("a" => 1, "b" => 2, "c" => 3);

   public function
__get($nm)
   {
       print
"Getting [$nm]\n";

       if (isset(
$this->x[$nm])) {
          
$r = $this->x[$nm];
           print
"Returning: $r\n";
           return
$r;
       } else {
           echo
"Nothing!\n";
       }
   }

   public function
__set($nm, $val)
   {
       print
"Setting [$nm] to $val\n";

       if (isset(
$this->x[$nm])) {
          
$this->x[$nm] = $val;
           echo
"OK!\n";
       } else {
           echo
"Not OK!\n";
       }
   }
}

$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_dump($foo);
?>

The above example will output:

Setting [a] to 100
OK!
Getting [a]
Returning: 100
Setting [a] to 101
OK!
Getting [z]
Nothing!
Setting [z] to 1
Not OK!
object(Setter)#1 (2) {
   ["n"]=>
   int(1)
   ["x:private"]=>
   array(3) {
       ["a"]=>
       int(101)
       ["b"]=>
       int(2)
       ["c"]=>
       int(3)
   }
}

Method overloading

mixed __call ( string name, array arguments )

Class methods can be overloaded to run custom code defined in your class by defining this specially named method. The $name parameter used is the name as the function name that was requested to be used. The arguments that were passed in the function will be defined as an array in the $arguments parameter. The value returned from the __call() method will be returned to the caller of the method.

Example 19-19. overloading with __call example

<?php
class Caller
{
   private
$x = array(1, 2, 3);

   public function
__call($m, $a)
   {
       print
"Method $m called:\n";
      
var_dump($a);
       return
$this->x;
   }
}

$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
?>

The above example will output:

Method test called:
array(4) {
   [0]=>
   int(1)
   [1]=>
   string(1) "2"
   [2]=>
   float(3.4)
   [3]=>
   bool(true)
}
array(3) {
   [0]=>
   int(1)
   [1]=>
   int(2)
   [2]=>
   int(3)
}


User Contributed Notes
Overloading
06-May-2005 06:50
Please note that PHP5's overloading behaviour is not compatible at all with PHP4's overloading behaviour.
Marius
02-May-2005 05:15
for anyone who's thinking about traversing some variable tree
by using __get() and __set(). i tried to do this and found one
problem: you can handle couple of __get() in a row by returning
an object which can handle consequential __get(), but you can't
handle __get() and __set() that way.
i.e. if you want to:
<?php
  
print($obj->val1->val2->val3); // three __get() calls
?> - this will work,
but if you want to:
<?php
   $obj
->val1->val2 = $val; // one __get() and one __set() call
?> - this will fail with message:
"Fatal error: Cannot access undefined property for object with
 overloaded property access"
however if you don't mix __get() and __set() in one expression,
it will work:
<?php
   $obj
->val1 = $val; // only one __set() call
  
$val2 = $obj->val1->val2; // two __get() calls
  
$val2->val3 = $val; // one __set() call
?>

as you can see you can split __get() and __set() parts of
expression into two expressions to make it work.

by the way, this seems like a bug to me, will have to report it.
ryo at shadowlair dot info
22-Mar-2005 01:22
Keep in mind that when your class has a __call() function, it will be used when PHP calls some other magic functions. This can lead to unexpected errors:

<?php
class TestClass {
   public
$someVar;

   public function
__call($name, $args) {
      
// handle the overloaded functions we know...
       // [...]

       // raise an error if the function is unknown, just like PHP would
      
trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $name), E_USER_ERROR);
   }
}

$obj = new TestClass();
$obj->someVar = 'some value';

echo
$obj; //Fatal error: Call to undefined function: TestClass::__tostring().
$serializedObj = serialize($obj); // Fatal error: Call to undefined function: TestClass::__sleep().
$unserializedObj = unserialize($someSerializedTestClassObject); // Fatal error: Call to undefined function: TestClass::__wakeup().
?>
thisisroot at gmail dot com
18-Feb-2005 10:27
You can't mix offsetSet() of the ArrayAccess interface (http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html) and __get() in the same line.

Below, "FileManagerPrefs" is an object of class UserData which implements ArrayAccess. There's a protected array of UserData objects in the User class, which are returned from __get().
<?php
// This produces an error...
Application::getInstance()->user->FileManagerPrefs[ 'base'] = 'uploads/jack';
?>

Creates this error:
Fatal error: Cannot access undefined property for object with overloaded property access in __FILE__ on line __LINE__

However, __get() and offsetGet() play deceptively well together.

<?php
// This works fine!
echo Application::getInstance()->user->FileManager['base'];
?>

I guess it's a dereferencing issue with __get(). In my case, it makes more sense to have a middle step (user->data['FileManager']['base']), but I wanted to tip off the community before I move on.
mileskeaton at gmail dot com
23-Dec-2004 03:23
<?php

## THE PROBLEM:  Class with lots of attributes. 
## You want to use $o->getVarName() or $o->get_varname() style getters
## Some attributes have custom get functions, but the rest don't

## THE SOLUTION:  __call

class Person
  
{
  
## this top stuff is just an example.  could be anything.
  
private $name;
   private
$age;
   private
$weight;
   function
__construct($name, $age, $weight)
       {
      
$this->name = $name;
      
$this->age = $age;
      
$this->weight = $weight;
       }

  
##    PORTABLE: use this __call function in any class
  
function __call($val, $x)
       {
      
# see if they're calling a getter method - and try to guess the variable requested
      
if(substr($val, 0, 4) == 'get_')
           {
          
$varname = substr($val, 4);
           }
       elseif(
substr($val, 0, 3) == 'get')
           {
          
$varname = substr($val, 3);
           }
       else
           {
           die(
"method $val does not exist\n");
           }
      
# now see if that variable exists:
      
foreach($this as $class_var=>$class_var_value)
           {
           if(
strtolower($class_var) == strtolower($varname))
               {
               return
$class_var_value;
               }
           }
       return
false;
       }

  
# IMPORTANT: you can keep some things private - or treat
   # some vars differently by giving them their own getter method
   # See how this function lies about Person's weight.
  
function getWeight()
       {
       return
intval($this->weight * .8);
       }
   }

$a = new Person('Miles', 35, 200);

# these all work (case-insensitive):
print $a->get_name() . "\n";
print
$a->getName() . "\n";
print
$a->get_Name() . "\n";
print
$a->getname() . "\n";

print
$a->get_age() . "\n";
print
$a->getAge() . "\n";
print
$a->getage() . "\n";
print
$a->get_Age() . "\n";

# defined functions still override the __call
print $a->getWeight() . "\n";

# trying to get something that doesn't exist returns false
print $a->getNothing();

# this still gets error:
print $a->hotdog();

?>
richard dot quadling at bandvulc dot co dot uk
26-Nov-2004 08:54
<?php

abstract class BubbleMethod
  
{
   public
$objOwner;

   function
__call($sMethod, $aParams)
       {
// Has the Owner been assigned?
      
if (isset($this->objOwner))
           {
           return
call_user_func_array(array($this->objOwner, $sMethod), $aParams);
           }
       else
           {
           echo
'Owner for ' . get_class($this) . ' not assigned.';
           }
       }
   }

class
A_WebPageContainer
  
{
   private
$sName;
  
   public function
__construct($sName)
       {
      
$this->sName = $sName;
       }
  
   public function
GetWebPageContainerName()
       {
       return
$this->sName;
       }
   }

class
A_WebFrame extends BubbleMethod
  
{
   private
$sName;
  
   public function
__construct($sName)
       {
      
$this->sName = $sName;
       }
  
   public function
GetWebFrameName()
       {
       return
$this->sName;
       }
   }

class
A_WebDocument extends BubbleMethod
  
{
   private
$sName;
  
   public function
__construct($sName)
       {
      
$this->sName = $sName;
       }
  
   public function
GetWebDocumentName()
       {
       return
$this->sName;
       }
   }

class
A_WebForm extends BubbleMethod
  
{
   private
$sName;
  
   public function
__construct($sName)
       {
      
$this->sName = $sName;
       }
  
   public function
GetWebFormName()
       {
       return
$this->sName;
       }
   }

class
A_WebFormElement extends BubbleMethod
  
{
   private
$sName;
  
   public function
__construct($sName)
       {
      
$this->sName = $sName;
       }
  
   public function
GetWebFormElementName()
       {
       return
$this->sName;
       }
   }

$objWPC = new A_WebPageContainer('The outer web page container.');

$objWF1 = new A_WebFrame('Frame 1');
$objWF1->objOwner = $objWPC;

$objWF2 = new A_WebFrame('Frame 2');
$objWF2->objOwner = $objWPC;

$objWD1 = new A_WebDocument('Doc 1');
$objWD1->objOwner = $objWF1;

$objWD2 = new A_WebDocument('Doc 2');
$objWD2->objOwner = $objWF2;

$objWFrm1 = new A_WebForm('Form 1');
$objWFrm1->objOwner = $objWD1;

$objWFrm2 = new A_WebForm('Form 2');
$objWFrm2->objOwner = $objWD2;

$objWE1 = new A_WebFormElement('Element 1');
$objWE1->objOwner = $objWFrm1;

$objWE2 = new A_WebFormElement('Element 2');
$objWE2->objOwner = $objWFrm1;

$objWE3 = new A_WebFormElement('Element 3');
$objWE3->objOwner = $objWFrm2;

$objWE4 = new A_WebFormElement('Element 4');
$objWE4->objOwner = $objWFrm2;

echo
"The name of the form that '" . $objWE1->GetWebFormElementName() . "' is in is '" . $objWE1->GetWebFormName() . "'<br />";
echo
"The name of the document that '" . $objWE2->GetWebFormElementName() . "' is in is '" . $objWE2->GetWebDocumentName(). "'<br />";
echo
"The name of the frame that '" . $objWE3->GetWebFormElementName() . "' is in is '" . $objWE3->GetWebFrameName(). "'<br />";
echo
"The name of the page container that '" . $objWE4->GetWebFormElementName() . "' is in is '" .$objWE4->GetWebPageContainerName(). "'<br />";
?>

Results in.

The name of the form that 'Element 1' is in is 'Form 1'
The name of the document that 'Element 2' is in is 'Doc 1'
The name of the frame that 'Element 3' is in is 'Frame 2'
The name of the page container that 'Element 4' is in is 'The outer web page container.'

By using the abstract BubbleMethod class as the starting point for further classes that are contained inside others (i.e. elements on a form are contained in forms, which are contained in documents which are contained in frames which are contained in a super wonder global container), you can find properties of owner without knowing their direct name.

Some work needs to be done on what to do if no method exists though.
bigtree at DONTSPAM dot 29a dot nl
17-Nov-2004 04:47
Keep in mind that the __call method will not be called if the method you are calling actually exists, even if it exists in a parent class. Check the following example:

<?php

  
class BaseClass
  
{
       function
realMethod()
       {
           echo
"realMethod has been called\n";
       }
   }
  
   class
ExtendClass extends BaseClass
  
{
       function
__call($m, $a)
       {
           echo
"virtual method " . $m . " has been called\n";
       }
   }
  
  
$tmpObject = new ExtendClass();
  
$tmpObject->virtualMethod();
  
$tmpObject->realMethod();

?>

Will output:

virtual method virtualMethod has been called
realMethod has been called

You might expect that all method calls on ExtendClass will be handled by __call because it has no other methods, but this is not the case, as the example above demonstrates.
xorith at gmail dot com
06-Oct-2004 10:40
A few things I've found about __get()...

First off, if you use $obj->getOne->getAnother, both intended to be resolved by __get, the __get() function only sees the first one at first. You can't access the second one. You can, however, return the pointer to an object that can handle the second one. In short, you can have the same class handle both by returning a new object with the data changed however you see fit.

Secondly, when using arrays like: $obj->getArray["one"], only the array name is passed on to __get. However, when you return the array, PHP treats it just as it should. THat is, you'd have to make an array with the index of "one" in __get in order to see any results. You can also have other indexes in there as well.

Also, for those of you like me, I've already tried to use func_get_args to see if you can get more than just that one.

If you're like me and were hoping you could pass some sort of argument onto __get in order to help gather the correct data, you're out of look. I do recommend using __call though. You could easily rig __call up to react to certain things, like: $account->properties( "type" );, which is my example. I'm using DOM for data storage (for now), and I'm trying to make an interface that'll let me easily switch to something else - MySQL, flat file, anything. This would work great though!

Hope I've been helpful and I hope I didn't restate something already stated.
Dot_Whut?
30-Sep-2004 09:52
Sharp readers (i.e. C++, Java programmers, etc.) may have noticed that the topic of "Overloading" mentioned in the documentation above really isn't about overloading at all...  Or, at least not in the Object Oriented Programming (OOP) sense of the word.

For those who don't already know, "overloading" a method (a.k.a. function) is the ability for functions of the same name to be defined within a Class as long as each of these methods have a different set of parameters (each method would have a different "signature", if you will).  At run-time, the script engine could then select and execute the appropriate function depending on which type or number of arguments were passed to it.  Unfortunately, PHP 5.0.2 doesn't support this sort of overloading.  The "Overloading" mentioned above could be better described as "Dynamic Methods and Properties"...

By using "Dynamic Methods and Properties", we can make our class instances "appear" to have methods and properties that were not originally present in our Class definitions.  Although this is a great new feature (which I plan to use often), through the use of "__get", "__set", and "__call", we can also emulate "real" overloading as shown in a few of the code examples below.
Dot_Whut?
29-Sep-2004 09:14
You can create automatic getter and setter methods for your private and protected variables by creating an AutoGetterSetter Abstract Class and having your Class inherit from it:

// Automatic Getter and Setter Class
//
// Note: when implementing your getter and setter methods, name your
// methods the same as your desired object properties, but prefix the
// function names with 'get_' and 'set_' respectively.

abstract class AutoGetAndSet {
  public function __get ($name) {
   try {
     // Check for getter method (throws an exception if none exists)
     if (!method_exists($this, "get_$name"))
       throw new Exception("$name has no Getter method.");
     eval("\$temp = \$this->get_$name();");
     return $temp;
   }
   catch (Exception $e) {
     throw $e;
   }
  }
 
  public function __set ($name, $value) {
   try {
     // Check for setter method (throws an exception if none exists)
     if (!method_exists($this, "set_$name"))
       throw new Exception("$name has no Setter method (is read-only).");
     eval("\$this->set_$name(\$value);");
   }
   catch (Exception $e) {
     throw $e;
   }
  }
}

class MyClass extends AutoGetAndSet
{
  // private and protected members
  private $mbr_privateVar = "privateVar";
  protected $mbr_protectedVar = "protectedVar";
 
  // 'privateVar' property, setter only (read-only property)
  function get_privateVar () {
   return $this->mbr_privateVar;
  }
 
  // 'protectedVar' property, both getter AND setter
  function get_protectedVar () {
   return $this->mbr_protectedVar;
  }
  function set_protectedVar ($value) {
   $this->mbr_protectedVar = $value;
  }
}

$myClass = new MyClass();

// Show original values, automatically uses 'get_' methods
echo '$myClass->protectedVar == ' . $myClass->protectedVar . '<br />';
echo '$myClass->privateVar == ' . $myClass->privateVar . '<br />';

// Modify 'protectedVar', automatically calls set_protectedVar()
$myClass->protectedVar = "Modded";

// Show modified value, automatically calls get_protectedVar()
echo '$myClass->protectedVar == ' . $myClass->protectedVar . '<br />';

// Attempt to modify 'privateVar', get_privateVar() doesn't exist
$myClass->privateVar = "Modded"; // throws an Exception

// Code never gets here
echo '$myClass->privateVar == ' . $myClass->privateVar . '<br />';
anthony at ectrolinux dot com
25-Sep-2004 09:40
For anyone who is interested in overloading a class method based on the number of arguments, here is a simplified example of how it can be accomplished:

<?php

function Error($message) { trigger_error($message, E_USER_ERROR); exit(1); }

class
Framework
{
  
// Pseudo function overloading
  
public function __call($func_name, $argv)
   {
      
$argc = sizeof($argv);

       switch (
$func_name) {
       case
'is_available':
          
$func_name = ($argc == 2) ? 'is_available_single' : 'is_available_multi';
           break;

       default:
// If no applicable function was found, generate the default PHP error message.
          
Error('Call to undefined function Framework::'. $func_name .'().');
       }

       return
call_user_func_array(array(&$this, $func_name), $argv);
   }

  
// protected array is_available_multi($mod_name, $mod_addon_0 [, $mod_addon_1 [, ...]])
  
protected function is_available_multi()
   {
       if ((
$argc = func_num_args()) < 2) {
          
Error('A minimum of two arguments are required for Framework::is_available().');
       }

      
$available_addons = array();
      
// --snip--

      
return $available_addons;
   }

   protected function
is_available_single($mod_name, $mod_addon)
   {
      
// --snip--

      
return true;
   }
}

$fw = new Framework;

$test_one = $fw->is_available('A_Module_Name', 'An_Addon');
var_dump($test_one);
// Test one produces a boolean value, supposedly representing whether 'An_Addon' is available and can be used.

$test_two = $fw->is_available('A_Module_Name', 'Addon_0', 'Addon_1', 'Addon_2');
var_dump($test_two);
// Test two produces an array, supposedly listing any of the three 'Addon_N' modules that are available and can be used.

// Here are the actual results of the above:
//    bool(true)
//    array(0) {
//    }

?>

---

By adding additional case statements to Framework::__call(), methods can easily be overloaded as needed. It's also possible to add any other overloading criteria you require inside the switch statement, allowing for more intricate overloading functionality.
DevilDude at darkmaker dot com
22-Sep-2004 10:57
Php 5 has a simple recursion system that stops you from using overloading within an overloading function, this means you cannot get an overloaded variable within the __get method, or within any functions/methods called by the _get method, you can however call __get manualy within itself to do the same thing.
31-Aug-2004 02:39
For those using isset():

Currently (php5.0.1) isset will not check the __get method to see if you can retrieve a value from that function.  So if you want to check if an overloaded value is set you'd need to use something like the __isset method below:

<?php

class foo
{
   public
$aryvars = array();

   function
__construct() {}

   function
__get($key)
   {
       return
array_key_exists($key, $this->aryvars) ?
              
$this->aryvars[$key] : null;
   }
  
   function
__isset($key) {
       if (!
$isset = isset($this->$key)) {
          
$isset = array_key_exists($key, $this->aryvars);
       }
       return
$isset;
   }
  
   function
__set($key, $value)
   {
      
$this->aryvars[$key] = $value;
   }
}

echo
'<pre>';
$foo = new foo();
$foo->a = 'test';
echo
'$foo->a == ' . $foo->a . "\n";
echo
'isset($foo->a) == ' . (int) isset($foo->a) . "\n";
echo
'isset($foo->aryvars[\'a\']) == ' . isset($foo->aryvars['a']) . "\n";
echo
'$foo->__isset(\'a\') == ' . $foo->__isset('a') . "\n";
var_dump($foo);

?>

<Object InterfacesObject Iteration>
 Last updated: Thu, 19 May 2005
Copyright © 2001-2005 The PHP Group
All rights reserved.
This unofficial mirror is operated at: The Server Pages
Last updated: Thu May 19 18:35:34 2005 EDT