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());
 }
 /**
  * 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();
 }
 /**
  * Get class dependencies form definition builder
  *
  * @param DefinitionBuilder $definitionBuilder
  * @return array
  * @throws ImplementerForTypeNotFoundException
  */
 protected function getClassDependencies(DefinitionBuilder $definitionBuilder) : array
 {
     $dependencyList = [];
     // Get dependencies which will be used for generation definitions
     foreach ($definitionBuilder->getDefinitionCollection() as $classDefinition) {
         //            if (!$classDefinition->isAnalyzed()) {
         // Iterate properties and get their dependencies
         foreach ($classDefinition->getPropertiesCollection() as $propertyDefinition) {
             // Add dependency to list if valid
             $this->addDependency($definitionBuilder, $dependencyList, $propertyDefinition->getDependency(), $propertyDefinition);
         }
         foreach ($classDefinition->getMethodsCollection() as $methodDefinition) {
             foreach ($methodDefinition->getParametersCollection() as $parameterDefinition) {
                 $this->addDependency($definitionBuilder, $dependencyList, $parameterDefinition->getDependency(), $parameterDefinition);
             }
         }
         //            }
     }
     return $dependencyList;
 }