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

ExtensionMaker   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 86
c 1
b 0
f 0
dl 0
loc 166
rs 10
wmc 17

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getFilesToGenerate() 0 7 1
A configureDependencies() 0 5 1
A getCommandName() 0 3 1
A generate() 0 22 3
A generateBlankFiles() 0 15 1
A __construct() 0 4 1
A generateClasses() 0 25 4
A getClassesToGenerate() 0 9 1
A generateFiles() 0 12 2
A configureCommand() 0 7 1
A createDirAndAutoload() 0 8 1
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
    }
103
104
    private function createDirAndAutoload(string $namespace): void
105
    {
106
        $projectDir = $this->fileManager->getRootDirectory();
107
        $fs = new Filesystem();
108
        [$vendor, $extensionName] = explode('\\', $namespace, 2);
109
        $this->extensionPath = $projectDir . '/src/extensions/' . mb_strtolower($vendor) . '/' . $extensionName;
110
        $fs->mkdir($this->extensionPath);
111
        $this->kernel->getAutoloader()->addPsr4($namespace . '\\', $this->extensionPath);
112
    }
113
114
    private function getClassesToGenerate(string $namespace, string $type): iterable
115
    {
116
        $bundleClassName = str_replace('\\', '', $namespace);
117
        [$vendor, $extensionName] = explode('\\', $namespace, 2);
118
119
        return [
120
            ['name' => $bundleClassName, 'prefix' => '', 'suffix' => $type, 'template' => 'BundleClass.tpl.php'],
121
            ['name' => $bundleClassName, 'prefix' => 'DependencyInjection', 'suffix' => 'Extension', 'template' => 'DIExtensionClass.tpl.php'],
122
            ['name' => $extensionName . $type, 'prefix' => '', 'suffix' => 'Installer', 'template' => 'InstallerClass.tpl.php'],
123
        ];
124
    }
125
126
    private function generateClasses(string $namespace, string $type): string
127
    {
128
        $bundleClassFullName = '';
129
        foreach ($this->getClassesToGenerate($namespace, $type) as $classInfo) {
130
            $bundleClassNameDetails = $this->localGenerator->createClassNameDetails(
131
                $classInfo['name'],
132
                $classInfo['prefix'],
133
                $classInfo['suffix'],
134
                'Invalid!' . $classInfo['name']
135
            );
136
            $bundleClassFullName = ('' === $classInfo['prefix'] && $type === $classInfo['suffix']) ? $bundleClassNameDetails->getShortName() : $bundleClassFullName;
137
            $this->localGenerator->generateClass(
138
                $bundleClassNameDetails->getFullName(),
139
                dirname(__DIR__) . '/Resources/skeleton/extension/' . $classInfo['template'],
140
                [
141
                    'namespace' => $namespace . $type,
142
                    'type' => $type,
143
                    'name' => $bundleClassNameDetails->getShortName(),
144
                    'vendor' => mb_substr($namespace, 0, mb_strpos($namespace, '\\'))
145
                ]
146
            );
147
148
        }
149
150
        return $bundleClassFullName;
151
    }
152
153
    private function getFilesToGenerate(): iterable
154
    {
155
        return [
156
            'Resources/config/services.yaml' => 'services.yaml.tpl.php',
157
            'README.md' => 'README.md.tpl.php',
158
            'composer.json' => 'composer.json.tpl.php',
159
            'LICENSE.txt' => 'MIT.txt.tpl.php',
160
        ];
161
    }
162
163
    private function generateFiles(string $namespace, string $type, string $bundleClass): void
164
    {
165
        foreach ($this->getFilesToGenerate() as $targetPath => $templateName) {
166
            $this->localGenerator->generateFile(
167
                $this->extensionPath . '/' . $targetPath,
168
                dirname(__DIR__) . '/Resources/skeleton/extension/' . $templateName,
169
                [
170
                    'namespace' => $namespace . $type,
171
                    'type' => $type,
172
                    'vendor' => mb_substr($namespace, 0, mb_strpos($namespace, '\\')),
173
                    'name' => mb_substr($namespace, mb_strpos($namespace, '\\') + 1),
174
                    'bundleClass' => $bundleClass
175
                ]
176
            );
177
        }
178
    }
179
180
    private function generateBlankFiles(): void
181
    {
182
        $fs = new Filesystem();
183
        $fs->mkdir($this->extensionPath . '/Resources/doc');
184
        $fs->touch($this->extensionPath . '/Resources/doc/index.md');
185
        $fs->mkdir($this->extensionPath . '/Resources/public/css');
186
        $fs->touch($this->extensionPath . '/Resources/public/css/style.css');
187
        $fs->mkdir($this->extensionPath . '/Resources/public/images');
188
        $fs->touch($this->extensionPath . '/Resources/public/images/.gitkeep');
189
        $fs->mkdir($this->extensionPath . '/Resources/public/js');
190
        $fs->touch($this->extensionPath . '/Resources/public/js/.gitkeep');
191
        $fs->mkdir($this->extensionPath . '/Resources/translations');
192
        $fs->touch($this->extensionPath . '/Resources/translations/.gitkeep');
193
        $fs->mkdir($this->extensionPath . '/Resources/views');
194
        $fs->touch($this->extensionPath . '/Resources/views/.gitkeep');
195
    }
196
}
197