ModuleCommandTrait::renderTable()   A
last analyzed

Complexity

Conditions 5
Paths 9

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 18
c 1
b 0
f 0
dl 0
loc 24
rs 9.3554
cc 5
nc 9
nop 2
1
<?php
2
3
/**
4
 * This file is part of web-stack
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Slick\WebStack\UserInterface\Console;
13
14
use Slick\ModuleApi\Infrastructure\SlickModuleInterface;
15
use Slick\WebStack\Infrastructure\Exception\InvalidModuleName;
16
use Symfony\Component\Console\Style\SymfonyStyle;
17
18
/**
19
 * ModuleCommandTrait
20
 *
21
 * @package Slick\WebStack\Infrastructure
22
 */
23
trait ModuleCommandTrait
24
{
25
    /**
26
     * @return array<string>
27
     */
28
    public function retrieveInstalledModules(): array
29
    {
30
        return file_exists($this->moduleListFile) ? require $this->moduleListFile : [];
31
    }
32
33
    /**
34
     * Checks if a module does not exist in the given list of modules
35
     *
36
     * @param string $moduleName The name of the module to check
37
     * @param array<string> $modules The list of modules to check against
38
     * @return false|string Returns false if the module already exists, otherwise returns the modified module name
39
     */
40
    protected function checkModuleNotExists(string $moduleName, array $modules): false|string
41
    {
42
        try {
43
            $retrieveModuleName = $this->retrieveModuleName($moduleName);
44
            if (in_array(ltrim($retrieveModuleName, '\\'), $modules)) {
45
                $this->outputStyle?->writeln(
46
                    "<comment>The '$moduleName' module is already enabled. No changes have been made.</comment>"
47
                );
48
                return false;
49
            }
50
        } catch (InvalidModuleName $exception) {
51
            $this->outputStyle?->writeln(
52
                "<error>{$exception->getMessage()}</error>"
53
            );
54
            return false;
55
        }
56
57
        return $retrieveModuleName;
58
    }
59
60
    /**
61
     * Checks if the config file exists and creates it if it doesn't.
62
     *
63
     * @return void
64
     */
65
    private function checkConfigFile(): void
66
    {
67
        $filename = $this->moduleListFile;
68
        if (!file_exists($filename)) {
69
            if (!is_dir($this->appRoot . '/config/modules')) {
70
                mkdir($this->appRoot . '/config/modules', 0755, true);
71
            }
72
            file_put_contents($filename, "<?php\n\nreturn [];\n");
73
        }
74
    }
75
76
    /**
77
     * Generates a module configuration file contents.
78
     *
79
     * @param array<string> $modules The modules to include in the configuration file.
80
     *
81
     * @return string The generated module configuration file content.
82
     */
83
    protected function generateModuleConfig(array $modules): string
84
    {
85
        $fixed = [];
86
        foreach ($modules as $module) {
87
            $fixedModule = str_ends_with(ltrim($module, '\\'), '::class');
88
            $fixed[] = $fixedModule ? $module : "\\" . ltrim($module, '\\') . "::class";
89
        }
90
91
        return empty($fixed)
92
            ? "<?php\n\nreturn [];\n"
93
            : "<?php\n\nreturn [\n    " . implode(",\n    ", $fixed) . "\n];\n";
94
    }
95
96
    /**
97
     * Retrieves the module class name.
98
     *
99
     * @param string $moduleName The module name.
100
     *
101
     * @return class-string The module name.
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
102
     *
103
     * @throws InvalidModuleName If the module name classname cannot be determined.
104
     */
105
    protected function retrieveModuleName(string $moduleName): string
106
    {
107
        foreach ($this->possibleNames($moduleName) as $name) {
108
            if (class_exists($name)) {
109
                return $name;
110
            }
111
        }
112
113
        throw new InvalidModuleName(
114
            "Could not determine module name classname. Check SlickModuleInterface "
115
            ."implementation for module '$moduleName'"
116
        );
117
    }
118
119
    /**
120
     * Returns possible names for a module.
121
     *
122
     * @param string $module The name of the module.
123
     * @return array<string> An array containing possible names for the module.
124
     */
125
    protected function possibleNames(string $module): array
126
    {
127
        $camelCaseModule = str_replace(' ', '', ucwords(str_replace(['_', '-'], ' ', $module)));
128
        $withoutSuffix = str_replace('Module', '', $camelCaseModule);
129
        $uppercaseModule = strtoupper($withoutSuffix);
130
        return [
131
            $module,
132
            "\\Slick\\WebStack\\{$camelCaseModule}Module",
133
            "\\Slick\\$withoutSuffix\\{$camelCaseModule}Module",
134
            "\\Slick\\$uppercaseModule\\{$camelCaseModule}Module",
135
            "\\Slick\\WebStack\\$camelCaseModule",
136
        ];
137
    }
138
139
    /**
140
     * Checks if a module exists in the given array of modules.
141
     *
142
     * @param string $moduleName The name of the module to check.
143
     * @param array<string> $modules The array of modules to search in.
144
     * @return false|string Returns the retrieved module name if it exists, otherwise returns false.
145
     */
146
    protected function checkModuleExists(string $moduleName, array $modules): false|string
147
    {
148
        try {
149
            $retrieveModuleName = $this->retrieveModuleName($moduleName);
150
            if (!in_array(ltrim($retrieveModuleName, '\\'), $modules)) {
151
                $this->outputStyle?->writeln(
152
                    "<comment>The '$moduleName' module is not enabled. No changes have been made.</comment>"
153
                );
154
                return false;
155
            }
156
        } catch (InvalidModuleName $exception) {
157
            $this->outputStyle?->writeln(
158
                "<error>{$exception->getMessage()}</error>"
159
            );
160
            return false;
161
        }
162
163
        return $retrieveModuleName;
164
    }
165
166
    /**
167
     * Renders a table of available modules.
168
     *
169
     * @param array<SlickModuleInterface> $modules The array of modules to display in the table.
170
     * @return void
171
     */
172
    protected function renderTable(array $modules, SymfonyStyle $style): void
173
    {
174
        $installedModules = $this->retrieveInstalledModules();
175
        $table = $style->createTable();
176
        $table->setHeaderTitle("Available Modules");
177
        $table->setHeaders(["Name", "Description", "Enabled"]);
178
179
        foreach ($modules as $module) {
180
            $installed = "   <info>√</info>";
181
            $core = "   <fg=gray>-</>";
182
            $moduleName = is_object($module) ? get_class($module) : $this->retrieveModuleName($module->name());
183
            $notInstalled = "   <fg=red>x</>";
184
            $status = $this->checkModuleNotExists($moduleName, $installedModules) ? $notInstalled : $installed;
185
            if (in_array($module->name(), ["console", "dispatcher", "front_controller"])) {
186
                $status = $core;
187
            }
188
            $table->addRow([$module->name(), $module->description(), $status]);
189
        }
190
        $table->setColumnMaxWidth(1, 100);
191
192
        $table->render();
193
        $this->outputStyle?->write("  (<info>√</info>) enabled, ");
194
        $this->outputStyle?->write("(<fg=gray>-</>) core, ");
195
        $this->outputStyle?->write("(<fg=red>x</>) not enabled.\n");
196
    }
197
}
198