Passed
Branch main (339f6d)
by Cornelia
17:44 queued 07:47
created

Definition   F

Complexity

Total Complexity 93

Size/Duplication

Total Lines 785
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 93
eloc 181
dl 0
loc 785
rs 2
c 0
b 0
f 0

59 Methods

Rating   Name   Duplication   Size   Complexity  
A getArgument() 0 7 2
A isSynthetic() 0 3 1
A setLazy() 0 7 1
A setProperties() 0 5 1
A removeMethodCall() 0 9 3
A setTags() 0 5 1
A addTag() 0 5 1
A getErrors() 0 11 4
A getConfigurator() 0 3 1
A getMethodCalls() 0 3 1
A addError() 0 9 2
A hasTag() 0 3 1
A isLazy() 0 3 1
A setSynthetic() 0 9 2
A setClass() 0 7 1
A setFile() 0 7 1
A isAbstract() 0 3 1
A isDeprecated() 0 3 1
A getFile() 0 3 1
A addMethodCall() 0 8 3
A getDecoratedService() 0 3 1
A setProperty() 0 5 1
A setAutoconfigured() 0 7 1
A getFactory() 0 3 1
A getProperties() 0 3 1
A setDeprecated() 0 16 5
A isAutoconfigured() 0 3 1
A getDeprecation() 0 6 1
A getBindings() 0 3 1
A setDecoratedService() 0 19 5
A getChanges() 0 3 1
A setChanges() 0 5 1
A hasMethodCall() 0 9 3
A isPublic() 0 3 1
A setFactory() 0 13 4
A isAutowired() 0 3 1
A setAbstract() 0 5 1
A setInstanceofConditionals() 0 5 1
A getInstanceofConditionals() 0 3 1
A __construct() 0 6 2
A isShared() 0 3 1
A hasErrors() 0 3 1
A clearTag() 0 5 1
A setConfigurator() 0 13 4
A setBindings() 0 15 5
A getArguments() 0 3 1
A setPublic() 0 7 1
A setArgument() 0 5 1
A clearTags() 0 5 1
A replaceArgument() 0 13 3
A addArgument() 0 5 1
A getTags() 0 3 1
A setArguments() 0 5 1
A setShared() 0 7 1
A setAutowired() 0 7 1
A isPrivate() 0 3 1
A getTag() 0 3 1
A setMethodCalls() 0 8 2
A getClass() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Definition often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Definition, and based on these observations, apply Extract Interface, too.

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;
13
14
use Symfony\Component\DependencyInjection\Argument\BoundArgument;
15
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16
use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
17
18
/**
19
 * Definition represents a service definition.
20
 *
21
 * @author Fabien Potencier <[email protected]>
22
 */
