PHP
downloads | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

Object Iteration> <Object Interfaces
Last updated: Sun, 25 Nov 2007

view this page in

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 not be defined as static. All overloading methods must be defined as public.

Since PHP 5.1.0 it is also possible to overload the isset() and unset() functions via the __isset and __unset methods respectively. Method __isset is called also with empty().

Member overloading

void __set ( string $name , mixed $value )
mixed __get ( string $name )
bool __isset ( string $name )
void __unset ( 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.

Note: The __set() method cannot take arguments by reference.

Example#1 overloading with __get, __set, __isset and __unset example

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

    public function 
__get($nm)
    {
        echo 
"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)
    {
        echo 
"Setting [$nm] to $val\n";

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

    public function 
__isset($nm)
    {
        echo 
"Checking if $nm is set\n";

        return isset(
$this->x[$nm]);
    }

    public function 
__unset($nm)
    {
        echo 
"Unsetting $nm\n";

        unset(
$this->x[$nm]);
    }
}

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

var_dump(isset($foo->a)); //true
unset($foo->a);
var_dump(isset($foo->a)); //false

// this doesn't pass through the __isset() method
// because 'n' is a public property
var_dump(isset($foo->n));

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!

Checking if a is set
bool(true)
Unsetting a
Checking if a is set
bool(false)
bool(true)

object(Setter)#1 (2) {
  ["n"]=>
  int(1)
  ["x":"Setter":private]=>
  array(2) {
    ["b"]=>
    int(2)
    ["c"]=>
    int(3)
  }
}

Method overloading

mixed __call ( string $name , array $arguments )

The magic method __call() allows to capture invocation of non existing methods. That way __call() can be used to implement user defined method handling that depends on the name of the actual method being called. This is for instance useful for proxy implementations. 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#2 overloading with __call example

<?php
class Caller
{
    private 
$x = array(123);

    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.4true);
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)
}


Object Iteration> <Object Interfaces
Last updated: Sun, 25 Nov 2007
 
add a note add a note User Contributed Notes
Overloading
strafvollzugsbeamter at gmx dot de
16-Jul-2008 12:57
The following works on my installation (5.2.6 / Windows):
<?php
class G
{
    private
$_p = array();
   
    public function
__isset($k)
    {
        return isset(
$this->_p[$k]);
    }
       
    public function
__get($k)
    {
       
$v = NULL;
        if (
array_key_exists($k, $this->_p))
        {
           
$v = $this->_p[$k];
        }
        else
        {
           
$v = $this->{$k} = $this;
        }
       
        return
$v;
    }
   
    public function
__set($k, $v)
    {
       
$this->_p[$k] = $v;
       
        return
$this;
    }   
}

$s = new G();
$s->A->B->C = 'FOO';
$s->X->Y->Z = array ('BAR');

if (isset(
$s->A->B->C))
{
    print(
$s->A->B->C);
}
else
{
    print(
'A->B->C is NOT set');
}

if (isset(
$s->X->Y->Z))
{
   
print_r($s->X->Y->Z);
}
else
{
    print(
'X->Y->Z is NOT set');
}

// prints: FOOArray ( [0] => BAR )
?>

... have fun and  ...
daveb at spamless dot davebeta dot com
02-Jul-2008 09:36
In reply to uramihsayibok, gmail, com:

The PHP behaviour is the behaviour I would expect.

You are assigning a value to a property: $obj->a=$b works just the same as $a=$b and I would expect both these expressions to evaluate to $b.

The __set() method is used behind the scenes. As a programmer using the class I might not be aware __set() is used unless I originally wrote the class. The return value of __set() is quite rightly ignored.
uramihsayibok, gmail, com
27-May-2008 02:01
Expanding in part on what Hayley Watson said (5 Feb 2008) regarding $a=$b=$c:

My first impression was that <?php $a=$foo->var=$c; ?> would assign $c to $foo->var (by using __set) and the *return value* of __set would be assigned to $a - like what happens in other languages. Perhaps the default (if __set returns void/null) would be $c.

This is not the case. The code above is equivalent to <?php $foo->var=$c; $a=$c; ?>. Regardless of what __set returns, $a will always have the value of $c.

