Completed
Push — master ( 8fb470...71ab64 )
by Vitaly
05:03
created

ContainerBuilder::buildResolverArgument()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 6.6

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 15
ccs 6
cts 10
cp 0.6
rs 8.8571
cc 5
eloc 9
nc 5
nop 2
crap 6.6
1
<?php declare(strict_types = 1);
2
/**
3
 * Created by PhpStorm.
4
 * User: root
5
 * Date: 02.08.16
6
 * Time: 0:46.
7
 */
8
namespace samsonframework\container;
9
10
use samsonframework\container\metadata\ClassMetadata;
11
use samsonframework\container\metadata\MethodMetadata;
12
use samsonframework\container\metadata\PropertyMetadata;
13
use samsonframework\container\resolver\ResolverInterface;
14
use samsonframework\di\Container;
15
use samsonframework\filemanager\FileManagerInterface;
16
use samsonphp\generator\Generator;
17
18
/**
19
 * Class Container.
20
 */
21
class ContainerBuilder
22
{
23
    /** Controller classes scope name */
24
    const SCOPE_CONTROLLER = 'controllers';
25
26
    /** Service classes scope name */
27
    const SCOPE_SERVICES = 'services';
28
29
    /** Generated resolving function name prefix */
30
    const DI_FUNCTION_PREFIX = 'container';
31
32
    /** Generated resolving function service static collection name */
33
    const DI_FUNCTION_SERVICES = '$' . self::SCOPE_SERVICES;
34
35
    /** @var string[] Collection of available container scopes */
36
    protected $scopes = [
37
        self::SCOPE_CONTROLLER => [],
38
        self::SCOPE_SERVICES => []
39
    ];
40
41
    /** @var ClassMetadata[] Collection of classes metadata */
42
    protected $classMetadata = [];
43
44
    /** @var array Collection of dependencies aliases */
45
    protected $classAliases = [];
46
47
    /** @var FileManagerInterface */
48
    protected $fileManger;
49
50
    /** @var ResolverInterface */
51
    protected $classResolver;
52
53
    /** @var Generator */
54
    protected $generator;
55
56
    /** @var string Resolver function name */
57
    protected $resolverFunction;
58
59
    /**
60
     * Container constructor.
61
     *
62
     * @param FileManagerInterface $fileManager
63
     * @param ResolverInterface    $classResolver
64
     * @param Generator            $generator
65
     */
66 11
    public function __construct(
67
        FileManagerInterface $fileManager,
68
        ResolverInterface $classResolver,
69
        Generator $generator
70
    )
71
    {
0 ignored issues
show
Coding Style introduced by
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
72 11
        $this->fileManger = $fileManager;
73 11
        $this->classResolver = $classResolver;
74 11
        $this->generator = $generator;
75 11
    }
76
77
    /**
78
     * Load classes from paths.
79
     *
80
     * @param array $paths Paths for importing
81
     *
82
     * @return $this
83
     */
84 2
    public function loadFromPaths(array $paths)
85
    {
86
        // Iterate all paths and get files
87 2
        foreach ($this->fileManger->scan($paths, ['php']) as $phpFile) {
88 2
            require_once($phpFile);
89
            // Read all classes in given file
90 2
            $this->loadFromClassNames($this->getDefinedClasses(file_get_contents($phpFile)));
91
        }
92
93 2
        return $this;
94
    }
95
96
    /**
97
     * Load classes from class names collection.
98
     *
99
     * @param string[] $classes Collection of class names for resolving
100
     *
101
     * @return $this
102
     */
103 11
    public function loadFromClassNames(array $classes)
104
    {
105
        // Read all classes in given file
106 11
        foreach ($classes as $className) {
107
            // Resolve class metadata
108 11
            $this->classMetadata[$className] = $this->classResolver->resolve(new \ReflectionClass($className));
109
            // Store by metadata name as alias
110 11
            $this->classAliases[$this->classMetadata[$className]->name] = $className;
111
            // Store class in defined scopes
112 11
            foreach ($this->classMetadata[$className]->scopes as $scope) {
113 11
                $this->scopes[$scope][] = $className;
114
            }
115
        }
116
117 11
        return $this;
118
    }
119
120
    /**
121
     * Find class names defined in PHP code.
122
     *
123
     * @param string $php PHP code for scanning
124
     *
125
     * @return string[] Collection of found class names in php code
126
     */
127 3
    protected function getDefinedClasses(string $php) : array
128
    {
129 3
        $classes = [];
130 3
        $namespace = null;
131
132
        // Append php marker for parsing file
133 3
        $php = strpos(is_string($php) ? $php : '', '<?php') !== 0 ? '<?php ' . $php : $php;
134
135 3
        $tokens = token_get_all($php);
136
137 3
        for ($i = 2, $count = count($tokens); $i < $count; $i++) {
138 3
            if ($tokens[$i - 2][0] === T_CLASS
139 3
                && $tokens[$i - 1][0] === T_WHITESPACE
140 3
                && $tokens[$i][0] === T_STRING
141
            ) {
142 3
                $classes[] = $namespace ? $namespace . '\\' . $tokens[$i][1] : $tokens[$i][1];
143 3
            } elseif ($tokens[$i - 2][0] === T_NAMESPACE
144 3
                && $tokens[$i - 1][0] === T_WHITESPACE
145 3
                && $tokens[$i][0] === T_STRING
146
            ) {
147 2
                while (isset($tokens[$i]) && is_array($tokens[$i])) {
148 2
                    $namespace .= $tokens[$i++][1];
149
                }
150
            }
151
        }
152
153 3
        return $classes;
154
    }
155
156
    /**
157
     * Load classes from PHP c ode.
158
     *
159
     * @param string $php PHP code
160
     *
161
     * @return $this
162
     */
163 1
    public function loadFromCode($php)
164
    {
165 1
        if (count($classes = $this->getDefinedClasses($php))) {
166
            // TODO: Consider writing cache file and require it
167 1
            eval($php);
168 1
            $this->loadFromClassNames($classes);
169
        }
170
171 1
        return $this;
172
    }
173
174
    /**
175
     * Build container class.
176
     *
177
     * @param string|null $containerClass Container class name
178
     * @param string      $namespace      Name space
179
     *
180
     * @return string Generated Container class code
181
     * @throws \InvalidArgumentException
182
     */
183 8
    public function build($containerClass = 'Container', $namespace = '')
184
    {
185
        // Build dependency injection container function name
186 8
        $this->resolverFunction = uniqid(self::DI_FUNCTION_PREFIX);
187
188 8
        $containerDependencies = [];
189 8
        $containerAliases = [];
190 8
        foreach ($this->classMetadata as $className => $classMetadata) {
191 8
            if ($classMetadata->alias !== null) {
192
                $containerAliases[$className] = $classMetadata->alias;
193
            }
194
            // Store inner dependencies
195 8
            if (array_key_exists('__construct', $classMetadata->methodsMetadata)) {
196 8
                $containerDependencies[$className] = array_values($classMetadata->methodsMetadata['__construct']->dependencies);
197
            }
198
        }
199
200 8
        $this->generator
201 8
            ->text('<?php declare(strict_types = 1);')
202 8
            ->newLine()
203 8
            ->defNamespace($namespace)
204 8
            ->multiComment(['Application container'])
205 8
            ->defClass($containerClass, '\\' . Container::class)
206 8
            ->defClassFunction('__construct')
207 8
            ->newLine('$this->dependencies = ')->arrayValue($containerDependencies)->text(';')
208 8
            ->newLine('$this->aliases = ')->arrayValue($containerAliases)->text(';')
209 8
            ->newLine('$this->' . self::SCOPE_SERVICES . ' = ')->arrayValue($this->scopes[self::SCOPE_SERVICES])->text(';')
0 ignored issues
show
Documentation introduced by
$this->scopes[self::SCOPE_SERVICES] is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
210 8
            ->endClassFunction()
211 8
            ->defClassFunction('logic', 'protected', ['$dependency'], ['{@inheritdoc}'])
212 8
            ->newLine('return $this->' . $this->resolverFunction . '($dependency);')
213 8
            ->endClassFunction();
214
215 8
        foreach ($this->classMetadata as $className => $classMetadata) {
216 8
            $dependencyName = $classMetadata->name ?? $className;
217
218
            // Generate camel case getter method
219 8
            $camelMethodName = 'get' . str_replace(' ', '', ucwords(ucfirst(str_replace(['\\', '_'], ' ', $dependencyName))));
220
221 8
            $this->generator
222 8
                ->defClassFunction($camelMethodName, 'public', [], ['@return \\' . $className . ' Get ' . $dependencyName . ' instance'])
223 8
                ->newLine('return $this->' . $this->resolverFunction . '(\'' . $dependencyName . '\');')
224 8
                ->endClassFunction();
225
        }
226
227
        // Build di container function and add to container class and return class code
228 8
        $this->buildDependencyResolver($this->resolverFunction);
229
230 8
        return $this->generator
231 8
            ->endClass()
232 8
            ->flush();
233
    }
234
235
    /**
236
     * Build dependency resolving function.
237
     *
238
     * @param string $functionName Function name
239
     *
240
     * @throws \InvalidArgumentException
241
     */
242 8
    protected function buildDependencyResolver($functionName)
243
    {
244 8
        $inputVariable = '$aliasOrClassName';
245 8
        $this->generator
246 8
            ->defClassFunction($functionName, 'protected', [$inputVariable], ['Dependency resolving function'])
247 8
            ->defVar('static ' . self::DI_FUNCTION_SERVICES . ' = []')
248 8
            ->newLine();
249
250
        // Generate all container and delegate conditions
251 8
        $this->generateConditions($inputVariable, false);
252
253
        // Add method not found
254 8
        $this->generator->endIfCondition()->endFunction();
255 8
    }
256
257
    /**
258
     * Generate logic conditions and their implementation for container and its delegates.
259
     *
260
     * @param string     $inputVariable Input condition parameter variable name
261
     * @param bool|false $started       Flag if condition branching has been started
262
     */
263 8
    public function generateConditions($inputVariable = '$alias', $started = false)
264
    {
265
        // Iterate all container dependencies
266 8
        foreach ($this->classMetadata as $className => $classMetadata) {
267
            // Generate condition statement to define if this class is needed
268 8
            $conditionFunc = !$started ? 'defIfCondition' : 'defElseIfCondition';
269
270
            // Output condition branch
271 8
            $this->generator->$conditionFunc(
272 8
                $this->buildResolverCondition($inputVariable, $className, $classMetadata->name)
273
            );
274
275
            // Define if this class has service scope
276 8
            $isService = in_array($className, $this->scopes[self::SCOPE_SERVICES], true);
277
278
            /** @var MethodMetadata[] Gather only valid method for container */
279 8
            $classValidMethods = $this->getValidClassMethodsMetadata($classMetadata->methodsMetadata);
280
281
            /** @var PropertyMetadata[] Gather only valid property for container */
282 8
            $classValidProperties = $this->getValidClassPropertiesMetadata($classMetadata->propertiesMetadata);
283
284
            // Define class or service variable
285 8
            $staticContainerName = $isService
286 8
                ? self::DI_FUNCTION_SERVICES . '[\'' . $classMetadata->name . '\']'
287 8
                : '$temp';
288
289 8
            if ($isService) {
290
                // Check if dependency was instantiated
291 8
                $this->generator->defIfCondition('!array_key_exists(\'' . $className . '\', ' . self::DI_FUNCTION_SERVICES . ')');
292
            }
293
294 8
            if (count($classValidMethods) || count($classValidProperties)) {
295 8
                $this->generator->newLine($staticContainerName . ' = ');
296 8
                $this->buildResolvingClassDeclaration($className);
297 8
                $this->buildConstructorDependencies($classMetadata->methodsMetadata);
298
299
                // Internal scope reflection variable
300 8
                $reflectionVariable = '$reflectionClass';
301
302 8
                $this->buildReflectionClass($className, $classValidProperties, $classValidMethods, $reflectionVariable);
303
304
                // Process class properties
305 8
                foreach ($classValidProperties as $property) {
306
                    // If such property has the dependency
307 8
                    if ($property->dependency) {
308
                        // Set value via refection
309 8
                        $this->buildResolverPropertyDeclaration(
310 8
                            $property->name,
311 8
                            $property->dependency,
312
                            $staticContainerName,
313
                            $reflectionVariable,
314 8
                            $property->isPublic
315
                        );
316
                    }
317
                }
318
319
                /** @var MethodMetadata $methodMetadata */
320 8
                foreach ($classValidMethods as $methodName => $methodMetadata) {
321 8
                    $this->buildResolverMethodDeclaration(
322 8
                        $methodMetadata->dependencies,
323
                        $methodName,
324
                        $staticContainerName,
325
                        $reflectionVariable,
326 8
                        $methodMetadata->isPublic
327
                    );
328
                }
329
330 8
                if ($isService) {
331 8
                    $this->generator->endIfCondition();
332
                }
333
334 8
                $this->generator->newLine()->newLine('return ' . $staticContainerName . ';');
335
            } else {
336 8
                $this->generator->newLine('return ');
337 8
                $this->buildResolvingClassDeclaration($className);
338 8
                $this->buildConstructorDependencies($classMetadata->methodsMetadata);
339
340 8
                if ($isService) {
341 1
                    $this->generator->endIfCondition()->newLine('return ' . $staticContainerName . ';');
342
                }
343
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
344
            }
345
346
            // Set flag that condition is started
347 8
            $started = true;
348
        }
349 8
    }
350
351
    /**
352
     * Build resolving function condition.
353
     *
354
     * @param string      $inputVariable Condition variable
355
     * @param string      $className
356
     * @param string|null $alias
357
     *
358
     * @return string Condition code
359
     */
360 8
    protected function buildResolverCondition(string $inputVariable, string $className, string $alias = null) : string
361
    {
362
        // Create condition branch
363 8
        $condition = $inputVariable . ' === \'' . $className . '\'';
364
365 8
        if ($alias !== null && $alias !== $className) {
366 8
            $condition .= '||' . $this->buildResolverCondition($inputVariable, $alias);
367
        }
368
369 8
        return $condition;
370
    }
371
372
    /**
373
     * Get valid class methods metadata.
374
     *
375
     * @param MethodMetadata[] $classMethodsMetadata All class methods metadata
376
     *
377
     * @return array Valid class methods metadata
378
     */
379 8
    protected function getValidClassMethodsMetadata(array $classMethodsMetadata)
380
    {
381
        /** @var MethodMetadata[] Gather only valid method for container */
382 8
        $classValidMethods = [];
383 8
        foreach ($classMethodsMetadata as $methodName => $methodMetadata) {
384
            // Skip constructor method and empty dependencies
385 8
            if ($methodName !== '__construct' && count($methodMetadata->dependencies) > 0) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $methodName (integer) and '__construct' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
386 8
                $classValidMethods[$methodName] = $methodMetadata;
387
            }
388
        }
389
390 8
        return $classValidMethods;
391
    }
