PhpDumper::addService()   F
last analyzed

Complexity

Conditions 25
Paths 3840

Size

Total Lines 115
Code Lines 74

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 115
rs 2
cc 25
eloc 74
nc 3840
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Symfony\Component\DependencyInjection\Dumper;
13
14
use Symfony\Component\DependencyInjection\Variable;
15
use Symfony\Component\DependencyInjection\Definition;
16
use Symfony\Component\DependencyInjection\ContainerBuilder;
17
use Symfony\Component\DependencyInjection\Container;
18
use Symfony\Component\DependencyInjection\ContainerInterface;
19
use Symfony\Component\DependencyInjection\Reference;
20
use Symfony\Component\DependencyInjection\Parameter;
21
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
22
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
23
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
24
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper;
25
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
26
use Symfony\Component\DependencyInjection\ExpressionLanguage;
27
use Symfony\Component\ExpressionLanguage\Expression;
28
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
29
30
/**
31
 * PhpDumper dumps a service container as a PHP class.
32
 *
33
 * @author Fabien Potencier <[email protected]>
34
 * @author Johannes M. Schmitt <[email protected]>
35
 *
36
 * @api
37
 */
38
class PhpDumper extends Dumper
39
{
40
    /**
41
     * Characters that might appear in the generated variable name as first character.
42
     *
43
     * @var string
44
     */
45
    const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz';
46
47
    /**
48
     * Characters that might appear in the generated variable name as any but the first character.
49
     *
50
     * @var string
51
     */
52
    const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_';
53
54
    private $inlinedDefinitions;
55
    private $definitionVariables;
56
    private $referenceVariables;
57
    private $variableCount;
58
    private $reservedVariables = array('instance', 'class');
59
    private $expressionLanguage;
60
    private $targetDirRegex;
61
    private $targetDirMaxMatches;
62
63
    /**
64
     * @var ExpressionFunctionProviderInterface[]
65
     */
66
    private $expressionLanguageProviders = array();
67
68
    /**
69
     * @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
70
     */
71
    private $proxyDumper;
72
73
    /**
74
     * {@inheritdoc}
75
     *
76
     * @api
77
     */
78
    public function __construct(ContainerBuilder $container)
79
    {
80
        parent::__construct($container);
81
82
        $this->inlinedDefinitions = new \SplObjectStorage();
83
    }
84
85
    /**
86
     * Sets the dumper to be used when dumping proxies in the generated container.
87
     *
88
     * @param ProxyDumper $proxyDumper
89
     */
90
    public function setProxyDumper(ProxyDumper $proxyDumper)
91
    {
92
        $this->proxyDumper = $proxyDumper;
93
    }
94
95
    /**
96
     * Dumps the service container as a PHP class.
97
     *
98
     * Available options:
99
     *
100
     *  * class:      The class name
101
     *  * base_class: The base class name
102
     *  * namespace:  The class namespace
103
     *
104
     * @param array $options An array of options
105
     *
106
     * @return string A PHP class representing of the service container
107
     *
108
     * @api
109
     */
110
    public function dump(array $options = array())
111
    {
112
        $this->targetDirRegex = null;
113
        $options = array_merge(array(
114
            'class' => 'ProjectServiceContainer',
115
            'base_class' => 'Container',
116
            'namespace' => '',
117
        ), $options);
118
119
        if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) {
120
            // Build a regexp where the first root dirs are mandatory,
121
            // but every other sub-dir is optional up to the full path in $dir
122
            // Mandate at least 2 root dirs and not more that 5 optional dirs.
123
124
            $dir = explode(DIRECTORY_SEPARATOR, realpath($dir));
125
            $i = count($dir);
126
127
            if (3 <= $i) {
128
                $regex = '';
129
                $lastOptionalDir = $i > 8 ? $i - 5 : 3;
130
                $this->targetDirMaxMatches = $i - $lastOptionalDir;
131
132 View Code Duplication
                while (--$i >= $lastOptionalDir) {
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...
133
                    $regex = sprintf('(%s%s)?', preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex);
134
                }
135
136 View Code Duplication
                do {
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...
137
                    $regex = preg_quote(DIRECTORY_SEPARATOR.$dir[$i], '#').$regex;
138
                } while (0 < --$i);
139
140
                $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#';
141
            }
142
        }
143
144
        $code = $this->startClass($options['class'], $options['base_class'], $options['namespace']);
145
146
        if ($this->container->isFrozen()) {
147
            $code .= $this->addFrozenConstructor();
148
            $code .= $this->addFrozenCompile();
149
        } else {
150
            $code .= $this->addConstructor();
151
        }
152
153
        $code .=
154
            $this->addServices().
155
            $this->addDefaultParametersMethod().
156
            $this->endClass().
157
            $this->addProxyClasses()
158
        ;
159
        $this->targetDirRegex = null;
160
161
        return $code;
162
    }
163
164
    /**
165
     * Retrieves the currently set proxy dumper or instantiates one.
166
     *
167
     * @return ProxyDumper
168
     */
169
    private function getProxyDumper()
170
    {
171
        if (!$this->proxyDumper) {
172
            $this->proxyDumper = new NullDumper();
173
        }
174
175
        return $this->proxyDumper;
176
    }
177
178
    /**
179
     * Generates Service local temp variables.
180
     *
181
     * @param string $cId
182
     * @param string $definition
183
     *
184
     * @return string
185
     */
186
    private function addServiceLocalTempVariables($cId, $definition)
187
    {
188
        static $template = "        \$%s = %s;\n";
189
190
        $localDefinitions = array_merge(
191
            array($definition),
192
            $this->getInlinedDefinitions($definition)
0 ignored issues
show
Documentation introduced by
$definition is of type string, but the function expects a object<Symfony\Component...cyInjection\Definition>.

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...
193
        );
194
195
        $calls = $behavior = array();
196
        foreach ($localDefinitions as $iDefinition) {
197
            $this->getServiceCallsFromArguments($iDefinition->getArguments(), $calls, $behavior);
198
            $this->getServiceCallsFromArguments($iDefinition->getMethodCalls(), $calls, $behavior);
199
            $this->getServiceCallsFromArguments($iDefinition->getProperties(), $calls, $behavior);
200
            $this->getServiceCallsFromArguments(array($iDefinition->getConfigurator()), $calls, $behavior);
201
            $this->getServiceCallsFromArguments(array($iDefinition->getFactory()), $calls, $behavior);
202
        }
203
204
        $code = '';
205
        foreach ($calls as $id => $callCount) {
206
            if ('service_container' === $id || $id === $cId) {
207
                continue;
208
            }
209
210
            if ($callCount > 1) {
211
                $name = $this->getNextVariableName();
212
                $this->referenceVariables[$id] = new Variable($name);
213
214
                if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
215
                    $code .= sprintf($template, $name, $this->getServiceCall($id));
216
                } else {
217
                    $code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
218
                }
219
            }
220
        }