<?php

class Foo {
    public function
__set($name,$value) {
        static
$var; $var=$value;
       
// return "abc"; // comment, uncomment at will
   
}
}

$f=new Foo();
$g=($f->var="value");
echo
$g;
// $g is always "value", even if the return above is uncommented

?>
Anonymous
30-Apr-2008 01:02
This is a generic implementation to use getter, setter, issetter and unsetter for your own classes.

<?php
abstract class properties
{
  public function
__get( $property )
  {
    if( !
is_callable( array($this,'get_'.(string)$property) ) )
      throw new
BadPropertyException($this, (string)$property);

    return
call_user_func( array($this,'get_'.(string)$property) );
  }

  public function
__set( $property, $value )
  {
    if( !
is_callable( array($this,'set_'.(string)$property) ) )
      throw new
BadPropertyException($this, (string)$property);

   
call_user_func( array($this,'set_'.(string)$property), $value );
  }
 
  public function
__isset( $property )
  {
    if( !
is_callable( array($this,'isset_'.(string)$property) ) )
      throw new
BadPropertyException($this, (string)$property);

    return
call_user_func( array($this,'isset_'.(string)$property) );
  }

  public function
__unset( $property )
  {
    if( !
is_callable( array($this,'unset_'.(string)$property) ) )
      throw new
BadPropertyException($this, (string)$property);

   
call_user_func( array($this,'unset_'.(string)$property) );
  }
}
?>
nospam michael AT netkey DOT at nospam
01-Apr-2008 11:10
you CAN write into ARRAYS by using __set and __get magic functions.

as has been mentioned before $obj->var['key'] = 'test'; does call the __get method of $obj, and there is no way to find out, if the method has been called for setting purposes.

the solution is quite simple: use __get to return the array by reference. then you can write into it:

<?php
class setter{
  private
$_arr = array();

  public function
__set($name, $value){
   
$this->_arr[$name] = $value;
  }

  public function &
__get($name){
    if (isset(
$this->_arr[$name])){
      return
$this->_arr[$name];
    } else return
null;
  }

}

?>
Matt Creenan
29-Feb-2008 08:34
PHP4 supports using __call but with a twist that I did not see mentioned anywhere on this page.

In 4, you must make the __call method signature with 3 parameters, the 3rd of which is the return value and must be declared by-reference.  Instead of using "return $value;" you would assign the 3rd argument to $value.

