Passed
Push — 3.1 ( 093d47...670c3c )
by Axel
15:03
created

ParameterHelper::getLocale()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 10
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\Configurator;
24
use Zikula\Bundle\CoreBundle\Helper\LocalDotEnvHelper;
25
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaKernel;
26
use Zikula\Bundle\CoreBundle\YamlDumper;
27
use Zikula\Component\Wizard\AbortStageException;
28
use Zikula\ExtensionsModule\Api\ApiInterface\VariableApiInterface;
29
use Zikula\ExtensionsModule\Api\VariableApi;
30
31
class ParameterHelper
32
{
33
    /**
34
     * @var string
35
     */
36
    private $configDir;
37
38
    /**
39
     * @var string
40
     */
41
    private $projectDir;
42
43
    /**
44
     * @var VariableApiInterface
45
     */
46
    private $variableApi;
47
48
    /**
49
     * @var CacheClearer
50
     */
51
    private $cacheClearer;
52
53
    /**
54
     * @var RequestStack
55
     */
56
    private $requestStack;
57
58
    private $encodedParameterNames = [
59
        'password',
60
        'username',
61
        'email',
62
        'transport',
63
        'mailer_id',
64
        'mailer_key',
65
        'host',
66
        'port',
67
        'customParameters',
68
        'enableLogging'
69
    ];
70
71
    /**
72
     * ParameterHelper constructor.
73
     */
74
    public function __construct(
75
        string $projectDir,
76
        VariableApiInterface $variableApi,
77
        CacheClearer $cacheClearer,
78
        RequestStack $requestStack
79
    ) {
80
        $this->configDir = $projectDir . '/config';
81
        $this->projectDir = $projectDir;
82
        $this->variableApi = $variableApi;
83
        $this->cacheClearer = $cacheClearer;
84
        $this->requestStack = $requestStack;
85
    }
86
87
    public function getYamlHelper(): YamlDumper
88
    {
89
        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

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