392
393
    /**
394
     * Get valid class properties metadata.
395
     *
396
     * @param PropertyMetadata[] $classPropertiesMetadata All class properties metadata
397
     *
398
     * @return array Valid class properties metadata
399
     */
400 8
    protected function getValidClassPropertiesMetadata(array $classPropertiesMetadata)
401
    {
402
        /** @var PropertyMetadata[] Gather only valid property for container */
403 8
        $classValidProperties = [];
404 8
        foreach ($classPropertiesMetadata as $propertyName => $propertyMetadata) {
405
            // Skip constructor method and empty dependencies
406 8
            if ($propertyMetadata->dependency) {
407 8
                $classValidProperties[$propertyName] = $propertyMetadata;
408
            }
409
        }
410
411 8
        return $classValidProperties;
412
    }
413
414
    /**
415
     * Build resolving function class block.
416
     *
417
     * @param string $className Class name for new instance creation
418
     */
419 8
    protected function buildResolvingClassDeclaration(string $className)
420
    {
421 8
        $this->generator->text('new \\' . ltrim($className, '\\') . '(');
422 8
    }
423
424
    /**
425
     * Build constructor arguments injection.
426
     *
427
     * @param MethodMetadata[] $methodsMetaData
428
     */
429 8
    protected function buildConstructorDependencies(array $methodsMetaData)
