Completed
Pull Request — master (#4571)
by Craig
06:06
created

ParameterHelper::configureWebpackPublicPath()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 16
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 22
rs 8.8333
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula - 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\CoreInstallerBundle\Helper;
15
16
use RandomLib\Factory;
17
use Symfony\Component\Filesystem\Exception\IOException;
18
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
19
use Symfony\Component\HttpFoundation\File\Exception\CannotWriteFileException;
20
use Symfony\Component\HttpFoundation\RequestStack;
21
use function Symfony\Component\String\u;
22
use Zikula\Bundle\CoreBundle\CacheClearer;
23
use Zikula\Bundle\CoreBundle\Helper\LocalDotEnvHelper;
24
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaKernel;
25
use Zikula\Bundle\CoreBundle\YamlDumper;
26
use Zikula\Component\Wizard\AbortStageException;
27
use Zikula\ExtensionsModule\Api\ApiInterface\VariableApiInterface;
28
use Zikula\ExtensionsModule\Api\VariableApi;
29
30
class ParameterHelper
31
{
32
    /**
33
     * @var string
34
     */
35
    private $configDir;
36
37
    /**
38
     * @var string
39
     */
40
    private $projectDir;
41
42
    /**
43
     * @var VariableApiInterface
44
     */
45
    private $variableApi;
46
47
    /**
48
     * @var CacheClearer
49
     */
50
    private $cacheClearer;
51
52
    /**
53
     * @var RequestStack
54
     */
55
    private $requestStack;
56
57
    private $encodedParameterNames = [
58
        'password',
59
        'username',
60
        'email',
61
        'transport',
62
        'mailer_id',
63
        'mailer_key',
64
        'host',
65
        'port',
66
        'customParameters',
67
        'enableLogging'
68
    ];
69
70
    /**
71
     * ParameterHelper constructor.
72
     */
73
    public function __construct(
74
        string $projectDir,
75
        VariableApiInterface $variableApi,
76
        CacheClearer $cacheClearer,
77
        RequestStack $requestStack
78
    ) {
79
        $this->configDir = $projectDir . '/config';
80
        $this->projectDir = $projectDir;
81
        $this->variableApi = $variableApi;
82
        $this->cacheClearer = $cacheClearer;
83
        $this->requestStack = $requestStack;
84
    }
85
86
    public function getYamlHelper(): YamlDumper
87
    {
88
        return new YamlDumper($this->configDir, 'temp_params.yaml');
0 ignored issues
show
Deprecated Code introduced by
The class Zikula\Bundle\CoreBundle\YamlDumper has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

88
        return /** @scrutinizer ignore-deprecated */ new YamlDumper($this->configDir, 'temp_params.yaml');
Loading history...
89
    }
90
91
    public function initializeParameters(array $paramsToMerge = []): bool
92
    {
93
        $yamlHelper = $this->getYamlHelper();
94
        $params = array_merge($yamlHelper->getParameters(), $paramsToMerge);
95
        $yamlHelper->setParameters($params);
96
        $this->cacheClearer->clear('symfony.config');
97
98
        return true;
99
    }
100
101
    /**
102
     * @throws IOExceptionInterface If .env.local could not be dumped
103
     */
104
    public function finalizeParameters(): bool
105
    {
106
        $yamlHelper = $this->getYamlHelper();
107
        $params = $this->decodeParameters($yamlHelper->getParameters());
108
109
        $this->variableApi->getAll(VariableApi::CONFIG); // forces initialization of API
110
        if (!isset($params['upgrading']) || !$params['upgrading']) {
111
            $this->variableApi->set(VariableApi::CONFIG, 'locale', $params['locale']);
112
            // Set the System Identifier as a unique string.
113
            if (!$this->variableApi->get(VariableApi::CONFIG, 'system_identifier')) {
114
                $this->variableApi->set(VariableApi::CONFIG, 'system_identifier', str_replace('.', '', uniqid((string) (random_int(1000000000, 9999999999)), true)));
115
            }
116
            // add admin email as site email
117
            $this->variableApi->set(VariableApi::CONFIG, 'adminmail', $params['email']);
118
            $this->setMailerData($params);
119
            $this->configureRequestContext($params);
120
        }
121
122
        $params = array_diff_key($params, array_flip($this->encodedParameterNames)); // remove all encoded params
123
124
        // store the recent version in a config var for later usage. This enables us to determine the version we are upgrading from
125
        $this->variableApi->set(VariableApi::CONFIG, 'Version_Num', ZikulaKernel::VERSION);
126
127
        if (isset($params['router.request_context.base_url'])) {
128
            $this->configureWebpackPublicPath($params['router.request_context.base_url']);
129
        }
130
131
        $this->writeEnvVars($params);
132
133
        $yamlHelper->deleteFile();
134
135
        // clear the cache
136
        $this->cacheClearer->clear('symfony.config');
137
138
        return true;
139
    }
140
141
    /**
142
     * Configure the Request Context
143
     * see https://symfony.com/doc/current/routing.html#generating-urls-in-commands
144
     * This is needed because emails are sent from CLI requiring routes to be built
145
     */
146
    private function configureRequestContext(array &$params): void
147
    {
148
        $request = $this->requestStack->getMasterRequest();
149
        $hostFromRequest = isset($request) ? $request->getHost() : 'localhost';
150
        $schemeFromRequest = isset($request) ? $request->getScheme() : 'http';
151
        $basePathFromRequest = isset($request) ? $request->getBasePath() : null;
152
        $params['router.request_context.host'] = $params['router.request_context.host'] ?? $hostFromRequest;
153
        $params['router.request_context.scheme'] = $params['router.request_context.scheme'] ?? $schemeFromRequest;
154
        $params['router.request_context.base_url'] = $params['router.request_context.base_url'] ?? $basePathFromRequest;
155
    }
156
157
    public function configureWebpackPublicPath(string $publicPath = ''): void
158
    {
159
        if (empty($publicPath) || '/' === $publicPath) {
160
            return;
161
        }
162
        // replace default `build` with `$publicPath . '/build'`
163
        $files = [
164
            '/webpack.config.js' => ['/\.setPublicPath\(\'\/build\'\)/', '.setPublicPath(\'' . $publicPath . '/build\')'],
165
            '/public/build/manifest.json' => ['/build\//', u($publicPath)->trimStart('/') . '/build/'],
166
            '/public/build/entrypoints.json' => ['/\/build/', $publicPath . '/build'],
167
            '/public/build/runtime.js' => ['/__webpack_require__.p = "\/build\/";/', '__webpack_require__.p = "' . $publicPath . '/build/";']
168
        ];
169
        foreach ($files as $path => $search) {
170
            $contents = file_get_contents($this->projectDir . $path);
171
            if (false === $contents) {
172
                continue;
173
            }
174
            $C = u($contents);
175
            if (!$C->containsAny($publicPath)) { // check if replaced previously
176
                $success = file_put_contents($this->projectDir . $path, $C->replaceMatches($search[0], $search[1])->toString());
177
                if (false === $success) {
178
                    throw new CannotWriteFileException(sprintf('Could not write to path %s, please check your file permissions.', $path));
179
                }
180
            }
181
        }
182
    }
183
184
    /**
185
     * @param array $params values from upgrade
186
     */
187
    private function writeEnvVars(array &$params): void
188
    {
189
        $randomLibFactory = new Factory();
190
        $generator = $randomLibFactory->getMediumStrengthGenerator();
191
        $secret = isset($params['secret']) && !empty($params['secret']) && '%env(APP_SECRET)%' !== $params['secret']
192
            ? $params['secret']
193
            : $generator->generateString(50)
194
        ;
195
        $vars = [
196
            'APP_ENV' => $params['env'] ?? 'prod',
197
            'APP_DEBUG' => isset($params['debug']) ? (int) ($params['debug']) : 0,
198
            'APP_SECRET' => '!\'' . $secret . '\'',
199
            'ZIKULA_INSTALLED' => '\'' . ZikulaKernel::VERSION . '\''
200
        ];
201
        if (isset($params['router.request_context.host'])) {
202
            $vars['DEFAULT_URI'] = sprintf('!%s://%s%s', $params['router.request_context.scheme'], $params['router.request_context.host'], $params['router.request_context.base_url']);
203
            unset($params['router.request_context.scheme'], $params['router.request_context.host'], $params['router.request_context.base_url']);
204
        }
205
        (new LocalDotEnvHelper($this->projectDir))->writeLocalEnvVars($vars);
206
    }
207
208
    /**
209
     * Write params to file as encoded values.
210
     *
211
     * @throws AbortStageException
212
     */
213
    public function writeEncodedParameters(array $data): void
214
    {
215
        $yamlHelper = $this->getYamlHelper();
216
        foreach ($data as $k => $v) {
217
            $data[$k] = is_string($v) ? base64_encode($v) : $v; // encode so values are 'safe' for json
218
        }
219
        $params = array_merge($yamlHelper->getParameters(), $data);
220
        try {
221
            $yamlHelper->setParameters($params);
222
        } catch (IOException $exception) {
223
            throw new AbortStageException(sprintf('Cannot write parameters to %s file.', 'temp_params.yaml'));
224
        }
225
    }
226
227
    /**
228
     * Remove base64 encoding for parameters.
229
     */
230
    public function decodeParameters(array $params = []): array
231
    {
232
        foreach ($this->encodedParameterNames as $parameterName) {
233
            if (!empty($params[$parameterName])) {
234
                $params[$parameterName] = is_string($params[$parameterName]) ? base64_decode($params[$parameterName]) : $params[$parameterName];
235
            }
236
        }
237
238
        return $params;
239
    }
240
241
    private function setMailerData(array $params): void
242
    {
243
        // params have already been decoded
244
        $mailerParams = array_intersect_key($params, array_flip($this->encodedParameterNames));
245
        unset($mailerParams['mailer_key'], $mailerParams['password'], $mailerParams['username'], $mailerParams['email']);
246
        $this->variableApi->setAll('ZikulaMailerModule', $mailerParams);
247
    }
248
}
249