Failed Conditions
Push — sf/package-api ( 831849 )
by Kiyotaka
05:53
created

ComposerApiService::foreachRequires()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 8
nop 5
dl 0
loc 20
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.co.jp/
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 Eccube\Service\Composer;
15
16
use Composer\Console\Application;
17
use Eccube\Common\EccubeConfig;
18
use Eccube\Exception\PluginException;
19
use Symfony\Component\Console\Input\ArrayInput;
20
use Symfony\Component\Console\Output\BufferedOutput;
21
use Symfony\Component\Console\Output\OutputInterface;
22
23
/**
24
 * Class ComposerApiService
25
 */
26
class ComposerApiService implements ComposerServiceInterface
27
{
28
    /**
29
     * @var EccubeConfig
30
     */
31
    protected $eccubeConfig;
32
33
    /**
34
     * @var Application
35
     */
36
    private $consoleApplication;
37
38
    private $workingDir;
39
40
    public function __construct(EccubeConfig $eccubeConfig)
41
    {
42
        $this->eccubeConfig = $eccubeConfig;
43
    }
44
45
    /**
46
     * Run get info command
47
     *
48
     * @param string $pluginName format foo/bar or foo/bar:1.0.0 or "foo/bar 1.0.0"
49
     * @param string|null $version
50
     * @return array
51
     *
52
     * @throws PluginException
53
     */
54
    public function execInfo($pluginName, $version)
55
    {
56
        $output = $this->runCommand([
57
            'command' => 'info',
58
            'package' => $pluginName,
59
            'version' => $version,
60
            '--available' => true
61
        ]);
62
63
        return OutputParser::parseInfo($output);
64
    }
65
66
    /**
67
     * Run execute command
68
     *
69
     * @param string $packageName format "foo/bar foo/bar:1.0.0"
70
     * @param null|OutputInterface $output
71
     *
72
     * @return string
73
     * @throws PluginException
74
     */
75
    public function execRequire($packageName, $output = null)
76
    {
77
        $packageName = explode(' ', trim($packageName));
78
        return $this->runCommand([
79
            'command' => 'require',
80
            'packages' => $packageName,
81
            '--no-interaction' => true,
82
            '--profile' => true,
83
            '--prefer-dist' => true,
84
            '--ignore-platform-reqs' => true,
85
            '--update-with-dependencies' => true,
86
            '--no-scripts' => true,
87
        ], $output);
88
    }
89
90
    /**
91
     * Run remove command
92
     *
93
     * @param string $packageName format "foo/bar foo/bar:1.0.0"
94
     * @param null|OutputInterface $output
95
     *
96
     * @return string
97
     * @throws PluginException
98
     */
99
    public function execRemove($packageName, $output = null)
100
    {
101
        $packageName = explode(' ', trim($packageName));
102
        return $this->runCommand([
103
            'command' => 'remove',
104
            'packages' => $packageName,
105
            '--ignore-platform-reqs' => true,
106
            '--no-interaction' => true,
107
            '--profile' => true,
108
            '--no-scripts' => true,
109
        ], $output);
110
    }
111
112
    /**
113
     * Run update command
114
     *
115
     * @param boolean $dryRun
116
     * @param null|OutputInterface $output
117
     *
118
     * @throws PluginException
119
     */
120 View Code Duplication
    public function execUpdate($dryRun, $output = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
    {
122
        $this->runCommand([
123
            'command' => 'update',
124
            '--no-interaction' => true,
125
            '--profile' => true,
126
            '--no-scripts' => true,
127
            '--dry-run' => (bool) $dryRun,
128
        ], $output);
129
    }
130
131
    /**
132
     * Run install command
133
     *
134
     * @param boolean $dryRun
135
     * @param null|OutputInterface $output
136
     *
137
     * @throws PluginException
138
     */
139 View Code Duplication
    public function execInstall($dryRun, $output = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
    {
141
        $this->runCommand([
142
            'command' => 'install',
143
            '--no-interaction' => true,
144
            '--profile' => true,
145
            '--no-scripts' => true,
146
            '--dry-run' => (bool) $dryRun,
147
        ], $output);
148
    }
149
150
    /**
151
     * Get require
152
     *
153
     * @param string $packageName
154
     * @param string|null $version
155
     * @param string $callback
156
     * @param null $typeFilter
157
     *
158
     * @param int $level
159
     * @throws PluginException
160
     */
161
    public function foreachRequires($packageName, $version, $callback, $typeFilter = null, $level = 0)
162
    {
163
        if (strpos($packageName, '/') === false) {
164
            return;
165
        }
166
        $info = $this->execInfo($packageName, $version);
167
        if (isset($info['requires'])) {
168
            foreach ($info['requires'] as $name => $version) {
0 ignored issues
show
Bug introduced by
The expression $info['requires'] of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
169
                if (isset($info['type']) && $info['type'] === $typeFilter) {
170
                    $this->foreachRequires($name, $version, $callback, $typeFilter, $level + 1);
171
                    if (isset($info['descrip.'])) {
172
                        $info['description'] = $info['descrip.'];
173
                    }
174
                    if ($level) {
175
                        $callback($info);
176
                    }
177
                }
178
            }
179
        }
180
    }
181
182
    /**
183
     * Run get config information
184
     *
185
     * @param string $key
186
     * @param null $value
187
     *
188
     * @return array|mixed
189
     *
190
     * @throws PluginException
191
     */
192
    public function execConfig($key, $value = null)
193
    {
194
        $commands = [
195
            'command' => 'config',
196
            'setting-key' => $key,
197
            'setting-value' => $value,
198
        ];
199
        if ($value) {
200
            $commands['setting-value'] = $value;
201
        }
202
        $output = $this->runCommand($commands);
203
204
        return OutputParser::parseConfig($output);
205
    }
206
207
    /**
208
     * Get config list
209
     *
210
     * @return array
211
     *
212
     * @throws PluginException
213
     */
214
    public function getConfig()
215
    {
216
        $output = $this->runCommand([
217
            'command' => 'config',
218
            '--list' => true,
219
        ]);
220
221
        return OutputParser::parseList($output);
222
    }
223
224
    /**
225
     * Set work dir
226
     *
227
     * @param string $workingDir
228
     */
229
    public function setWorkingDir($workingDir)
230
    {
231
        $this->workingDir = $workingDir;
232
    }
233
234
    /**
235
     * Run composer command
236
     *
237
     * @param array $commands
238
     * @param null|OutputInterface $output
239
     *
240
     * @return string
241
     *
242
     * @throws PluginException
243
     */
244
    public function runCommand($commands, $output = null)
245
    {
246
        $this->init();
247
        $commands['--working-dir'] = $this->workingDir;
248
        $commands['--no-ansi'] = true;
249
        $input = new ArrayInput($commands);
250
        $output = $output ?: new BufferedOutput();
251
252
        $exitCode = $this->consoleApplication->run($input, $output);
253
254
        if ($output instanceof BufferedOutput) {
255
            $log = $output->fetch();
256
            if ($exitCode) {
257
                log_error($log);
258
                throw new PluginException($log);
259
            }
260
            log_info($log, $commands);
261
262
            return $log;
263
        } elseif ($exitCode) {
264
            throw new PluginException();
265
        }
266
    }
267
268
    /**
269
     * Init composer console application
270
     */
271
    private function init()
272
    {
273
        set_time_limit(0);
274
        ini_set('memory_limit', '-1');
275
        // Config for some environment
276
        putenv('COMPOSER_HOME='.$this->eccubeConfig['plugin_realdir'].'/.composer');
277
        $consoleApplication = new Application();
278
        $consoleApplication->resetComposer();
279
        $consoleApplication->setAutoExit(false);
280
        $this->consoleApplication = $consoleApplication;
281
        $this->workingDir = $this->workingDir ? $this->workingDir : $this->eccubeConfig['kernel.project_dir'];
282
    }
283
284
    /**
285
     * Get version of composer
286
     *
287
     * @return null|string
288
     *
289
     * @throws PluginException
290
     */
291
    public function composerVersion()
292
    {
293
        $this->init();
294
        $output = $this->runCommand([
295
            '--version' => true,
296
        ]);
297
298
        return OutputParser::parseComposerVersion($output);
299
    }
300
301
    /**
302
     * Get mode
303
     *
304
     * @return string
305
     */
306
    public function getMode()
307
    {
308
        return 'API';
309
    }
310
}
311