Completed
Pull Request — master (#4226)
by Craig
10:56 queued 05:13
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\HttpKernel\ZikulaHttpKernelInterface;
28
29
class ExtensionMaker extends AbstractMaker
30
{
31
    /**
32
     * @var ZikulaHttpKernelInterface
33
     */
34
    private $kernel;
35
36
    /**
37
     * @var FileManager
38
     */
39
    private $fileManager;
40
41
    /**
42
     * @var Generator
43
     */
44
    private $localGenerator;
45
46
    /**
47
     * @var string
48
     */
49
    private $extensionPath;
50
51
    public function __construct(ZikulaHttpKernelInterface $kernel, FileManager $fileManager)
52
    {
53
        $this->kernel = $kernel;
54
        $this->fileManager = $fileManager;
55
    }
56
57
    public static function getCommandName(): string
58
    {
59
        return 'make:zikula-extension';
60
    }
61
62
    public function configureCommand(Command $command, InputConfiguration $inputConfig)
63
    {
64
        $command
65
            ->setDescription('Creates a new zikula extension bundle')
66
            ->addArgument('namespace', InputArgument::OPTIONAL, sprintf('Choose a namespace (e.g. <fg=yellow>Acme\%s</>)', Str::asClassName(Str::getRandomTerm())))
67
            ->addArgument('type', InputArgument::OPTIONAL, sprintf('Choose a extension type (<fg=yellow>module or theme</>)'))
68
            ->setHelp(file_get_contents(dirname(__DIR__) . '/Resources/help/ExtensionMaker.txt'))
69
        ;
70
    }
71
72
    public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
73
    {
74
        try {
75
            $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

75
            $namespace = Validators::validateBundleNamespace(/** @scrutinizer ignore-type */ $input->getArgument('namespace'));
Loading history...
76
        } catch (\InvalidArgumentException $exception) {
77
            $io->error($exception->getMessage());
78
79
            return 1;
80
        }
81
        $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

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