echochamber Posted September 1, 2013 Share Posted September 1, 2013 Quick Forward: I'm writing this with the intention of getting a better understanding of dependency injection and IoC containers, but also so that afterwards I can correct the mistakes in it and use it to help teach a few friends of mine about them as well. As of now I've tried reading through the documentation for various frameworks(laravel, fuel, codeigniter, symphony) and I found that there were too many different aspects of the frameworks that I needed to feel comfortable using it that I decided to try to just learn each of the major pieces individually on my own before trying to use them in the frameworks themselves. I've spent hours googling various meanings, looking over stackoverflow responses, and reading various articles trying to understand what an IoC and how to use it to correctly manage dependencies, and I believe I understand what it is in concept, but I am still grey on how to implement it correctly. I think the best way for anybody reading this to help me is to give what my current understanding of IoC containers and dependency injection is, and then let people who have a better understanding than myself point out where my understanding falls short. My understanding: A dependency is when an instance of ClassA requires an instance of ClassB to instantiate a new instance of ClassA. A dependency injection is when ClassA is passed an instance of ClassB, either through a parameter in ClassA's constructor or through a set~DependencyNameHere~(~DependencyNameHere~ $param) function. (This is one of the areas I'm not completely certain on). An IoC container is a singleton Class(can only have 1 instance instantiated at any given time) where the specific way of instantiating objects of those class for this project can be registered. I included some code examples of what I'm trying to describe at the bottom. So at this point is where I start trying use the IoC container for more complicated scenarios. As of now it seems in order to use the IoC container, I am limited to a has-a relationship for pretty much any class I want to create that has dependencies it wants to define in the IoC container. What if I want to create a class that inherits a class, but only if the parent class has been created in a specific way it was registered in the IoC container. So for example: I want to create a child class of mysqli, but I want to register this class in the IoC container to only instantiate with the parent class constructed in a way I've previously registered in the IoC container. I cannot think of a way to do this without duplicating code (and since this is a learning project I'm trying to keep it as 'pure' as possible). There are examples of what I'm trying to describe at the bottom. So here are some of my questions: Is what I'm trying to do above possible without breaking some principle of OOP? I know in c++ I could use dynamic memory and a copy constructor to accomplish it, but I haven't been able to find that sort of functionality in php. (I will admit that I have very little experience using any of the other magic methods besides __construct, but from reading __clone if I understood correctly, I couldn't use in the constructor it to make the child class being instantiated a clone of an instance of the parent class). Where should all my dependency class definitions go in relation to the IoC? (Should my IoC.php just have a bunch of require_once('dependencyClassDefinition.php') at the top? My gut reaction is that there is a better way, but I haven't come up with one yet) What file should I be registering my objects in? Currently I'm doing all the calls to IoC::register() in the IoC.php file after the class definition. Do I need to register a dependency in the IoC before I register a class that needs that dependency? Since I'm not invoking the anonymous function until I actually instantiate an object registered in the IoC, I'm guessing not, but its still a concern. Is there anything else I'm overlooking that I should be doing or using? I'm trying to take it one step at a time, but I also don't want to know that my code will be reusable and, most importantly, that somebody who knows nothing about my project can read it and understand it. I know this is extremely long, and just wanted to give a thanks in advance to anybody who took the time to read it, and even more so to anybody shares their knowledge. //The IoC Class, Copied From Here: http://net.tutsplus.com/tutorials/php/dependency-injection-huh/ class IoC { /** * @var PDO The connection to the database */ protected static $registry = array(); /** * Add a new resolver to the registry array. * @param string $name The id * @param object $resolve Closure that creates instance * @return void */ public static function register($name, Closure $resolve) { static::$registry[$name] = $resolve; } /** * Create the instance * @param string $name The id * @return mixed */ public static function resolve($name) { if ( static::registered($name) ) { $name = static::$registry[$name]; return $name(); } else { throw new Exception('Nothing registered with that name.'); } } /** * Determine whether the id is registered * @param string $name The id * @return bool Whether to id exists or not */ public static function registered($name) { return array_key_exists($name, static::$registry); } } <? //registering mysqli with the credentials specific to this project in the IoC IoC::register('mysqli', function(){ $host = 'localhost'; $username = 'ProjectName_User'; $password = 'somePassword'; $database = 'Project_Database'; return new mysqli($host, $username, $password, $database); }); <? class mysqliWrapper extends mysqli { function __construct() { /* This won't work since the parent constructor is generic, and I want it constructed with the specific parameters defined by the IoC */ parent::__construct(); /* This won't work you're prohibited from setting the $this variable, */ $this = IoC::resolve('mysqli'); } function __construct($mysqli) { $this->dependency1 = $mysqli->dependency1; /* This requires me to know all the dependencies for my parent class */ } } IoC::register('mysqliWrapper', function(){ /* Doing it this way requires me to repeat code, and know all the dependencies of the parent class, which I'm trying to avoid. */ $mysqliWrapper = new mysqliWrapper(); $mysqliWrapper->dependency = '...'; $mysqli = IoC::resolve('mysqli'); }); Quote Link to comment Share on other sites More sharing options...
ignace Posted September 1, 2013 Share Posted September 1, 2013 (edited) A dependency is when one thing requires another to work. For example: class A implements FrameworkSpecificInterface class A extends FrameworkClassB class A { public function setFoo(FrameworkSpecificInterface $i) } class A { const B = FrameworkFoo::B; }These are hard depencies on an interface, a class, a constant, and a soft depency on the framework it's from (if these were framework things). These are hard depencies since your code won't run if it can't find the class/interface/constant. Dependency injection/IoC does not remove these dependencies, they merely help you to bind these dependencies dynamically. It also allows you to decouple any using code from having to know how to construct the object. That said, IoC is not a new concept, in fact there are many patterns you may already know that provide IoC: a Factory, a Facade, a Builder. These all share the same thing they hide the construction of an object from the code that uses it, though less dynamically then a dependency injection container. http://en.wikipedia.org/wiki/Inversion_of_control Edited September 1, 2013 by ignace Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.