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));
     }
 }