/** * @param IInjectionTarget $InjectionTarget * @param string $parameterName * @param Type $RequiredType */ public function __construct(IInjectionTarget $InjectionTarget, $parameterName, Type $RequiredType) { $this->RequiredType = $RequiredType; $message = $RequiredType->getName() . ' is not defined as a service (means: has not been added to the ServiceDefinitions instance with the add() method). '; $message .= 'Each parameter of an injection target must have a defined service as type hint.'; parent::__construct($InjectionTarget, $parameterName, $message); }
/** * 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 The_getType_method_returns_the_type_passed_to_the_is_method() { // given $Type = Type::fromName('tueena\\spec\\core\\stubs\\IMyService'); // when $ReturnValue = IdentifyingType::is($Type); // then $this->assertSame($Type, $ReturnValue->getType()); }
/** * @test */ public function getInjectionTargetTypeName_returns_the_string_constructor() { // given $Target = new InjectionTargetConstructor(Type::fromName('tueena\\spec\\core\\stubs\\E')); // when $injectionTargetTypeName = $Target->getInjectionTargetTypeName(); // then $this->assertEquals('constructor', $injectionTargetTypeName); }
/** * @test */ public function getInjectionTargetTypeName_returns_the_string_static_method() { // given $Target = new InjectionTargetStaticMethod(Type::fromName('tueena\\spec\\core\\stubs\\D'), 'myStaticMethod'); // when $injectionTargetTypeName = $Target->getInjectionTargetTypeName(); // then $this->assertEquals('static method', $injectionTargetTypeName); }
/** * @test */ public function The_static_is_method_creates_and_returns_a_new_instance() { // given $Type = Type::fromName('tueena\\spec\\core\\stubs\\MyService'); // when $ReturnValue = ImplementingType::is($Type); // then $this->assertInstanceOf('tueena\\core\\Services\\ServiceDefinitionParameters\\ImplementingType', $ReturnValue); $this->assertSame($Type, $ReturnValue->getType()); $this->assertFalse($ReturnValue->isSameAsIdentifyingType()); }
/** * @param Type $IdentifyingType * @return ServiceDefinition */ public function get(Type $IdentifyingType) { $identifyingTypeName = $IdentifyingType->getName(); return isset($this->serviceDefinitions[$identifyingTypeName]) ? $this->serviceDefinitions[$identifyingTypeName] : null; }
/** * @param \ReflectionParameter $ReflectionParameter * @return Type */ private function getRequiredType(\ReflectionParameter $ReflectionParameter) { $parameterName = $ReflectionParameter->getName(); try { $ReflectionClass = $ReflectionParameter->getClass(); } catch (\Exception $Exception) { throw new TypeHintIsNotAnExistingClassOrInterface($this, $parameterName); } if ($ReflectionClass === null) { throw new TypeHintIsMissingOrNotAClassOrInterfaceName($this, $parameterName); } if ($ReflectionParameter->isOptional()) { throw new ParameterIsOptional($this, $parameterName); } return Type::fromName($ReflectionClass->name); }
/** * @test */ public function A_servcie_instance_can_be_added() { // given $ServiceDefinitions = new ServiceDefinitions(); $Target = new ServiceFactory($ServiceDefinitions); // when $A = new A(); $Target->addService(IdentifyingType::is(Type::fromName('tueena\\spec\\core\\stubs\\A')), $A); $Injector = new Injector($Target); // then $this->assertSame($A, $Target->getService(Type::fromName('tueena\\spec\\core\\stubs\\A'), $Injector)); }
/** * @test */ public function hasInitFunction_returns_false_if_no_init_function_passed_in() { // given $IdentifyingType = IdentifyingType::is(Type::fromName(__CLASS__)); $ImplementingType = ImplementingType::is(Type::fromName(__CLASS__)); $FactoryFunction = FactoryFunction::is(function () { }); // when $Target = new ServiceDefinition('', 0, $IdentifyingType, $ImplementingType, $FactoryFunction, null); // then $this->AssertTrue($Target->hasFactoryFunction()); $this->AssertFalse($Target->hasInitFunction()); }
/** * @test */ public function The_main_method_is_resolved_by_the_Injector() { // given $loaderConfigurator = function () { }; $serviceDefiner = function (ServiceDefinitions $ServiceDefinitions) { $ServiceDefinitions->add(IdentifyingType::is(Type::fromName('tueena\\spec\\core\\stubs\\IMyService')), ImplementingType::is(Type::fromName('tueena\\spec\\core\\stubs\\MyService'))); }; $passedInServiceInstance = null; $mainFunction = function (IMyService $MyService) use(&$passedInServiceInstance) { $passedInServiceInstance = $MyService; }; $Target = new Application($loaderConfigurator, $serviceDefiner, $mainFunction); // when $Target->run(); // then $this->assertInstanceOf('\\tueena\\spec\\core\\stubs\\MyService', $passedInServiceInstance); }
/** * @param ServiceDefinition $ServiceDefinition * @param string $functionType * @param Type $RequiredType * @param string $parameterName */ private static function ensureFactoryFunctionDoesNotRequireTheServiceItself(ServiceDefinition $ServiceDefinition, $functionType, Type $RequiredType, $parameterName) { if ($functionType === 'init') { return; } if ($RequiredType == $ServiceDefinition->getIdentifyingType()) { $message = 'The type hint of the parameter $' . $parameterName . ' is ' . $RequiredType->getName() . '. '; $message .= 'A factory function cannot be injected with the service it is about to build.'; throw new InvalidServiceDefinition($ServiceDefinition, $message); } }
/** * @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); }
/** * @test */ public function The_isInstanceOf_method_returns_false_if_the_type_does_not_implement_the_other() { // given $Target = Type::fromName('tueena\\spec\\core\\stubs\\MyService'); $OtherType = Type::fromName(__CLASS__); // when, then $this->assertFalse($Target->isInstanceOf($OtherType)); }
public function run() { // Call the Loader configurator. $Loader = new Loader(); $LoaderConfigurator = $this->LoaderConfigurator; $LoaderConfigurator($Loader); // Build the service definitions collection. $ServiceDefinitions = new ServiceDefinitions(); // Call the service definer. $ServiceDefiner = $this->ServiceDefiner; $ServiceDefiner($ServiceDefinitions); // Build the service factory and the injector. $ServiceFactory = new ServiceFactory($ServiceDefinitions); $Injector = new Injector($ServiceFactory); // Register the Injector and the Loader as services. $ServiceFactory->addService(IdentifyingType::is(Type::fromName('tueena\\core\\services\\IInjector')), $Injector)->addService(IdentifyingType::is(Type::fromName('tueena\\core\\ILoader')), $Loader); // Call the main function. $MainFunction = new InjectionTargetClosure($this->MainFunction); $Injector->resolve($MainFunction); }
/** * @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 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); }