Problem with the magic method __call

7

I'm trying to use the magic method __call and the call_user_func_array function to retrieve the method name to be able to load a file. I'm doing it this way

//Classe que importa os objetos

$obj = 'NovoObjeto';

$metodo = 'importarXML';

$objCriado = new $obj();

$objCriado->$metodo();

Each object of my extends from a class called FullObject and in this class I have the following declaration of the magic method:

//FullObject
function __call($name,$arguments = null) {
    if(method_exists($this, $name)) { 
        $this->method = $name;
        return call_user_func_array(array($this,$name),$arguments);
    } else {
        throw new Exception('erro');
    }
}

//Classo NovoObjeto
class NovoObjeto extends FullObject {
    function importarXML() { ... }
}

The problem that happens is that if the method does not exist, it throws a Exception , but if the method exists it does not pass the method name to the property $this->method .

I need to save this name, because I'll be using it elsewhere, but I want to avoid having to be forced to always pass it as a parameter.

    
asked by anonymous 15.12.2014 / 11:48

2 answers

9

Your problem, if I understand correctly, boils down to not understanding how exactly the magic method works and therefore has created a logic that would never work as expected.

See what the manual says (free translation):

  

Overload methods are invoked when interacting with properties or methods that have not been declared or are not visible in the current scope

Can you understand why I have highlighted this term?

If the method does not exist , __call () is invoked. But your test throws an Exception if it does not exist, so its logic kills the functionality of this method overload.

If the method exists, __call () will not be invoked and therefore its property will not be populated.

What you could do is name the method somehow slightly different from the real, like an underline, a number, or even by simply changing the visibility of the method (though this is wrong from the point of view of the Principle of Concealment of information), and then consider this modification done in __call (), before invoking.

Another possible solution, a bit more complex, is to use a Design Pattern called Decorator , which, roughly speaking, is an object that surrounds another by changing its functionality in runtime .

    
15.12.2014 / 11:57
2

I decided to do this:

$objCriado->{'_'.$metodo}();

//FullObject
public function __call($name, $arguments = null) 
{
  $realName = substr($name, 1);
  if(method_exists($this, $realName)) { 
    $this->method = $realName;
    return call_user_func_array(array($this, $realName), $arguments); 
  }else{ 
    throw new Exception('erro'); 
  } 
} 

I have now discovered that the magic method __call is only called when there is no method found, so I forced the past methods to not call it and could do what I needed.

I do not know if it is the best solution or not, but it was the one I found right now so I can continue.

    
15.12.2014 / 12:26