2

I have a base class which uses php magic methods __get and __set to be able to modify private attributes in the extended class. Then I built setter getter functions for the relevant private attributes (similar to what is found here http://www.beaconfire-red.com/epic-stuff/better-getters-and-setters-php )

So my child class will look as follows:

class User extends BaseObject {
    public $id = -1;
    private $_status = "";


    function __construct($members = array()) {
        parent::__construct($members);

    }

    //Setter/Getter
    public function status($value = null) {
       if($value) {
           $this->_status = $value;

       } else {
           return $this->_status;
      }
}

Now when I serialize this object which is a JsonSerialize method in the base class, the serialization will only pick up public attributes from the child class (ie "Id") but it won't pick up the private attributes (ie "_status") This is the serialization function:

 public function jsonSerialize() {
    $json = array();
    foreach($this as $key => $value) {

            $json[$key] = $value;

    }
    return $json;
}

Is there any way the above method in the base class can identify all Getters in the child class so that they can be included in the serialization? In other words I want the serialization to include both "id" and "status"

I realize I could get all methods on the class and use some kind of naming convention to identify the getter/setter but i specifically need to keep the getter/setter name the same as the attribute name, ie _status MUST have a getter setter called status() so is there any other way to identify these specific functions?

Gotts
  • 2,274
  • 3
  • 23
  • 32
  • Read this [post](https://stackoverflow.com/questions/7005860/php-json-encode-class-private-members) on stackoverflow. – vodasan Feb 06 '18 at 14:53
  • FWIW, if that's your actual setter, you should not use that. It doesn't do anything. Just make the property `public` and assign to it. – deceze Feb 06 '18 at 15:20
  • Thanks. Its not my actual setter. my actual setter is setting some other attributes. – Gotts Feb 06 '18 at 18:13

3 Answers3

1

Maybe a bit different approach:

 public function toArray()
    {
        $descriptor = new \ReflectionClass(get_class($this));
        /** @var \ReflectionMethod[] $methods */
        $methods = $descriptor->getMethods(\ReflectionMethod::IS_PUBLIC);
        $array   = [];
        foreach ($methods as $method) {
            if (substr_compare('get', $method->getName(), 0, 3) === 0) {
                $property         = lcfirst(substr($method->getName(), 3));
                $value            = $method->invoke($this);
                $array[$property] = $value;
            }
        }

        return $array;
    }

Instead of returning array, you can also serialize it. This approach uses public getter methods on child classes.

0

Use Reflection and ReflectionProperty::setAccessible:

    public function jsonSerialize()
    {
        $json = array();
        $class = new ReflectionClass($this);
        foreach ($class->getProperties() as $key => $value) {
            $value->setAccessible(true);
            $json[$value->getName()] = $value->getValue($this);

        }
        return $json;
    }

This is for to answer to explicit question. But I'm not sure that you should use this design in order to solve your problem.

Luca Rainone
  • 16,138
  • 2
  • 38
  • 52
  • Do you have a better solution then? Surely this has been done before...serializing an object which has private attributes and getter/setter functions? Whats the best approach? – Gotts Feb 06 '18 at 18:14
  • What's your problem? :) if you want an object represantation, i would build it manually with all getter that I need. If you can't modify the base class, then the Reflection is the way – Luca Rainone Feb 06 '18 at 18:33
  • I can modify the base class and since i am building multiple child classes which all need getter/setter serialization i want to put it into the base class. I think i prefer the getter/setter registration (that i mentioned above) over reflection – Gotts Feb 07 '18 at 11:29
0

I am going with a different approach...I think i prefer it over reflection. But am interested to hear if others agree.

I am going to register each Getter/Setter in the child class based on when its accessed, ie I will store the getter/setter name in an array. At serialization time I will not only interate over all the public attributes but also over the registered getter/setters.

Here is when I register (store the getter/setter name) in my base class, storing it in an array called _getters.

function __set($name,$value){
    if(method_exists($this, $name)){
        $this->$name($value);
        //save in getters list for serialization
        if(!in_array($name,$this->_getters))
            array_push($this->_getters,$name);
    }
    else{
        // Getter/Setter not defined so set as property of object
        $this->$name = $value;
    }
}

And now in my serialization I also retrieve any registered getters and serialize them. its working well!

    public function jsonSerialize() {
    $json = array();
    //do public properties

    foreach($this as $key => $value) {
            $json[$key] = $value;

    }

    //do any getter setters
    foreach($this->_getters as $key => $getter) {
        $value = $this->$getter;
        $json[$getter] = $value;

    }
    return $json;
}

Any issues with this approach?

Gotts
  • 2,274
  • 3
  • 23
  • 32