23
class Definition
24
{
25
    private const DEFAULT_DEPRECATION_TEMPLATE = 'The "%service_id%" service is deprecated. You should stop using it, as it will be removed in the future.';
26
27
    private ?string $class = null;
28
    private ?string $file = null;
29
    private string|array|null $factory = null;
30
    private bool $shared = true;
31
    private array $deprecation = [];
32
    private array $properties = [];
33
    private array $calls = [];
34
    private array $instanceof = [];
35
    private bool $autoconfigured = false;
36
    private string|array|null $configurator = null;
37
    private array $tags = [];
38
    private bool $public = false;
39
    private bool $synthetic = false;
40
    private bool $abstract = false;
41
    private bool $lazy = false;
42
    private ?array $decoratedService = null;
43
    private bool $autowired = false;
44
    private array $changes = [];
45
    private array $bindings = [];
46
    private array $errors = [];
47
48
    protected array $arguments = [];
49
50
    /**
51
     * @internal
52
     *
53
     * Used to store the name of the inner id when using service decoration together with autowiring
54
     */
55
    public ?string $innerServiceId = null;
56
57
    /**
58
     * @internal
59
     *
60
     * Used to store the behavior to follow when using service decoration and the decorated service is invalid
61
     */
62
    public ?int $decorationOnInvalid = null;
63
64
    public function __construct(?string $class = null, array $arguments = [])
65
    {
66
        if (null !== $class) {
67
            $this->setClass($class);
68
        }
69
        $this->arguments = $arguments;
70
    }
71
72
    /**
73
     * Returns all changes tracked for the Definition object.
74
     */
75
    public function getChanges(): array
76
    {
77
        return $this->changes;
78
    }
79
80
    /**
81
     * Sets the tracked changes for the Definition object.
82
     *
83
     * @param array $changes An array of changes for this Definition
84
     *
85
     * @return $this
86
     */
87
    public function setChanges(array $changes): static
88
    {
89
        $this->changes = $changes;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Sets a factory.
96
     *
97
     * @param string|array|Reference|null $factory A PHP function, reference or an array containing a class/Reference and a method to call
98
     *
99
     * @return $this
100
     */
101
    public function setFactory(string|array|Reference|null $factory): static
102
    {
103
        $this->changes['factory'] = true;
104
105
        if (\is_string($factory) && str_contains($factory, '::')) {
0 ignored issues
show
introduced by
The condition is_string($factory) is always false.
Loading history...
106
            $factory = explode('::', $factory, 2);
107
        } elseif ($factory instanceof Reference) {
0 ignored issues
show
introduced by
$factory is never a sub-type of Symfony\Component\DependencyInjection\Reference.
Loading history...
108
            $factory = [$factory, '__invoke'];
109
        }
110
111
        $this->factory = $factory;
112
113
        return $this;
114
    }
115
116
    /**
117
     * Gets the factory.
118
     *
119
     * @return string|array|null The PHP function or an array containing a class/Reference and a method to call
120
     */
121
    public function getFactory(): string|array|null
122
    {
123
        return $this->factory;
124
    }
125
126
    /**
127
     * Sets the service that this service is decorating.
128
     *
129
     * @param string|null $id        The decorated service id, use null to remove decoration
130
     * @param string|null $renamedId The new decorated service id
131
     *
132
     * @return $this
133
     *
134
     * @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals
135
     */
136
    public function setDecoratedService(?string $id, ?string $renamedId = null, int $priority = 0, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE): static
137
    {
138
        if ($renamedId && $id === $renamedId) {
139
            throw new InvalidArgumentException(\sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id));
140
        }
141
142
        $this->changes['decorated_service'] = true;
143
144
        if (null === $id) {
145
            $this->decoratedService = null;
146
        } else {
147
            $this->decoratedService = [$id, $renamedId, $priority];
148
149
            if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
150
                $this->decoratedService[] = $invalidBehavior;
151
            }
152
        }
153
154
        return $this;
155
    }
156
157
    /**
158
     * Gets the service that this service is decorating.
159
     *
160
     * @return array|null An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated
161
     */
162
    public function getDecoratedService(): ?array
163
    {
164
        return $this->decoratedService;
165
    }
166
167
    /**
168
     * Sets the service class.
169
     *
170
     * @return $this
171
     */
172
    public function setClass(?string $class): static
173
    {
174
        $this->changes['class'] = true;
175
176
        $this->class = $class;
177
178
        return $this;
179
    }
180
181
    /**
182
     * Gets the service class.
183
     */
184
    public function getClass(): ?string
185
    {
186
        return $this->class;
187
    }
188
189
    /**
190
     * Sets the arguments to pass to the service constructor/factory method.
191
     *
192
     * @return $this
193
     */
194
    public function setArguments(array $arguments): static
195
    {
196
        $this->arguments = $arguments;
197
198
        return $this;
199
    }
200
201
    /**
202
     * Sets the properties to define when creating the service.
203
     *
204
     * @return $this
205
     */
206
    public function setProperties(array $properties): static
207
    {
208
        $this->properties = $properties;
209
210
        return $this;
211
    }
212
213
    /**
214
     * Gets the properties to define when creating the service.
215
     */
216
    public function getProperties(): array
217
    {
218
        return $this->properties;
219
    }
220
221
    /**
222
     * Sets a specific property.
223
     *
224
     * @return $this
225
     */
226
    public function setProperty(string $name, mixed $value): static
227
    {
228
        $this->properties[$name] = $value;
229
230
        return $this;
231
    }
232
233
    /**
234
     * Adds an argument to pass to the service constructor/factory method.
235
     *
236
     * @return $this
237
     */
238
    public function addArgument(mixed $argument): static
239
    {
240
        $this->arguments[] = $argument;
241
242
        return $this;
243
    }
244
245
    /**
246
     * Replaces a specific argument.
247
     *
248
     * @return $this
249
     *
250
     * @throws OutOfBoundsException When the replaced argument does not exist
251
     */
252
    public function replaceArgument(int|string $index, mixed $argument): static
253
    {
254
        if (0 === \count($this->arguments)) {
255
            throw new OutOfBoundsException(\sprintf('Cannot replace arguments for class "%s" if none have been configured yet.', $this->class));
256
        }
257
258
        if (!\array_key_exists($index, $this->arguments)) {
259
            throw new OutOfBoundsException(\sprintf('The argument "%s" doesn\'t exist in class "%s".', $index, $this->class));
260
        }
261
262
        $this->arguments[$index] = $argument;
263
264
        return $this;
265
    }