Example (both implementations below have the same result when run in the respective PHP versions:

<?php

// Will only work in PHP4
class Foo
{
    function
__call($method_name, $parameters, &$return_value)
    {
       
$return_value = "Method $method_name was called with " . count($parameters) . " parameters";
    }
}

// Will only work in PHP5
class Foo
{
    function
__call($method_name, $parameters)
    {
        return
"Method $method_name was called with " . count($parameters) . " parameters";
    }
}

?>
dave at mozaiq dot org
10-Feb-2008 03:58
Several users have mentioned ways to allow setting of array properties via magic methods. In particular, PHP calls the __get() method instead of the __set() method when you try to do: $obj->prop['offset'] = $val.

The suggestions that I've read below all work, except that they do not allow you make properties read-only. After a bit of struggling, I have found a solution. Essentially, if the property is supposed to be a read-only array, create an new ArrayObject() out of it, then clone it and return the clone.

<?php

public function __get($var) {
    if(isset(
$this->read_only_props[$var])) {
       
$ret = null;
        if (
is_array($this->read_only_props[$var]))
            return clone new
ArrayObject($this->read_only_props[$var]);
        else if (
is_object($this->read_only_props[$var]))
            return clone
$this->read_only_props[$var];
        else
            return
$this->read_only_props[$var];
    }
    else if (!isset(
$this->writeable_props[$var]))
       
$this->writeable_props[$var] = NULL;
    return
$this->writeable_props[$var];
}

public function
__set($var, $val) {
    if (isset(
$this->read_only_props[$var]))
        throw new
Exception('tried to set a read only property on the event object');
    return
$this->writeable_props[$var] = $val;
}
?>

Note that __get() does not explicitly return by reference as many examples have suggested. Also, I have not found a way to detect when __get() is being called for setting purposes, thus my code can not throw an exception when necessary in these cases.
Hayley Watson
05-Feb-2008 05:53
Chained assignments are still right-associative and still work as expected. That is to say, $a = $foo->b = $c is equivalent to $a = ($foo->b = $c) and results in $a being set to the value of $c even if $foo->b is a property handled by __get/__set and even if those methods adjust/validate their arguments (and even if they fail).

In other words, "$a = $b = $c;" and "$a = ($b = $c);" are both equivalent to "$a = $c; $b = $c;" and not to the equally plausible "$b = $c; $a = $b;".

Assignment expressions evaluate to the value that gets assigned - the value that appears on the right-hand side - and what happens to whatever is on the left is treated as a side-effect.
So in the expression $a=$b=$c, $a doesn't care about what does or doesn't happen to $b - all it sees is the *value* of $b=$c, which is just the value of $c. The parentheses make this clearer: in $a=($b=$c) we have $b being assigned the value of $c and $a being assigned the value of ($b=$c).

As already hinted, this is nothing new: it's just made more obvious by __get and __set: $a = $foo->b = $c does NOT involve looking up the value of $foo->b (let alone assigning it to $a), and $foo->__get() isn't called.
timshaw at mail dot NOSPAMusa dot com
28-Jan-2008 09:47
The __get overload method will be called on a declared public member of an object if that member has been unset.

<?php
class c {
  public
$p ;
  public function
__get($name) { return "__get of $name" ; }
}

$c = new c ;
echo
$c->p, "\n" ;    // declared public member value is empty
$c->p = 5 ;
echo
$c->p, "\n" ;    // declared public member value is 5
unset($c->p) ;
echo
$c->p, "\n" ;    // after unset, value is "__get of p"
?>
jj dhoT maturana aht gmail dhot com
25-Jan-2008 04:16
There isn't some way to overload a method when it's called as a reflection method:

<?php

class TestClass {
  function
__call($method, $args) {
    echo
"Method {$method} called with args: " . print_r($args, TRUE);
  }
}

$class = new ReflectionClass("TestClass");
$method = $class->getMethod("myMehtod");

//Fatal error:  Uncaught exception 'ReflectionException' with message 'Method myMethod' does not exist'

?>

Juan.
Bjrn Wikkeling
21-Jan-2008 02:25
In response to Anonymous: 09-Jan-2008 10:45.

PHP 5.3 will include the magic method __callStatic to use for static method calls.

see also:
http://bugs.php.net/bug.php?id=26739
v dot umang at gmail dot com
11-Jan-2008 09:20
If you want to be able to overload a variable from within a class and this is your code:
<?php
class myClass
{
    private
$data;
    public function
__set($var, $val)
    {
       
$this->data[$var] = $val;
    }
    public function
__get($var)
    {
       
$this->data[$var] = $val;
    }
}
?>

There is a problem if you want to call these variables from within the class, as you you want to access data['data'] then you can't say $this->data as it will return the array $data. Therefore a simple solution is to name the array $_data. So in your __get and __set you will say $this->_data ... rather than $this->data. I.E:
<?php
class myClass
{
    private
$_data;
    public function
__set($var, $val)
    {
       
$this->_data[$var] = $val;
    }
    public function
__get($var)
    {
       
$this->_data[$var] = $val;
    }
}
?>

Umang
soldair at NOSPAM gmail.com
10-Jan-2008 09:21
the documentation states a falsehood:
 "All overloading methods must be defined as public."

<?php
class test{
   
#################
    #public use methods#
    #################
   
public static function echoData(){
       
$obj = self::getInstance();
        echo
$obj->find('data');
        return
true;
    }
   
######################
    #only use a single instance#
    ######################
   
private static $instance;
    private static function
getInstance(){
        if(!isset(
self::$instance)){
           
self::$instance = new test;
        }
        return
self::$instance;
    }
   
#################
    #private instantiation#
    #################
   
private $data = array('data'=>'i am data');
    private function
__construct(){}
    private function
__call($nm,$args){
        if(isset(
$this->data[$args[0]])){
            return
$this->data[$args[0]];
        }
        return
null;
    }
}

test::echoData();
?>
--------------OUTPUT---------------
i am data
-----------------------------------

this test was run using PHP Version 5.2.4
Anonymous
09-Jan-2008 07:45
it should be noted that __call will trigger only for method calls on an instantiated object, and cannot be used to 'overload' static methods.  for example:

<?php

class TestClass {
  function
__call($method, $args) {
    echo
"Method {$method} called with args: " . print_r($args, TRUE);
  }
}

// this will succeed
$obj = new TestClass();
$obj->method_doesnt_exist();

// this will not
TestClass::method_doesnt_exist();

?>

It would be useful if the PHP devs would include this in a future release, but in the meantime, just be aware of that pitfall.
egingell at sisna dot com
20-Dec-2007 07:54
The PHP devs aren't going to implement true overloading because: PHP is not strictly typed by any stretch of the imagination (0, "0", null, false, and "" are the same, for example) and unlike Java and C++, you can pass as many values as you want to a function. The extras are ignored unless you fetch them using func_get_arg(int) or func_get_args(), which is often how I "overload" a function/method, and fewer than the declared number of arguments will generate an E_WARNING, which can be suppressed by putting '@' before the function call, but the function will still run as if you had passed null where a value was expected.

<?php
class someClass {
    function
whatever() {
       
$args = func_get_args();
   
       
// public boolean whatever(boolean arg1) in Java
       
if (is_bool($args[0])) {
           
// whatever(true);
           
return $args[0];
   
       
// public int whatever(int arg1, boolean arg2) in Java
       
} elseif(is_int($args[0]) && is_bool($args[1])) {
           
// whatever(1, false)
           
return $args[0];
   
        } else {
           
// public void whatever() in Java
           
echo 'Usage: whatever([int], boolean)';
        }
    }
}
?>

// The Java version:
public class someClass {
    public boolean whatever(boolean arg1) {
        return arg1;
    }
   
    public int whatever(int arg1, boolean arg2) {
        return arg1;
    }
   
    public void whatever() {
        System.out.println("Usage: whatever([int], boolean)");
    }
}
matthijs at yourmediafactory dot com
16-Dec-2007 11:09
While PHP does not support true overloading natively, I have to disagree with those that state this can't be achieved trough __call.

Yes, it's not pretty but it is definately possible to overload a member based on the type of its argument. An example:
<?php
class A {
  
  public function
__call ($member, $arguments) {
    if(
is_object($arguments[0]))
     
$member = $member . 'Object';
    if(
is_array($arguments[0]))
     
$member = $member . 'Array';
   
$this -> $member($arguments);
  }
  
  private function
testArray () {
    echo
"Array.";
  }
  
  private function
testObject () {
    echo
"Object.";
  }
}

class
B {
}

$class = new A;
$class -> test(array()); // echo's 'Array.'
$class -> test(new B); // echo's 'Object.'
?>

Of course, the use of this is questionable (I have never needed it myself, but then again, I only have a very minimalistic C++ & JAVA background). However, using this general principle and optionally building forth on other suggestions a 'form' of overloading is definately possible, provided you have some strict naming conventions in your functions.

It would of course become a LOT easier once PHP'd let you declare the same member several times but with different arguments, since if you combine that with the reflection class 'real' overloading comes into the grasp of a good OO programmer. Lets keep our fingers crossed!
anthony dot parsons at manx dot net
09-Nov-2007 02:57
You can't use __set to set arrays, but if you really want to, you can emulate it yourself:
<?php
class test {
    public
$x = array();
    public
$y = array();
    function
__set($var, $value)
    {
        if (
preg_match('/(.*)\[(.*)\]/', $var, $names) ) {
           
$this->y[$names[1]][$names[2]] = $value;
        }
        else {
           
$this->x[$var] = $value;
        }
    }
}

$z = new test;
$z->variable = 'abc';
$z->{'somearray[key]'} = 'def';

var_dump($z->x);
var_dump($z->y);
?>
php_is_painful at world dot real
19-Oct-2007 07:49
This is a misuse of the term overloading. This article should call this technique "interpreter hooks".
egingell at sisna dot com
12-Oct-2007 11:26
@ zachary dot craig at goebelmediagroup dot com

I do something like that, too. My way might be even more clunky.

<?php

function sum() {
   
$args = func_get_args();
    if (!
count($args)) {
        echo
'You have to supply something to the function.';
    } elseif (
count($args) == 1) {
        return
$args[0];
    } elseif (
count($args) == 2) {
        if (
is_numeric($args[0]) && is_numeric($args[1])) {
            return
$args[0] + $args[1];
        } else {
            return
$args[0] . $args[1];
        }
    }
}

?>

I like the "__call" method, though. You can "declare" multiple functions that don't pollute the namespace. Although, it might be better looking to use this instead of a series of if...elseif... statemenets.

<?php
class myClass {
    function
__call($fName, $fArgs) {
        switch(
$fName) {
            case
'sum':
            if (
count ($fArgs) === 1) {
                return
$fArgs[0];
            } else {
               
$retVal = 0;
                foreach(
$fArgs as $arg ) {
                   
$retVal += $arg;
                }
                return
$retVal;
            }
            break;
            case
'other_method':
            ...
            default:
            die (
'<div><b>Fetal Error:</b> unknown method ' . $fName . ' in ' . __CLASS__ . '.</div>');
        }
// end switch
   
}
}

?>

Side note: if you don't force lower/upper case, you will have case sensitive method names. E.g. $myclass->sum() !== $myclass->Sum().
zachary dot craig at goebelmediagroup dot com
08-Oct-2007 10:13
@egingell at sisna dot com -
  Use of __call makes "overloading" possible also, although somewhat clunky...  i.e.
<?php
class overloadExample
{
  function
__call($fName, $fArgs)
  {
    if (
$fName == 'sum')
    {
      if (
count ($fArgs) === 1)
      {
        return
$fArgs[0];
      }
      else
      {
       
$retVal = 0;
        foreach(
$fArgs as $arg )
        {
         
$retVal += $arg;
        }
        return
$retVal;
      }
    }
  }
}

/*
Simple and trivial I realize, but the point is made.  Now an object of class overloadExample can take
*/
echo $obj->sum(4); // returns 4
echo $obj->sum(4, 5); // returns 9
?>
bgoldschmidt at rapidsoft dot de
28-Sep-2007 01:23
"These methods will only be triggered when your object or inherited object doesn't contain the member or method you're trying to access."
is not quite correct:
they get called when the member you trying to access in not visible:

<?php
class test {

  public
$a;
  private
$b;

  function
__set($name, $value) {
    echo(
"__set called to set $name to $value\n");
   
$this->$name = $value;
  }
}

$t = new test;
$t->a = 'a';
$t->b = 'b';

?>

Outputs:
__set called to set b to b

Be aware that set ist not called for public properties
lokrain at gmail dot com
26-Sep-2007 02:05
Let us look at the following example:

class objDriver {
    private $value;

    public function __construct()
    {
        $value = 1;
    }

    public function doSomething($parameterList)
    {
        //We make actions with the value
    }
}

class WantStaticCall {
    private static $objectList;

    private function __construct()

    public static function init()
    {
         self::$objectList = array();
    }

    public static function register($alias)
    {
        self::$objectList[$alias] = new objDriver();
    }

    public static function __call($method, $arguments)
    {
        $alias = $arguments[0];
    array_shift($arguments);
        call_user_method($method, self::$objectList[$alias], $arguments);
    }
}

// The deal here is to use following code:
WantStaticCall::register('logger');
WantStaticCall::doSomething('logger', $argumentList);

// and we will make objDriver to call his doSomething function with arguments
// $argumentList. This is not common pattern but very usefull in some cases.
// The problem here is that __call() cannot be static, Is there a way to work it around
Typer85 at gmail dot com
18-Sep-2007 08:28
Just to clarify something the manual states about method overloading.

"All overloading methods must be defined as public."

As of PHP 5.2.2, this should be considered more of a coding convention rather than a requirement. In PHP 5.2.2, declaring a __get or __set function with a visibility other than public, will be silently ignored by the parser and will not trigger a parse error!

What is more, PHP will completely ignore the visibility modifier either of these functions are declared with and will always treat them as if they were public.

I am not sure if this is a bug or not so to be on the safe side,
stick with always declaring them public.
egingell at sisna dot com
15-Sep-2007 05:12
Small vocabulary note: This is *not* "overloading", this is "overriding".

Overloading: Declaring a function multiple times with a different set of parameters like this:
<?php

function foo($a) {
    return
$a;
}

function
foo($a, $b) {
    return
$a + $b;
}

echo
foo(5); // Prints "5"
echo foo(5, 2); // Prints "7"

?>

Overriding: Replacing the parent class's method(s) with a new method by redeclaring it like this:
<?php

class foo {
    function new(
$args) {
       
// Do something.
   
}
}

class
bar extends foo {
    function new(
$args) {
       
// Do something different.
   
}
}

?>
ac221 at sussex dot ac dot uk
01-Sep-2007 10:07
Just Noting the interesting behavior of __set __get , when modifying objects contained in overloaded properties.

<?php
class foo {
    public
$propObj;
    public function
__construct(){
       
$propObj = new stdClass();
    }
    public function
__get($prop){
        echo(
"I'm Being Got ! \n");
        return
$this->propObj->$prop;
    }
    public function
__set($prop,$val){
        echo(
"I'm Being Set ! \n");
       
$this->propObj->$prop = $val;
    }
}
$test = new foo();
$test->barProp = new stdClass(); // I should invoke set
$test->barProp->barSubProp = 'As Should I';
$test->barProp->barSubProp = 'As Should I';
$test->barProp = new stdClass(); // As should i
?>

Outputs:

I'm Being Set !
I'm Being Got !
I'm Being Got !
I'm Being Set !

Whats happening, is PHP is acquiring a reference to the object, triggering __get; Then applying the changes to the object via the reference.

Which is the correct behaviour; objects being special creatures, with an aversion to being cloned...

Unfortunately this will never invoke the __set handler, even though it is modifying a property within 'foo', which is slightly annoying if you wanted to keep track of changes to an objects overloaded properties.

I guess Journaled Objects will have to wait till PHP 6 :)
stephen dot cuppett at webmastersinc dot net
16-Aug-2007 07:10
Please note, this example will not work on later PHP versions.  You must return from __get() by reference using &__get()
php at sleep is the enemy dot co dot uk
23-Jul-2007 07:23
Just to reinforce and elaborate on what DevilDude at darkmaker dot com said way down there on 22-Sep-2004 07:57.