221
222
        if ('' !== $code) {
223
            $code .= "\n";
224
        }
225
226
        return $code;
227
    }
228
229
    /**
230
     * Generates code for the proxies to be attached after the container class.
231
     *
232
     * @return string
233
     */
234
    private function addProxyClasses()
235
    {
236
        /* @var $definitions Definition[] */
237
        $definitions = array_filter(
238
            $this->container->getDefinitions(),
239
            array($this->getProxyDumper(), 'isProxyCandidate')
240
        );
241
        $code = '';
242
243
        foreach ($definitions as $definition) {
244
            $code .= "\n".$this->getProxyDumper()->getProxyCode($definition);
245
        }
246
247
        return $code;
248
    }
249
250
    /**
251
     * Generates the require_once statement for service includes.
252
     *
253
     * @param string     $id         The service id
254
     * @param Definition $definition
255
     *
256
     * @return string
257
     */
258
    private function addServiceInclude($id, $definition)
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
259
    {
260
        $template = "        require_once %s;\n";
261
        $code = '';
262
263 View Code Duplication
        if (null !== $file = $definition->getFile()) {
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...
264
            $code .= sprintf($template, $this->dumpValue($file));
265
        }
266
267
        foreach ($this->getInlinedDefinitions($definition) as $definition) {
268 View Code Duplication
            if (null !== $file = $definition->getFile()) {
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...
269
                $code .= sprintf($template, $this->dumpValue($file));
270
            }
271
        }
272
273
        if ('' !== $code) {
274
            $code .= "\n";
275
        }
276
277
        return $code;
278
    }
279
280
    /**
281
     * Generates the inline definition of a service.
282
     *
283
     * @param string     $id
284
     * @param Definition $definition
285
     *
286
     * @return string
287
     *
288
     * @throws RuntimeException                  When the factory definition is incomplete
289
     * @throws ServiceCircularReferenceException When a circular reference is detected
290
     */
291
    private function addServiceInlinedDefinitions($id, $definition)
292
    {
293
        $code = '';
294
        $variableMap = $this->definitionVariables;
295
        $nbOccurrences = new \SplObjectStorage();
296
        $processed = new \SplObjectStorage();
297
        $inlinedDefinitions = $this->getInlinedDefinitions($definition);
298
299
        foreach ($inlinedDefinitions as $definition) {
300
            if (false === $nbOccurrences->contains($definition)) {
301
                $nbOccurrences->offsetSet($definition, 1);
302
            } else {
303
                $i = $nbOccurrences->offsetGet($definition);
304
                $nbOccurrences->offsetSet($definition, $i + 1);
305
            }
306
        }
307
308
        foreach ($inlinedDefinitions as $sDefinition) {
309
            if ($processed->contains($sDefinition)) {
310
                continue;
311
            }
312
            $processed->offsetSet($sDefinition);
313
314
            $class = $this->dumpValue($sDefinition->getClass());
315
            if ($nbOccurrences->offsetGet($sDefinition) > 1 || $sDefinition->getMethodCalls() || $sDefinition->getProperties() || null !== $sDefinition->getConfigurator() || false !== strpos($class, '$')) {
316
                $name = $this->getNextVariableName();
317
                $variableMap->offsetSet($sDefinition, new Variable($name));
318
319
                // a construct like:
320
                // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
321
                // this is an indication for a wrong implementation, you can circumvent this problem
322
                // by setting up your service structure like this:
323
                // $b = new ServiceB();
324
                // $a = new ServiceA(ServiceB $b);
325
                // $b->setServiceA(ServiceA $a);
326
                if ($this->hasReference($id, $sDefinition->getArguments())) {
327
                    throw new ServiceCircularReferenceException($id, array($id));
328
                }
329
330
                $code .= $this->addNewInstance($id, $sDefinition, '$'.$name, ' = ');
331
332
                if (!$this->hasReference($id, $sDefinition->getMethodCalls(), true) && !$this->hasReference($id, $sDefinition->getProperties(), true)) {
333
                    $code .= $this->addServiceMethodCalls(null, $sDefinition, $name);
334
                    $code .= $this->addServiceProperties(null, $sDefinition, $name);
335
                    $code .= $this->addServiceConfigurator(null, $sDefinition, $name);
336
                }
337
338
                $code .= "\n";
339
            }
340
        }
341
342
        return $code;
343
    }
344
345
    /**
346
     * Adds the service return statement.
347
     *
348
     * @param string     $id         Service id
349
     * @param Definition $definition
350
     *
351
     * @return string
352
     */
353
    private function addServiceReturn($id, $definition)
354
    {
355
        if ($this->isSimpleInstance($id, $definition)) {
356
            return "    }\n";
357
        }
358
359
        return "\n        return \$instance;\n    }\n";
360
    }
361
362
    /**
363
     * Generates the service instance.
364
     *
365
     * @param string     $id
366
     * @param Definition $definition
367
     *
368
     * @return string
369
     *
370
     * @throws InvalidArgumentException
371
     * @throws RuntimeException
372
     */
373
    private function addServiceInstance($id, $definition)
374
    {
375
        $class = $this->dumpValue($definition->getClass());
376
377
        if (0 === strpos($class, "'") && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) {
378
            throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id));
379
        }
380
381
        $simple = $this->isSimpleInstance($id, $definition);
382
        $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
383
        $instantiation = '';
384
385
        if (!$isProxyCandidate && ContainerInterface::SCOPE_CONTAINER === $definition->getScope()) {
386
            $instantiation = "\$this->services['$id'] = ".($simple ? '' : '$instance');
387
        } elseif (!$isProxyCandidate && ContainerInterface::SCOPE_PROTOTYPE !== $scope = $definition->getScope()) {
388
            $instantiation = "\$this->services['$id'] = \$this->scopedServices['$scope']['$id'] = ".($simple ? '' : '$instance');
389
        } elseif (!$simple) {
390
            $instantiation = '$instance';
391
        }
392
393
        $return = '';
394
        if ($simple) {
395
            $return = 'return ';
396
        } else {
397
            $instantiation .= ' = ';
398
        }
399
400
        $code = $this->addNewInstance($id, $definition, $return, $instantiation);
401
402
        if (!$simple) {
403
            $code .= "\n";
404
        }
