diff --git a/README.md b/README.md index c96689e..8221bf9 100644 --- a/README.md +++ b/README.md @@ -142,4 +142,56 @@ Inject::bindByArray(["TestInterface" => "Test", "FirstClassInterface" => "FirstClass"]); $class = Inject::instantiation("FirstClassInterface"); - var_dump($class instanceof FirstClass); // must return true \ No newline at end of file + var_dump($class instanceof FirstClass); // must return true + + ### Different classes to one interface via DocComment + firstVariable = $my; + } + + public function getFirst() + { + return $this->firstVariable; + } + } + + use \Sixx\DependencyInjection\Inject; + Inject::bindByArray(["SomeInterface" => ["thisAnnotatnionForFristClass" => "First", "thisAnnotationForSecondClass" => "Second"]]); + + $class = Inject::instantiation("A"); + var_dump($class->secondVariable instanceof Second); // must return true + var_dump($class->getFirst() instanceof First); // must return true \ No newline at end of file diff --git a/VERSION b/VERSION index 8a9ecc2..7bcd0e3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.1 \ No newline at end of file +0.0.2 \ No newline at end of file diff --git a/src/Inject.php b/src/Inject.php index 3ce7147..9ce8e40 100644 --- a/src/Inject.php +++ b/src/Inject.php @@ -9,6 +9,11 @@ class Inject protected static $services = []; protected static $injectAnnotation = '@var'; + /** + * @var ServiceContainer + */ + protected static $serviceContainer; + /** * @param string $className * @param array $parameters @@ -20,8 +25,8 @@ public static function instantiation($className, array $parameters = null) if (! class_exists($className)) { if (interface_exists($className)) { - if (self::injectedParameter(new \ReflectionClass($className))) - return self::instantiation(self::getServiceName($className), $parameters); + if (self::container()->isInjected($className)) + return self::instantiation(self::container()->getServiceName($className), $parameters); throw new InjectException("Inject error: interface " . $className . " exist but not injected yet."); } @@ -54,8 +59,8 @@ public static function method($className, $methodName, array $parameters = null) if (! class_exists($className)) { if (interface_exists($className)) { - if (self::injectedParameter(new \ReflectionClass($className))) - return self::method(self::getServiceName($className), $methodName, $parameters); + if (self::container()->isInjected($className)) + return self::method(self::container()->getServiceName($className), $methodName, $parameters); throw new InjectException("Inject error: interface " . $className . " exist but not injected yet."); } @@ -86,9 +91,9 @@ protected static function getParameters(\ReflectionMethod $method, array $parame foreach ($method->getParameters() as $parameter) { if (isset($parameters[$parameter->getName()])) $arguments[$parameter->getName()] = $parameters[$parameter->getName()]; - elseif (self::injectedParameter($parameter->getClass())) - $arguments[$parameter->getName()] = self::instantiation(self::getServiceName($parameter->getClass()->name)); - elseif (self::instantiatedParameter($parameter->getClass())) + elseif (null != $parameter->getClass() && self::container()->isInjected($parameter->getClass()->name, $method->getDocComment())) + $arguments[$parameter->getName()] = self::instantiation(self::container()->getServiceName($parameter->getClass()->name, $method->getDocComment())); + elseif (self::container()->isInstantiate($parameter->getClass())) $arguments[$parameter->getName()] = self::instantiation($parameter->getClass()->name); elseif (true != $parameter->isOptional()) throw new InjectException("Required parameter [" . $parameter->getName() . "] in " . $method->getDeclaringClass()->name . "::" . $method->getName() . " is not specified."); @@ -100,33 +105,6 @@ protected static function getParameters(\ReflectionMethod $method, array $parame return $arguments; } - /** - * @param \ReflectionClass|null $class - * @return bool - */ - protected static function injectedParameter(\ReflectionClass $class = null) - { - if (null == $class || null == self::getServiceName($class->name)) - return false; - - if (false == (new \ReflectionClass(self::getServiceName($class->name)))->implementsInterface($class->name)) - throw new InjectException("Inject error: class " . self::getServiceName($class->name) . " must implement " . $class->name); - - return true; - } - - /** - * @param \ReflectionClass|null $class - * @return bool - */ - protected static function instantiatedParameter(\ReflectionClass $class = null) - { - if (null == $class || $class->isAbstract() || $class->isInterface()) - return false; - - return true; - } - /** * @param object $class * @param array|null $properties @@ -142,10 +120,9 @@ protected static function fillProperties($class, array $properties = null) $className = self::getVariableTypeName($property->getDocComment(), self::$injectAnnotation); if (class_exists($className) || interface_exists($className)) { - $propertyClass = new \ReflectionClass($className); - if (self::injectedParameter($propertyClass)) - $class->$name = self::instantiation(self::getServiceName($className)); - elseif (self::instantiatedParameter($propertyClass)) + if (self::container()->isInjected($className, $property->getDocComment())) + $class->$name = self::instantiation(self::container()->getServiceName($className, $property->getDocComment())); + elseif (self::container()->isInstantiate(new \ReflectionClass($className))) $class->$name = self::instantiation($className); } } @@ -168,24 +145,14 @@ protected static function getVariableTypeName($string, $injectAnnotation) } /** - * @param string $serviceName - * @param string $annotation - * @return null|string + * @return ServiceContainer */ - protected static function getServiceName($serviceName, $annotation = "") + protected static function container() { - if (isset(self::$services[$serviceName])) { - if (is_string(self::$services[$serviceName])) - return self::$services[$serviceName]; - elseif (is_array(self::$services[$serviceName])) { - foreach (self::$services[$serviceName] as $name => $service) { - if (false !== strpos($annotation, "@" . $name)) - return $service; - } - } - } + if (empty(self::$serviceContainer)) + self::$serviceContainer = new ServiceContainer(); - return null; + return self::$serviceContainer; } /** @@ -193,7 +160,7 @@ protected static function getServiceName($serviceName, $annotation = "") */ public static function flushServices() { - self::$services = []; + self::container()->flushServices(); } /** @@ -203,25 +170,7 @@ public static function flushServices() */ public static function bind($interface, $class) { - if (false == is_string($interface) || false == (is_string($class) || is_array($class))) - return false; - - if (is_string($class)) - self::$services[$interface] = $class; - else { - $classes = []; - foreach ($class as $name => $value) { - if (is_string($name) && is_string($value)) - $classes[$name] = $value; - } - - if (0 == count($classes)) - return false; - - self::$services[$interface] = $classes; - } - - return true; + return self::container()->bind($interface, $class); } /** diff --git a/src/ServiceContainer.php b/src/ServiceContainer.php new file mode 100644 index 0000000..6a059d9 --- /dev/null +++ b/src/ServiceContainer.php @@ -0,0 +1,114 @@ +services[$serviceName])) { + if (is_string($this->services[$serviceName])) + return $this->services[$serviceName]; + elseif (is_array($this->services[$serviceName]) && 0 < count($this->services[$serviceName])) { + reset($this->services[$serviceName]); + + if (false == is_string($annotation) || empty($annotation)) + return current($this->services[$serviceName]); + + foreach ($this->services[$serviceName] as $name => $service) { + if (false !== strpos($annotation, "@" . $name)) + return $service; + + } + + reset($this->services[$serviceName]); + return current($this->services[$serviceName]); + } + } + + return null; + } + + /** + * Flush all services from ServiceContainer + */ + public function flushServices() + { + $this->services = []; + } + + /** + * @param string $interface + * @param string|array $class + * @return bool + */ + public function bind($interface, $class) + { + if (false == (is_string($interface) && ! empty($interface)) || false == (is_string($class) && ! empty($class) || is_array($class))) + return false; + + if (is_string($class)) + $this->services[$interface] = $class; + else { + $classes = []; + foreach ($class as $name => $value) { + if (is_string($name) && ! empty($name) && is_string($value) && ! empty($value)) + $classes[$name] = $value; + } + + if (0 == count($classes)) + return false; + + $this->services[$interface] = $classes; + } + + return true; + } + + /** + * @param null|string $name + * @param null|string $annotation + * @return bool + */ + public function isInjected($name = null, $annotation = null) + { + if (null == $name || null == $this->getServiceName($name)) + return false; + + if (false == $this->isImplement($this->getServiceName($name, $annotation), $name)) + throw new InjectException("Inject error: class " . $this->getServiceName($name, $annotation) . " must implement " . $name); + + return true; + } + + /** + * @param string $className + * @param string $interfaceName + * @return bool + */ + protected function isImplement($className, $interfaceName) + { + return (new \ReflectionClass($className))->implementsInterface($interfaceName); + } + + /** + * @param \ReflectionClass|null $class + * @return bool + */ + public function isInstantiate(\ReflectionClass $class = null) + { + if (null == $class || $class->isAbstract() || $class->isInterface()) + return false; + + return true; + } +} diff --git a/tests/InjectExceptionTest.php b/tests/InjectExceptionTest.php index b134953..c3d1814 100644 --- a/tests/InjectExceptionTest.php +++ b/tests/InjectExceptionTest.php @@ -1,6 +1,7 @@ "wer"]); } + + /** + * @expectedException \Sixx\DependencyInjection\Exceptions\InjectException + * @expectedExceptionMessage Inject error: class Start must implement INext + */ + public function testExceptionClassMustImplements() + { + $class = new ServiceContainer(); + + $class->bind("INext", "Start"); + $class->isInjected("INext"); + } } diff --git a/tests/InjectTest.php b/tests/InjectTest.php index ab2cfaf..412a450 100644 --- a/tests/InjectTest.php +++ b/tests/InjectTest.php @@ -7,7 +7,7 @@ class InjectTest extends PHPUnit_Framework_TestCase public function setUp() { require_once __DIR__ . "/TestClasses/Classes.php"; - Inject::bindByArray(["IStart" => "Start", "INext" => "Next"]); + Inject::bindByArray(["IStart" => ["star" => "Start", "second" => "Starter"], "INext" => "Next"]); } public function testInstantiation() @@ -17,7 +17,7 @@ public function testInstantiation() public function testMethod() { - $this->assertInstanceOf("Start", Inject::method("INext", "tryMe")); + $this->assertInstanceOf("Starter", Inject::method("INext", "tryMe")); } public function testCheckAllInInjected() @@ -25,6 +25,8 @@ public function testCheckAllInInjected() $class = Inject::method("ChildClass", "hello", ["c" => "fff"]); $this->assertInstanceOf("Next", $class->getNext()); $this->assertInstanceOf("Start", $class->getStart()); - $this->assertInstanceOf("Start", $class->getNext()->tryMe()); + $this->assertInstanceOf("Starter", $class->getNext()->tryMe()); + $this->assertInstanceOf("Starter", Inject::method("ChildClass", "getStarter", ["c" => "fff"])); + $this->assertInstanceOf("Starter", $class->starter); } } diff --git a/tests/ServiceContainerTest.php b/tests/ServiceContainerTest.php new file mode 100644 index 0000000..54a7a82 --- /dev/null +++ b/tests/ServiceContainerTest.php @@ -0,0 +1,43 @@ +serviceContainer = new ServiceContainer(); + } + + public function testBind() + { + $this->assertTrue($this->serviceContainer->bind("ITest", "Test")); + $this->assertFalse($this->serviceContainer->bind("ITest", 1)); + $this->assertFalse($this->serviceContainer->bind(1, "Test")); + $this->assertTrue($this->serviceContainer->bind("ITest", ["hello" => "Test"])); + $this->assertFalse($this->serviceContainer->bind("", ["hello" => "Test"])); + $this->assertFalse($this->serviceContainer->bind("ITest", ["" => "Test"])); + $this->assertFalse($this->serviceContainer->bind("ITest", ["Test" => ""])); + } + + public function testGetServiceNameAndFlushAll() + { + $this->serviceContainer->bind("ITest", "Test"); + $this->assertEquals("Test", $this->serviceContainer->getServiceName("ITest")); + $this->serviceContainer->flushServices(); + $this->assertNull($this->serviceContainer->getServiceName("ITest")); + $this->serviceContainer->bind("ITest", ["first" => "Test", "second" => "TestSecond", "third" => "TestThird"]); + $this->assertEquals("Test", $this->serviceContainer->getServiceName("ITest")); + $this->assertEquals("TestSecond", $this->serviceContainer->getServiceName("ITest", "/* @second")); + $this->assertEquals("TestThird", $this->serviceContainer->getServiceName("ITest", "/* @third")); + $this->serviceContainer->bind("INext", "Next"); + $this->assertTrue($this->serviceContainer->isInjected("INext")); + $this->assertFalse($this->serviceContainer->isInjected("ITemp")); + } +} diff --git a/tests/TestClasses/Classes.php b/tests/TestClasses/Classes.php index 85d5095..34ed525 100644 --- a/tests/TestClasses/Classes.php +++ b/tests/TestClasses/Classes.php @@ -7,6 +7,17 @@ class ChildClass extends ParentClass */ protected $start; + /** + * @second + * @var IStart + */ + public $starter; + + /** + * @star + * @param IStart $start + * @return $this + */ public function hello(IStart $start) { $this->start = $start; @@ -46,12 +57,23 @@ public function __construct(INext $next, $c) $this->next = $next; $this->c = $c; } + + /** + * @second + * @param IStart $start + * @return IStart + */ + public function getStarter(IStart $start) + { + return $start; + } } class Next implements INext { /** * @var IStart + * @second */ public $start; @@ -76,6 +98,11 @@ class Start implements IStart } +class Starter implements IStart +{ + +} + interface IStart {