The recursion detection feature can prove especially perilous when using __set. When PHP comes across a statement that would usually call __set but would lead to recursion, rather than firing off a warning or simply not executing the statement it will act as though there is no __set method defined at all. The default behaviour in this instance is to dynamically add the specified property to the object thus breaking the desired functionality of all further calls to __set or __get for that property.

Example:

<?php

class TestClass{

    public
$values = array();
   
    public function
__get($name){
        return
$this->values[$name];
    }
   
    public function
__set($name, $value){
       
$this->values[$name] = $value;
       
$this->validate($name);
    }

    public function
validate($name){
       
/*
        __get will be called on the following line
        but as soon as we attempt to call __set
        again PHP will refuse and simply add a
        property called $name to $this
        */
       
$this->$name = trim($this->$name);
    }
}

$tc = new TestClass();

$tc->foo = 'bar';
$tc->values['foo'] = 'boing';

echo
'$tc->foo == ' . $tc->foo . '<br>';
echo
'$tc ' . (property_exists($tc, 'foo') ? 'now has' : 'still does not have') . ' a property called "foo"<br>';

/*
OUPUTS:
$tc->foo == bar
$tc now has a property called "foo"
*/

?>
Adeel Khan
10-Jul-2007 01:18
Observe:

<?php
class Foo {
    function
__call($m, $a) {
        die(
$m);
    }
}

