Completed
Push — master ( 6a1503...7e8e90 )
by Axel
05:21
created

ParameterHelper::finalizeParameters()   D

Complexity

Conditions 12
Paths 324

Size

Total Lines 64
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
cc 12
eloc 35
nc 324
nop 1
dl 0
loc 64
rs 4.5833
c 4
b 1
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
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula Foundation - 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\IOExceptionInterface;
18
use Symfony\Component\Filesystem\Filesystem;
19
use Symfony\Component\HttpFoundation\RequestStack;
20
use Symfony\Component\Yaml\Yaml;
21
use Zikula\Bundle\CoreBundle\CacheClearer;
22
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface;
23
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaKernel;
24
use Zikula\Bundle\CoreBundle\YamlDumper;
25
use Zikula\ExtensionsModule\Api\ApiInterface\VariableApiInterface;
26
use Zikula\ExtensionsModule\Api\VariableApi;
27
28
class ParameterHelper
29
{
30
    /**
31
     * @var string
32
     */
33
    private $configDir;
34
35
    /**
36
     * @var string
37
     */
38
    private $localEnvFile;
39
40
    /**
41
     * @var VariableApiInterface
42
     */
43
    private $variableApi;
44
45
    /**
46
     * @var CacheClearer
47
     */
48
    private $cacheClearer;
49
50
    /**
51
     * @var RequestStack
52
     */
53
    private $requestStack;
54
55
    /**
56
     * @var ZikulaHttpKernelInterface
57
     */
58
    private $kernel;
59
60
    /**
61
     * ParameterHelper constructor.
62
     */
63
    public function __construct(
64
        string $projectDir,
65
        VariableApiInterface $variableApi,
66
        CacheClearer $cacheClearer,
67
        RequestStack $requestStack,
68
        ZikulaHttpKernelInterface $kernel
69
    ) {
70
        $this->configDir = $projectDir . '/config';
71
        $this->localEnvFile = $projectDir . '/.env.local';
72
        $this->variableApi = $variableApi;
73
        $this->cacheClearer = $cacheClearer;
74
        $this->requestStack = $requestStack;
75
        $this->kernel = $kernel;
76
    }
77
78
    public function getYamlHelper(bool $initCopy = false): YamlDumper
79
    {
80
        $copyFile = $initCopy ? 'services.yaml' : null;
81
82
        return new YamlDumper($this->configDir, 'services_custom.yaml', $copyFile);
83
    }
84
85
    public function initializeParameters(array $paramsToMerge = []): bool
86
    {
87
        $yamlHelper = $this->getYamlHelper(true);
88
        $params = array_merge($yamlHelper->getParameters(), $paramsToMerge);
89
        $yamlHelper->setParameters($params);
90
        $this->cacheClearer->clear('symfony.config');
91
92
        return true;
93
    }
94
95
    /**
96
     * Load and set new default values from the original services.yaml file into the services_custom.yaml file.
97
     */
98
    public function reInitParameters(): bool
99
    {
100
        $originalParameters = Yaml::parse(file_get_contents($this->kernel->getProjectDir() . '/config/services.yaml'));
101
        $yamlHelper = $this->getYamlHelper();
102
        $yamlHelper->setParameters(array_merge($originalParameters['parameters'], $yamlHelper->getParameters()));
103
        $this->cacheClearer->clear('symfony.config');
104
105
        return true;
106
    }
107
108
    /**
109
     * @throws IOExceptionInterface If .env.local could not be dumped
110
     */
111
    public function finalizeParameters(bool $configureRequestContext = true): bool
112
    {
113
        $yamlHelper = $this->getYamlHelper();
114
        $params = $this->decodeParameters($yamlHelper->getParameters());
115
116
        $this->variableApi->getAll(VariableApi::CONFIG); // forces initialization of API
117
        if (!isset($params['upgrading'])) {
118
            $this->variableApi->set(VariableApi::CONFIG, 'locale', $params['locale']);
119
            // Set the System Identifier as a unique string.
120
            if (!$this->variableApi->get(VariableApi::CONFIG, 'system_identifier')) {
121
                $this->variableApi->set(VariableApi::CONFIG, 'system_identifier', str_replace('.', '', uniqid((string) (random_int(1000000000, 9999999999)), true)));
122
            }
123
            // add admin email as site email
124
            $this->variableApi->set(VariableApi::CONFIG, 'adminmail', $params['email']);
125
        }
126
127
        // add remaining parameters and remove unneeded ones
128
        unset($params['username'], $params['password'], $params['email']);
129
        $params['datadir'] = !empty($params['datadir']) ? $params['datadir'] : 'public/uploads';
130
131
        if ($configureRequestContext) {
132
            // Configure the Request Context
133
            // see http://symfony.com/doc/current/cookbook/console/sending_emails.html#configuring-the-request-context-globally
134
            $request = $this->requestStack->getMasterRequest();
135
            $hostFromRequest = isset($request) ? $request->getHost() : null;
136
            $schemeFromRequest = isset($request) ? $request->getScheme() : 'http';
137
            $basePathFromRequest = isset($request) ? $request->getBasePath() : null;
138
            $params['router.request_context.host'] = $params['router.request_context.host'] ?? $hostFromRequest;
139
            $params['router.request_context.scheme'] = $params['router.request_context.scheme'] ?? $schemeFromRequest;
140
            $params['router.request_context.base_url'] = $params['router.request_context.base_url'] ?? $basePathFromRequest;
141
        }
142
        $params['umask'] = $params['umask'] ?? null;
143
        $params['installed'] = true;
144
        // set currently installed version into parameters
145
        $params[ZikulaKernel::CORE_INSTALLED_VERSION_PARAM] = ZikulaKernel::VERSION;
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
        if (isset($params['upgrading'])) {
150
            $params['zikula_asset_manager.combine'] = false;
151
152
            // unset start page information to avoid missing module errors
153
            $this->variableApi->set(VariableApi::CONFIG, 'startController_en', '');
154
155
            // on upgrade, if a user doesn't add their custom theme back to the /theme dir, it should be reset to a core theme, if available.
156
            $defaultTheme = (string) $this->variableApi->getSystemVar('Default_Theme');
157
            if (!$this->kernel->isBundle($defaultTheme) && $this->kernel->isBundle('ZikulaBootstrapTheme')) {
158
                $this->variableApi->set(VariableApi::CONFIG, 'Default_Theme', 'ZikulaBootstrapTheme');
159
            }
160
        }
161
162
        // write parameters into config/services_custom.yaml
163
        $yamlHelper->setParameters($params);
164
165
        if (isset($params['upgrading'])) {
166
            unset($params['upgrading']);
167
        } else {
168
            $this->writeEnvVars();
169
        }
170
171
        // clear the cache
172
        $this->cacheClearer->clear('symfony.config');
173
174
        return true;
175
    }
176
177
    private function writeEnvVars()
178
    {
179
        // write env vars into .env.local
180
        $content = explode("\n", file_get_contents($this->localEnvFile));
181
        $databaseSetting = $content[0];
182
183
        $randomLibFactory = new Factory();
184
        $generator = $randomLibFactory->getMediumStrengthGenerator();
185
        $lines = [];
186
        $lines[] = 'APP_ENV=prod';
187
        $lines[] = 'APP_DEBUG=1';
188
        $lines[] = 'APP_SECRET=\'' . $generator->generateString(50) . '\'';
189
        $lines[] = $databaseSetting;
190
191
        $fileSystem = new Filesystem();
192
        try {
193
            $fileSystem->dumpFile($this->localEnvFile, implode("\n", $lines));
194
        } catch (IOExceptionInterface $exception) {
195
            throw $exception;
196
        }
197
    }
198
199
    public function protectFiles(): bool
200
    {
201
        // protect services_custom.yaml files
202
        $files = array_diff(scandir($this->configDir), ['.', '..']);
0 ignored issues
show
Bug introduced by
It seems like scandir($this->configDir) can also be of type false; however, parameter $array1 of array_diff() does only seem to accept array, 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

202
        $files = array_diff(/** @scrutinizer ignore-type */ scandir($this->configDir), ['.', '..']);
Loading history...
203
        foreach ($files as $file) {
204
            $this->protectFile($this->configDir . '/' . $file);
205
        }
206
207
        $this->protectFile($this->localEnvFile);
208
209
        // clear the cache
210
        $this->cacheClearer->clear('symfony.config');
211
212
        return true;
213
    }
214
215
    private function protectFile(string $filePath): void
216
    {
217
        return; // see #4099
218
        //@chmod($filePath, 0400);
219
        //if (!is_readable($filePath)) {
220
        @chmod($filePath, 0440);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

220
        /** @scrutinizer ignore-unhandled */ @chmod($filePath, 0440);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Unused Code introduced by
@chmod($filePath, 288) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
221
        if (!is_readable($filePath)) {
222
            @chmod($filePath, 0444);
223
        }
224
        //}
225
    }
226
227
    /**
228
     * Remove base64 encoding for admin parameters.
229
     */
230
    public function decodeParameters(array $params = []): array
231
    {
232
        if (!empty($params['password'])) {
233
            $params['password'] = base64_decode($params['password']);
234
        }
235
        if (!empty($params['username'])) {
236
            $params['username'] = base64_decode($params['username']);
237
        }
238
        if (!empty($params['email'])) {
239
            $params['email'] = base64_decode($params['email']);
240
        }
241
242
        return $params;
243
    }
244
}
245