public function testWrongClassName() { $this->expectException(\ReflectionException::class); $definitionBuilder = new DefinitionBuilder(new ParameterBuilder()); $definitionBuilder->addDefinition('sdf'); $this->callAnalyze($definitionBuilder); }
public function testCreatingDefinitions() { $builder = new DefinitionBuilder(new ParameterBuilder()); $builder->addDefinition(Car::class)->defineConstructor()->defineParameter('driver')->defineDependency(new ClassReference(SlowDriver::class))->end()->end()->end(); $compiler = new DefinitionCompiler(new DefinitionGenerator(new ClassGenerator()), $this->getAnalyzer()); $list = $this->callMethod('getClassDependencies', $compiler, [$builder]); $this->callMethod('generateDefinitions', $compiler, [$builder, $list]); static::assertCount(2, $builder->getDefinitionCollection()); }
public function testMethodAnnotations() { new InjectClass(''); $definitionBuilder = new DefinitionBuilder(new ParameterBuilder()); $definitionBuilder->addDefinition(PropClass::class)->end(); $this->callAnalyze($definitionBuilder); $parameterDefinition = $this->getParameterDefinition($definitionBuilder, PropClass::class, '__construct', 'car'); static::assertEquals('\\' . Car::class, $parameterDefinition->getDependency()->getClassName()); }
public function testGetCode() { $definitionBuilder = new DefinitionBuilder(new ParameterBuilder()); $definitionBuilder->addDefinition(Car::class)->defineIsSingleton()->defineConstructor()->defineParameter('driver')->defineDependency(new ClassReference(SlowDriver::class))->end()->end()->defineProperty('driver')->defineDependency(new ClassReference(SlowDriver::class))->end()->end()->addDefinition(WheelController::class)->defineConstructor()->defineParameter('fastDriver')->defineDependency(new ClassReference(SlowDriver::class))->end()->defineParameter('slowDriver')->defineDependency(new ClassReference(SlowDriver::class))->end()->defineParameter('car')->defineDependency(new ClassReference(Car::class))->end()->defineParameter('params')->defineDependency((new CollectionReference([CollectionItem::create(new ConstantReference('PHP_VERSION'), 1), 'key0' => 33, 'key1' => new ConstantReference('PHP_MAJOR_VERSION'), 'kye2' => new StringReference('value'), 1 => 5555]))->addItem(CollectionItem::create(2, new ConstantReference('PHP_MINOR_VERSION')))->addItem(CollectionItem::create(3, 'sdf'))->addItem(CollectionItem::create('key3', 'dsddd')))->end()->defineParameter('id')->defineDependency(new ConstantReference('PHP_RELEASE_VERSION'))->end()->end()->defineMethod('setLeg')->defineParameter('leg')->defineDependency(new ClassReference(Leg::class))->end()->end()->defineProperty('car')->defineDependency(new ClassReference(Car::class))->end()->end()->addDefinition(ProductClass::class)->defineConstructor()->end()->end(); $compiler = new DefinitionCompiler(new DefinitionGenerator(new ClassGenerator()), $this->getAnalyzer()); $namespace = (new \ReflectionClass(self::class))->getNamespaceName(); /** @var ContainerInterface $container */ $container = $compiler->compile($definitionBuilder, 'ContainerGeneratorTest', $namespace, __DIR__ . '/../generated'); static::assertInstanceOf(WheelController::class, $container->get(WheelController::class)); }
public function testXml() { $builder = new DefinitionBuilder(new ParameterBuilder()); $builder->addDefinition(\PDO::class)->defineConstructor()->defineParameter('dsn')->defineDependency(new StringReference('mysql:host=localhost;port=3306;dbname=stylelike.io;charset=UTF8'))->end()->defineParameter('username')->defineDependency(new StringReference('samsonos'))->end()->defineParameter('passwd')->defineDependency(new StringReference('AzUzrcVe4LJJre9f'))->end()->defineParameter('options')->defineDependency((new CollectionReference())->addItem(new CollectionItem(new ConstantReference('\\PDO::ATTR_ERRMODE'), new ParameterReference('pdo_exception')))->addItem(new CollectionItem(new ConstantReference('\\PDO::ATTR_DEFAULT_FETCH_MODE'), new ConstantReference('\\PDO::FETCH_ASSOC'))))->end()->end()->end()->addDefinition(XmlProductClass::class)->defineConstructor()->defineParameter('shoes')->defineDependency(new ClassReference(Shoes::class))->end()->defineParameter('val')->defineDependency(new StringReference('value'))->end()->defineParameter('val1')->defineDependency(new StringReference('value1'))->end()->defineParameter('arr')->defineDependency(new CollectionReference(['param' => 'value']))->end()->end()->defineMethod('setLeg')->defineParameter('driver')->defineDependency(new ClassReference(SlowDriver::class))->end()->end()->defineProperty('driver')->defineDependency(new ClassReference(SlowDriver::class))->end()->end(); $definitionBuilder = new DefinitionBuilder(new ParameterBuilder()); $resolver = new XmlResolver(); $resolver->resolve($definitionBuilder, $this->getMainXml()); static::assertCount(2, $definitionBuilder->getDefinitionCollection()); static::assertCount(1, $definitionBuilder->getParameterCollection()); }
/** * Analyze definition builder * * @param DefinitionBuilder $definitionBuilder * @throws ParameterNotFoundException * @return bool */ public function analyze(DefinitionBuilder $definitionBuilder) : bool { $isAnalyzed = false; // Analyze class definitions foreach ($definitionBuilder->getDefinitionCollection() as $classDefinition) { // Analyze only not analyzed classes if (!$classDefinition->isAnalyzed()) { // Get reflection $reflectionClass = new \ReflectionClass($classDefinition->getClassName()); // Analyze class $this->analyzeClass($classDefinition, $reflectionClass); // Analyze properties $this->analyzeProperty($reflectionClass, $classDefinition); // Analyze methods $this->analyzeMethod($reflectionClass, $classDefinition); // Class was analyzed $classDefinition->setIsAnalyzed(true); // Do not analyze this definition $isAnalyzed = true; } } return $isAnalyzed; }
/** * Compile and get container * * @param DefinitionBuilder $definitionBuilder * @return string Get container code * @throws ReferenceNotImplementsException * @throws \InvalidArgumentException */ public function generateClass(DefinitionBuilder $definitionBuilder) : string { // Generate parameters if exists if (count($definitionBuilder->getParameterCollection())) { $parameterMethodGenerator = $this->generator->defMethod('parameter')->defProtected()->defArgument('parameterName'); $isFirstCondition = true; // Generate parameters foreach ($definitionBuilder->getParameterCollection() as $parameterName => $reference) { $parameterMethodGenerator->defLine($this->generateParameterCondition($parameterName, $reference, $isFirstCondition)); $isFirstCondition = false; } // Close method $parameterMethodGenerator->end(); } $methodGenerator = $this->generator->defMethod('logic')->defProtected()->defArgument('classNameOrServiceName')->defLine('static $singletonCollection = [];'); $scopes = []; $isFirstCondition = true; /** @var ClassDefinition $classDefinition */ foreach ($definitionBuilder->getDefinitionCollection() as $classDefinition) { // Get scopes foreach ($classDefinition->getScopes() as $scope) { $id = $scope::getId(); if (!array_key_exists($id, $scopes)) { $scopes[$id] = []; } // Store definition id $scopes[$id][] = $classDefinition->getServiceName() ?: $classDefinition->getClassName(); } $className = $classDefinition->getClassName(); $serviceName = $classDefinition->getServiceName(); // Name for static service collection $serviceId = $serviceName ?? $className; // Generate if condition by class name or value $methodGenerator->defLine($this->generateStartIfCondition($isFirstCondition, $className, $serviceName)); // Generate static property service access if service is singleton // TODO Move this from if condition if ($classDefinition->isSingleton()) { $methodGenerator->defLine($this->generateStaticFunctionCall($serviceId)); } // Generate constructor call if not $methodGenerator->defLine($this->generateConstructor($classDefinition)); // Generate methods foreach ($classDefinition->getMethodsCollection() as $methodDefinition) { // Constructor is not a method skip it if ($methodDefinition->getMethodName() !== '__construct') { $methodGenerator->defLine($this->generateSetters($classDefinition, $methodDefinition)); } } // Generate properties foreach ($classDefinition->getPropertiesCollection() as $propertyDefinition) { // Generate properties $methodGenerator->defLine($this->generateProperty($classDefinition, $propertyDefinition)); } // Generate return operator $methodGenerator->defLine($this->generateReturnOperator($classDefinition, $serviceId)); // Close if $methodGenerator->defLine($this->generateEndIfCondition()); $isFirstCondition = false; } // Generate if condition by class name or value $methodGenerator->defLine($this->generateStartIfCondition(false, '\\' . $this->generator->getNamespace() . '\\' . $this->generator->getClassName(), self::CONTAINER_DEPENDENCY_NAME)); // Return container $methodGenerator->defLine("\treturn \$this;"); // Close if $methodGenerator->defLine($this->generateEndIfCondition()); // Close method $methodGenerator->end(); // Generate constructor $constructorGenerator = $this->generator->defMethod('__construct'); // Generate scope list $this->generateScopeList($constructorGenerator, $scopes); // Close constructor $constructorGenerator->end(); return "<?php \n" . $this->generator->code(); }
/** * Resolve xml code * * @param DefinitionBuilder $definitionBuilder * @param string $xml * * @throws ClassDefinitionAlreadyExistsException * @throws \InvalidArgumentException * @throws PropertyDefinitionAlreadyExistsException * @throws ReferenceNotImplementsException * @throws MethodDefinitionAlreadyExistsException * @throws ParameterDefinitionAlreadyExistsException * @throws ParameterAlreadyExistsException */ public function resolve(DefinitionBuilder $definitionBuilder, string $xml) { /** * Iterate config and resolve single instance * * @var string $key * @var array $arrayData */ foreach ($this->xml2array(new \SimpleXMLElement($xml)) as $key => $arrayData) { // Resolve parameters if ($key === self::PARAMETERS_KEY) { // Define parameters foreach ($arrayData as $parameterKey => $parameterArray) { $definitionBuilder->defineParameter($parameterKey, $this->resolveDependency($parameterArray)); } } // Resolve dependencies if ($key === self::DEPENDENCIES_KEY) { // Iterate instances foreach ($arrayData as $dependencyKey => $definitionsArrayData) { // Get only definition instances if ($dependencyKey === self::INSTANCE_KEY) { /** * If we have only one instance we need to add array * @var array $collection */ $collection = !array_key_exists(0, $definitionsArrayData) ? [$definitionsArrayData] : $definitionsArrayData; /** * Iterate collection of instances * @var array $definitionsArrayData */ foreach ($collection as $definitionArrayData) { /** * Create class definition * @var ClassDefinition $classDefinition */ $classDefinition = $definitionBuilder->addDefinition($definitionArrayData['@attributes']['class']); // Resolve constructor if (array_key_exists('constructor', $definitionArrayData)) { $this->resolveConstructor($classDefinition, $definitionArrayData['constructor']); } // Resolve methods if (array_key_exists('methods', $definitionArrayData)) { // Iteare methods foreach ($definitionArrayData['methods'] as $methodName => $methodArray) { $this->resolveMethod($classDefinition, $methodArray, $methodName); } } // Resolve properties if (array_key_exists('properties', $definitionArrayData)) { // Iterate properties foreach ($definitionArrayData['properties'] as $propertyName => $propertyArray) { $this->resolveProperty($classDefinition, $propertyArray, $propertyName); } } } } } } } }
/** * Get class which implements the interface * * @param DefinitionBuilder $definitionBuilder * @param string $interfaceName * @return string * @throws ImplementerForTypeNotFoundException * TODO Add interface resolvers functionality */ protected function resolveTypeImplementer(DefinitionBuilder $definitionBuilder, string $interfaceName) : ReferenceInterface { // // Gather all interface implementations // foreach (get_declared_classes() as $class) { // $classImplements = class_implements($class); // // Remove slash for start of interface // if (in_array(ltrim($interfaceName, '\\'), $classImplements, true)) { // return $class; // } // } // throw new ImplementerForTypeNotFoundException( // sprintf('Type "%s" does not have some implementers', $interfaceName) // ); $interfaces = $definitionBuilder->getImplementors(); if (array_key_exists($interfaceName, $interfaces)) { return $interfaces[$interfaceName]; } else { throw new ImplementerForTypeNotFoundException(sprintf('Type "%s" does not have some implementers', $interfaceName)); } }