$foo = new Foo;
print
$foo->{'wow!'}();

// outputs 'wow!'
?>

This method allows you to call functions with invalid characters.
alexandre at nospam dot gaigalas dot net
07-Jul-2007 10:59
PHP 5.2.1

Its possible to call magic methods with invalid names using variable method/property names:

<?php

class foo
{
    function
__get($n)
    {
       
print_r($n);
    }
    function
__call($m, $a)
    {
       
print_r($m);
    }
}

$test = new foo;
$varname = 'invalid,variable+name';
$test->$varname;
$test->$varname();

?>

I just don't know if it is a bug or a feature :)
BenBE at omorphia dot de
05-May-2007 10:48
While playing a bit with the __call magic method I found you can not emulate implementing methods of an interface as you might think:

<?
class Iteratable implements Iterator {
    public function __call($funcname) {
        if(in_array($funcname, array('current', 'next', /*...*/)) {
            //Redirect the call or perform the actual action
        }
    }
}
?>

Using this code you'll get a "class Iteratable contains abstract methods ..." fatal error message. You'll ALWAYS have to implement those routines by hand.
j dot stubbs at linkthink dot co dot jp
26-Feb-2007 09:53
In reply to james at thunder-removeme-monkey dot net,

Unfortunately it seems that there is no way to have completely transparent array properties with 5.2.x. The code you supplied works until working with built-in functions that perform type-checks:

<?

// Using the same View class
$view = new View();
$view->bar = array();
$view->bar[] = "value";

if (is_array($view->bar))
    print "is array!\n"; // Not printed

// Warning: in_array(): Wrong datatype for second argument in ...
if (in_array("value", $view->bar))
    print "found!\n"; // Not printed

// Successful
if (in_array("value", (array)$view->bar))
    print "found!\n";

?>

It also seems that 5.1.x is no longer maintained as it's not listed on the downloads page.. Quite frustrating. :/
mafu at spammenot-mdev dot dk
24-Feb-2007 02:24
As a reply to james at thunder-removeme-monkey dot net, I found that there is a much simpler way to restore the behavior of __get() to 5.1.x state; just force __get() to return by reference, like this:

<?php
class View {
 
/* Somewhere to store our overloaded properties */
 
private $v = array();

 
/* Store a new property */
 
function __set($varName, $varValue) {
  
$this->v[$varName] = $varValue;
  }

 
/* Retrieve a property */
 
function & __get($varName) {
   if(!isset(
$this->v[$varName])) {
    
$this->v[$varName] = NULL;
   }
   return
$this->v[$varName];
  }
}
?>

The only problem is that the code generates a notice if null is returned in __get(), because null cannot be returned by reference. If somebody finds a solution, feel free to email me. :)

