Completed
Pull Request — master (#4433)
by Craig
04:45
created

ParameterHelper::configureRequestContext()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 7
c 1
b 0
f 0
nc 8
nop 1
dl 0
loc 9
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\RequestStack;
20
use Symfony\Component\Yaml\Yaml;
21
use Zikula\Bundle\CoreBundle\CacheClearer;
22
use Zikula\Bundle\CoreBundle\Helper\LocalDotEnvHelper;
23
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface;
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
    /**
58
     * @var ZikulaHttpKernelInterface
59
     */
60
    private $kernel;
61
62
    private $encodedParameterNames = [
63
        'password',
64
        'username',
65
        'email',
66
        'transport',
67
        'mailer_id',
68
        'mailer_key',
69
        'host',
70
        'port',
71
        'customParameters',
72
        'enableLogging'
73
    ];
74
75
    /**
76
     * ParameterHelper constructor.
77
     */
78
    public function __construct(
79
        string $projectDir,
80
        VariableApiInterface $variableApi,
81
        CacheClearer $cacheClearer,
82
        RequestStack $requestStack,
83
        ZikulaHttpKernelInterface $kernel
84
    ) {
85
        $this->configDir = $projectDir . '/config';
86
        $this->projectDir = $projectDir;
87
        $this->variableApi = $variableApi;
88
        $this->cacheClearer = $cacheClearer;
89
        $this->requestStack = $requestStack;
90
        $this->kernel = $kernel;
91
    }
92
93
    public function getYamlHelper(bool $initCopy = false): YamlDumper
94
    {
95
        $copyFile = $initCopy ? 'services.yaml' : null;
96
97
        return new YamlDumper($this->configDir, 'services_custom.yaml', $copyFile);
98
    }
99
100
    public function initializeParameters(array $paramsToMerge = []): bool
101
    {
102
        $yamlHelper = $this->getYamlHelper(true);
103
        $params = array_merge($yamlHelper->getParameters(), $paramsToMerge);
104
        $yamlHelper->setParameters($params);
105
        $this->cacheClearer->clear('symfony.config');
106
107
        return true;
108
    }
109
110
    /**
111
     * Load and set new default values from the original services.yaml file into the services_custom.yaml file.
112
     */
113
    public function reInitParameters(): bool
114
    {
115
        $originalParameters = Yaml::parse(file_get_contents($this->kernel->getProjectDir() . '/config/services.yaml'));
116
        $yamlHelper = $this->getYamlHelper();
117
        $yamlHelper->setParameters(array_merge($originalParameters['parameters'], $yamlHelper->getParameters()));
118
        $this->cacheClearer->clear('symfony.config');
119
120
        return true;
121
    }
122
123
    /**
124
     * @throws IOExceptionInterface If .env.local could not be dumped
125
     */
126
    public function finalizeParameters(): bool
127
    {
128
        $yamlHelper = $this->getYamlHelper();
129
        $params = $this->decodeParameters($yamlHelper->getParameters());
130
131
        $this->variableApi->getAll(VariableApi::CONFIG); // forces initialization of API
132
        if (!isset($params['upgrading']) || !$params['upgrading']) {
133
            $this->variableApi->set(VariableApi::CONFIG, 'locale', $params['locale']);
134
            // Set the System Identifier as a unique string.
135
            if (!$this->variableApi->get(VariableApi::CONFIG, 'system_identifier')) {
136
                $this->variableApi->set(VariableApi::CONFIG, 'system_identifier', str_replace('.', '', uniqid((string) (random_int(1000000000, 9999999999)), true)));
137
            }
138
            // add admin email as site email
139
            $this->variableApi->set(VariableApi::CONFIG, 'adminmail', $params['email']);
140
            $this->setMailerData($params);
141
            $this->configureRequestContext($params);
142
        }
143
144
        $params = array_diff_key($params, array_flip($this->encodedParameterNames)); // remove all encoded params
145
146
        // store the recent version in a config var for later usage. This enables us to determine the version we are upgrading from
147
        $this->variableApi->set(VariableApi::CONFIG, 'Version_Num', ZikulaKernel::VERSION);
148
149
        $this->writeEnvVars($params);
150
151
        if (isset($params['upgrading']) && $params['upgrading']) {
152
            $this->resetLegacyParams($params);
153
        }
154
155
        // write parameters into config/services_custom.yaml
156
        $yamlHelper->setParameters($params);
157
158
        // clear the cache
159
        $this->cacheClearer->clear('symfony.config');
160
161
        return true;
162
    }
163
164
    /**
165
     * Configure the Request Context
166
     * see https://symfony.com/doc/current/routing.html#generating-urls-in-commands
167
     * This is needed because emails are sent from CLI requiring routes to be built
168
     */
169
    private function configureRequestContext(array &$params): void
170
    {
171
        $request = $this->requestStack->getMasterRequest();
172
        $hostFromRequest = isset($request) ? $request->getHost() : 'localhost';
173
        $schemeFromRequest = isset($request) ? $request->getScheme() : 'http';
174
        $basePathFromRequest = isset($request) ? $request->getBasePath() : null;
175
        $params['router.request_context.host'] = $params['router.request_context.host'] ?? $hostFromRequest;
176
        $params['router.request_context.scheme'] = $params['router.request_context.scheme'] ?? $schemeFromRequest;
177
        $params['router.request_context.base_url'] = $params['router.request_context.base_url'] ?? $basePathFromRequest;
178
    }
179
180
    /**
181
     * @param array $params values from upgrade
182
     */
183
    private function writeEnvVars(array &$params): void
184
    {
185
        $randomLibFactory = new Factory();
186
        $generator = $randomLibFactory->getMediumStrengthGenerator();
187
        $secret = isset($params['secret']) && !empty($params['secret']) && '%env(APP_SECRET)%' !== $params['secret']
188
            ? $params['secret']
189
            : $generator->generateString(50)
190
        ;
191
        $vars = [
192
            'APP_ENV' => $params['env'] ?? 'prod',
193
            'APP_DEBUG' => isset($params['debug']) ? (int) ($params['debug']) : 0,
194
            'APP_SECRET' => '!\'' . $secret . '\'',
195
            'ZIKULA_INSTALLED' => '\'' . ZikulaKernel::VERSION . '\''
196
        ];
197
        if (isset($params['router.request_context.host'])) {
198
            $vars['DEFAULT_URI'] = sprintf('!%s://%s%s', $params['router.request_context.scheme'], $params['router.request_context.host'], $params['router.request_context.base_url']);
199
            unset($params['router.request_context.scheme'], $params['router.request_context.host'], $params['router.request_context.base_url']);
200
        }
201
        (new LocalDotEnvHelper($this->projectDir))->writeLocalEnvVars($vars);
202
    }
203
204
    private function resetLegacyParams(array &$params): void
205
    {
206
        unset(
207
            $params['temp_dir'],
208
            $params['system.chmod_dir'],
209
            $params['url_secret'],
210
            $params['umask'],
211
            $params['env'],
212
            $params['debug'],
213
            $params['secret'],
214
            $params['database_driver'],
215
            $params['database_host'],
216
            $params['database_port'],
217
            $params['database_name'],
218
            $params['database_user'],
219
            $params['database_password'],
220
            $params['database_path'],
221
            $params['database_socket'],
222
            $params['database_server_version']
223
        );
224
    }
225
226
    /**
227
     * Write params to file as encoded values.
228
     *
229
     * @throws AbortStageException
230
     */
231
    public function writeEncodedParameters(array $data): void
232
    {
233
        $yamlHelper = $this->getYamlHelper();
234
        foreach ($data as $k => $v) {
235
            $data[$k] = is_string($v) ? base64_encode($v) : $v; // encode so values are 'safe' for json
236
        }
237
        $params = array_merge($yamlHelper->getParameters(), $data);
238
        try {
239
            $yamlHelper->setParameters($params);
240
        } catch (IOException $exception) {
241
            throw new AbortStageException(sprintf('Cannot write parameters to %s file.', 'services_custom.yaml'));
242
        }
243
    }
244
245
    /**
246
     * Remove base64 encoding for parameters.
247
     */
248
    public function decodeParameters(array $params = []): array
249
    {
250
        foreach ($this->encodedParameterNames as $parameterName) {
251
            if (!empty($params[$parameterName])) {
252
                $params[$parameterName] = is_string($params[$parameterName]) ? base64_decode($params[$parameterName]) : $params[$parameterName];
253
            }
254
        }
255
256
        return $params;
257
    }
258
259
    private function setMailerData(array $params): void
260
    {
261
        // params have already been decoded
262
        $mailerParams = array_intersect_key($params, array_flip($this->encodedParameterNames));
263
        unset($mailerParams['mailer_key'], $mailerParams['password'], $mailerParams['username'], $mailerParams['email']);
264
        $this->variableApi->setAll('ZikulaMailerModule', $mailerParams);
265
    }
266
}
267