Question

I have some code where a user can set any amount of array elements via a database. I'm duck-typing functions based off of key names, but not all key names will be objects to call, and I can't guarantee the keys can be in a static order. The code is similar to this:

$arr = get_arr_from_db();

foreach($arr as $key => $val){
    if($obj = new $key){
        unset($arr[$key]);

        $obj->give_data($arr);

        break;
    }
}

$obj->quack();

The above code doesn't work because $obj = new $key will die. Is there any way I could keep the above loop from dieing upon failed object creation?

Was it helpful?

Solution

You could use class_exists() before you try to instantiate, also a try/catch block will catch any errors if it could not be instantiated.

foreach($arr as $key => $val){
  if(class_exists($key)){
    try {
        if($obj = new $key){
            unset($arr[$key]);

            if(method_exists($obj, 'give_data'){
                $obj->give_data($arr);
            }

            break;
        }
    } catch(Exception $e) {
        // do something with the exception
    }
  }
}

OTHER TIPS

Serialize

It would be a lot more straightforward to use serialize and unserialize, which will handle the class initialization and type-checking for you.

E.g:

$person = new Person();
$person->firstname = 'Chuck';
$person->lastname = 'Jones';

$blob = serialize($person); // put blob in the database

Serialize / Unserialize Interface

If you want to follow the give_data() approach (so your class properties match column names in the database, for example) you should specify an interface. An interface provides a guarantee that the class you're calling has the unserialize method available and that it behaves the way you expect (the example below uses a factory pattern):

<?php
interface ArraySerializable
{
    public static function createFromArray($array);
}

class Person implements ArraySerializable
{
    public static function createFromArray($array)
    {
        $temp = new self();
        $temp->firstname = $array['first_name'];
        $temp->lastname = $array['last_name'];
        return $temp;
    }
}

Then you'd test for class_implements()

$class_name = 'Person';

if (class_exists($class_name)
    && in_array('ArraySerializable', class_implements($class_name))
){
    $person = $class_name::createFromArray(array(
        'last_name' => 'Jones',
        'first_name' => 'Chuck'
    ));

    var_dump($person);
}

To use this with more classes you just implement the ArraySerializable interface for each one.

If you don't want to roll this from scratch you can use a full-featured ORM like Doctrine to abstract the database away completely.

It is better to use the Reflector class :

foreach($arr as $key => $val){
    $reflection = new ReflectionClass($key);
    if ($reflection->IsInstantiable()) {
        unset($arr[$key]);

        $obj = $reflection->newInstance();
        $obj->give_data($arr);

        break;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top