405
406
        return $code;
407
    }
408
409
    /**
410
     * Checks if the definition is a simple instance.
411
     *
412
     * @param string     $id
413
     * @param Definition $definition
414
     *
415
     * @return bool
416
     */
417
    private function isSimpleInstance($id, $definition)
418
    {
419
        foreach (array_merge(array($definition), $this->getInlinedDefinitions($definition)) as $sDefinition) {
420
            if ($definition !== $sDefinition && !$this->hasReference($id, $sDefinition->getMethodCalls())) {
421
                continue;
422
            }
423
424
            if ($sDefinition->getMethodCalls() || $sDefinition->getProperties() || $sDefinition->getConfigurator()) {
425
                return false;
426
            }
427
        }
428
429
        return true;
430
    }
431
432
    /**
433
     * Adds method calls to a service definition.
434
     *
435
     * @param string     $id
436
     * @param Definition $definition
437
     * @param string     $variableName
438
     *
439
     * @return string
440
     */
441
    private function addServiceMethodCalls($id, $definition, $variableName = 'instance')
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
442
    {
443
        $calls = '';
444
        foreach ($definition->getMethodCalls() as $call) {
445
            $arguments = array();
446
            foreach ($call[1] as $value) {
447
                $arguments[] = $this->dumpValue($value);
448
            }
449
450
            $calls .= $this->wrapServiceConditionals($call[1], sprintf("        \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments)));
451
        }
452
453
        return $calls;
454
    }
455
456
    private function addServiceProperties($id, $definition, $variableName = 'instance')
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
457
    {
458
        $code = '';
459
        foreach ($definition->getProperties() as $name => $value) {
460
            $code .= sprintf("        \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value));
461
        }
462
463
        return $code;
464
    }
465
466
    /**
467
     * Generates the inline definition setup.
468
     *
469
     * @param string     $id
470
     * @param Definition $definition
471
     *
472
     * @return string
473
     *
474
     * @throws ServiceCircularReferenceException when the container contains a circular reference
475
     */
476
    private function addServiceInlinedDefinitionsSetup($id, $definition)
477
    {
478
        $this->referenceVariables[$id] = new Variable('instance');
479
480
        $code = '';
481
        $processed = new \SplObjectStorage();
482
        foreach ($this->getInlinedDefinitions($definition) as $iDefinition) {
483
            if ($processed->contains($iDefinition)) {
484
                continue;
485
            }
486
            $processed->offsetSet($iDefinition);
487
488
            if (!$this->hasReference($id, $iDefinition->getMethodCalls(), true) && !$this->hasReference($id, $iDefinition->getProperties(), true)) {
489
                continue;
490
            }
491
492
            // if the instance is simple, the return statement has already been generated
493
            // so, the only possible way to get there is because of a circular reference
494
            if ($this->isSimpleInstance($id, $definition)) {
495
                throw new ServiceCircularReferenceException($id, array($id));
496
            }
497
498
            $name = (string) $this->definitionVariables->offsetGet($iDefinition);
499
            $code .= $this->addServiceMethodCalls(null, $iDefinition, $name);
500
            $code .= $this->addServiceProperties(null, $iDefinition, $name);
501
            $code .= $this->addServiceConfigurator(null, $iDefinition, $name);
502
        }
503
504
        if ('' !== $code) {
505
            $code .= "\n";
506
        }
507
508
        return $code;
509
    }
510
511
    /**
512
     * Adds configurator definition.
513
     *
514
     * @param string     $id
515
     * @param Definition $definition
516
     * @param string     $variableName
517
     *
518
     * @return string
519
     */
520
    private function addServiceConfigurator($id, $definition, $variableName = 'instance')
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
521
    {
522
        if (!$callable = $definition->getConfigurator()) {
523
            return '';
524
        }
525
526
        if (is_array($callable)) {
527
            if ($callable[0] instanceof Reference
528
                || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
529
                return sprintf("        %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
530
            }
531
532
            $class = $this->dumpValue($callable[0]);
533
            // If the class is a string we can optimize call_user_func away
534
            if (strpos($class, "'") === 0) {
535
                return sprintf("        %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName);
536
            }
537
538
            return sprintf("        call_user_func(array(%s, '%s'), \$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName);
539
        }
540
541
        return sprintf("        %s(\$%s);\n", $callable, $variableName);
542
    }
543
544
    /**
545
     * Adds a service.
546
     *
547
     * @param string     $id
548
     * @param Definition $definition
549
     *
550
     * @return string
551
     */
552
    private function addService($id, $definition)
553
    {
554
        $this->definitionVariables = new \SplObjectStorage();
555
        $this->referenceVariables = array();
556
        $this->variableCount = 0;
557
558
        $return = array();
559
560
        if ($definition->isSynthetic()) {
561
            $return[] = '@throws RuntimeException always since this service is expected to be injected dynamically';
562
        } elseif ($class = $definition->getClass()) {
563
            $return[] = sprintf('@return %s A %s instance.', 0 === strpos($class, '%') ? 'object' : '\\'.$class, $class);
564
        } elseif ($definition->getFactory()) {
565
            $factory = $definition->getFactory();
566
            if (is_string($factory)) {
567
                $return[] = sprintf('@return object An instance returned by %s().', $factory);
568
            } elseif (is_array($factory) && (is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) {
569
                if (is_string($factory[0]) || $factory[0] instanceof Reference) {
570
                    $return[] = sprintf('@return object An instance returned by %s::%s().', (string) $factory[0], $factory[1]);
571
                } elseif ($factory[0] instanceof Definition) {
572
                    $return[] = sprintf('@return object An instance returned by %s::%s().', $factory[0]->getClass(), $factory[1]);
573
                }
574
            }
575
        } elseif ($definition->getFactoryClass(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Bug Best Practice introduced by
The expression $definition->getFactoryClass(false) of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
576
            $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryClass(false), $definition->getFactoryMethod(false));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
577
        } elseif ($definition->getFactoryService(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Bug Best Practice introduced by
The expression $definition->getFactoryService(false) of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
578
            $return[] = sprintf('@return object An instance returned by %s::%s().', $definition->getFactoryService(false), $definition->getFactoryMethod(false));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
579
        }
580
581
        $scope = $definition->getScope();
582
        if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
583
            if ($return && 0 === strpos($return[count($return) - 1], '@return')) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $return of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
584
                $return[] = '';
585
            }
586
            $return[] = sprintf("@throws InactiveScopeException when the '%s' service is requested while the '%s' scope is not active", $id, $scope);
587
        }
588
589
        $return = implode("\n     * ", $return);
590
591
        $doc = '';
592
        if (ContainerInterface::SCOPE_PROTOTYPE !== $scope) {
593
            $doc .= <<<EOF
594
595
     *
596
     * This service is shared.
597
     * This method always returns the same instance of the service.
598
EOF;
599
        }
600
601
        if (!$definition->isPublic()) {
602
            $doc .= <<<EOF
603
604
     *
605
     * This service is private.
606
     * If you want to be able to request this service from the container directly,
607
     * make it public, otherwise you might end up with broken code.
608
EOF;
609
        }
610
611
        if ($definition->isLazy()) {
612
            $lazyInitialization = '$lazyLoad = true';
613
            $lazyInitializationDoc = "\n     * @param bool    \$lazyLoad whether to try lazy-loading the service with a proxy\n     *";
614
        } else {
615
            $lazyInitialization = '';
616
            $lazyInitializationDoc = '';
617
        }
618
619
        // with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer
620
        $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
621
        $visibility = $isProxyCandidate ? 'public' : 'protected';
622
        $code = <<<EOF
623
624
    /**
625
     * Gets the '$id' service.$doc
626
     *$lazyInitializationDoc
627
     * $return
628
     */
629
    {$visibility} function get{$this->camelize($id)}Service($lazyInitialization)
630
    {
631
632
EOF;
633
634
        $code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id) : '';
635
636
        if (!in_array($scope, array(ContainerInterface::SCOPE_CONTAINER, ContainerInterface::SCOPE_PROTOTYPE))) {
637
            $code .= <<<EOF
638
        if (!isset(\$this->scopedServices['$scope'])) {
639
            throw new InactiveScopeException('$id', '$scope');
640
        }
641
642
643
EOF;
644
        }
645
646
        if ($definition->isSynthetic()) {
647
            $code .= sprintf("        throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n    }\n", $id);
648
        } else {
649
            $code .=
650
                $this->addServiceInclude($id, $definition).
651
                $this->addServiceLocalTempVariables($id, $definition).
0 ignored issues
show
Documentation introduced by
$definition is of type object<Symfony\Component...cyInjection\Definition>, but the function expects a string.

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...
652
                $this->addServiceInlinedDefinitions($id, $definition).
653
                $this->addServiceInstance($id, $definition).
654
                $this->addServiceInlinedDefinitionsSetup($id, $definition).
655
                $this->addServiceMethodCalls($id, $definition).
656
                $this->addServiceProperties($id, $definition).
657
                $this->addServiceConfigurator($id, $definition).
658
                $this->addServiceReturn($id, $definition)
659
            ;
660
        }
661
662
        $this->definitionVariables = null;
663
        $this->referenceVariables = null;
664
665
        return $code;
666
    }
667
668
    /**
669
     * Adds multiple services.
670
     *
671
     * @return string
672
     */
673
    private function addServices()
674
    {
675
        $publicServices = $privateServices = $synchronizers = '';
676
        $definitions = $this->container->getDefinitions();
677
        ksort($definitions);
678
        foreach ($definitions as $id => $definition) {
679
            if ($definition->isPublic()) {
680
                $publicServices .= $this->addService($id, $definition);
681
            } else {
682
                $privateServices .= $this->addService($id, $definition);
683
            }
684
685
            $synchronizers .= $this->addServiceSynchronizer($id, $definition);
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ddServiceSynchronizer() has been deprecated with message: since version 2.7, will be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
686
        }
687
688
        return $publicServices.$synchronizers.$privateServices;
689
    }
690
691
    /**
692
     * Adds synchronizer methods.
693
     *
694
     * @param string     $id         A service identifier
695
     * @param Definition $definition A Definition instance
696
     *
697
     * @return string|null
698
     *
699
     * @deprecated since version 2.7, will be removed in 3.0.
700
     */
701
    private function addServiceSynchronizer($id, Definition $definition)
702
    {
703
        if (!$definition->isSynchronized(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ition::isSynchronized() has been deprecated with message: since version 2.7, will be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
704
            return;
705
        }
706
707
        if ('request' !== $id) {
708
            trigger_error('Synchronized services were deprecated in version 2.7 and won\'t work anymore in 3.0.', E_USER_DEPRECATED);
709
        }
710
711
        $code = '';
712
        foreach ($this->container->getDefinitions() as $definitionId => $definition) {
713
            foreach ($definition->getMethodCalls() as $call) {
714
                foreach ($call[1] as $argument) {
715
                    if ($argument instanceof Reference && $id == (string) $argument) {
716
                        $arguments = array();
717
                        foreach ($call[1] as $value) {
718
                            $arguments[] = $this->dumpValue($value);
719
                        }
720
721
                        $call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments)));
722
723
                        $code .= <<<EOF
724
        if (\$this->initialized('$definitionId')) {
725
            $call
726
        }
727
728
EOF;
729
                    }
730
                }
731
            }
732
        }
733
734
        if (!$code) {
735
            return;
736
        }
737
738
        return <<<EOF
739
740
    /**
741
     * Updates the '$id' service.
742
     */
743
    protected function synchronize{$this->camelize($id)}Service()
744
    {
745
$code    }
746
747
EOF;
748
    }
749
750
    private function addNewInstance($id, Definition $definition, $return, $instantiation)
751
    {
752
        $class = $this->dumpValue($definition->getClass());
753
754
        $arguments = array();
755
        foreach ($definition->getArguments() as $value) {
756
            $arguments[] = $this->dumpValue($value);
757
        }
758
759
        if (null !== $definition->getFactory()) {
760
            $callable = $definition->getFactory();
761
            if (is_array($callable)) {
762
                if ($callable[0] instanceof Reference
763
                    || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) {
764
                    return sprintf("        $return{$instantiation}%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : '');
765
                }
766
767
                $class = $this->dumpValue($callable[0]);
768
                // If the class is a string we can optimize call_user_func away
769 View Code Duplication
                if (strpos($class, "'") === 0) {
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...
770
                    return sprintf("        $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : '');
771
                }
772
773
                return sprintf("        $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? ', '.implode(', ', $arguments) : '');
774
            }
775
776
            return sprintf("        $return{$instantiation}\\%s(%s);\n", $callable, $arguments ? implode(', ', $arguments) : '');
777
        } elseif (null !== $definition->getFactoryMethod(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
778
            if (null !== $definition->getFactoryClass(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
779
                $class = $this->dumpValue($definition->getFactoryClass(false));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
780
781
                // If the class is a string we can optimize call_user_func away
782 View Code Duplication
                if (strpos($class, "'") === 0) {
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...
783
                    return sprintf("        $return{$instantiation}%s::%s(%s);\n", $this->dumpLiteralClass($class), $definition->getFactoryMethod(false), $arguments ? implode(', ', $arguments) : '');
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
784
                }
785
786
                return sprintf("        $return{$instantiation}call_user_func(array(%s, '%s')%s);\n", $this->dumpValue($definition->getFactoryClass(false)), $definition->getFactoryMethod(false), $arguments ? ', '.implode(', ', $arguments) : '');
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
787
            }
788
789
            if (null !== $definition->getFactoryService(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
790
                return sprintf("        $return{$instantiation}%s->%s(%s);\n", $this->getServiceCall($definition->getFactoryService(false)), $definition->getFactoryMethod(false), implode(', ', $arguments));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
791
            }
792
793
            throw new RuntimeException(sprintf('Factory method requires a factory service or factory class in service definition for %s', $id));
794
        }
795
796
        if (false !== strpos($class, '$')) {
797
            return sprintf("        \$class = %s;\n\n        $return{$instantiation}new \$class(%s);\n", $class, implode(', ', $arguments));
798
        }
799
800
        return sprintf("        $return{$instantiation}new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments));
801
    }
802
803
    /**
804
     * Adds the class headers.
805
     *
806
     * @param string $class     Class name
807
     * @param string $baseClass The name of the base class
808
     * @param string $namespace The class namespace
809
     *
810
     * @return string
811
     */
812
    private function startClass($class, $baseClass, $namespace)
813
    {
814
        $bagClass = $this->container->isFrozen() ? 'use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;' : 'use Symfony\Component\DependencyInjection\ParameterBag\\ParameterBag;';
815
        $namespaceLine = $namespace ? "namespace $namespace;\n" : '';
816
817
        return <<<EOF
818
<?php
819
$namespaceLine
820
use Symfony\Component\DependencyInjection\ContainerInterface;
821
use Symfony\Component\DependencyInjection\Container;
822
use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
823
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
824
use Symfony\Component\DependencyInjection\Exception\LogicException;
825
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
826
$bagClass
827
828
/**
829
 * $class.
830
 *
831
 * This class has been auto-generated
832
 * by the Symfony Dependency Injection Component.
833
 */
834
class $class extends $baseClass
835
{
836
    private \$parameters;
837
    private \$targetDirs = array();
838
839
EOF;
840
    }
841
842
    /**
843
     * Adds the constructor.
844
     *
845
     * @return string
846
     */
847
    private function addConstructor()
848
    {
849
        $targetDirs = $this->exportTargetDirs();
850
        $arguments = $this->container->getParameterBag()->all() ? 'new ParameterBag($this->getDefaultParameters())' : null;
851
852
        $code = <<<EOF
853
854
    /**
855
     * Constructor.
856
     */
857
    public function __construct()
858
    {{$targetDirs}
859
        parent::__construct($arguments);
860
861
EOF;
862
863 View Code Duplication
        if (count($scopes = $this->container->getScopes()) > 0) {
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...
864
            $code .= "\n";
865
            $code .= "        \$this->scopes = ".$this->dumpValue($scopes).";\n";
866
            $code .= "        \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
867
        }
868
869
        $code .= $this->addMethodMap();
870
        $code .= $this->addAliases();
871
872
        $code .= <<<EOF
873
    }
874
875
EOF;
876
877
        return $code;
878
    }
879
880
    /**
881
     * Adds the constructor for a frozen container.
882
     *
883
     * @return string
884
     */
885
    private function addFrozenConstructor()
886
    {
887
        $targetDirs = $this->exportTargetDirs();
888
889
        $code = <<<EOF
890
891
    /**
892
     * Constructor.
893
     */
894
    public function __construct()
895
    {{$targetDirs}
896
EOF;
897
898
        if ($this->container->getParameterBag()->all()) {
899
            $code .= "\n        \$this->parameters = \$this->getDefaultParameters();\n";
900
        }
901
902
        $code .= <<<EOF
903
904
        \$this->services =
905
        \$this->scopedServices =
906
        \$this->scopeStacks = array();
907
EOF;
908
909
        $code .= "\n";
910 View Code Duplication
        if (count($scopes = $this->container->getScopes()) > 0) {
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...
911
            $code .= "        \$this->scopes = ".$this->dumpValue($scopes).";\n";
912
            $code .= "        \$this->scopeChildren = ".$this->dumpValue($this->container->getScopeChildren()).";\n";
913
        } else {
914
            $code .= "        \$this->scopes = array();\n";
915
            $code .= "        \$this->scopeChildren = array();\n";
916
        }
917
918
        $code .= $this->addMethodMap();
919
        $code .= $this->addAliases();
920
921
        $code .= <<<EOF
922
    }
923
924
EOF;
925
926
        return $code;
927
    }
928
929
    /**
930
     * Adds the constructor for a frozen container.
931
     *
932
     * @return string
933
     */
934
    private function addFrozenCompile()
935
    {
936
        return <<<EOF
937
938
    /**
939
     * {@inheritdoc}
940
     */
941
    public function compile()
942
    {
943
        throw new LogicException('You cannot compile a dumped frozen container.');
944
    }
945
946
EOF;
947
    }
948
949
    /**
950
     * Adds the methodMap property definition.
951
     *
952
     * @return string
953
     */
954
    private function addMethodMap()
955
    {
956
        if (!$definitions = $this->container->getDefinitions()) {
957
            return '';
958
        }
959
960
        $code = "        \$this->methodMap = array(\n";
961
        ksort($definitions);
962
        foreach ($definitions as $id => $definition) {
963
            $code .= '            '.var_export($id, true).' => '.var_export('get'.$this->camelize($id).'Service', true).",\n";
964
        }
965
966
        return $code."        );\n";
967
    }
968
969
    /**
970
     * Adds the aliases property definition.
971
     *
972
     * @return string
973
     */
974
    private function addAliases()
975
    {
976
        if (!$aliases = $this->container->getAliases()) {
977
            if ($this->container->isFrozen()) {
978
                return "\n        \$this->aliases = array();\n";
979
            } else {
980
                return '';
981
            }
982
        }
983
984
        $code = "        \$this->aliases = array(\n";
985
        ksort($aliases);
986
        foreach ($aliases as $alias => $id) {
987
            $id = (string) $id;
988
            while (isset($aliases[$id])) {
989
                $id = (string) $aliases[$id];
990
            }
991
            $code .= '            '.var_export($alias, true).' => '.var_export($id, true).",\n";
992
        }
993
994
        return $code."        );\n";
995
    }
996
997
    /**
998
     * Adds default parameters method.
999
     *
1000
     * @return string
1001
     */
1002
    private function addDefaultParametersMethod()
1003
    {
1004
        if (!$this->container->getParameterBag()->all()) {
1005
            return '';
1006
        }
1007
1008
        $parameters = $this->exportParameters($this->container->getParameterBag()->all());
1009
1010
        $code = '';
1011
        if ($this->container->isFrozen()) {
1012
            $code .= <<<EOF
1013
1014
    /**
1015
     * {@inheritdoc}
1016
     */
1017
    public function getParameter(\$name)
1018
    {
1019
        \$name = strtolower(\$name);
1020
1021
        if (!(isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters))) {
1022
            throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', \$name));
1023
        }
1024
1025
        return \$this->parameters[\$name];
1026
    }
1027
1028
    /**
1029
     * {@inheritdoc}
1030
     */
1031
    public function hasParameter(\$name)
1032
    {
1033
        \$name = strtolower(\$name);
1034
1035
        return isset(\$this->parameters[\$name]) || array_key_exists(\$name, \$this->parameters);
1036
    }
1037
1038
    /**
1039
     * {@inheritdoc}
1040
     */
1041
    public function setParameter(\$name, \$value)
1042
    {
1043
        throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
1044
    }
1045
1046
    /**
1047
     * {@inheritdoc}
1048
     */
1049
    public function getParameterBag()
1050
    {
1051
        if (null === \$this->parameterBag) {
1052
            \$this->parameterBag = new FrozenParameterBag(\$this->parameters);
1053
        }
1054
1055
        return \$this->parameterBag;
1056
    }
1057
1058
EOF;
1059
        }
1060
1061
        $code .= <<<EOF
1062
1063
    /**
1064
     * Gets the default parameters.
1065
     *
1066
     * @return array An array of the default parameters
1067
     */
1068
    protected function getDefaultParameters()
1069
    {
1070
        return $parameters;
1071
    }
1072
1073
EOF;
1074
1075
        return $code;
1076
    }
1077
1078
    /**
1079
     * Exports parameters.
1080
     *
1081
     * @param array  $parameters
1082
     * @param string $path
1083
     * @param int    $indent
1084
     *
1085
     * @return string
1086
     *
1087
     * @throws InvalidArgumentException
1088
     */
1089
    private function exportParameters($parameters, $path = '', $indent = 12)
1090
    {
1091
        $php = array();
1092
        foreach ($parameters as $key => $value) {
1093
            if (is_array($value)) {
1094
                $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4);
1095
            } elseif ($value instanceof Variable) {
1096
                throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key));
1097
            } elseif ($value instanceof Definition) {
1098
                throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain service definitions. Definition for "%s" found in "%s".', $value->getClass(), $path.'/'.$key));
1099
            } elseif ($value instanceof Reference) {
1100
                throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain references to other services (reference to service "%s" found in "%s").', $value, $path.'/'.$key));
1101
            } elseif ($value instanceof Expression) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\ExpressionLanguage\Expression does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1102
                throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain expressions. Expression "%s" found in "%s".', $value, $path.'/'.$key));
1103
            } else {
1104
                $value = $this->export($value);
1105
            }
1106
1107
            $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), var_export($key, true), $value);
1108
        }
1109
1110
        return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4));
1111
    }
1112
1113
    /**
1114
     * Ends the class definition.
1115
     *
1116
     * @return string
1117
     */
1118
    private function endClass()
1119
    {
1120
        return <<<EOF
1121
}
1122
1123
EOF;
1124
    }
1125
1126
    /**
1127
     * Wraps the service conditionals.
1128
     *
1129
     * @param string $value
1130
     * @param string $code
1131
     *
1132
     * @return string
1133
     */
1134
    private function wrapServiceConditionals($value, $code)
1135
    {
1136
        if (!$services = ContainerBuilder::getServiceConditionals($value)) {
1137
            return $code;
1138
        }
1139
1140
        $conditions = array();
1141
        foreach ($services as $service) {
1142
            $conditions[] = sprintf("\$this->has('%s')", $service);
1143
        }
1144
1145
        // re-indent the wrapped code
1146
        $code = implode("\n", array_map(function ($line) { return $line ? '    '.$line : $line; }, explode("\n", $code)));
1147
1148
        return sprintf("        if (%s) {\n%s        }\n", implode(' && ', $conditions), $code);
1149
    }
1150
1151
    /**
1152
     * Builds service calls from arguments.
1153
     *
1154
     * @param array $arguments
1155
     * @param array &$calls    By reference
1156
     * @param array &$behavior By reference
1157
     */
1158
    private function getServiceCallsFromArguments(array $arguments, array &$calls, array &$behavior)
1159
    {
1160
        foreach ($arguments as $argument) {
1161
            if (is_array($argument)) {
1162
                $this->getServiceCallsFromArguments($argument, $calls, $behavior);
1163
            } elseif ($argument instanceof Reference) {
1164
                $id = (string) $argument;
1165
1166
                if (!isset($calls[$id])) {
1167
                    $calls[$id] = 0;
1168
                }
1169
                if (!isset($behavior[$id])) {
1170
                    $behavior[$id] = $argument->getInvalidBehavior();
1171
                } elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
1172
                    $behavior[$id] = $argument->getInvalidBehavior();
1173
                }
1174
1175
                ++$calls[$id];
1176
            }
1177
        }
1178
    }
1179
1180
    /**
1181
     * Returns the inline definition.
1182
     *
1183
     * @param Definition $definition
1184
     *
1185
     * @return array
1186
     */
1187
    private function getInlinedDefinitions(Definition $definition)
1188
    {
1189
        if (false === $this->inlinedDefinitions->contains($definition)) {
1190
            $definitions = array_merge(
1191
                $this->getDefinitionsFromArguments($definition->getArguments()),
1192
                $this->getDefinitionsFromArguments($definition->getMethodCalls()),
1193
                $this->getDefinitionsFromArguments($definition->getProperties()),
1194
                $this->getDefinitionsFromArguments(array($definition->getConfigurator())),
1195
                $this->getDefinitionsFromArguments(array($definition->getFactory()))
1196
            );
1197
1198
            $this->inlinedDefinitions->offsetSet($definition, $definitions);
1199
1200
            return $definitions;
1201
        }
1202
1203
        return $this->inlinedDefinitions->offsetGet($definition);
1204
    }
1205
1206
    /**
1207
     * Gets the definition from arguments.
1208
     *
1209
     * @param array $arguments
1210
     *
1211
     * @return array
1212
     */
1213
    private function getDefinitionsFromArguments(array $arguments)
1214
    {
1215
        $definitions = array();
1216
        foreach ($arguments as $argument) {
1217
            if (is_array($argument)) {
1218
                $definitions = array_merge($definitions, $this->getDefinitionsFromArguments($argument));
1219
            } elseif ($argument instanceof Definition) {
1220
                $definitions = array_merge(
1221
                    $definitions,
1222
                    $this->getInlinedDefinitions($argument),
1223
                    array($argument)
1224
                );
1225
            }
1226
        }
1227
1228
        return $definitions;
1229
    }
1230
1231
    /**
1232
     * Checks if a service id has a reference.
1233
     *
1234
     * @param string $id
1235
     * @param array  $arguments
1236
     * @param bool   $deep
1237
     * @param array  $visited
1238
     *
1239
     * @return bool
1240
     */
1241
    private function hasReference($id, array $arguments, $deep = false, array &$visited = array())
1242
    {
1243
        foreach ($arguments as $argument) {
1244
            if (is_array($argument)) {
1245
                if ($this->hasReference($id, $argument, $deep, $visited)) {
1246
                    return true;
1247
                }
1248
            } elseif ($argument instanceof Reference) {
1249
                $argumentId = (string) $argument;
1250
                if ($id === $argumentId) {
1251
                    return true;
1252
                }
1253
1254
                if ($deep && !isset($visited[$argumentId])) {
1255
                    $visited[$argumentId] = true;
1256
1257
                    $service = $this->container->getDefinition($argumentId);
1258
                    $arguments = array_merge($service->getMethodCalls(), $service->getArguments(), $service->getProperties());
1259
1260
                    if ($this->hasReference($id, $arguments, $deep, $visited)) {
1261
                        return true;
1262
                    }
1263
                }
1264
            }
1265
        }
1266
1267
        return false;
1268
    }
1269
1270
    /**
1271
     * Dumps values.
1272
     *
1273
     * @param mixed $value
1274
     * @param bool  $interpolate
1275
     *
1276
     * @return string
1277
     *
1278
     * @throws RuntimeException
1279
     */
1280
    private function dumpValue($value, $interpolate = true)
1281
    {
1282
        if (is_array($value)) {
1283
            $code = array();
1284
            foreach ($value as $k => $v) {
1285
                $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate));
1286
            }
1287
1288
            return sprintf('array(%s)', implode(', ', $code));
1289
        } elseif ($value instanceof Definition) {
1290
            if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
1291
                return $this->dumpValue($this->definitionVariables->offsetGet($value), $interpolate);
1292
            }
