/** * Returns a service instance for the passed in identifying type name. If the * instance does not exist, it creates one. * * @param Type $IdentifyingType * @param Injector $Injector * @return object * @throws ServiceNotDefined */ public function getService(Type $IdentifyingType, Injector $Injector) { $identifyingTypeName = $IdentifyingType->getName(); if (isset($this->builtServices[$identifyingTypeName])) { return $this->builtServices[$identifyingTypeName]; } $ServiceDefinition = $this->ServiceDefinitions->get($IdentifyingType); if (is_null($ServiceDefinition)) { $callerFilePath = debug_backtrace()[0]['file']; $callerLineNumber = debug_backtrace()[0]['line']; throw new ServiceNotDefined($IdentifyingType->getName(), $callerFilePath, $callerLineNumber); } if ($ServiceDefinition->hasFactoryFunction()) { $InjectionTarget = new InjectionTargetClosure($ServiceDefinition->getFactoryFunction()); $Service = $Injector->resolve($InjectionTarget); $this->ensureValueImplementsIdentifyingType($ServiceDefinition, $Service); } else { $InjectionTarget = new InjectionTargetConstructor($ServiceDefinition->getImplementingType()); $Service = $Injector->resolve($InjectionTarget); } $this->builtServices[$identifyingTypeName] = $Service; if ($ServiceDefinition->hasInitFunction()) { $InjectionTarget = new InjectionTargetClosure($ServiceDefinition->getInitFunction()); $Injector->resolve($InjectionTarget); } return $Service; }
/** * @test */ public function If_a_service_requires_itself_in_its_init_function_this_is_not_considered_as_circular_reference() { // given $ServiceDefinitions = new ServiceDefinitions(); $ServiceDefinitions->add(IdentifyingType::is(Type::fromName('tueena\\spec\\core\\stubs\\A')), ImplementingType::isTheSame(), InitFunction::is(function (A $A) { })); $Target = new ServiceDefinitionsValidator(); // when, then $Target->validate($ServiceDefinitions); }
/** * @test */ public function The_init_function_can_be_injected() { // given $counter = 0; $ServiceDefinitions = new ServiceDefinitions(); $ServiceDefinitions->add(IdentifyingType::is(Type::fromName('tueena\\spec\\core\\stubs\\A')), ImplementingType::isTheSame())->add(IdentifyingType::is(Type::fromName('tueena\\spec\\core\\stubs\\B')), ImplementingType::isTheSame(), InitFunction::is(function (A $A, B $B) use(&$counter) { $counter++; })); $Target = new ServiceFactory($ServiceDefinitions); $Injector = new Injector($Target); // when $Target->getService(Type::fromName('tueena\\spec\\core\\stubs\\B'), $Injector); // then // We just have to ensure, the init function has been called. A and B must // have been injected. $this->AssertEquals(1, $counter); }
/** * @test */ public function If_a_service_is_not_defined_with_a_factory_function_the_constructor_of_that_service_gets_injected_with_the_required_services() { // given $serviceDefinitions = new ServiceDefinitions(); $serviceDefinitions->add(IdentifyingType::is(Type::fromName('tueena\\spec\\core\\stubs\\D')), ImplementingType::isTheSame())->add(IdentifyingType::is(Type::fromName('tueena\\spec\\core\\stubs\\E')), ImplementingType::isTheSame()); $ServiceFactory = new ServiceFactory($serviceDefinitions); $Target = new Injector($ServiceFactory); $PropertyDOfE = null; $Closure = function (\tueena\spec\core\stubs\E $E) use(&$PropertyDOfE) { $PropertyDOfE = $E->D; }; // when $Target->resolve(new InjectionTargetClosure($Closure)); // then $this->assertInstanceOf('tueena\\spec\\core\\stubs\\D', $PropertyDOfE); }
/** * @test */ public function The_getAll_method_returns_all_service_definitions() { // given $Target = new ServiceDefinitions(); $Target->add(IdentifyingType::is(Type::fromName('tueena\\spec\\core\\stubs\\IMyService')), ImplementingType::isTheSame())->add(IdentifyingType::is(Type::fromName('tueena\\spec\\core\\stubs\\MyService')), ImplementingType::isTheSame()); // when $result = $Target->getAll(); // then $this->assertEquals(['tueena\\spec\\core\\stubs\\IMyService' => $Target->get(Type::fromName('tueena\\spec\\core\\stubs\\IMyService')), 'tueena\\spec\\core\\stubs\\MyService' => $Target->get(Type::fromName('tueena\\spec\\core\\stubs\\MyService'))], $result); }
/** * @param ServiceDefinition $ServiceDefinition * @param ServiceDefinitions $ServiceDefinitions * @param ServiceDefinition $InitialServiceDefinition * @param ServiceDefinition[] $alreadyCheckedRequirements * @param array[] $trace * @param \Closure $Closure */ private static function throwOnCircularReferenceOfRequiresServicesHelper(ServiceDefinition $ServiceDefinition, ServiceDefinitions $ServiceDefinitions, ServiceDefinition $InitialServiceDefinition, array &$alreadyCheckedRequirements, array $trace, \Closure $Closure = null) { if ($Closure === null) { return; } $InjectionTarget = new InjectionTargetClosure($Closure); foreach ($InjectionTarget->getRequiredTypes() as $requiredType) { $ServiceDefinition = $ServiceDefinitions->get($requiredType); if (in_array($ServiceDefinition, $alreadyCheckedRequirements)) { continue; } // The second part of the condition ensures, that no exception is thrown // if an init function of a service requires the service itself. This // is absolutely valid. if ($ServiceDefinition === $InitialServiceDefinition && $trace[0][1] !== 'init') { self::throwCircularReferenceException($InitialServiceDefinition, $trace); } $alreadyCheckedRequirements[] = $ServiceDefinition; self::throwOnCircularReferenceOfRequiredServices($ServiceDefinition, $ServiceDefinitions, $InitialServiceDefinition, $alreadyCheckedRequirements, $trace); } }