Completed
Pull Request — master (#4226)
by Craig
04:58
created

ExtensionMaker::generateBlankFiles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 15
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 17
rs 9.7666
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(
102
            'maker',
103
            [
104
                'root_namespace' => $namespace . $type,
105
            ],
106
            true
107
        );
108
        $io->success(sprintf('The `config/generated_dev.yaml` file has been updated to set `maker:root_namespace` value to %s.', $namespace . $type));
109
110
        $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));
111
112
        return 0;
113
    }
114
115
    public function configureDependencies(DependencyBuilder $dependencies)
116
    {
117
        $dependencies->addClassDependency(
118
            Command::class,
119
            'console'
120
        );
121
        $dependencies->addClassDependency(
122
            FileManager::class,
123
            'maker-bundle'
124
        );
125
        $dependencies->addClassDependency(
126
            ZikulaHttpKernelInterface::class,
127
            'zikula/core-bundle'
128
        );
129
        $dependencies->addClassDependency(
130
            DynamicConfigDumper::class,
131
            'zikula/core-bundle'
132
        );
133
    }
134
135
    private function createDirAndAutoload(string $namespace): void
136
    {
137
        $projectDir = $this->fileManager->getRootDirectory();
138
        $fs = new Filesystem();
139
        [$vendor, $extensionName] = explode('\\', $namespace, 2);
140
        $this->extensionPath = $projectDir . '/src/extensions/' . mb_strtolower($vendor) . '/' . $extensionName;
141
        $fs->mkdir($this->extensionPath);
142
        $this->kernel->getAutoloader()->addPsr4($namespace . '\\', $this->extensionPath);
143
    }
144
145
    private function getClassesToGenerate(string $namespace, string $type): iterable
146
    {
147
        $bundleClassName = str_replace('\\', '', $namespace);
148
        [$vendor, $extensionName] = explode('\\', $namespace, 2);
149
150
        return [
151
            ['name' => $bundleClassName, 'prefix' => '', 'suffix' => $type, 'template' => 'BundleClass.tpl.php'],
152
            ['name' => $bundleClassName, 'prefix' => 'DependencyInjection', 'suffix' => 'Extension', 'template' => 'DIExtensionClass.tpl.php'],
153
            ['name' => $extensionName . $type, 'prefix' => '', 'suffix' => 'Installer', 'template' => 'InstallerClass.tpl.php'],
154
        ];
155
    }
156
157
    private function generateClasses(string $namespace, string $type): string
158
    {
159
        $bundleClassFullName = '';
160
        foreach ($this->getClassesToGenerate($namespace, $type) as $classInfo) {
161
            $bundleClassNameDetails = $this->localGenerator->createClassNameDetails(
162
                $classInfo['name'],
163
                $classInfo['prefix'],
164
                $classInfo['suffix'],
165
                'Invalid!' . $classInfo['name']
166
            );
167
            $bundleClassFullName = ('' === $classInfo['prefix'] && $type === $classInfo['suffix']) ? $bundleClassNameDetails->getShortName() : $bundleClassFullName;
168
            $this->localGenerator->generateClass(
169
                $bundleClassNameDetails->getFullName(),
170
                dirname(__DIR__) . '/Resources/skeleton/extension/' . $classInfo['template'],
171
                [
172
                    'namespace' => $namespace . $type,
173
                    'type' => $type,
174
                    'name' => $bundleClassNameDetails->getShortName(),
175
                    'vendor' => mb_substr($namespace, 0, mb_strpos($namespace, '\\'))
176
                ]
177
            );
178
        }
179
180
        return $bundleClassFullName;
181
    }
182
183
    private function getFilesToGenerate(): iterable
184
    {
185
        return [
186
            'Resources/config/services.yaml' => 'services.yaml.tpl.php',
187
            'README.md' => 'README.md.tpl.php',
188
            'composer.json' => 'composer.json.tpl.php',
189
            'LICENSE.txt' => 'MIT.txt.tpl.php',
190
        ];
191
    }
192
193
    private function generateFiles(string $namespace, string $type, string $bundleClass): void
194
    {
195
        foreach ($this->getFilesToGenerate() as $targetPath => $templateName) {
196
            $this->localGenerator->generateFile(
197
                $this->extensionPath . '/' . $targetPath,
198
                dirname(__DIR__) . '/Resources/skeleton/extension/' . $templateName,
199
                [
200
                    'namespace' => $namespace . $type,
201
                    'type' => $type,
202
                    'vendor' => mb_substr($namespace, 0, mb_strpos($namespace, '\\')),
203
                    'name' => mb_substr($namespace, mb_strpos($namespace, '\\') + 1),
204
                    'bundleClass' => $bundleClass
205
                ]
206
            );
207
        }
208
    }
209
210
    private function generateBlankFiles(): void
211
    {
212
        $fs = new Filesystem();
213
        $fs->mkdir($this->extensionPath . '/Resources/doc');
214
        $fs->touch($this->extensionPath . '/Resources/doc/index.md');
215
        $fs->mkdir($this->extensionPath . '/Resources/doc/help/en');
216
        $fs->touch($this->extensionPath . '/Resources/doc/help/en/README.md');
217
        $fs->mkdir($this->extensionPath . '/Resources/public/css');
218
        $fs->touch($this->extensionPath . '/Resources/public/css/style.css');
219
        $fs->mkdir($this->extensionPath . '/Resources/public/images');
220
        $fs->touch($this->extensionPath . '/Resources/public/images/.gitkeep');
221
        $fs->mkdir($this->extensionPath . '/Resources/public/js');
222
        $fs->touch($this->extensionPath . '/Resources/public/js/.gitkeep');
223
        $fs->mkdir($this->extensionPath . '/Resources/translations');
224
        $fs->touch($this->extensionPath . '/Resources/translations/.gitkeep');
225
        $fs->mkdir($this->extensionPath . '/Resources/views');
226
        $fs->touch($this->extensionPath . '/Resources/views/.gitkeep');
227
    }
228
}
229