1293
            if (count($value->getMethodCalls()) > 0) {
1294
                throw new RuntimeException('Cannot dump definitions which have method calls.');
1295
            }
1296
            if (null !== $value->getConfigurator()) {
1297
                throw new RuntimeException('Cannot dump definitions which have a configurator.');
1298
            }
1299
1300
            $arguments = array();
1301
            foreach ($value->getArguments() as $argument) {
1302
                $arguments[] = $this->dumpValue($argument);
1303
            }
1304
            $class = $this->dumpValue($value->getClass());
1305
1306
            if (false !== strpos($class, '$')) {
1307
                throw new RuntimeException('Cannot dump definitions which have a variable class name.');
1308
            }
1309
1310
            if (null !== $value->getFactory()) {
1311
                $factory = $value->getFactory();
1312
1313
                if (is_string($factory)) {
1314
                    return sprintf('\\%s(%s)', $factory, implode(', ', $arguments));
1315
                }
1316
1317
                if (is_array($factory)) {
1318
                    if (is_string($factory[0])) {
1319
                        return sprintf('\\%s::%s(%s)', $factory[0], $factory[1], implode(', ', $arguments));
1320
                    }
1321
1322
                    if ($factory[0] instanceof Definition) {
1323
                        return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($factory[0]), $factory[1], count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
1324
                    }
1325
1326
                    if ($factory[0] instanceof Reference) {
1327
                        return sprintf('%s->%s(%s)', $this->dumpValue($factory[0]), $factory[1], implode(', ', $arguments));
1328
                    }
1329
                }
1330
1331
                throw new RuntimeException('Cannot dump definition because of invalid factory');
1332
            }
