Domanda

I'm working with an existing code base that has been used to deploy multiple web sites. Some of the sites customize a few of the classes. I've built an autoload function that at least finds the classes. Unfortunately, some of the classes have a different number of parameters in the constructor. It isn't practical at this point to go "correct" all of these classes so that they have the same constructor signature.

In addition to the autoloader (which is working), I am trying to build a factory that will instantiate the basic classes. The factory should handle a request for the "cart" class and pass back an instance of whatever cart class is used on that particular site. The varying number of parameters in the constructor are muddying up the code. I'm looking for a way to clean up the final part (see comments in the following code).

/**
 * find and instantiate an object of the type $classtype.
 * example usage: factory::instance('cart');
 *
 * @param string $classtype  the class to instantiate
 * @param array $options     optional array of parameters for the constructor
 * @return object
 * @throws Exception
 */
static public function instance($classtype, $options = array()) {

    // so you request an instance of 'cart'
    // if CART_CLASS is defined, we set the class name to the value of that constant
    // if CART_CLASS is not defined, we set the class name to basic_cart
    $define = strtoupper("{$classtype}_CLASS");
    $class = defined($define) ? constant($define) : strtolower("basic_$classtype");

    $reflection = new ReflectionClass($class); // autoload function kicks in here to find the class

    // get the parameter list for the constructor
    $parameters = $reflection->getConstructor()->getParameters();

    $p = array();
    foreach($parameters as $parameter) {
        if(array_key_exists($parameter->name, $options)) {
            // making sure the order is correct by creating a new array
            $p[] = $options[$parameter->name];
        } else {
            if($parameter->isOptional()) {
                break; // todo: get the default value and pass that on instantiation
            } else {
                throw new Exception("required parameter '{$parameter->name}' was not provided in the options array when loading $class");
            }
        }
    }

    // todo: there must be a better way to pass a variable number of parameters
    if(count($p) == 1) {
        return new $class($p[0]);
    } else if(count($p) == 2) {
        return new $class($p[0], $p[1]);
    } else if(count($p) == 3) {
        return new $class($p[0], $p[1], $p[2]);
    } else if(count($p) == 4) {
        return new $class($p[0], $p[1], $p[2], $p[3]);
    } else {
        return new $class();
    }

}

And an example of the "request" for an instance follows.

$c = factory::instance('cart');
$s = factory::instance('ship_controller', array('cart' => $c));

Comments on all parts of the code above are fine but I'm really looking for a better solution to that final part. Sooner or later, I will run into a class with five parameters and "return new $class($p[0], $p[1], $p[2], $p[3]);" is already irritating me.

È stato utile?

Soluzione

Simple as that:

return $reflection->newInstanceArgs($p);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top