Dynamically call a method within a class

0

is it possible to call methods dynamically in PHP?

Within a class you want to have different methods with a pattern

class MyClass 
{

    private $tables;

    function __construct(){
        $this->tables = array('a','b','n');
    }

    public function runMigrations() {
        foreach ($this->tables as $table) {
            // Excecute method migration_$table)
            call_user_func('migration_'.$table.'()');  // Try but not work  
            // call_user_func('this->migration_'.$table.'()');;  // Try but not work  
            // call_user_func('$this->migration_'.$table.'()');
        }
    }

    private function migration_a() {
        // Some code
    }

    ..

    private function migration_n() {
        // Some code
    }

}



[ErrorException]                                                                                                                
  call_user_func() expects parameter 1 to be a valid callback, function 'migration_a()' not found or invalid function name  
    
asked by abkrim 09.07.2016 в 11:49
source

3 answers

0

Even if the question is asked to make the code with call_user_func , the truth is that it is not necessary, nor do I think it is a good practice.

The solution is simpler:

$this->{'migration_'.$table}();

With call_user_func it would be:

call_user_func([$this, 'migration_'.$table]);
    
answered by 09.07.2016 / 12:47
source
0

You must call the function in the following way:

call_user_func(array($this, 'metodo'));

With the 0 position of the array you indicate that it is the current object

Greetings

    
answered by 09.07.2016 в 12:36
0

Here the problem that occurs when calling the functions in a non-static way, is that at some point it could happen that a method was called that does not exist .

However, the first error is to create an array in this way:

$this->tables = ('a','b','n');

Ideally, do it like this:

$this->tables = array('a','b','n', 'z'); // <------- mejor así !
$this->tables = ['a','b','n', 'z'];       // <------- o así !

The quick way:

class MyClass1
{
    private $tables;

    function __construct()
    {
        $this->tables = array('a', 'b', 'n', 'z');
    }

    /**
     * Lo rápido
     * Pero si no existe el método lanza un error:
     * Fatal error: Call to undefined method MyClass::migration_z() in //...
     */
    public function runMigrations1()
    {
        foreach($this->tables as $table)
        {
            $method  = 'migration_' . $table;
            $array[] = $this->$method();
        }

        return $array;
    }

    private function migration_a()
    {
        return 'a';
    }

    private function migration_b()
    {
        return 'b';
    }

    private function migration_n()
    {
        return 'n';
    }
}

The problem is that if the method does not exist, it throws an error. Which we could control by:

class MyClass2
{
    /**
     * La segunda opción para evitar errores
     * Comprobar que el método lamado existe mediante getMethod()
     */
    public function runMigrations2()
    {
        foreach($this->tables as $table)
        {
            $method  = 'migration_' . $table;
            $array[] = $this->getMethod($method);
        }

        return $array;
    }

    private function getMethod($method)
    {
        if(method_exists($this, $method)):
            return $this->$method();
        else:
            return null;
        endif;
    }
}

If you decide to continue with the practice of using call_user_func a correct way would be like this:

class MyClass3
{
    /**
     * El problema es igual que runMigrations1()
     * Si el método llamado no existe, lanza un error de tipo:
     * Warning: call_user_func() expects parameter 1 to be a valid callback,
     * class 'MyClass' does not have a method 'migration_z' in //...
     */
    public function runMigrations3()
    {
        foreach($this->tables as $table)
        {
            $method  = 'migration_' . $table;
            $array[] = call_user_func(array($this, $method));
        }

        return $array;
    }
}

Errors, whenever they are likely to occur, should be controlled to follow good practices.

class MyClass4
{
    /**
     * Podemos solucionarlo contorlando el error
     */
    public function runMigrations4()
    {
        foreach($this->tables as $table)
        {
            $method  = 'migration_' . $table;
            $array[] = $this->getMethodCallUserFunc($method);
        }

        return $array;
    }

    private function getMethodCallUserFunc($method)
    {
        if(method_exists($this, $method)):
            return call_user_func(array($this, $method));
        else:
            return null;
        endif;
    }
}

If you make a var_dump() on any of the runMigrations you will see the result that each one throws, except for those that throw error because they do not find the z method created in error mode.

$instance = new MyClass1(); // 2,  3, 4
var_dump($instance->runMigrations1()); // 2,  3, 4

It will give a result like this with those that have error control

  array (size=4)
    0 => string 'a' (length=1)
    1 => string 'b' (length=1)
    2 => string 'n' (length=1)
    3 => null
    
answered by 09.07.2016 в 13:06