430
    {
431
        // Process constructor dependencies
432 8
        $argumentsCount = 0;
433 8
        if (array_key_exists('__construct', $methodsMetaData)) {
434 8
            $constructorArguments = $methodsMetaData['__construct']->dependencies;
435 8
            $argumentsCount = count($constructorArguments);
436 8
            $i = 0;
437
438
            // Add indentation to move declaration arguments
439 8
            $this->generator->tabs++;
440
441
            // Process constructor arguments
442 8
            foreach ($constructorArguments as $argument => $dependency) {
443 8
                $this->buildResolverArgument($dependency);
444
445
                // Add comma if this is not last dependency
446 8
                if (++$i < $argumentsCount) {
447 8
                    $this->generator->text(',');
448
                }
449
            }
450
451
            // Restore indentation
452 8
            $this->generator->tabs--;
453
        }
454
455
        // Close declaration block, multiline if we have dependencies
456 8
        $argumentsCount ? $this->generator->newLine(');') : $this->generator->text(');');
457 8
    }
458
459
    /**
460
     * Build resolving function dependency argument.
461
     *
462
     * @param mixed $argument Dependency argument
463
     */
464 8
    protected function buildResolverArgument($argument, $textFunction = 'newLine')
465
    {
466
        // This is a dependency which invokes resolving function
467 8
        if (array_key_exists($argument, $this->classMetadata)) {
468
            // Call container logic for this dependency
469 8
            $this->generator->$textFunction('$this->' . $this->resolverFunction . '(\'' . $argument . '\')');
470 1
        } elseif (array_key_exists($argument, $this->classAliases)) {
471
            // Call container logic for this dependency
472 1
            $this->generator->$textFunction('$this->' . $this->resolverFunction . '(\'' . $argument . '\')');
473
        } elseif (is_string($argument)) { // String variable
474
            $this->generator->$textFunction()->stringValue($argument);
475
        } elseif (is_array($argument)) { // Dependency value is array
476
            $this->generator->$textFunction()->arrayValue($argument);
477
        }
478 8
    }