1333
1334
            if (null !== $value->getFactoryMethod(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1335
                if (null !== $value->getFactoryClass(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1336
                    return sprintf("call_user_func(array(%s, '%s')%s)", $this->dumpValue($value->getFactoryClass(false)), $value->getFactoryMethod(false), count($arguments) > 0 ? ', '.implode(', ', $arguments) : '');
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...tion::getFactoryClass() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1337
                } elseif (null !== $value->getFactoryService(false)) {
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1338
                    $service = $this->dumpValue($value->getFactoryService(false));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1339
1340
                    return sprintf('%s->%s(%s)', 0 === strpos($service, '$') ? sprintf('$this->get(%s)', $service) : $this->getServiceCall($value->getFactoryService(false)), $value->getFactoryMethod(false), implode(', ', $arguments));
0 ignored issues
show
Deprecated Code introduced by
The method Symfony\Component\Depend...on::getFactoryService() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Deprecated Code introduced by
The method Symfony\Component\Depend...ion::getFactoryMethod() has been deprecated with message: since version 2.6, to be removed in 3.0.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1341
                } else {
1342
                    throw new RuntimeException('Cannot dump definitions which have factory method without factory service or factory class.');
1343
                }
1344
            }
1345
1346
            return sprintf('new \\%s(%s)', substr(str_replace('\\\\', '\\', $class), 1, -1), implode(', ', $arguments));
1347
        } elseif ($value instanceof Variable) {
1348
            return '$'.$value;
1349
        } elseif ($value instanceof Reference) {
1350
            if (null !== $this->referenceVariables && isset($this->referenceVariables[$id = (string) $value])) {
1351
                return $this->dumpValue($this->referenceVariables[$id], $interpolate);
1352
            }
1353
1354
            return $this->getServiceCall((string) $value, $value);
1355
        } elseif ($value instanceof Expression) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\ExpressionLanguage\Expression does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1356
            return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container'));
1357
        } elseif ($value instanceof Parameter) {
1358
            return $this->dumpParameter($value);
1359
        } elseif (true === $interpolate && is_string($value)) {
1360
            if (preg_match('/^%([^%]+)%$/', $value, $match)) {
1361
                // we do this to deal with non string values (Boolean, integer, ...)
1362
                // the preg_replace_callback converts them to strings
1363
                return $this->dumpParameter(strtolower($match[1]));
1364
            } else {
1365
                $that = $this;
1366
                $replaceParameters = function ($match) use ($that) {
1367
                    return "'.".$that->dumpParameter(strtolower($match[2])).".'";
1368
                };
1369
1370
                $code = str_replace('%%', '%', preg_replace_callback('/(?<!%)(%)([^%]+)\1/', $replaceParameters, $this->export($value)));
1371
1372
                return $code;
1373
            }
1374
        } elseif (is_object($value) || is_resource($value)) {
1375
            throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
1376
        } else {
1377
            return $this->export($value);
1378
        }
