Search code examples
phpoopparent-childextend

return type of container from subclass


I have a PHP library which I don't want to edit, and implement to my code by extending/overriding some methods. But I'm stuck with chainability. For example:

class MomentPHP extends Moment {
    public $uniqueSettings;
    public function formatJS(){
        return parent::format($this->uniqueSettings);
    }
}

class Moment {
    public function startOf(){
       //some code
       return $this;
    }
}

I want to do this:

$momentphp = new MomentPHP();
$dateStart = $momentphp->startof('month')->formatJs();

And the way to do this is overriding all the methods in the child class inside MomentPHP to return itself.

Is there any other simple way to do this? like using _call or something?


Edit: Found one way to do this:

  1. Remove the inheritance,
  2. Create a instance variable of parent class,
  3. use __call method to switch between classes.

Like this:

class MomentPHP {
    private $instance = null;
    public $uniqueSettings;

    public function __construct(){
       $this->instance = new Moment();
    }

    public function __call($method,$args){
        if(in_array($method, get_class_methods($this))){
            call_user_func(array($this,$method),$args);
        else
            call_user_func(array($this->instance,$method),$args);
        return $this;
    }

    public function formatJS(){
        return $this->instance->format($this->uniqueSettings);
    }
}

class Moment {
    public function startOf(){
       //some code
       return $this;
    }
}

Is there any better way?


Solution

  • One proper way to do this is:

    class MomentPHP {
        private $instance = null;
        public $uniqueSettings;
    
        public function __construct(){
           $this->instance = new Moment();
           // settings etc.
        }
    
        public function __call($method,$args){
            $result = NULL;
            if(in_array($method, get_class_methods($this))){
                $result = call_user_func(array($this,$method),$args);
            else
                $result = call_user_func(array($this->instance,$method),$args);
            if($result instanceof Moment)
                $this->instance = $result;
            return $this;
        }
    
        public function format(){
            return $this->instance->format($this->uniqueSettings);
        }
    }
    

    Updating the instance from the method result is the key operation, and using $this instead of $this->instance allows you to use the extender class in every call. So you can override the function while using other methods in the parent class with chaining ability.