479
480
    /**
481
     * Generate reflection class for private/protected methods or properties
482
     * in current scope.
483
     *
484
     * @param string             $className          Reflection class source class name
485
     * @param PropertyMetadata[] $propertiesMetadata Properties metadata
486
     * @param MethodMetadata[]   $methodsMetadata    Methods metadata
487
     * @param string             $reflectionVariable Reflection class variable name
488
     */
489 8
    protected function buildReflectionClass(string $className, array $propertiesMetadata, array $methodsMetadata, string $reflectionVariable)
490
    {
491
        /** @var bool $reflectionClassCreated Flag showing that reflection class already created in current scope */
492 8
        $reflectionClassCreated = false;
493
494
        /**
495
         * Iterate all properties and create internal scope reflection class instance if
496
         * at least one property in not public
497
         */
498 8 View Code Duplication
        foreach ($propertiesMetadata as $propertyMetadata) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
499 8
            if (!$propertyMetadata->isPublic) {
500 8
                $this->generator
501 8
                    ->comment('Create reflection class for injecting private/protected properties and methods')
502 8
                    ->newLine($reflectionVariable . ' = new \ReflectionClass(\'' . $className . '\');')
503 8
                    ->newLine();
504
505 8
                $reflectionClassCreated = true;
506
507 8
                break;
508
            }
509
        }