266
267
    /**
268
     * Sets a specific argument.
269
     *
270
     * @return $this
271
     */
272
    public function setArgument(int|string $key, mixed $value): static
273
    {
274
        $this->arguments[$key] = $value;
275
276
        return $this;
277
    }
278
279
    /**
280
     * Gets the arguments to pass to the service constructor/factory method.
281
     */
282
    public function getArguments(): array
283
    {
284
        return $this->arguments;
285
    }
286
287
    /**
288
     * Gets an argument to pass to the service constructor/factory method.
289
     *
290
     * @throws OutOfBoundsException When the argument does not exist
291
     */
292
    public function getArgument(int|string $index): mixed
293
    {
294
        if (!\array_key_exists($index, $this->arguments)) {
295
            throw new OutOfBoundsException(\sprintf('The argument "%s" doesn\'t exist in class "%s".', $index, $this->class));
296
        }
297
298
        return $this->arguments[$index];
299
    }
300
301
    /**
302
     * Sets the methods to call after service initialization.
303
     *
304
     * @return $this
305
     */
306
    public function setMethodCalls(array $calls = []): static
307
    {
308
        $this->calls = [];
309
        foreach ($calls as $call) {
310
            $this->addMethodCall($call[0], $call[1], $call[2] ?? false);
311
        }
312
313
        return $this;
314
    }
315
316
    /**
317
     * Adds a method to call after service initialization.
318
     *
319
     * @param string $method       The method name to call
320
     * @param array  $arguments    An array of arguments to pass to the method call
321
     * @param bool   $returnsClone Whether the call returns the service instance or not
322
     *
323
     * @return $this
324
     *
325
     * @throws InvalidArgumentException on empty $method param
326
     */
327
    public function addMethodCall(string $method, array $arguments = [], bool $returnsClone = false): static
328
    {
329
        if (!$method) {
330
            throw new InvalidArgumentException('Method name cannot be empty.');
331
        }
332
        $this->calls[] = $returnsClone ? [$method, $arguments, true] : [$method, $arguments];
333
334
        return $this;
335
    }
336
337
    /**
338
     * Removes a method to call after service initialization.
339
     *
340
     * @return $this
341
     */
342
    public function removeMethodCall(string $method): static
343
    {
344
        foreach ($this->calls as $i => $call) {
345
            if ($call[0] === $method) {
346
                unset($this->calls[$i]);
347
            }
348
        }
349
350
        return $this;
351
    }
352
353
    /**
354
     * Check if the current definition has a given method to call after service initialization.
355
     */
356
    public function hasMethodCall(string $method): bool
357
    {
358
        foreach ($this->calls as $call) {
359
            if ($call[0] === $method) {
360
                return true;
361
            }
362
        }
363
364
        return false;
365
    }
366
367
    /**
368
     * Gets the methods to call after service initialization.
369
     */
370
    public function getMethodCalls(): array
371
    {
372
        return $this->calls;
373
    }
374
375
    /**
376
     * Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class.
377
     *
378
     * @param ChildDefinition[] $instanceof
379
     *
380
     * @return $this
381
     */
382
    public function setInstanceofConditionals(array $instanceof): static
383
    {
384
        $this->instanceof = $instanceof;
385
386
        return $this;
387
    }
388
389
    /**
390
     * Gets the definition templates to conditionally apply on the current definition, keyed by parent interface/class.
391
     *
392
     * @return ChildDefinition[]
393
     */
394
    public function getInstanceofConditionals(): array
395
    {
396
        return $this->instanceof;
397
    }
398
399
    /**
400
     * Sets whether or not instanceof conditionals should be prepended with a global set.
401
     *
402
     * @return $this
403
     */
404
    public function setAutoconfigured(bool $autoconfigured): static
405
    {
406
        $this->changes['autoconfigured'] = true;
407
408
        $this->autoconfigured = $autoconfigured;
409
410
        return $this;
411
    }
412
413
    public function isAutoconfigured(): bool
414
    {
415
        return $this->autoconfigured;
416
    }
417
418
    /**
419
     * Sets tags for this definition.
420
     *
421
     * @return $this
422
     */
423
    public function setTags(array $tags): static
424
    {
425
        $this->tags = $tags;
426
427
        return $this;
428
    }
429
430
    /**
431
     * Returns all tags.
432
     */
433
    public function getTags(): array
434
    {
435
        return $this->tags;
436
    }
437
438
    /**
439
     * Gets a tag by name.
440
     */
441
    public function getTag(string $name): array
442
    {
443
        return $this->tags[$name] ?? [];
444
    }