1379
    }
1380
1381
    /**
1382
     * Dumps a string to a literal (aka PHP Code) class value.
1383
     *
1384
     * @param string $class
1385
     *
1386
     * @return string
1387
     */
1388
    private function dumpLiteralClass($class)
1389
    {
1390
        return '\\'.substr(str_replace('\\\\', '\\', $class), 1, -1);
1391
    }
1392
1393
    /**
1394
     * Dumps a parameter.
1395
     *
1396
     * @param string $name
1397
     *
1398
     * @return string
1399
     */
1400
    public function dumpParameter($name)
1401
    {
1402
        if ($this->container->isFrozen() && $this->container->hasParameter($name)) {
1403
            return $this->dumpValue($this->container->getParameter($name), false);
1404
        }
1405
1406
        return sprintf("\$this->getParameter('%s')", strtolower($name));
1407
    }
1408
1409
    /**
1410
     * @deprecated since version 2.6.2, to be removed in 3.0.
1411
     *             Use \Symfony\Component\DependencyInjection\ContainerBuilder::addExpressionLanguageProvider instead.
1412
     *
1413
     * @param ExpressionFunctionProviderInterface $provider
1414
     */
1415
    public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
1416
    {
1417
        trigger_error('The '.__METHOD__.' method is deprecated since version 2.6.2 and will be removed in 3.0. Use the Symfony\Component\DependencyInjection\ContainerBuilder::addExpressionLanguageProvider method instead.', E_USER_DEPRECATED);
1418
1419
        $this->expressionLanguageProviders[] = $provider;
1420
    }
