Failed Conditions
Push — dev/store-tests ( 348834...5bbfa9 )
by Kiyotaka
05:33
created

ComposerApiService::execConfig()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 14
rs 9.7998
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\Entity\BaseInfo;
19
use Eccube\Exception\PluginException;
20
use Eccube\Repository\BaseInfoRepository;
21
use Symfony\Component\Console\Input\ArrayInput;
22
use Symfony\Component\Console\Output\BufferedOutput;
23
use Symfony\Component\Console\Output\OutputInterface;
24
25
/**
26
 * Class ComposerApiService
27
 */
28
class ComposerApiService implements ComposerServiceInterface
29
{
30
    /**
31
     * @var EccubeConfig
32
     */
33
    protected $eccubeConfig;
34
35
    /**
36
     * @var Application
37
     */
38
    private $consoleApplication;
39
40
    private $workingDir;
41
    /**
42
     * @var BaseInfoRepository
43
     */
44
    private $baseInfoRepository;
45
46
    public function __construct(EccubeConfig $eccubeConfig, BaseInfoRepository $baseInfoRepository)
47
    {
48
        $this->eccubeConfig = $eccubeConfig;
49
        $this->baseInfoRepository = $baseInfoRepository;
50
    }
51
52
    /**
53
     * Run get info command
54
     *
55
     * @param string $pluginName format foo/bar or foo/bar:1.0.0 or "foo/bar 1.0.0"
56
     * @param string|null $version
57
     *
58
     * @return array
59
     *
60
     * @throws PluginException
61
     * @throws \Doctrine\ORM\NoResultException
62
     * @throws \Doctrine\ORM\NonUniqueResultException
63
     */
64
    public function execInfo($pluginName, $version)
65
    {
66
        $output = $this->runCommand([
67
            'command' => 'info',
68
            'package' => $pluginName,
69
            'version' => $version,
70
            '--available' => true,
71
        ]);
72
73
        return OutputParser::parseInfo($output);
74
    }
75
76
    /**
77
     * Run execute command
78
     *
79
     * @param string $packageName format "foo/bar foo/bar:1.0.0"
80
     * @param null|OutputInterface $output
81
     *
82
     * @return string
83
     *
84
     * @throws PluginException
85
     * @throws \Doctrine\ORM\NoResultException
86
     * @throws \Doctrine\ORM\NonUniqueResultException
87
     */
88
    public function execRequire($packageName, $output = null)
89
    {
90
        $packageName = explode(' ', trim($packageName));
91
92
        return $this->runCommand([
93
            'command' => 'require',
94
            'packages' => $packageName,
95
            '--no-interaction' => true,
96
            '--profile' => true,
97
            '--prefer-dist' => true,
98
            '--ignore-platform-reqs' => true,
99
            '--update-with-dependencies' => true,
100
            '--no-scripts' => true,
101
        ], $output);
102
    }
103
104
    /**
105
     * Run remove command
106
     *
107
     * @param string $packageName format "foo/bar foo/bar:1.0.0"
108
     * @param null|OutputInterface $output
109
     *
110
     * @return string
111
     *
112
     * @throws PluginException
113
     * @throws \Doctrine\ORM\NoResultException
114
     * @throws \Doctrine\ORM\NonUniqueResultException
115
     */
116
    public function execRemove($packageName, $output = null)
117
    {
118
        $packageName = explode(' ', trim($packageName));
119
120
        return $this->runCommand([
121
            'command' => 'remove',
122
            'packages' => $packageName,
123
            '--ignore-platform-reqs' => true,
124
            '--no-interaction' => true,
125
            '--profile' => true,
126
            '--no-scripts' => true,
127
        ], $output);
128
    }
129
130
    /**
131
     * Run update command
132
     *
133
     * @param boolean $dryRun
134
     * @param null|OutputInterface $output
135
     *
136
     * @throws PluginException
137
     * @throws \Doctrine\ORM\NoResultException
138
     * @throws \Doctrine\ORM\NonUniqueResultException
139
     */
140 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...
141
    {
142
        $this->runCommand([
143
            'command' => 'update',
144
            '--no-interaction' => true,
145
            '--profile' => true,
146
            '--no-scripts' => true,
147
            '--dry-run' => (bool) $dryRun,
148
        ], $output);
149
    }
150
151
    /**
152
     * Run install command
153
     *
154
     * @param boolean $dryRun
155
     * @param null|OutputInterface $output
156
     *
157
     * @throws PluginException
158
     * @throws \Doctrine\ORM\NoResultException
159
     * @throws \Doctrine\ORM\NonUniqueResultException
160
     */
161 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...
162
    {
163
        $this->runCommand([
164
            'command' => 'install',
165
            '--no-interaction' => true,
166
            '--profile' => true,
167
            '--no-scripts' => true,
168
            '--dry-run' => (bool) $dryRun,
169
        ], $output);
170
    }
171
172
    /**
173
     * Get require
174
     *
175
     * @param string $packageName
176
     * @param string|null $version
177
     * @param string $callback
178
     * @param null $typeFilter
179
     * @param int $level
180
     *
181
     * @throws PluginException
182
     * @throws \Doctrine\ORM\NoResultException
183
     * @throws \Doctrine\ORM\NonUniqueResultException
184
     */
185
    public function foreachRequires($packageName, $version, $callback, $typeFilter = null, $level = 0)
186
    {
187
        if (strpos($packageName, '/') === false) {
188
            return;
189
        }
190
        $info = $this->execInfo($packageName, $version);
191
        if (isset($info['requires'])) {
192
            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...
193
                if (isset($info['type']) && $info['type'] === $typeFilter) {
194
                    $this->foreachRequires($name, $version, $callback, $typeFilter, $level + 1);
195
                    if (isset($info['descrip.'])) {
196
                        $info['description'] = $info['descrip.'];
197
                    }
198
                    if ($level) {
199
                        $callback($info);
200
                    }
201
                }
202
            }
203
        }
204
    }
