5
\$\begingroup\$

I am working on a base framework for my various PHP related projects. I have grown since beginning all of this, but the one area I am still having difficulty/concerns with involves the dependencies of my various classes. The real dependencies involve only the 'Core' Helper classes for the site including the following classes:

  • Database
  • Session Handler (sessions saved via DB)
  • System Log (logs user actions, invalid permission attempts, etc.)
  • Form (form builder/CSRF handler)
  • Input Sanitizer
  • Load Class

DB and the Session Handler should have only 1 instance, unless another db is being used (has ability to set new cred's to access another db). The rest can be instantiated everywhere. However I am not sure which route i should proceed with.

Right now I am using a basic registry:

class Registry {   
private static $container = [];


/**
 *  Set Var Within Registry Container
 *  
 *  @param string $name             Name Used to Set Registry Var
 *  @param mixed  $value            Value to Assign to Registry Var    
 */
public function set($var, $value) {
    self::$container[$var] = $value;
}


/**
 *  Get Var From Registry Container
 * 
 *  @param  string $name            Name of Registry Var Requesting
 *  @return mixed
 */
public function get($var) {
    if (isset(self::$container[$var])) {    
        return self::$container[$var];
    }
    return '';
}


/**
 *  Erase Var Within Registry Container
 * 
 *  @param  string $name            Name of Registry Var Erasing
 */
public function erase($var) {
    if (isset(self::$container[$var])) {    
        unset(self::$container[$var]);
    }  
}


private function __clone()      {}
private function __wakeup()     {}
}   

I have a base 'helper' class that child classes extend:

class Base {

/**
 *  Request Dependencies From Registry       
 *  
 *  @param array $D             Constructor Passed Dependencies From Child Classes
 */
public function __construct($dependency = []) {
    $this->registry = new \Core\Registry;              

    if ($dependency) {
        foreach ($dependency as $d) {
            $this->$d = $this->registry->get($d);
        }
    }
}
}

And from a class needing dependencies I just do the following:

class Example {
    private $dependencies = [
        'db', 'session', 'log', 'load'
    ];

    public function __construct() {
        parent::__construct($this->dependencies);
    }
}

From the 'bootstrap' file within my app i instantiate and set the core classes. After that nothing else is set, only retrieved. Really this is all the registry is used for. It is just used to pass these 'core' classes that are needed throughout the app. So i am asking if this is still a horrible anti-pattern to use and what the alternative should be. I am in the process of looking into dependency injection, I haven't begun to read yet, I just began looking into this last night. Before i drive myself crazy i decided to look for outside help.

I've just found this tutorial and it is similar to what I am trying to achieve. Is there any downside or issues with using this?

\$\endgroup\$
1
\$\begingroup\$

The pattern is pretty alright. I do have a few questions/comments on it.

First currently it seems that the way you handle missing dependency is to just return an empty string, this will cause you tons of problems down the line, ideally you would like to throw some sort of RuntimeException that the dependency is not currently instantiated. Or, an even better approach would be to lazy instantiate dependencies as you need them, that way you don't have to register all your classes in the "bootstrap" but you will only wire the dependencies you need per request.

In your case your dependency resolve method looks like:

public function get($var) {
    if (isset(self::$container[$var])) {    
        return self::$container[$var];
    }
    return '';
}

which assumes you are wiring all dependencies in every request, and then only injecting the needed ones, a more optimal solution would be to only lazy load the ones you actually need per request.

Remember that PHP doesn't have true singletons that span across requests, everything is ran in a request scope.

A better alternative would be something like:

public function get($var) {
    if (isset(self::$container[$var])) {    
        return self::$container[$var];
    }else{
        return tryToInitDependency($var); //throws RuntimeException if you cant instantiate it
    }
    return null;
}

My suggestion disregards the fact that dependencies might depend on other dependencies and the problem with circular dependencies.

A part from that the core Registry you have is pretty good.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ I was thinking of auto-instantiating dependencies if they were absent, but the namespaces would cause an issue, or i would have to serve each dependency within an assoc array containing their respective namespace, and if the namespace changed for some reason down the line i would have to revisit each page and alter when i can just pass the core dependencies within the bootstrap file and request the name down the line. Session/Log are the only core dependency that require another ( DB, Input ) Remaining core classes have 0 other dependencies, they are just loaded in nearly every class. \$\endgroup\$ – ICJ Feb 21 '15 at 4:30
  • \$\begingroup\$ I actually assumed you will have sort of a global array of your dependencies so for instance you have a big static array like: $deps = array('db'=> array(impl => '\Core\Db', params => array()); Then call new {impl}(params) when you wire the dep. AM i wrong in this assumption ? \$\endgroup\$ – Feras Feb 21 '15 at 4:55
  • \$\begingroup\$ Technically you shouldnt have any namespace references in your files, you only have to call $this->registry->get('db')->dbOperation()...There is not hardcoded \Core\Db in your files which is the whole point of using DI. Later if you change the implementaion, you only go to your static settings array and change the class and params, everything else stays the same ;) \$\endgroup\$ – Feras Feb 21 '15 at 5:03
  • \$\begingroup\$ I see it now. Ok, now with this will i end up suffering the fate of wishing i had converted to dependency injection? Or Is this subject to a horrible failure in the future when i am attempting to expand upon the project? \$\endgroup\$ – ICJ Feb 21 '15 at 5:11
  • \$\begingroup\$ It really depends on your needs. Generally if you start without DI, adding it later on is next to impossible. I personally find it useful, some people are against it. With the code you already have you are like 80% of the way there anyways. I think this is a simple but solid DI library you can have a look as an inspiration or use it directly: pimple.sensiolabs.org \$\endgroup\$ – Feras Feb 21 '15 at 6:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.