445
446
    /**
447
     * Adds a tag for this definition.
448
     *
449
     * @return $this
450
     */
451
    public function addTag(string $name, array $attributes = []): static
452
    {
453
        $this->tags[$name][] = $attributes;
454
455
        return $this;
456
    }
457
458
    /**
459
     * Whether this definition has a tag with the given name.
460
     */
461
    public function hasTag(string $name): bool
462
    {
463
        return isset($this->tags[$name]);
464
    }
465
466
    /**
467
     * Clears all tags for a given name.
468
     *
469
     * @return $this
470
     */
471
    public function clearTag(string $name): static
472
    {
473
        unset($this->tags[$name]);
474
475
        return $this;
476
    }
477
478
    /**
479
     * Clears the tags for this definition.
480
     *
481
     * @return $this
482
     */
483
    public function clearTags(): static
484
    {
485
        $this->tags = [];
486
487
        return $this;
488
    }
489
490
    /**
491
     * Sets a file to require before creating the service.
492
     *
493
     * @return $this
494
     */
495
    public function setFile(?string $file): static
496
    {
497
        $this->changes['file'] = true;
498
499
        $this->file = $file;
500
501
        return $this;
502
    }
503
504
    /**
505
     * Gets the file to require before creating the service.
506
     */
507
    public function getFile(): ?string
508
    {
509
        return $this->file;
510
    }
511
512
    /**
513
     * Sets if the service must be shared or not.
514
     *
515
     * @return $this
516
     */
517
    public function setShared(bool $shared): static
518
    {
519
        $this->changes['shared'] = true;
520
521
        $this->shared = $shared;
522
523
        return $this;
524
    }
525
526
    /**
527
     * Whether this service is shared.
528
     */
529
    public function isShared(): bool
530
    {
531
        return $this->shared;
532
    }
533
534
    /**
535
     * Sets the visibility of this service.
536
     *
537
     * @return $this
538
     */
539
    public function setPublic(bool $boolean): static
540
    {
541
        $this->changes['public'] = true;
542
543
        $this->public = $boolean;
544
545
        return $this;
546
    }
547
548
    /**
549
     * Whether this service is public facing.
550
     */
551
    public function isPublic(): bool
552
    {
553
        return $this->public;
554
    }
555
556
    /**
557
     * Whether this service is private.
558
     */
559
    public function isPrivate(): bool
560
    {
561
        return !$this->public;
562
    }
563
564
    /**
565
     * Sets the lazy flag of this service.
566
     *
567
     * @return $this
568
     */
569
    public function setLazy(bool $lazy): static
570
    {
571
        $this->changes['lazy'] = true;
572
573
        $this->lazy = $lazy;
574
575
        return $this;
576
    }
577
578
    /**
579
     * Whether this service is lazy.
580
     */
581
    public function isLazy(): bool
582
    {
583
        return $this->lazy;
584
    }
585
586
    /**
587
     * Sets whether this definition is synthetic, that is not constructed by the
588
     * container, but dynamically injected.
589
     *
590
     * @return $this
591
     */
592
    public function setSynthetic(bool $boolean): static
593
    {
594
        $this->synthetic = $boolean;
595
596
        if (!isset($this->changes['public'])) {
597
            $this->setPublic(true);
598
        }
599
600
        return $this;
601
    }
602
603
    /**
604
     * Whether this definition is synthetic, that is not constructed by the
605
     * container, but dynamically injected.
606
     */
607
    public function isSynthetic(): bool
608
    {
609
        return $this->synthetic;
610
    }
611
612
    /**
613
     * Whether this definition is abstract, that means it merely serves as a
614
     * template for other definitions.
615
     *
616
     * @return $this
617
     */
618
    public function setAbstract(bool $boolean): static
619
    {
620
        $this->abstract = $boolean;
621
622
        return $this;
623
    }
624
625
    /**
626
     * Whether this definition is abstract, that means it merely serves as a
627
     * template for other definitions.
628
     */
629
    public function isAbstract(): bool
630
    {
631
        return $this->abstract;
632
    }
633
634
    /**
635
     * Whether this definition is deprecated, that means it should not be called
636
     * anymore.
637
     *
638
     * @param string $package The name of the composer package that is triggering the deprecation
639
     * @param string $version The version of the package that introduced the deprecation
640
     * @param string $message The deprecation message to use
641
     *
642
     * @return $this
643
     *
644
     * @throws InvalidArgumentException when the message template is invalid
645
     */
646
    public function setDeprecated(string $package, string $version, string $message): static
