1

I've got some PHP cruft that I would like to delegate methods. Sort of a poor-man's mixin.

Basically I would like the following:

<?php

class Apprentice
{
    public function magic() {
        echo 'Abracadabra!';
    }
}

class Sourcerer // I work magic with the source
{
    private $apprentice;

    public function __construct(){
        $this->apprentice = new Apprentice();
    }

    public function __get($key) {
        if (method_exists($this->apprentice, $key)) {
            return $this->apprentice->{$key};
        }
        throw Exception("no magic left");
    }
}

$source = new Sourcerer();
$source->magic();
?>

To not throw a Fatal error: Call to undefined method Sourcerer::magic() in .../test__get.php.

4 Answers 4

9
public function __call($name, $args) {
    if (method_exists($this->apprentice, $name)) {
        return $this->apprentice->$name($args);
    }
    throw Exception("no magic left");
}

ps: use __call for methods as __get is only for properties. And yes, it is better to use call_user_func_array, otherwise arguments are supplied as array to the magic function.

return call_user_func_array(array($this->apprentice, $name), $args);
3
  • This++. __call is what you're really looking for. Additionally, you might want to investigate __callStatic if you ever want to use this kind of functionality in a static function in the future.
    – Navarr
    Commented Feb 7, 2012 at 1:35
  • Ohhh... I misread the docs entirely, I thought __call() was for calling an instance as though it were a function, that it was for implementing $s = new S(); $s();. Thanks! Commented Feb 7, 2012 at 3:38
  • @quodlibetor the method __invoke is for calling an instance as though it were a function.
    – Wilt
    Commented Jun 11, 2016 at 7:40
3
  1. To actually call the method on apprentice, you'll have to actually call it like so:

    return $this->apprentice->$key();
    
  2. You're using $source->magic(), which does not invoke the __get method. __get is for variable access like $source->magic, but $source->magic() is a function call. If you want a magic method for function calls, that's __call.

0
2

More like __call instead of __get in your case:

class Sourcerer // I work magic with the source
{
    private $apprentice;

    public function __construct(){
        $this->apprentice = new Apprentice();
    }

    public function __call($name, $arguments) {
        if (method_exists($this->apprentice, $name)) {
            return call_user_func_array(array($this->apprentice, $name), $arguments);
        }
        throw Exception("no magic left");
    }
}
1

Your call would instead be:

$source = new Sourcerer();
$source->apprentice->magic();

Also, I believe the __get() magic method works on properties, not method names.

Finally, in your actual __get() definition, the syntax is wrong: it should be throw new Exception('message');. I'd also move this to an else clause, otherwise it's going to get triggered each call regardless, as it's outside any if/else or other logic.

1
  • you're right about the syntax, and wrong about the necessity of the else (the if has a return.) But the chain of lookups is what I'm trying to avoid. Commented Feb 7, 2012 at 3:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.