Permalink
Browse files

Added support for DI.

  • Loading branch information...
1 parent cc2ae89 commit 705afd274d13a42847cdac6bbc0a71c60e11ee52 = committed Mar 27, 2012
@@ -0,0 +1,62 @@
+<?php
+/**
+ * RedBean Dependency Injector
+ *
+ * @file RedBean/DependencyInjector.php
+ * @description A default dependency injector that can be subclassed to
+ * suit your needs.
+ *
+ * @author Gabor de Mooij
+ * @license BSD
+ *
+ * copyright (c) G.J.G.T. (Gabor) de Mooij
+ * This source file is subject to the BSD/GPLv2 License that is bundled
+ * with this source code in the file license.txt.
+ *
+ */
+class RedBean_DependencyInjector {
+
+ /**
+ * List of dependencies.
+ * @var array
+ */
+ protected $dependencies = array();
+
+ /**
+ * Adds a dependency to the list.
+ * You can add dependencies using this method. Pass both the key of the
+ * dependency and the dependency itself. The key of the dependency is a
+ * name that should match the setter. For instance if you have a dependency
+ * class called My_Mailer and a setter on the model called setMailSystem
+ * you should pass an instance of My_Mailer with key MailSystem.
+ * The injector will now look for a setter called setMailSystem.
+ *
+ * @param string $dependencyID name of the dependency (should match setter)
+ * @param mixed $dependency the service to be injected
+ */
+ public function addDependency($dependencyID,$dependency) {
+ $this->dependencies[$dependencyID] = $dependency;
+ }
+
+ /**
+ * Returns an instance of the class $modelClassName completely
+ * configured as far as possible with all the available
+ * service objects in the dependency list.
+ *
+ * @param string $modelClassName the name of the class of the model
+ *
+ * @return mixed $object the model/object
+ */
+ public function getInstance($modelClassName) {
+ $object = new $modelClassName;
+ if ($this->dependencies && is_array($this->dependencies)) {
+ foreach($this->dependencies as $key=>$dep) {
+ $depSetter = 'set'.$key;
+ if (method_exists($object,$depSetter)) {
+ $object->$depSetter($dep);
+ }
+ }
+ }
+ return $object;
+ }
+}
View
@@ -21,6 +21,13 @@ class RedBean_ModelHelper implements RedBean_Observer {
* @var RedBean_IModelFormatter
*/
private static $modelFormatter;
+
+
+ /**
+ * Holds a dependency injector
+ * @var type
+ */
+ private static $dependencyInjector;
/**
* Connects OODB to a model if a model exists for that
@@ -61,6 +68,36 @@ public static function getModelName( $model, $bean = null ) {
public static function setModelFormatter( $modelFormatter ) {
self::$modelFormatter = $modelFormatter;
}
+
+
+ /**
+ * Obtains a new instance of $modelClassName, using a dependency injection
+ * container if possible.
+ *
+ * @param string $modelClassName name of the model
+ */
+ public static function factory( $modelClassName ) {
+ if (self::$dependencyInjector) {
+ return self::$dependencyInjector->getInstance($modelClassName);
+ }
+ return new $modelClassName();
+ }
-
-}
+ /**
+ * Sets the dependency injector to be used.
+ *
+ * @param RedBean_DependencyInjector $di injecto to be used
+ */
+ public static function setDepdencyInjector( RedBean_DependencyInjector $di ) {
+ self::$dependencyInjector = $di;
+ }
+
+ /**
+ * Stops the dependency injector from resolving dependencies. Removes the
+ * reference to the dependency injector.
+ */
+ public static function clearDependencyInjector() {
+ self::$dependencyInjector = null;
+ }
+
+}
View
@@ -361,10 +361,9 @@ public function copyMetaFrom(RedBean_OODBBean $bean) {
*/
public function __call($method, $args) {
if (!isset($this->__info["model"])) {
- //@todo eliminate this dependency!
$modelName = RedBean_ModelHelper::getModelName( $this->getMeta("type"), $this );
if (!class_exists($modelName)) return null;
- $obj = new $modelName();
+ $obj = RedBean_ModelHelper::factory($modelName);
$obj->loadBean($this);
$this->__info["model"] = $obj;
}
View
@@ -87,6 +87,8 @@
require($dir.'Cooker.php');
require($dir.'SQLHelper.php');
+require($dir.'DependencyInjector.php');
+
class R extends RedBean_Facade{}
View
@@ -42,5 +42,6 @@
<item type="php">RedBean/Cooker.php</item>
<item type="php">RedBean/Plugin/QueryLogger.php</item>
<item type="php">RedBean/Plugin/TimeLine.php</item>
+ <item type="php">RedBean/DependencyInjector.php</item>
</load>
</root>
@@ -0,0 +1,102 @@
+<?php
+/**
+ * RedUNIT_Blackhole_DIContainer
+ *
+ * @file RedUNIT/Blackhole/DIContainer.php
+ * @description Tests dependency injection architecture.
+ * This class is part of the RedUNIT test suite for RedBeanPHP.
+ * @author Gabor de Mooij
+ * @license BSD
+ *
+ *
+ * (c) G.J.G.T. (Gabor) de Mooij
+ * This source file is subject to the BSD/GPLv2 License that is bundled
+ * with this source code in the file license.txt.
+ */
+class RedUNIT_Blackhole_DIContainer extends RedUNIT_Blackhole {
+ /**
+ * Begin testing.
+ * This method runs the actual test pack.
+ *
+ * @return void
+ */
+ public function run() {
+
+ //base scenario
+ $geek = R::dispense('geek');
+ list($coffee,$cocoa) = $geek->getObjects();
+ asrt(($coffee instanceof Dependency_Coffee),false);
+ asrt(($cocoa instanceof Dependency_Cocoa),false);
+
+ //base scenario with empty container, dont fail
+ $di = new RedBean_DependencyInjector;
+ $geek = R::dispense('geek');
+ list($coffee,$cocoa) = $geek->getObjects();
+ asrt(($coffee instanceof Dependency_Coffee),false);
+ asrt(($cocoa instanceof Dependency_Cocoa),false);
+
+ //succesful scenario, one missing
+ $di = new RedBean_DependencyInjector;
+ $di->addDependency('Coffee',new Dependency_Coffee);
+ RedBean_ModelHelper::setDepdencyInjector( $di );
+ $geek = R::dispense('geek');
+ list($coffee,$cocoa) = $geek->getObjects();
+ asrt(($coffee instanceof Dependency_Coffee),true);
+ asrt(($cocoa instanceof Dependency_Cocoa),false);
+
+ //success scenario
+ $di = new RedBean_DependencyInjector;
+ $di->addDependency('Coffee',new Dependency_Coffee);
+ $di->addDependency('Cocoa',new Dependency_Cocoa);
+ RedBean_ModelHelper::setDepdencyInjector( $di );
+ $geek = R::dispense('geek');
+ list($coffee,$cocoa) = $geek->getObjects();
+ asrt(($coffee instanceof Dependency_Coffee),true);
+ asrt(($cocoa instanceof Dependency_Cocoa),true);
+
+ //dont fail if not exists
+ $di->addDependency('NonExistantObject',new Dependency_Coffee);
+ $geek = R::dispense('geek');
+ $geek = R::dispense('geek');
+ list($coffee,$cocoa) = $geek->getObjects();
+ asrt(($coffee instanceof Dependency_Coffee),true);
+ asrt(($cocoa instanceof Dependency_Cocoa),true);
+
+ //can we go back to base scenario?
+ RedBean_ModelHelper::clearDependencyInjector();
+ $geek = R::dispense('geek');
+ list($coffee,$cocoa) = $geek->getObjects();
+ asrt(($coffee instanceof Dependency_Coffee),false);
+ asrt(($cocoa instanceof Dependency_Cocoa),false);
+
+
+ }
+}
+
+/*
+ * Mock object needed for DI testing
+ */
+class Dependency_Coffee {}
+/*
+ * Mock object needed for DI testing
+ */
+class Dependency_Cocoa {}
+
+/*
+ * Mock object needed for DI testing
+ */
+class Model_Geek extends RedBean_SimpleModel {
+ private $cocoa;
+ private $coffee;
+ public function setCoffee(Dependency_Coffee $coffee) {
+ $this->coffee = $coffee;
+ }
+ public function setCocoa(Dependency_Cocoa $cocoa) {
+ $this->cocoa = $cocoa;
+ }
+ public function getObjects() {
+ return array($this->coffee,$this->cocoa);
+ }
+}
+
+
@@ -57,6 +57,7 @@ function activate_driver($d) {
$allPacks = array(
'Blackhole/Version',
+ 'Blackhole/DIContainer',
'Blackhole/Fusebox',
'Blackhole/Labels',
'Blackhole/Tainted',

0 comments on commit 705afd2

Please sign in to comment.