1421
1422
    /**
1423
     * Gets a service call.
1424
     *
1425
     * @param string    $id
1426
     * @param Reference $reference
1427
     *
1428
     * @return string
1429
     */
1430
    private function getServiceCall($id, Reference $reference = null)
1431
    {
1432
        if ('service_container' === $id) {
1433
            return '$this';
1434
        }
1435
1436
        if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
1437
            return sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
1438
        } else {
1439
            if ($this->container->hasAlias($id)) {
1440
                $id = (string) $this->container->getAlias($id);
1441
            }
1442
1443
            return sprintf('$this->get(\'%s\')', $id);
1444
        }
1445
    }
1446
1447
    /**
1448
     * Convert a service id to a valid PHP method name.
1449
     *
1450
     * @param string $id
1451
     *
1452
     * @return string
1453
     *
1454
     * @throws InvalidArgumentException
1455
     */
1456
    private function camelize($id)
1457
    {
1458
        $name = Container::camelize($id);
1459
1460
        if (!preg_match('/^[a-zA-Z0-9_\x7f-\xff]+$/', $name)) {
1461
            throw new InvalidArgumentException(sprintf('Service id "%s" cannot be converted to a valid PHP method name.', $id));
1462
        }
1463
1464
        return $name;
1465
    }
1466
1467
    /**
1468
     * Returns the next name to use.
1469
     *
1470
     * @return string
1471
     */
