ConfigureCommand::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 9
cts 9
cp 1
rs 9.7
c 0
b 0
f 0
cc 1
nc 1
nop 6
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Paysera\PhpStormHelper\Command;
6
7
use Paysera\PhpStormHelper\Service\ConfigurationOptionFinder;
8
use Paysera\PhpStormHelper\Service\GitignoreHelper;
9
use Paysera\PhpStormHelper\Service\SourceFolderHelper;
10
use Paysera\PhpStormHelper\Service\StructureConfigurator;
11
use Paysera\PhpStormHelper\Service\WorkspaceConfigurationHelper;
12
use Symfony\Component\Console\Command\Command;
13
use Symfony\Component\Console\Input\InputArgument;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use Symfony\Component\Filesystem\Filesystem;
18
19
class ConfigureCommand extends Command
20
{
21
    private $structureConfigurator;
22
    private $gitignoreHelper;
23
    private $configurationOptionFinder;
24
    private $workspaceConfigurationHelper;
25
    private $sourceFolderHelper;
26
    private $filesystem;
27
28 6
    public function __construct(
29
        StructureConfigurator $structureConfigurator,
30
        GitignoreHelper $gitignoreHelper,
31
        ConfigurationOptionFinder $configurationOptionFinder,
32
        WorkspaceConfigurationHelper $workspaceConfigurationHelper,
33
        SourceFolderHelper $sourceFolderHelper,
34
        Filesystem $filesystem
35
    ) {
36 6
        parent::__construct();
37
38 6
        $this->structureConfigurator = $structureConfigurator;
39 6
        $this->gitignoreHelper = $gitignoreHelper;
40 6
        $this->configurationOptionFinder = $configurationOptionFinder;
41 6
        $this->workspaceConfigurationHelper = $workspaceConfigurationHelper;
42 6
        $this->sourceFolderHelper = $sourceFolderHelper;
43 6
        $this->filesystem = $filesystem;
44 6
    }
45
46 6
    protected function configure()
47
    {
48
        $this
49 6
            ->setName('configure')
50 6
            ->addArgument(
51 6
                'project-root-dir',
52 6
                InputArgument::OPTIONAL,
53 6
                'Default is current directory'
54
            )
55 6
            ->addArgument('path-to-configuration-template-structure', InputArgument::OPTIONAL)
56 6
            ->addOption(
57 6
                'update-gitignore',
58 6
                null,
59 6
                null,
60 6
                'Modify gitignore file – use this when you intend to version common .idea files'
61
            )
62 6
            ->addOption(
63 6
                'docker-image',
64 6
                null,
65 6
                InputOption::VALUE_OPTIONAL,
66
                <<<'DOC'
67 6
Docker image to use for this project. For example, php:7.3-cli or example.org/image:latest.
68
If not provided, currently configured image is maintained in the configuration.
69
DOC
70
            )
71 6
            ->addOption(
72 6
                'webpack-config-path',
73 6
                null,
74 6
                InputOption::VALUE_OPTIONAL,
75
                <<<'DOC'
76 6
Relative path from project root dir to webpack configuration file.
77
If not provided, currently configured path is maintained in the configuration.
78
DOC
79
            )
80 6
            ->addOption(
81 6
                'server',
82 6
                's',
83 6
                InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
84
                <<<'DOC'
85 6
Server mappings, for example my-project.docker:443@/path/in/server.
86
Server is added in the list unless one already exists with such host and post.
87
DOC
88
            )
89 6
            ->addOption(
90 6
                'no-diff',
91 6
                null,
92 6
                InputOption::VALUE_NONE,
93 6
                'Pass if you don\'t want diff to be outputed'
94
            )
95
        ;
96 6
    }
97
98 6
    protected function execute(InputInterface $input, OutputInterface $output)
99
    {
100 6
        $target = $input->getArgument('project-root-dir');
101 6
        if ($target === null) {
102
            $target = realpath('.');
103
        }
104
105 6
        $backupFolder = $this->backupConfiguration($target, $output);
0 ignored issues
show
Bug introduced by
It seems like $target defined by $input->getArgument('project-root-dir') on line 100 can also be of type array<integer,string>; however, Paysera\PhpStormHelper\C...::backupConfiguration() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
106 6
        $this->configureStructure($input, $target);
107 6
        $this->configureWorkspace($input, $target);
0 ignored issues
show
Bug introduced by
It seems like $target defined by $input->getArgument('project-root-dir') on line 100 can also be of type array<integer,string>; however, Paysera\PhpStormHelper\C...d::configureWorkspace() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
108
109 6
        if ($input->getOption('update-gitignore')) {
110 1
            $this->gitignoreHelper->setupGitignore($target . '/.gitignore');
111
        }
112
113 6
        if ($backupFolder !== null && !$input->getOption('no-diff')) {
114 4
            $this->printDiffFromBackup($backupFolder, $target, $output);
0 ignored issues
show
Bug introduced by
It seems like $target defined by $input->getArgument('project-root-dir') on line 100 can also be of type array<integer,string>; however, Paysera\PhpStormHelper\C...::printDiffFromBackup() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
115
        }
116
117 6
        $output->writeln('Restart PhpStorm instance for changes to take effect');
118 6
    }
119
120
    /**
121
     * @param InputInterface $input
122
     * @param $target
123
     */
124 6
    private function configureStructure(InputInterface $input, $target)
125
    {
126 6
        $path = $input->getArgument('path-to-configuration-template-structure');
127 6
        if ($path === null) {
128 5
            $path = __DIR__ . '/../../config/default';
129
        }
130
131 6
        $composerPath = $target . '/composer.json';
132 6
        $options = [];
133
134 6
        if ($input->getOption('docker-image')) {
135 1
            $options['dockerImage'] = $input->getOption('docker-image');
136
        } else {
137 5
            $options['dockerImage'] = $this->configurationOptionFinder->findUsedDockerImage($target);
138
        }
139
140 6
        if ($input->getOption('webpack-config-path')) {
141 1
            $options['webpackConfigPath'] = $input->getOption('webpack-config-path');
142
        } else {
143 5
            $options['webpackConfigPath'] = $this->configurationOptionFinder->findWebpackConfigPath($target);
144
        }
145
146 6
        if (file_exists($target . '/.php_cs')) {
147 2
            $options['phpCsFixerConfigPath'] = '.php_cs';
148 2
            $fixerBinary = $this->hasPayseraPhpCsFixerInstalled($composerPath)
149
                ? 'paysera-php-cs-fixer'
150 2
                : 'php-cs-fixer';
151 2
            $options['phpCsFixerExecutable'] = $this->getBinDirectory($composerPath) . '/' . $fixerBinary;
152
        }
153
154 6
        $options['symfonyEnabled'] = $this->checkSymfonySupport($composerPath);
155
156 6
        $options['sourceFolders'] = $this->sourceFolderHelper->getSourceFolders($target);
157
158 6
        $options['phpVersion'] = $this->determinePhpVersion($composerPath);
159
160 6
        $this->structureConfigurator->configure($path, $target, $options);
0 ignored issues
show
Bug introduced by
It seems like $path defined by $input->getArgument('pat...on-template-structure') on line 126 can also be of type array<integer,string>; however, Paysera\PhpStormHelper\S...nfigurator::configure() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
161 6
    }
162
163 6
    private function configureWorkspace(InputInterface $input, string $target)
164
    {
165 6
        $pathToWorkspaceXml = $target . '/.idea/workspace.xml';
166
167 6
        $this->workspaceConfigurationHelper->configureComposer($pathToWorkspaceXml);
168 6
        $this->workspaceConfigurationHelper->configureFileTemplateScheme($pathToWorkspaceXml);
169 6
        $this->workspaceConfigurationHelper->setupPhpUnitRunConfiguration($pathToWorkspaceXml);
170
171 6
        $serverMappings = $input->getOption('server');
172 6
        $this->workspaceConfigurationHelper->setupServerMappings($pathToWorkspaceXml, $serverMappings);
173 6
    }
174
175 6
    private function checkSymfonySupport(string $composerPath)
176
    {
177
        return (
178 6
            $this->isPackageRequired($composerPath, 'symfony/symfony')
179 6
            || $this->isPackageRequired($composerPath, 'symfony/framework-bundle')
180
        );
181
    }
182
183 2
    private function hasPayseraPhpCsFixerInstalled(string $composerPath)
184
    {
185 2
        return $this->isPackageRequired($composerPath, 'paysera/lib-php-cs-fixer-config');
186
    }
187
188 2
    private function getBinDirectory(string $composerPath)
189
    {
190 2
        return rtrim($this->parseComposer($composerPath)['config']['bin-dir'] ?? 'vendor/bin', '/');
191
    }
192
193 6
    private function determinePhpVersion(string $composerPath): ?string
194
    {
195 6
        $composerContents = $this->parseComposer($composerPath);
196 6
        $phpVersionConstraint = $composerContents['require']['php'] ?? null;
197 6
        if ($phpVersionConstraint === null) {
198 4
            return null;
199
        }
200
201 2
        if (preg_match('/\d\.\d/', $phpVersionConstraint, $matches) === 1) {
202 2
            return $matches[0];
203
        }
204
205
        return null;
206
    }
207
208 6
    private function isPackageRequired(string $composerPath, string $package)
209
    {
210 6
        $composerContents = $this->parseComposer($composerPath);
211 6
        return isset($composerContents['require'][$package]) || isset($composerContents['require-dev'][$package]);
212
    }
213
214 6
    private function parseComposer(string $composerPath)
215
    {
216 6
        if (!file_exists($composerPath)) {
217 4
            return [];
218
        }
219
220 2
        return json_decode(file_get_contents($composerPath), true) ?: [];
221
    }
222
223 6
    private function backupConfiguration(string $target, OutputInterface $output)
224
    {
225 6
        $pathToIdea = $target . '/.idea/';
226 6
        if (!is_dir($pathToIdea)) {
227 2
            return null;
228
        }
229
230 4
        $backupFolder = sprintf(
231 4
            '%s/phpstorm-helper-backups/%s/%s',
232 4
            sys_get_temp_dir(),
233 4
            basename($target),
234 4
            time()
235
        );
236
237 4
        $this->filesystem->mirror($pathToIdea, $backupFolder);
238
239 4
        $output->writeln('Made backup of <info>.idea</info> to <info>' . $backupFolder . '</info>');
240
241 4
        return $backupFolder;
242
    }
243
244 4
    private function printDiffFromBackup(string $backupFolder, string $target, OutputInterface $output)
245
    {
246 4
        $command = sprintf(
247 4
            'git --no-pager diff --color=always --no-index %s %s',
248 4
            escapeshellarg($backupFolder),
249 4
            escapeshellarg($target . '/.idea/')
250
        );
251 4
        exec($command, $diffOutput);
252
253 4
        if (count($diffOutput) === 0) {
254
            $output->writeln('No changes were made.');
255
            return;
256
        }
257
258 4
        $filteredDiffOutput = $this->filterDiff($diffOutput);
0 ignored issues
show
Bug introduced by
It seems like $diffOutput can also be of type null; however, Paysera\PhpStormHelper\C...reCommand::filterDiff() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
259
260 4
        if (count($filteredDiffOutput) !== count($diffOutput)) {
261 4
            $output->writeln('Changes in workspace.xml were made or workspace.xml was updated by PhpStorm itself.');
262
        }
263
264 4
        if (count($filteredDiffOutput) === 0) {
265
            return;
266
        }
267
268 4
        $output->writeln("Diff of the changes, excluding workspace.xml:\n\n");
269 4
        $output->writeln(implode("\n", $filteredDiffOutput) . "\n\n");
270 4
    }
271
272 4
    private function filterDiff(array $diffOutput): array
273
    {
274 4
        $include = true;
275 4
        $filtered = [];
276 4
        foreach ($diffOutput as $line) {
277 4
            if (strpos($line, 'diff --git') !== false) {
278 4
                $include = strpos($line, 'workspace.xml') === false;
279
            }
280
281 4
            if ($include) {
282 4
                $filtered[] = $line;
283
            }
284
        }
285
286 4
        return $filtered;
287
    }
288
}
289