510
511
        /**
512
         * Iterate all properties and create internal scope reflection class instance if
513
         * at least one property in not public
514
         */
515 8
        if (!$reflectionClassCreated) {
516 8 View Code Duplication
            foreach ($methodsMetadata as $methodMetadata) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
517 8
                if (!$methodMetadata->isPublic) {
518
                    $this->generator
519
                        ->comment('Create reflection class for injecting private/protected properties and methods')
520
                        ->newLine($reflectionVariable . ' = new \ReflectionClass(\'' . $className . '\');')
521
                        ->newLine();
522
523 8
                    break;
524
                }
525
            }
526
        }
527 8
    }
528
529
    /**
530
     * Build resolving property injection declaration.
531
     *
532
     * @param string $propertyName       Target property name
533
     * @param string $dependency         Dependency class name
534
     * @param string $containerVariable  Container declaration variable name
535
     * @param string $reflectionVariable Reflection class variable name
536
     * @param bool   $isPublic           Flag if property is public
537
     */
538 8
    protected function buildResolverPropertyDeclaration(
539
        string $propertyName,
540
        string $dependency,
541
        string $containerVariable,
542
        string $reflectionVariable,
543
        bool $isPublic
544
    )
545
    {
0 ignored issues
show
Coding Style introduced by
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
546 8
        if ($isPublic) {
547 8
            $this->generator
548 8
                ->comment('Inject public dependency for $' . $propertyName)
549 8
                ->newLine($containerVariable . '->' . $propertyName . ' = ');
550 8
            $this->buildResolverArgument($dependency, 'text');
551 8
            $this->generator->text(';');
552
        } else {
553 8
            $this->generator
554 8
                ->comment('Inject private dependency for $' . $propertyName)
555 8
                ->newLine('$property = ' . $reflectionVariable . '->getProperty(\'' . $propertyName . '\');')
556 8
                ->newLine('$property->setAccessible(true);')
557 8
                ->newLine('$property->setValue(')
558 8
                ->increaseIndentation()
559 8
                ->newLine($containerVariable . ',');
560
561 8
            $this->buildResolverArgument($dependency);
562
563 8
            $this->generator
564 8
                ->decreaseIndentation()
565 8
                ->newLine(');')
566 8
                ->newLine('$property->setAccessible(false);')
567 8
                ->newLine();
568
        }
569 8
    }
