DebugContainerCommand::execute()   D
last analyzed

Complexity

Conditions 19
Paths 62

Size

Total Lines 110
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 19
eloc 69
c 1
b 0
f 0
nc 62
nop 2
dl 0
loc 110
rs 4.5166

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
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Debug\Command;
6
7
use Psr\Container\ContainerInterface;
8
use ReflectionClass;
9
use Symfony\Component\Console\Attribute\AsCommand;
10
use Symfony\Component\Console\Command\Command;
11
use Symfony\Component\Console\Helper\Table;
12
use Symfony\Component\Console\Input\InputArgument;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use Symfony\Component\Console\Style\SymfonyStyle;
17
use Yiisoft\Config\ConfigInterface;
18
use Yiisoft\Definitions\ArrayDefinition;
19
use Yiisoft\Definitions\CallableDefinition;
20
use Yiisoft\Definitions\ValueDefinition;
21
use Yiisoft\Di\Helpers\DefinitionNormalizer;
22
use Yiisoft\VarDumper\VarDumper;
23
use Yiisoft\Yii\Console\ExitCode;
24
use Yiisoft\Yii\Debug\Debugger;
25
26
#[AsCommand(
27
    name: 'debug:container',
28
    description: 'Show information about container',
29
)]
30
final class DebugContainerCommand extends Command
31
{
32
    public function __construct(
33
        private readonly ContainerInterface $container,
34
        private readonly Debugger $debugger,
35
    ) {
36
        parent::__construct();
37
    }
38
39
    protected function configure(): void
40
    {
41
        $this
42
            ->addArgument('id', InputArgument::IS_ARRAY, 'Service ID')
43
            ->addOption('groups', null, InputOption::VALUE_NONE, 'Show groups')
44
            ->addOption('group', 'g', InputOption::VALUE_REQUIRED, 'Show group');
45
    }
46
47
    protected function execute(InputInterface $input, OutputInterface $output): int
48
    {
49
        $this->debugger->stop();
50
        $config = $this->container->get(ConfigInterface::class);
51
52
        $io = new SymfonyStyle($input, $output);
53
54
        if ($input->hasArgument('id') && !empty($ids = $input->getArgument('id'))) {
55
            $build = $this->getConfigBuild($config);
56
            foreach ($ids as $id) {
57
                $definition = null;
58
                foreach ($build as $definitions) {
59
                    if (array_key_exists($id, $definitions)) {
60
                        $definition = $definitions[$id];
61
                    }
62
                }
63
                if ($definition === null) {
64
                    $io->error(
65
                        sprintf(
66
                            'Service "%s" not found.',
67
                            $id,
68
                        )
69
                    );
70
                    continue;
71
                }
72
                $io->title($id);
73
74
                $normalizedDefinition = DefinitionNormalizer::normalize($definition, $id);
75
                if ($normalizedDefinition instanceof ArrayDefinition) {
76
                    $definitionList = ['ID' => $id];
77
                    if (class_exists($normalizedDefinition->getClass())) {
78
                        $definitionList[] = ['Class' => $normalizedDefinition->getClass()];
79
                    }
80
                    if (!empty($normalizedDefinition->getConstructorArguments())) {
81
                        $definitionList[] = [
82
                            'Constructor' => $this->export(
83
                                $normalizedDefinition->getConstructorArguments()
84
                            ),
85
                        ];
86
                    }
87
                    if (!empty($normalizedDefinition->getMethodsAndProperties())) {
88
                        $definitionList[] = [
89
                            'Methods' => $this->export(
90
                                $normalizedDefinition->getMethodsAndProperties()
91
                            ),
92
                        ];
93
                    }
94
                    if (isset($definition['tags'])) {
95
                        $definitionList[] = ['Tags' => $this->export($definition['tags'])];
96
                    }
97
98
                    $io->definitionList(...$definitionList);
99
100
                    continue;
101
                }
102
                if ($normalizedDefinition instanceof CallableDefinition || $normalizedDefinition instanceof ValueDefinition) {
103
                    $io->text(
104
                        $this->export($definition)
105
                    );
106
                    continue;
107
                }
108
109
                $output->writeln([
110
                    $id,
111
                    VarDumper::create($normalizedDefinition)->asString(),
112
                ]);
113
            }
114
115
            return ExitCode::OK;
116
        }
117
118
        if ($input->hasOption('groups') && $input->getOption('groups')) {
119
            $build = $this->getConfigBuild($config);
120
            $groups = array_keys($build);
121
            sort($groups);
122
123
            $io->table(['Groups'], array_map(static fn ($group) => [$group], $groups));
124
125
            return ExitCode::OK;
126
        }
127
        if ($input->hasOption('group') && !empty($group = $input->getOption('group'))) {
128
            $data = $config->get($group);
129
            ksort($data);
130
131
            $rows = $this->getGroupServices($data);
132
133
            $table = new Table($output);
134
            $table
135
                ->setHeaderTitle($group)
136
                ->setHeaders(['Service', 'Definition'])
137
                ->setRows($rows);
138
            $table->render();
139
140
            return ExitCode::OK;
141
        }
142
143
        $build = $this->getConfigBuild($config);
144
145
        foreach ($build as $group => $data) {
146
            $rows = $this->getGroupServices($data);
147
148
            $table = new Table($output);
149
            $table
150
                ->setHeaderTitle($group)
151
                ->setHeaders(['Group', 'Services'])
152
                ->setRows($rows);
153
            $table->render();
154
        }
155
156
        return ExitCode::OK;
157
    }
158
159
    private function getConfigBuild(mixed $config): array
160
    {
161
        $reflection = new ReflectionClass($config);
162
        $buildReflection = $reflection->getProperty('build');
163
        $buildReflection->setAccessible(true);
164
        return $buildReflection->getValue($config);
165
    }
166
167
    protected function getGroupServices(array $data): array
168
    {
169
        $rows = [];
170
        foreach ($data as $id => $definition) {
171
            $class = '';
172
            if (is_string($definition)) {
173
                $class = $definition;
174
            }
175
            if (is_array($definition)) {
176
                $class = $definition['class'] ?? $id;
177
            }
178
            if (is_object($definition)) {
179
                $class = $definition::class;
180
            }
181
182
            $rows[] = [
183
                $id,
184
                $class,
185
            ];
186
        }
187
        return $rows;
188
    }
189
190
    protected function export(mixed $value): string
191
    {
192
        return VarDumper::create($value)->asString();
193
    }
194
}
195