Completed
Pull Request — master (#4226)
by Craig
05:47
created

ExtensionMaker::getCommandName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula Foundation - https://ziku.la/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Zikula\Bundle\CoreBundle\Maker;
15
16
use Symfony\Bundle\MakerBundle\ConsoleStyle;
17
use Symfony\Bundle\MakerBundle\DependencyBuilder;
18
use Symfony\Bundle\MakerBundle\FileManager;
19
use Symfony\Bundle\MakerBundle\Generator;
20
use Symfony\Bundle\MakerBundle\InputConfiguration;
21
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
22
use Symfony\Bundle\MakerBundle\Str;
23
use Symfony\Component\Console\Command\Command;
24
use Symfony\Component\Console\Input\InputArgument;
25
use Symfony\Component\Console\Input\InputInterface;
26
use Symfony\Component\Filesystem\Filesystem;
27
use Zikula\Bundle\CoreBundle\DynamicConfigDumper;
28
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface;
29
30
class ExtensionMaker extends AbstractMaker
31
{
32
    /**
33
     * @var ZikulaHttpKernelInterface
34
     */
35
    private $kernel;
36
37
    /**
38
     * @var FileManager
39
     */
40
    private $fileManager;
41
42
    /**
43
     * @var DynamicConfigDumper
44
     */
45
    private $configDumper;
46
47
    /**
48
     * @var Generator
49
     */
50
    private $localGenerator;
51
52
    /**
53
     * @var string
54
     */
55
    private $extensionPath;
56
57
    public function __construct(
58
        ZikulaHttpKernelInterface $kernel,
59
        FileManager $fileManager,
60
        DynamicConfigDumper $configDumper
61
    ) {
62
        $this->kernel = $kernel;
63
        $this->fileManager = $fileManager;
64
        $this->configDumper = $configDumper;
65
    }
66
67
    public static function getCommandName(): string
68
    {
69
        return 'make:zikula-extension';
70
    }
71
72
    public function configureCommand(Command $command, InputConfiguration $inputConfig)
73
    {
74
        $command
75
            ->setDescription('Creates a new zikula extension bundle')
76
            ->addArgument('namespace', InputArgument::OPTIONAL, sprintf('Choose a namespace (e.g. <fg=yellow>Acme\%s</>)', Str::asClassName(Str::getRandomTerm())))
77
            ->addArgument('type', InputArgument::OPTIONAL, sprintf('Choose a extension type (<fg=yellow>module or theme</>)'))
78
            ->setHelp(file_get_contents(dirname(__DIR__) . '/Resources/help/ExtensionMaker.txt'))
79
        ;
80
    }
81
82
    public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
83
    {
84
        try {
85
            $namespace = Validators::validateBundleNamespace($input->getArgument('namespace'));
0 ignored issues
show
Bug introduced by
It seems like $input->getArgument('namespace') can also be of type null and string[]; however, parameter $namespace of Zikula\Bundle\CoreBundle...lidateBundleNamespace() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

85
            $namespace = Validators::validateBundleNamespace(/** @scrutinizer ignore-type */ $input->getArgument('namespace'));
Loading history...
86
        } catch (\InvalidArgumentException $exception) {
87
            $io->error($exception->getMessage());
88
89
            return 1;
90
        }
91
        $type = 'theme' === trim(mb_strtolower($input->getArgument('type'))) ? 'Theme' : 'Module';
0 ignored issues
show
Bug introduced by
It seems like $input->getArgument('type') can also be of type string[]; however, parameter $str of mb_strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

91
        $type = 'theme' === trim(mb_strtolower(/** @scrutinizer ignore-type */ $input->getArgument('type'))) ? 'Theme' : 'Module';
Loading history...
92
        $this->localGenerator = new Generator($this->fileManager, $namespace . $type);
93
94
        $this->createDirAndAutoload($namespace . $type);
95
        $bundleClass = $this->generateClasses($namespace, $type);
96
        $this->generateFiles($namespace, $type, $bundleClass);
97
        $this->generateBlankFiles();
98
99
        $this->localGenerator->writeChanges();
100
        $this->writeSuccessMessage($io);
101
        $this->configDumper->setConfiguration('maker',
102
            [
103
                'root_namespace' => $namespace . $type,
104
            ]
105
        );
106
        $io->success(sprintf('The `config/generated.yaml` file has been updated to set `maker:root_namespace` value to %s.', $namespace . $type));
107
108
        $io->warning(sprintf("In order to use other make:foo commands, you must install the extension!\nfirst run `php bin/console cache:clear`\nsecond run `php bin/console z:e:i %s`", $bundleClass));
109
110
        return 0;
111
    }
112
113
    public function configureDependencies(DependencyBuilder $dependencies)
114
    {
115
        $dependencies->addClassDependency(
116
            Command::class,
117
            'console'
118
        );
119
        $dependencies->addClassDependency(
120
            FileManager::class,
121
            'maker-bundle'
122
        );
123
        $dependencies->addClassDependency(
124
            ZikulaHttpKernelInterface::class,
125
            'zikula/core-bundle'
126
        );
127
        $dependencies->addClassDependency(
128
            DynamicConfigDumper::class,
129
            'zikula/core-bundle'
130
        );
131
    }
132
133
    private function createDirAndAutoload(string $namespace): void
134
    {
135
        $projectDir = $this->fileManager->getRootDirectory();
136
        $fs = new Filesystem();
137
        [$vendor, $extensionName] = explode('\\', $namespace, 2);
138
        $this->extensionPath = $projectDir . '/src/extensions/' . mb_strtolower($vendor) . '/' . $extensionName;
139
        $fs->mkdir($this->extensionPath);
140
        $this->kernel->getAutoloader()->addPsr4($namespace . '\\', $this->extensionPath);
141
    }
142
143
    private function getClassesToGenerate(string $namespace, string $type): iterable
144
    {
145
        $bundleClassName = str_replace('\\', '', $namespace);
146
        [$vendor, $extensionName] = explode('\\', $namespace, 2);
147
148
        return [
149
            ['name' => $bundleClassName, 'prefix' => '', 'suffix' => $type, 'template' => 'BundleClass.tpl.php'],
150
            ['name' => $bundleClassName, 'prefix' => 'DependencyInjection', 'suffix' => 'Extension', 'template' => 'DIExtensionClass.tpl.php'],
151
            ['name' => $extensionName . $type, 'prefix' => '', 'suffix' => 'Installer', 'template' => 'InstallerClass.tpl.php'],
152
        ];
153
    }
154
155
    private function generateClasses(string $namespace, string $type): string
156
    {
157
        $bundleClassFullName = '';
158
        foreach ($this->getClassesToGenerate($namespace, $type) as $classInfo) {
159
            $bundleClassNameDetails = $this->localGenerator->createClassNameDetails(
160
                $classInfo['name'],
161
                $classInfo['prefix'],
162
                $classInfo['suffix'],
163
                'Invalid!' . $classInfo['name']
164
            );
165
            $bundleClassFullName = ('' === $classInfo['prefix'] && $type === $classInfo['suffix']) ? $bundleClassNameDetails->getShortName() : $bundleClassFullName;
166
            $this->localGenerator->generateClass(
167
                $bundleClassNameDetails->getFullName(),
168
                dirname(__DIR__) . '/Resources/skeleton/extension/' . $classInfo['template'],
169
                [
170
                    'namespace' => $namespace . $type,
171
                    'type' => $type,
172
                    'name' => $bundleClassNameDetails->getShortName(),
173
                    'vendor' => mb_substr($namespace, 0, mb_strpos($namespace, '\\'))
174
                ]
175
            );
176
        }
177
178
        return $bundleClassFullName;
179
    }
180
181
    private function getFilesToGenerate(): iterable
182
    {
183
        return [
184
            'Resources/config/services.yaml' => 'services.yaml.tpl.php',
185
            'README.md' => 'README.md.tpl.php',
186
            'composer.json' => 'composer.json.tpl.php',
187
            'LICENSE.txt' => 'MIT.txt.tpl.php',
188
        ];
189
    }
190
191
    private function generateFiles(string $namespace, string $type, string $bundleClass): void
192
    {
193
        foreach ($this->getFilesToGenerate() as $targetPath => $templateName) {
194
            $this->localGenerator->generateFile(
195
                $this->extensionPath . '/' . $targetPath,
196
                dirname(__DIR__) . '/Resources/skeleton/extension/' . $templateName,
197
                [
198
                    'namespace' => $namespace . $type,
199
                    'type' => $type,
200
                    'vendor' => mb_substr($namespace, 0, mb_strpos($namespace, '\\')),
201
                    'name' => mb_substr($namespace, mb_strpos($namespace, '\\') + 1),
202
                    'bundleClass' => $bundleClass
203
                ]
204
            );
205
        }
206
    }
207
208
    private function generateBlankFiles(): void
209
    {
210
        $fs = new Filesystem();
211
        $fs->mkdir($this->extensionPath . '/Resources/doc');
212
        $fs->touch($this->extensionPath . '/Resources/doc/index.md');
213
        $fs->mkdir($this->extensionPath . '/Resources/doc/help/en');
214
        $fs->touch($this->extensionPath . '/Resources/doc/help/en/README.md');
215
        $fs->mkdir($this->extensionPath . '/Resources/public/css');
216
        $fs->touch($this->extensionPath . '/Resources/public/css/style.css');
217
        $fs->mkdir($this->extensionPath . '/Resources/public/images');
218
        $fs->touch($this->extensionPath . '/Resources/public/images/.gitkeep');
219
        $fs->mkdir($this->extensionPath . '/Resources/public/js');
220
        $fs->touch($this->extensionPath . '/Resources/public/js/.gitkeep');
221
        $fs->mkdir($this->extensionPath . '/Resources/translations');
222
        $fs->touch($this->extensionPath . '/Resources/translations/.gitkeep');
223
        $fs->mkdir($this->extensionPath . '/Resources/views');
224
        $fs->touch($this->extensionPath . '/Resources/views/.gitkeep');
225
    }
226
}
227