Cheers
james at thunder-removeme-monkey dot net
31-Jan-2007 01:07
Following up on the comment by "jstubbs at work-at dot co dot jp" and after reading "http://weierophinney.net/matthew/archives/ 131-Overloading-arrays-in-PHP-5.2.0.html", the following methods handle property overloading pretty neatly and return variables in read/write mode.

<?php
class View {
 
/* Somewhere to store our overloaded properties */
 
private $v = array();

 
/* Store a new property */
 
function __set($varName, $varValue) {
   
$this->v[$varName] = $varValue;
  }

 
/* Retrieve a property */
 
function __get($varName) {
    if(!isset(
$this->v[$varName])) {
     
$this->v[$varName] = NULL;
    }
    return
is_array($this->v[$varName]) ? new ArrayObject($this->v[$varName]) : $this->v[$varName];
  }
}
?>

This is an amalgm of previous solutions with the key difference being the use of ArrayObject in the return value. This is more flexible than having to extend the whole class from ArrayObject.

Using the above class, we can do ...

<?php
$obj
= new SomeOtherObject();
$view = new View();

$view->list = array();
$view->list[] = "hello";
$view->list[] = "goat";
$view->list[] = $group;
$view->list[] = array("a", "b", "c");
$view->list[3][] = "D";
$view->list[2]->aprop = "howdy";