1472
    private function getNextVariableName()
1473
    {
1474
        $firstChars = self::FIRST_CHARS;
1475
        $firstCharsLength = strlen($firstChars);
1476
        $nonFirstChars = self::NON_FIRST_CHARS;
1477
        $nonFirstCharsLength = strlen($nonFirstChars);
1478
1479
        while (true) {
1480
            $name = '';
1481
            $i = $this->variableCount;
1482
1483
            if ('' === $name) {
1484
                $name .= $firstChars[$i % $firstCharsLength];
1485
                $i = (int) ($i / $firstCharsLength);
1486
            }
1487
1488
            while ($i > 0) {
1489
                --$i;
1490
                $name .= $nonFirstChars[$i % $nonFirstCharsLength];
1491
                $i = (int) ($i / $nonFirstCharsLength);
1492
            }
1493
1494
            ++$this->variableCount;
1495
1496
            // check that the name is not reserved
1497
            if (in_array($name, $this->reservedVariables, true)) {
1498
                continue;
1499
            }
1500
1501
            return $name;
1502
        }
1503
    }
1504
1505
    private function getExpressionLanguage()
1506
    {
1507
        if (null === $this->expressionLanguage) {
1508
            if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
1509
                throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
1510
            }
1511
            $providers = array_merge($this->container->getExpressionLanguageProviders(), $this->expressionLanguageProviders);
1512
            $this->expressionLanguage = new ExpressionLanguage(null, $providers);
1513
1514
            if ($this->container->isTrackingResources()) {
1515
                foreach ($providers as $provider) {
1516
                    $this->container->addObjectResource($provider);
1517
                }
1518
            }
1519
        }
1520
1521
        return $this->expressionLanguage;
1522
    }
1523
1524
    private function exportTargetDirs()
1525
    {
1526
        return null === $this->targetDirRegex ? '' : <<<EOF
1527
1528
        \$dir = __DIR__;
1529
        for (\$i = 1; \$i <= {$this->targetDirMaxMatches}; ++\$i) {
1530
            \$this->targetDirs[\$i] = \$dir = dirname(\$dir);
1531
        }
1532
EOF;
1533
    }
1534
1535
    private function export($value)
1536
    {
1537
        if (null !== $this->targetDirRegex && is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) {
1538
            $prefix = $matches[0][1] ? var_export(substr($value, 0, $matches[0][1]), true).'.' : '';
1539
            $suffix = $matches[0][1] + strlen($matches[0][0]);
1540
            $suffix = isset($value[$suffix]) ? '.'.var_export(substr($value, $suffix), true) : '';
1541
            $dirname = '__DIR__';
1542
1543
            if (0 < $offset = 1 + $this->targetDirMaxMatches - count($matches)) {
1544
                $dirname = sprintf('$this->targetDirs[%d]', $offset);
1545
            }
1546
1547
            if ($prefix || $suffix) {
1548
                return sprintf('(%s%s%s)', $prefix, $dirname, $suffix);
1549
            }
1550
1551
            return $dirname;
1552
        }
1553
1554
        return var_export($value, true);
1555
    }
1556
}
1557