5
<?php 

abstract class AbstractClass
{
    public function __get($theName)
    {
        return (isset($this->$theName)) ? $this->$theName : NULL;
    }

    public function __set($theName, $theValue)
    {
        if (false === property_exists(get_class(), $theName)) {
            throw new Exception(get_class()." does not have '".$theName."' property.");
        } else {
            $this->$theName = $theValue;
        }
    }
}

class ConcreteClass extends AbstractClass
{
        private $x;
        private $y;

        public function __construct($theX, $theY)
        {
            $this->x = $theX;
            $this->y = $theY;
        }
}

$concreteClass = new ConcreteClass(10, 20);

var_dump( $concreteClass->x );

Is there some way to amke this work or will I have to add those magic methods to the extended class?

hakre
  • 193,403
  • 52
  • 435
  • 836
Richard Knop
  • 81,041
  • 149
  • 392
  • 552

3 Answers3

8

This would work:

public function __get($theName)
{
    if(property_exists($this, $theName)) {
        $reflection = new ReflectionProperty($this, $theName);
        $reflection->setAccessible($theName);
        return $reflection->getValue($this);
    }
}

IMO, you shouldn't use __get and __set as a replacement for getters and setters. Since they are triggered when trying to access a non-accessible property, they are much more related to error-handling. And they are also much slower than a regular getter or setter.

Gordon
  • 312,688
  • 75
  • 539
  • 559
  • Thanks. I wanted to make my life easier nad have one abstract class for all my classes with defined getters and setters. I have too many get and set methods already. – Richard Knop Mar 24 '11 at 15:35
5

Your problem is that the $x and $y members set in the ConcreteClass are private. Since the __get() method is defined in the parent class, it does not have the access to the child class's private members (since private members are accessible only by the class itself, not by any child or parent class). For them to be visible to the parent class, they have to be either protected or public. For your case, you'd have to use protected to maintain the magical function outside the class.

This, however, is a very odd practice at best. If the variables can be accessed via magic methods, there is no much point in making them private in the first place.

Riimu
  • 1,427
  • 8
  • 12
  • This was the problem for me. Makes a lot of sense setting to protected. This is not an odd practice however ... if you write your magic functions properly, then it either acts as a simple accessor function (getting/setting the value) ... but it first checks if other "get_PROPERTY" or "set_PROPERTY" functions have been specified. If they have, those are returned. I found this useful because the bulk of my properties can just be get/set normally, but some have an action applied (i.e. a trim() or something) or validation logic. – MikeMurko May 11 '11 at 22:43
1

This is because your x and y belong to ConcreteClass, while parents functions only work for parents properties. For instance if you had:

abstract class AbstractClass
{
    private $x;
//...
}
class ConcreteClass extends AbstractClass
{
        private $y;

        public function __construct($theX, $theY)
    //...

then it would've worked. Parent can't access child's private properties, you must reconsider your object structure if you ran into this.

Update

As Rinuwise said you can make x and y visible by declaring them protected or public, but it's still wrong logic.

Also as an alternative you can copy-paste the __get method from AbstractClass to ConcreteClass, providing that way the parent access to private properties of its descedant; but again "you can do that" doesn't mean "you must do that":).

Slava
  • 2,040
  • 15
  • 15