205
206
    /**
207
     * Run get config information
208
     *
209
     * @param string $key
210
     * @param null $value
211
     *
212
     * @return array|mixed
213
     *
214
     * @throws PluginException
215
     * @throws \Doctrine\ORM\NoResultException
216
     * @throws \Doctrine\ORM\NonUniqueResultException
217
     */
218
    public function execConfig($key, $value = null)
219
    {
220
        $commands = [
221
            'command' => 'config',
222
            'setting-key' => $key,
223
            'setting-value' => $value,
224
        ];
225
        if ($value) {
226
            $commands['setting-value'] = $value;
227
        }
228
        $output = $this->runCommand($commands, null, false);
229
230
        return OutputParser::parseConfig($output);
231
    }
232
233
    /**
234
     * Get config list
235
     *
236
     * @return array
237
     *
238
     * @throws PluginException
239
     * @throws \Doctrine\ORM\NoResultException
240
     * @throws \Doctrine\ORM\NonUniqueResultException
241
     */
242
    public function getConfig()
243
    {
244
        $output = $this->runCommand([
245
            'command' => 'config',
246
            '--list' => true,
247
        ], null, false);
248
249
        return OutputParser::parseList($output);
250
    }
251
252
    /**
253
     * Set work dir
254
     *
255
     * @param string $workingDir
256
     */
257
    public function setWorkingDir($workingDir)
258
    {
259
        $this->workingDir = $workingDir;
260
    }
261
262
    /**
263
     * Run composer command
264
     *
265
     * @param array $commands
266
     * @param null|OutputInterface $output
267
     *
268
     * @param bool $init
269
     * @return string
270
     *
271
     * @throws PluginException
272
     * @throws \Doctrine\ORM\NoResultException
273
     * @throws \Doctrine\ORM\NonUniqueResultException
274
     * @throws \Exception
275
     */
276
    public function runCommand($commands, $output = null, $init = true)
277
    {
278
        if ($init) {
279
            $this->init();
280
        }
281
        $commands['--working-dir'] = $this->workingDir;
282
        $commands['--no-ansi'] = true;
283
        $input = new ArrayInput($commands);
284
        $useBufferedOutput = $output === null;
285
286
        if ($useBufferedOutput) {
287
            $output = new BufferedOutput();
288
            ob_start(function ($buffer) use ($output) {
289
                $output->write($buffer);
290
291
                return null;
292
            });
293
        }
294
295
        $exitCode = $this->consoleApplication->run($input, $output);
296
297
        if ($useBufferedOutput) {
298
            ob_end_clean();
299
            $log = $output->fetch();
300
            if ($exitCode) {
301
                log_error($log);
302
                throw new PluginException($log);
303
            }
304
            log_info($log, $commands);
305
306
            return $log;
307
        } elseif ($exitCode) {
308
            throw new PluginException();
309
        }
310
311
        return null;
312
    }
313
314
    /**
315
     * Init composer console application
316
     * @param BaseInfo|null $BaseInfo
317
     * @throws PluginException
318
     * @throws \Doctrine\ORM\NoResultException
319
     * @throws \Doctrine\ORM\NonUniqueResultException
320
     */
321
    private function init($BaseInfo = null)
322
    {
323
        $BaseInfo = $BaseInfo ?: $this->baseInfoRepository->get();
324
325
        set_time_limit(0);
326
        ini_set('memory_limit', '-1');
327
        // Config for some environment
328
        putenv('COMPOSER_HOME='.$this->eccubeConfig['plugin_realdir'].'/.composer');
329
        $this->initConsole();
330
        $this->workingDir = $this->workingDir ? $this->workingDir : $this->eccubeConfig['kernel.project_dir'];
331
        $url = $this->eccubeConfig['eccube_package_api_url'];
332
        $json = json_encode([
333
            'type' => 'composer',
334
            'url' => $url,
335
            'options' => [
336
                'http' => [
337
                    'header' => ['X-ECCUBE-KEY: '.$BaseInfo->getAuthenticationKey()]
338
                ]
339
            ]
340
        ]);
341
        $this->execConfig('repositories.eccube', [$json]);
342
        if (strpos($url, 'http://') == 0) {
343
            $this->execConfig('secure-http', ['false']);
344
        }
345
        $this->initConsole();
346
    }
347
348
    private function initConsole()
349
    {
350
        $consoleApplication = new Application();
351
        $consoleApplication->resetComposer();
352
        $consoleApplication->setAutoExit(false);
353
        $this->consoleApplication = $consoleApplication;
354
    }
355
356
    /**
357
     * @param BaseInfo $BaseInfo
358
     * @throws PluginException
359
     * @throws \Doctrine\ORM\NoResultException
360
     * @throws \Doctrine\ORM\NonUniqueResultException
361
     */
362
    public function configureRepository(BaseInfo $BaseInfo)
363
    {
364
        $this->init($BaseInfo);
365
    }
366
}
367