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

ParameterHelper::configureWebpackPublicPath()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 16
c 1
b 0
f 0
nc 6
nop 1
dl 0
loc 22
rs 9.1111
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 Zikula\Bundle\CoreBundle\CacheClearer;
22
use Zikula\Bundle\CoreBundle\Helper\LocalDotEnvHelper;
23
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaKernel;
24
use Zikula\Bundle\CoreBundle\YamlDumper;
25
use Zikula\Component\Wizard\AbortStageException;
26
use Zikula\ExtensionsModule\Api\ApiInterface\VariableApiInterface;
27
use Zikula\ExtensionsModule\Api\VariableApi;
28
use function Symfony\Component\String\u;
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
        $this->configureWebpackPublicPath($params['router.request_context.base_url']);
128
129
        $this->writeEnvVars($params);
130
131
        $yamlHelper->deleteFile();
132
133
        // clear the cache
134
        $this->cacheClearer->clear('symfony.config');
135
136
        return true;
137
    }
138
139
    /**
140
     * Configure the Request Context
141
     * see https://symfony.com/doc/current/routing.html#generating-urls-in-commands
142
     * This is needed because emails are sent from CLI requiring routes to be built
143
     */
144
    private function configureRequestContext(array &$params): void
145
    {
146
        $request = $this->requestStack->getMasterRequest();
147
        $hostFromRequest = isset($request) ? $request->getHost() : 'localhost';
148
        $schemeFromRequest = isset($request) ? $request->getScheme() : 'http';
149
        $basePathFromRequest = isset($request) ? $request->getBasePath() : null;
150
        $params['router.request_context.host'] = $params['router.request_context.host'] ?? $hostFromRequest;
151
        $params['router.request_context.scheme'] = $params['router.request_context.scheme'] ?? $schemeFromRequest;
152
        $params['router.request_context.base_url'] = $params['router.request_context.base_url'] ?? $basePathFromRequest;
153
    }
154
155
    public function configureWebpackPublicPath(string $publicPath = ''): void
156
    {
157
        if (empty($publicPath)) {
158
            return;
159
        }
160
        // replace default `build` with `$publicPath . '/build'`
161
        $files = [
162
            '/webpack.config.js' => ["/\.setPublicPath\('\/build'\)/", ".setPublicPath('" . $publicPath . "/build')"],
163
            '/public/build/manifest.json' => ['/build\//', u($publicPath)->trimStart('/') . '/build/'],
164
            '/public/build/entrypoints.json' => ['/\/build/', $publicPath . '/build'],
165
            '/public/build/runtime.js' => ['/__webpack_require__.p = "\/build\/";/', '__webpack_require__.p = "' . $publicPath . '/build/";']
166
        ];
167
        foreach ($files as $path => $search) {
168
            $contents = file_get_contents($this->projectDir . $path);
169
            if (false === $contents) {
170
                continue;
171
            }
172
            $C = u($contents);
173
            if (!$C->containsAny($publicPath)) { // check if replaced previously
174
                $success = file_put_contents($this->projectDir . $path, $C->replaceMatches($search[0], $search[1])->toString());
175
                if (false === $success) {
176
                    throw new CannotWriteFileException(sprintf('Could not write to path %s, please check your file permissions.', $path));
177
                }
178
            }
179
        }
180
    }
181
182
    /**
183
     * @param array $params values from upgrade
184
     */
185
    private function writeEnvVars(array &$params): void
186
    {
187
        $randomLibFactory = new Factory();
188
        $generator = $randomLibFactory->getMediumStrengthGenerator();
189
        $secret = isset($params['secret']) && !empty($params['secret']) && '%env(APP_SECRET)%' !== $params['secret']
190
            ? $params['secret']
191
            : $generator->generateString(50)
192
        ;
193
        $vars = [
194
            'APP_ENV' => $params['env'] ?? 'prod',
195
            'APP_DEBUG' => isset($params['debug']) ? (int) ($params['debug']) : 0,
196
            'APP_SECRET' => '!\'' . $secret . '\'',
197
            'ZIKULA_INSTALLED' => '\'' . ZikulaKernel::VERSION . '\''
198
        ];
199
        if (isset($params['router.request_context.host'])) {
200
            $vars['DEFAULT_URI'] = sprintf('!%s://%s%s', $params['router.request_context.scheme'], $params['router.request_context.host'], $params['router.request_context.base_url']);
201
            unset($params['router.request_context.scheme'], $params['router.request_context.host'], $params['router.request_context.base_url']);
202
        }
203
        (new LocalDotEnvHelper($this->projectDir))->writeLocalEnvVars($vars);
204
    }
205
206
    /**
207
     * Write params to file as encoded values.
208
     *
209
     * @throws AbortStageException
210
     */
211
    public function writeEncodedParameters(array $data): void
212
    {
213
        $yamlHelper = $this->getYamlHelper();
214
        foreach ($data as $k => $v) {
215
            $data[$k] = is_string($v) ? base64_encode($v) : $v; // encode so values are 'safe' for json
216
        }
217
        $params = array_merge($yamlHelper->getParameters(), $data);
218
        try {
219
            $yamlHelper->setParameters($params);
220
        } catch (IOException $exception) {
221
            throw new AbortStageException(sprintf('Cannot write parameters to %s file.', 'temp_params.yaml'));
222
        }
223
    }
224
225
    /**
226
     * Remove base64 encoding for parameters.
227
     */
228
    public function decodeParameters(array $params = []): array
229
    {
230
        foreach ($this->encodedParameterNames as $parameterName) {
231
            if (!empty($params[$parameterName])) {
232
                $params[$parameterName] = is_string($params[$parameterName]) ? base64_decode($params[$parameterName]) : $params[$parameterName];
233
            }
234
        }
235
236
        return $params;
237
    }
238
239
    private function setMailerData(array $params): void
240
    {
241
        // params have already been decoded
242
        $mailerParams = array_intersect_key($params, array_flip($this->encodedParameterNames));
243
        unset($mailerParams['mailer_key'], $mailerParams['password'], $mailerParams['username'], $mailerParams['email']);
244
        $this->variableApi->setAll('ZikulaMailerModule', $mailerParams);
245
    }
246
}
247