createFromThemeJson()   F
last analyzed

Complexity

Conditions 14
Paths 1939

Size

Total Lines 61
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 36
nc 1939
nop 4
dl 0
loc 61
rs 2.1
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types=1);
2
3
namespace Shopware\Storefront\Theme\StorefrontPluginConfiguration;
4
5
use Shopware\Core\Framework\Bundle;
6
use Shopware\Core\Framework\Log\Package;
7
use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
8
use Shopware\Storefront\Framework\ThemeInterface;
9
use Shopware\Storefront\Theme\Exception\InvalidThemeBundleException;
10
use Shopware\Storefront\Theme\Exception\ThemeCompileException;
11
use Symfony\Component\Finder\Finder;
12
13
#[Package('storefront')]
14
class StorefrontPluginConfigurationFactory extends AbstractStorefrontPluginConfigurationFactory
15
{
16
    /**
17
     * @internal
18
     */
19
    public function __construct(private readonly string $projectDir)
20
    {
21
    }
22
23
    public function getDecorated(): AbstractStorefrontPluginConfigurationFactory
24
    {
25
        throw new DecorationPatternException(self::class);
26
    }
27
28
    public function createFromBundle(Bundle $bundle): StorefrontPluginConfiguration
29
    {
30
        if ($bundle instanceof ThemeInterface) {
31
            return $this->createThemeConfig($bundle->getName(), $bundle->getPath());
32
        }
33
34
        return $this->createPluginConfig($bundle->getName(), $bundle->getPath());
35
    }
36
37
    public function createFromApp(string $appName, string $appPath): StorefrontPluginConfiguration
38
    {
39
        $absolutePath = $this->projectDir . '/' . $appPath;
40
        if (file_exists($absolutePath . '/Resources/theme.json')) {
41
            return $this->createThemeConfig($appName, $absolutePath);
42
        }
43
44
        return $this->createPluginConfig($appName, $absolutePath);
45
    }
46
47
    /**
48
     * @param array<string, mixed> $data
49
     */
50
    public function createFromThemeJson(string $name, array $data, string $path, bool $isFullpath = true): StorefrontPluginConfiguration
51
    {
52
        try {
53
            if (!$isFullpath) {
54
                $path = $this->projectDir . \DIRECTORY_SEPARATOR . str_replace(\DIRECTORY_SEPARATOR . 'Resources', '', $path);
55
            }
56
            $pathname = $path . \DIRECTORY_SEPARATOR . 'Resources/theme.json';
57
58
            $basePath = realpath(pathinfo($pathname, \PATHINFO_DIRNAME)) ?: $pathname;
0 ignored issues
show
Bug introduced by
It seems like pathinfo($pathname, PATHINFO_DIRNAME) can also be of type array; however, parameter $path of realpath() 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

58
            $basePath = realpath(/** @scrutinizer ignore-type */ pathinfo($pathname, \PATHINFO_DIRNAME)) ?: $pathname;
Loading history...
59
60
            $basePath = $this->stripProjectDir($basePath);
61
62
            $config = new StorefrontPluginConfiguration($name);
63
64
            $config->setThemeJson($data);
65
            $config->setBasePath($this->stripProjectDir($basePath));
66
            $config->setStorefrontEntryFilepath($this->getEntryFile($path));
67
            $config->setIsTheme(true);
68
            $config->setName($data['name']);
69
            $config->setAuthor($data['author']);
70
71
            if (\array_key_exists('style', $data) && \is_array($data['style'])) {
72
                $this->resolveStyleFiles($data['style'], $basePath, $config);
73
            }
74
75
            if (\array_key_exists('script', $data) && \is_array($data['script'])) {
76
                $fileCollection = FileCollection::createFromArray($data['script']);
77
                $config->setScriptFiles($this->addBasePathToCollection($fileCollection, $basePath));
78
            }
79
80
            if (\array_key_exists('asset', $data)) {
81
                $config->setAssetPaths($this->addBasePathToArray($data['asset'], $basePath));
82
            }
83
84
            if (\array_key_exists('previewMedia', $data)) {
85
                $config->setPreviewMedia($basePath . \DIRECTORY_SEPARATOR . $data['previewMedia']);
86
            }
87
88
            if (\array_key_exists('config', $data)) {
89
                $config->setThemeConfig($data['config']);
90
            }
91
92
            if (\array_key_exists('views', $data)) {
93
                $config->setViewInheritance($data['views']);
94
            }
95
96
            if (\array_key_exists('configInheritance', $data)) {
97
                $config->setConfigInheritance($data['configInheritance']);
98
                $baseConfig = $config->getThemeConfig();
99
                $baseConfig['configInheritance'] = $data['configInheritance'];
100
                $config->setThemeConfig($baseConfig);
101
            }
102
103
            if (\array_key_exists('iconSets', $data)) {
104
                $config->setIconSets($data['iconSets']);
105
            }
106
        } catch (\Throwable) {
107
            $config = new StorefrontPluginConfiguration($name);
108
        }
109
110
        return $config;
111
    }
112
113
    private function createPluginConfig(string $name, string $path): StorefrontPluginConfiguration
114
    {
115
        $config = new StorefrontPluginConfiguration($name);
116
        $config->setIsTheme(false);
117
        $config->setStorefrontEntryFilepath($this->getEntryFile($path));
118
        $config->setBasePath($this->stripProjectDir($path));
119
120
        $stylesPath = $path . \DIRECTORY_SEPARATOR . 'Resources/app/storefront/src/scss';
121
        $config->setStyleFiles(FileCollection::createFromArray($this->getScssEntryFileInDir($stylesPath)));
122
123
        $scriptPath = $path . \DIRECTORY_SEPARATOR . 'Resources/app/storefront/dist/storefront/js';
124
        $config->setScriptFiles(FileCollection::createFromArray($this->getFilesInDir($scriptPath)));
125
126
        return $config;
127
    }
128
129
    private function createThemeConfig(string $name, string $path): StorefrontPluginConfiguration
130
    {
131
        $pathname = $path . \DIRECTORY_SEPARATOR . 'Resources/theme.json';
132
133
        if (!file_exists($pathname)) {
134
            throw new InvalidThemeBundleException($name);
135
        }
136
137
        try {
138
            $fileContent = file_get_contents($pathname);
139
            if ($fileContent === false) {
140
                throw new ThemeCompileException(
141
                    $name,
142
                    'Unable to read theme.json'
143
                );
144
            }
145
146
            /** @var array<string, mixed> $data */
147
            $data = json_decode($fileContent, true);
148
            if (json_last_error() !== \JSON_ERROR_NONE) {
149
                throw new ThemeCompileException(
150
                    $name,
151
                    'Unable to parse theme.json. Message: ' . json_last_error_msg()
152
                );
153
            }
154
155
            $config = $this->createFromThemeJson($name, $data, $path);
156
        } catch (ThemeCompileException $e) {
157
            throw $e;
158
        } catch (\Exception $e) {
159
            throw new ThemeCompileException(
160
                $name,
161
                sprintf(
162
                    'Got exception while parsing theme config. Exception message "%s"',
163
                    $e->getMessage()
164
                ),
165
                $e
166
            );
167
        }
168
169
        return $config;
170
    }
171
172
    private function getEntryFile(string $path): ?string
173
    {
174
        $path = rtrim($path, \DIRECTORY_SEPARATOR) . \DIRECTORY_SEPARATOR . 'Resources/app/storefront/src';
175
176
        if (file_exists($path . \DIRECTORY_SEPARATOR . 'main.ts')) {
177
            return $this->stripProjectDir($path . \DIRECTORY_SEPARATOR . 'main.ts');
178
        }
179
180
        if (file_exists($path . \DIRECTORY_SEPARATOR . 'main.js')) {
181
            return $this->stripProjectDir($path . \DIRECTORY_SEPARATOR . 'main.js');
182
        }
183
184
        return null;
185
    }
186
187
    private function addBasePathToCollection(FileCollection $fileCollection, string $basePath): FileCollection
188
    {
189
        foreach ($fileCollection as $file) {
190
            if (mb_strpos($file->getFilepath(), '@') === 0) {
191
                continue;
192
            }
193
            $file->setFilepath($this->addBasePath($file->getFilepath(), $basePath));
194
        }
195
196
        return $fileCollection;
197
    }
198
199
    /**
200
     * @param array<int, string> $files
201
     *
202
     * @return array<int, string>
203
     */
204
    private function addBasePathToArray(array $files, string $basePath): array
205
    {
206
        array_walk($files, function (&$path) use ($basePath): void {
207
            if (mb_strpos($path, '@') === 0) {
208
                return;
209
            }
210
            $path = $this->addBasePath($path, $basePath);
211
        });
212
213
        return $files;
214
    }
215
216
    private function addBasePath(string $path, string $basePath): string
217
    {
218
        return $basePath . \DIRECTORY_SEPARATOR . $path;
219
    }
220
221
    /**
222
     * @return array<int, string>
223
     */
224
    private function getFilesInDir(string $path): array
225
    {
226
        if (!is_dir($path)) {
227
            return [];
228
        }
229
        $finder = new Finder();
230
        $finder->files()->in($path);
231
232
        $files = [];
233
        foreach ($finder as $file) {
234
            $files[] = $this->stripProjectDir($file->getPathname());
235
        }
236
237
        return $files;
238
    }
239
240
    /**
241
     * @return array<int, string>
242
     */
243
    private function getScssEntryFileInDir(string $path): array
244
    {
245
        if (!is_dir($path)) {
246
            return [];
247
        }
248
        $finder = new Finder();
249
        $finder->files()->name('base.scss')->in($path)->depth('0');
250
251
        $files = [];
252
        foreach ($finder as $file) {
253
            $files[] = $this->stripProjectDir($file->getPathname());
254
        }
255
256
        return $files;
257
    }
258
259
    private function stripProjectDir(string $path): string
260
    {
261
        if (str_starts_with($path, $this->projectDir)) {
262
            return substr($path, \strlen($this->projectDir) + 1);
263
        }
264
265
        return $path;
266
    }
267
268
    /**
269
     * @param array<int, string|array<string, array<string, array<int, string>>>> $styles
270
     */
271
    private function resolveStyleFiles(array $styles, string $basePath, StorefrontPluginConfiguration $config): void
272
    {
273
        $fileCollection = new FileCollection();
274
        foreach ($styles as $style) {
275
            if (!\is_array($style)) {
276
                $fileCollection->add(new File($this->stripProjectDir($style)));
277
278
                continue;
279
            }
280
281
            foreach ($style as $filename => $additional) {
282
                if (!\array_key_exists('resolve', $additional)) {
283
                    $fileCollection->add(new File($this->stripProjectDir($filename)));
284
285
                    continue;
286
                }
287
288
                foreach ($additional['resolve'] as &$resolvePath) {
289
                    $resolvePath = $this->addBasePath($resolvePath, $basePath);
290
                }
291
                unset($resolvePath);
292
293
                $fileCollection->add(new File($this->stripProjectDir($filename), $additional['resolve'] ?? []));
294
            }
295
        }
296
        $config->setStyleFiles($this->addBasePathToCollection($fileCollection, $basePath));
297
    }
298
}
299