570
571
    /**
572
     * Build resolving method injection declaration.
573
     *
574
     * @param array  $dependencies       Collection of method dependencies
575
     * @param string $methodName         Method name
576
     * @param string $containerVariable  Container declaration variable name
577
     * @param string $reflectionVariable Reflection class variable name
578
     * @param bool   $isPublic           Flag if method is public
579
     */
580 8
    protected function buildResolverMethodDeclaration(
581
        array $dependencies,
582
        string $methodName,
583
        string $containerVariable,
584
        string $reflectionVariable,
585
        bool $isPublic
586
    )
587
    {
0 ignored issues
show
Coding Style introduced by
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
588
        // Get method arguments
589 8
        $argumentsCount = count($dependencies);
590
591 8
        $this->generator->comment('Invoke ' . $methodName . '() and pass dependencies(y)');
592
593 8
        if ($isPublic) {
594 8
            $this->generator->newLine($containerVariable . '->' . $methodName . '(')->increaseIndentation();
595
        } else {
596 8
            $this->generator
597 8
                ->newLine('$method = ' . $reflectionVariable . '->getMethod(\'' . $methodName . '\');')
598 8
                ->newLine('$method->setAccessible(true);')
599 8
                ->newLine('$method->invoke(')
600 8
                ->increaseIndentation()
601 8
                ->newLine($containerVariable . ',');
602
        }
603
604 8
        $i = 0;
605
        // Iterate method arguments
606 8
        foreach ($dependencies as $argument => $dependency) {
607
            // Add dependencies
608 8
            $this->buildResolverArgument($dependency);
609
610
            // Add comma if this is not last dependency
611 8
            if (++$i < $argumentsCount) {
612 8
                $this->generator->text(',');
613
            }
614
        }
615
616 8
        $this->generator->decreaseIndentation()->newLine(');');
617 8
    }
618
}
619