/*
$view->list now contains:
[0] => "hello"
[1] => "goat"
[2] => SomeOtherObject { aprop => "howdy" }
[3] => array("a", "b", "c", "D")

and
$obj === $view->list[2] // equates to TRUE
*/
?>
mhherrera31 at hotmail dot com
25-Nov-2006 10:11
example for read only properties in class object. Lets you manage read only properties with var names like $ro_var.

The property must be PRIVATE, otherwise the overload method __get doesn't be called.

class Session {
 private $ro_usrName;

 function __construct (){
  $this->ro_usrName = "Marcos";
 }

 function __set($set, $val){
  if(property_exists($this,"ro_".$set))
    echo "The property '$set' is read only";
  else
    if(property_exists($this,$set))
      $this->{$set}=$val;
    else
      echo "Property '$set' doesn't exist";
 }

 function __get{$get}{
  if(property_exists($this,"ro_".$get))
    return $this->{"ro_".$get};
  else
    if(property_exists($this,$get))
     return $this->{$get};
    else
     echo "Property '$get' doesn't exist";
 }
}
MagicalTux at ooKoo dot org
06-Sep-2006 02:35
Since many here probably wanted to do «real» overloading without having to think too much, here's a generic __call() function for those cases.

Little example :
<?php
class OverloadedClass {
        public function
__call($f, $p) {
                if (
method_exists($this, $f.sizeof($p))) return call_user_func_array(array($this, $f.sizeof($p)), $p);
               
// function does not exists~
               
throw new Exception('Tried to call unknown method '.get_class($this).'::'.$f);
        }

