Dynamic methods with parameters typing in PHP

1.2k views Asked by At

I have the PHP legacy code below that intends to return the name of a method dynamically.

public function getMethod($fieldName){
    //do stuff
    return $methodName;
}

The returned method names are something like:

setClient

setName

setAge

All right here. The problem is when I use these methods. I have a class named Model, that has the folowing method:

class Model {
    public function find () {
        $nameClass = get_called_class();
        $instance  = new $nameClass;

        $result = $this->conn->select();//the select method returns a SQL SELECT from the database

        if (isset($result[0])) {
            foreach ($result[0] as $key => $val) {
                $methodProp = $this->getMethod(key);
                $instance->$methodProp($val);
            }
        } else {
            throw new Exception('You have a error in your class');
        }
    }

}

Here is the var_dump($result) as requested:

array (size=1)
    0 => 
    object(stdClass)[6]
         public 'id' => string '1' (length=1)
         public 'id_erp' => string '0' (length=1)
         public 'name' => string 'Derp' (length=18)
         public 'email' => null
         public 'type_ota' => null
         public 'user' => string 'derp_derp' (length=7)
         public 'password' => string '1234' (length=4)
         public 'url_logo' => null
         public 'status' => string '1' (length=1)
         public 'date' => string '2015-06-08 14:41:50' (length=19)

Then I have some classes that extends this Model:

class Company extends Model {
    public function setClient (Client $cli) {
       //do stuff
    }
    public function setName($name) {
       //do stuff
    }
    public function setAge($age) {
       //do stuff
    }
}
//here I use the class
$myCompany = new Company();
$myCompany->find();

The methods setName() and setAge() works fine, but setClient() returns

Catchable fatal error: Argument 1 passed to setClient() must be an instance of Client, string given

In short, how can I deal with dynamic methods and typing in PHP? Looking for help, I found something about Reflection, but I never used this class before and the examples that I found didn't help me, although I think that it's the right way.

Has anyone had a similar experience?

UPDATE

I tried to put some code in my question to clarify my problem.

2

There are 2 answers

0
Katrina On BEST ANSWER

If I understand your question correctly, you want to check what typehint the method wants. I took this code snippet from the docs.

<?php
//Target our class
$reflector = new ReflectionClass('MyClass');

//Get the parameters of a method
$parameters = $reflector->getMethod('FireCannon')->getParameters();

//Loop through each parameter and get the type
foreach($parameters as $param)
{
     //Before you call getClass() that class must be defined!
     echo $param->getClass()->name;
}

To add the code above to your code. This would check if the parameter in the method is an object, if is an object it will check that the class is the same as the value given. If the parameter is not an object, you can either just call the method, or if you typehint your non-object parameters, you can check that they are equal as well. This code should definitely be modified and cleaned up, but I thought it would be enough to get my point across.

class Model {
    public function find () {
        $nameClass = get_called_class();
        $instance  = new $nameClass;

        $result = $this->conn->select();//the select method returns a SQL SELECT from the database

        if (!isset($result[0])) {
            throw new Exception('You have a error in your class');
        }
        foreach ($result[0] as $key => $val) {
            $methodProp = $this->getMethod($key);

            $reflector = new ReflectionClass($nameClass);
            $reflectorMethod = $reflector->getMethod($methodProp);

            // Make sure method doesn't require more than one param,
            // and it has defined at least one param.
            if ($reflectorMethod->getNumberOfRequiredParameters() > 1 ||
                $reflectorMethod->getNumberOfParameters() < 1
            ) {
                // Throw error
            }

            //Get the parameters of a method
            $parameters = $reflectorMethod->getParameters();

           if (is_object($parameters[0])) {
               if (!is_object($val) {
                   // Throw error
               }
               //Before you call get_class() that class must be defined!
               //Use either class_exists($nameClass); or 
               //in_array($nameClass, get_declared_classes()); to check.
               if ($val->getClass()->name !== get_class($parameters[0])) {
                   // Throw error
               }
           } else if (gettype($parameters[0]) !== gettype($val)) {
               // Throw error
           }
           $instance->$methodProp($val);
        }
    }
}
3
Machavity On

Your problem is here

public function setClient (Client $cli) {
   //do stuff
}

See how you have a class name next to the argument? That's called type hinting. It means that argument MUST be an instance of the class you specified or you'll get the error you posted. Somewhere in your code you're calling

$method($param);

And $param is not an instance of Client. So that is what you have to fix. Whatever calls setClient has to have an instance of Client.

$param = new Client();
$method($param);