647
    {
648
        if ('' !== $message) {
649
            if (preg_match('#[\r\n]|\*/#', $message)) {
650
                throw new InvalidArgumentException('Invalid characters found in deprecation template.');
651
            }
652
653
            if (!str_contains($message, '%service_id%')) {
654
                throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.');
655
            }
656
        }
657
658
        $this->changes['deprecated'] = true;
659
        $this->deprecation = ['package' => $package, 'version' => $version, 'message' => $message ?: self::DEFAULT_DEPRECATION_TEMPLATE];
660
661
        return $this;
662
    }
663
664
    /**
665
     * Whether this definition is deprecated, that means it should not be called
666
     * anymore.
667
     */
668
    public function isDeprecated(): bool
669
    {
670
        return (bool) $this->deprecation;
671
    }
672
673
    /**
674
     * @param string $id Service id relying on this definition
675
     */
676
    public function getDeprecation(string $id): array
677
    {
678
        return [
679
            'package' => $this->deprecation['package'],
680
            'version' => $this->deprecation['version'],
681
            'message' => str_replace('%service_id%', $id, $this->deprecation['message']),
682
        ];
683
    }
684
685
    /**
686
     * Sets a configurator to call after the service is fully initialized.
687
     *
688
     * @param string|array|Reference|null $configurator A PHP function, reference or an array containing a class/Reference and a method to call
689
     *
690
     * @return $this
691
     */
692
    public function setConfigurator(string|array|Reference|null $configurator): static
693
    {
694
        $this->changes['configurator'] = true;
695
696
        if (\is_string($configurator) && str_contains($configurator, '::')) {
0 ignored issues
show
introduced by
The condition is_string($configurator) is always false.
Loading history...
697
            $configurator = explode('::', $configurator, 2);
698
        } elseif ($configurator instanceof Reference) {
0 ignored issues
show
introduced by
$configurator is never a sub-type of Symfony\Component\DependencyInjection\Reference.
Loading history...
699
            $configurator = [$configurator, '__invoke'];
700
        }
701
702
        $this->configurator = $configurator;
703
704
        return $this;
705
    }
706
707
    /**
708
     * Gets the configurator to call after the service is fully initialized.
709
     */
710
    public function getConfigurator(): string|array|null
711
    {
712
        return $this->configurator;
713
    }
714
715
    /**
716
     * Is the definition autowired?
717
     */
718
    public function isAutowired(): bool
719
    {
720
        return $this->autowired;
721
    }
722
723
    /**
724
     * Enables/disables autowiring.
725
     *
726
     * @return $this
727
     */
728
    public function setAutowired(bool $autowired): static
729
    {
730
        $this->changes['autowired'] = true;
731
732
        $this->autowired = $autowired;
733
734
        return $this;
735
    }
736
737
    /**
738
     * Gets bindings.
739
     *
740
     * @return BoundArgument[]
741
     */
742
    public function getBindings(): array
743
    {
744
        return $this->bindings;
745
    }
746
747
    /**
748
     * Sets bindings.
749
     *
750
     * Bindings map $named or FQCN arguments to values that should be
751
     * injected in the matching parameters (of the constructor, of methods
752
     * called and of controller actions).
753
     *
754
     * @return $this
755
     */
756
    public function setBindings(array $bindings): static
757
    {
758
        foreach ($bindings as $key => $binding) {
759
            if (0 < strpos($key, '$') && $key !== $k = preg_replace('/[ \t]*\$/', ' $', $key)) {
760
                unset($bindings[$key]);
761
                $bindings[$key = $k] = $binding;
762
            }
763
            if (!$binding instanceof BoundArgument) {
764
                $bindings[$key] = new BoundArgument($binding);
765
            }
766
        }
767
768
        $this->bindings = $bindings;
769
770
        return $this;
771
    }
772
773
    /**
774
     * Add an error that occurred when building this Definition.
775
     *
776
     * @return $this
777
     */
778
    public function addError(string|\Closure|self $error): static
779
    {
780
        if ($error instanceof self) {
781
            $this->errors = array_merge($this->errors, $error->errors);
782
        } else {
783
            $this->errors[] = $error;
784
        }
785
786
        return $this;
787
    }
788
789
    /**
790
     * Returns any errors that occurred while building this Definition.
791
     */
792
    public function getErrors(): array
793
    {
794
        foreach ($this->errors as $i => $error) {
795
            if ($error instanceof \Closure) {
796
                $this->errors[$i] = (string) $error();
797
            } elseif (!\is_string($error)) {
798
                $this->errors[$i] = (string) $error;
799
            }
800
        }
801
802
        return $this->errors;
803
    }
804
805
    public function hasErrors(): bool
806
    {
807
        return (bool) $this->errors;
808
    }
809
}
810