        function
Param2($a, $b) {
                echo
"Param2($a,$b)\n";
        }

        function
Param3($a, $b, $c) {
                echo
"Param3($a,$b,$c)\n";
        }
}

$o = new OverloadedClass();
$o->Param(4,5);
$o->Param(4,5,6);
$o->ParamX(4,5,6,7);
?>

Will output :
Param2(4,5)
Param3(4,5,6)

Fatal error: Uncaught exception 'Exception' with message 'Tried to call unknown method OverloadedClass::ParamX' in overload.php:7
Stack trace:
#0 [internal function]: OverloadedClass->__call('ParamX', Array)
#1 overload.php(22): OverloadedClass->ParamX(4, 5, 6, 7)
#2 {main}
  thrown in overload.php on line 7
jstubbs at work-at dot co dot jp
02-Sep-2006 09:12
$myclass->foo['bar'] = 'baz';
 
When overriding __get and __set, the above code can work (as expected) but it depends on your __get implementation rather than your __set. In fact, __set is never called with the above code. It appears that PHP (at least as of 5.1) uses a reference to whatever was returned by __get. To be more verbose, the above code is essentially identical to:
 
$tmp_array = &$myclass->foo;
$tmp_array['bar'] = 'baz';
unset($tmp_array);
 
Therefore, the above won't do anything if your __get implementation resembles this:
 
function __get($name) {
    return array_key_exists($name, $this->values)
        ? $this->values[$name] : null;
 }
 
You will actually need to set the value in __get and return that, as in the following code:
 
function __get($name) {
    if (!array_key_exists($name, $this->values))
        $this->values[$name] = null;
    return $this->values[$name];
 }
mnaul at nonsences dot angelo dot edu
11-Jul-2006 12:58
This is just my contribution. It based off of many diffrent suggestions I've see thought the manual postings.
It should fit into any class and create default get and set methods for all you member variables. Hopfuly its usefull.
<?php
   
public function __call($name,$params)
    {
        if(
preg_match('/(set|get)(_)?/',$name) )
        {
            if(
substr($name,0,3)=="set")
            {
               
$name = preg_replace('/set(_)?/','',$name);
                if(
property_exists(__class__,$name))
                {
                       
$this->{$name}=array_pop($params);
                    return
true;
                }
                else
                {
                   
//call to class error handler
                   
return false;
                }
                return
true;
            }
            elseif(
substr($name,0,3)=="get")
            {
               
$name = preg_replace('/get(_)?/','',$name);
                if(
property_exists(__class__,$name) )
                {
                    return
$this->{$name};
                }
                else
                {
           &nbs