Completed
Push — master ( 5d71a8...69686b )
by Arman
23s queued 17s
created

ModuleManager::copyDirectoryWithTemplates()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Quantum PHP Framework
5
 *
6
 * An open source software development framework for PHP
7
 *
8
 * @package Quantum
9
 * @author Arman Ag. <[email protected]>
10
 * @copyright Copyright (c) 2018 Softberg LLC (https://softberg.org)
11
 * @link http://quantum.softberg.org/
12
 * @since 2.9.7
13
 */
14
15
namespace Quantum\Module;
16
17
use Symfony\Component\VarExporter\Exception\ExceptionInterface;
18
use Quantum\Libraries\Storage\Factories\FileSystemFactory;
19
use Quantum\Libraries\Config\Exceptions\ConfigException;
20
use Quantum\Module\Exceptions\ModuleLoaderException;
21
use Quantum\Di\Exceptions\DiException;
22
use Quantum\Exceptions\BaseException;
23
use Quantum\Environment\Environment;
24
use ReflectionException;
25
use Exception;
26
27
/**
28
 * Class ModuleManager
29
 * @package Quantum\Module
30
 */
31
class ModuleManager
32
{
33
    /**
34
     * @var mixed
35
     */
36
    protected $fs;
37
38
    /**
39
     * @var bool
40
     */
41
    protected $optionEnabled;
42
43
    /**
44
     * @var string
45
     */
46
    private $moduleName;
47
48
    /**
49
     * @var string
50
     */
51
    private $template;
52
53
    /**
54
     * @var string
55
     */
56
    private $modulePath;
57
58
    /**
59
     * @var string
60
     */
61
    private $assetsPath;
62
63
    /**
64
     * @var string
65
     */
66
    private $templatePath;
67
68
    /**
69
     * @var bool
70
     */
71
    private $withAssets;
72
73
    /**
74
     * @var string
75
     */
76
    private $modulesConfigPath;
77
78
    /**
79
     * @param string $moduleName
80
     * @param string $template
81
     * @param bool $enabled
82
     * @param bool $withAssets
83
     * @throws BaseException
84
     * @throws DiException
85
     * @throws ConfigException
86
     * @throws ReflectionException
87
     */
88
    public function __construct(string $moduleName, string $template, bool $enabled, bool $withAssets = false)
89
    {
90
        $this->fs = FileSystemFactory::get();
91
92
        $this->moduleName = $moduleName;
93
94
        $this->withAssets = $withAssets;
95
        $this->optionEnabled = $enabled;
96
        $this->template = $template;
97
98
        $this->assetsPath = assets_dir() . DS . $moduleName;
99
        $this->modulePath = modules_dir() . DS . $moduleName;
100
        $this->templatePath = __DIR__ . DS . 'Templates' . DS . ucfirst($template);
101
        $this->modulesConfigPath = base_dir() . DS . 'shared' . DS . 'config' . DS . 'modules.php';
102
    }
103
104
    /**
105
     * @return string
106
     */
107
    public function getModuleName(): string
108
    {
109
        return $this->moduleName;
110
    }
111
112
    /**
113
     * @return string
114
     */
115
    public function getBaseNamespace(): string
116
    {
117
        return Environment::getInstance()->getAppEnv() === 'testing'
118
            ? "Quantum\\Tests\\_root\\modules"
119
            : "Modules";
120
    }
121
122
    /**
123
     * @throws ModuleLoaderException
124
     * @throws ExceptionInterface
125
     * @throws Exception
126
     */
127
    public function addModuleConfig()
128
    {
129
        if (!$this->fs->isDirectory($this->modulePath)) {
130
            throw new Exception("Module directory does not exist, skipping config update.");
131
        }
132
133
        $moduleConfigs = ModuleLoader::getInstance()->getModuleConfigs();
134
135
        foreach ($moduleConfigs as $module => $options) {
136
            if ($module == $this->moduleName || $options['prefix'] == strtolower($this->moduleName)) {
137
                throw new Exception("A module or prefix named '" . $this->moduleName . "' already exists");
138
            }
139
        }
140
141
        $moduleConfigs[$this->moduleName] = $this->getModuleOptions($this->moduleName);
142
143
        $this->updateModuleConfigFile($moduleConfigs);
144
    }
145
146
    /**
147
     * @throws Exception
148
     */
149
    public function writeContents()
150
    {
151
        if (!$this->fs->isDirectory(modules_dir())) {
152
            $this->fs->makeDirectory(modules_dir());
153
        }
154
155
        $copiedTemplates = $this->copyDirectoryWithTemplates($this->templatePath . DS . "src", $this->modulePath);
156
157
        if ($this->withAssets) {
158
            $copiedAssets = $this->copyAssets($this->templatePath . DS . "assets", $this->assetsPath);
159
        }
160
161
        if (!$this->validateModuleFiles(array_merge($copiedTemplates, $copiedAssets ?? []))) {
162
            throw new Exception("Module creation incomplete: missing files.");
163
        }
164
    }
165
166
    /**
167
     * @param string $src
168
     * @param string $dst
169
     * @return array
170
     * @throws Exception
171
     */
172
    private function copyDirectoryWithTemplates(string $src, string $dst): array
173
    {
174
        return $this->copyDirectory($src, $dst, true);
175
    }
176
177
    /**
178
     * @param string $src
179
     * @param string $dst
180
     * @return array
181
     * @throws Exception
182
     */
183
    private function copyAssets(string $src, string $dst): array
184
    {
185
        return $this->copyDirectory($src, $dst, false);
186
    }
187
188
    /**
189
     * @param string $src
190
     * @param string $dst
191
     * @param bool $processTemplates
192
     * @param array $copiedFiles
193
     * @return array
194
     * @throws Exception
195
     */
196
    private function copyDirectory(string $src, string $dst, bool $processTemplates, array $copiedFiles = []): array
197
    {
198
        if (!$this->fs->isDirectory($src)) {
199
            throw new Exception("Template '$this->template' does not exist");
200
        }
201
202
        if (!$this->fs->isDirectory($dst)) {
203
            $this->fs->makeDirectory($dst);
204
        }
205
206
        $dir = $this->fs->listDirectory($src);
207
208
        foreach ($dir as $file) {
209
            $srcPath = $file;
210
            $dstPath = str_replace($src, $dst, $file);
211
212
            if ($this->fs->isDirectory($srcPath)) {
213
                $copiedFiles = $this->copyDirectory($srcPath, $dstPath, $processTemplates, $copiedFiles);
214
            } else {
215
                if ($processTemplates) {
216
                    $this->processTemplates($srcPath, $dstPath);
217
                }
218
                else {
219
                    $this->fs->copy($srcPath, $dstPath);
220
                }
221
                $copiedFiles[] = $dstPath;
222
            }
223
        }
224
225
        return $copiedFiles;
226
    }
227
228
    /**
229
     * @param string $srcPath
230
     * @param string $dstPath
231
     */
232
    private function processTemplates(string $srcPath, string &$dstPath){
233
        $dstPath = str_replace('.tpl', '.php', $dstPath);
234
        $content = $this->fs->get($srcPath);
235
        $processedContent = $this->replacePlaceholders($content);
236
        $this->fs->put($dstPath, $processedContent);
237
    }
238
239
    /**
240
     * @param string $content
241
     * @return string
242
     */
243
    private function replacePlaceholders(string $content): string
244
    {
245
        $placeholders = [
246
            '{{MODULE_NAMESPACE}}' => $this->getBaseNamespace() .'\\' . $this->getModuleName(),
247
            '{{MODULE_NAME}}' => $this->getModuleName(),
248
        ];
249
250
        return str_replace(array_keys($placeholders), array_values($placeholders), $content);
251
    }
252
253
    /**
254
     * @param string $module
255
     * @return array
256
     */
257
    private function getModuleOptions(string $module): array
258
    {
259
        return [
260
            'prefix' => $this->template == "DemoWeb" ? "" : strtolower($module),
261
            'enabled' => $this->optionEnabled,
262
        ];
263
    }
264
265
    /**
266
     * @param array $moduleConfigs
267
     * @return void
268
     * @throws ExceptionInterface
269
     */
270
    private function updateModuleConfigFile(array $moduleConfigs): void
271
    {
272
        $this->fs->put(
273
            $this->modulesConfigPath,
274
            "<?php\n\nreturn " . export($moduleConfigs) . ";\n"
275
        );
276
    }
277
278
    /**
279
     * @param array $copiedFiles
280
     * @return bool
281
     */
282
    protected function validateModuleFiles(array $copiedFiles): bool
283
    {
284
        foreach ($copiedFiles as $file) {
285
            if (!$this->fs->exists($file)) {
286
                return false;
287
            }
288
        }
289
        return true;
290
    }
291
}