Completed
Push — 4.0 ( 268f2c...88f012 )
by Hideki
05:48 queued 10s
created

src/Eccube/Service/Composer/ComposerApiService.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.ec-cube.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)
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)
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
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
     * @param bool $init
268
     *
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
     *
317
     * @param BaseInfo|null $BaseInfo
318
     *
319
     * @throws PluginException
320
     * @throws \Doctrine\ORM\NoResultException
321
     * @throws \Doctrine\ORM\NonUniqueResultException
322
     */
323
    private function init($BaseInfo = null)
324
    {
325
        $BaseInfo = $BaseInfo ?: $this->baseInfoRepository->get();
326
327
        set_time_limit(0);
328
329
        $composerMemory = $this->eccubeConfig['eccube_composer_memory_limit'];
330
        ini_set('memory_limit', $composerMemory);
331
332
        // Config for some environment
333
        putenv('COMPOSER_HOME='.$this->eccubeConfig['plugin_realdir'].'/.composer');
334
        $this->initConsole();
335
        $this->workingDir = $this->workingDir ? $this->workingDir : $this->eccubeConfig['kernel.project_dir'];
336
        $url = $this->eccubeConfig['eccube_package_api_url'];
337
        $json = json_encode([
338
            'type' => 'composer',
339
            'url' => $url,
340
            'options' => [
341
                'http' => [
342
                    'header' => ['X-ECCUBE-KEY: '.$BaseInfo->getAuthenticationKey()],
343
                ],
344
            ],
345
        ]);
346
        $this->execConfig('repositories.eccube', [$json]);
347
        if (strpos($url, 'http://') === 0) {
348
            $this->execConfig('secure-http', ['false']);
349
        }
350
        $this->initConsole();
351
    }
352
353
    private function initConsole()
354
    {
355
        $consoleApplication = new Application();
356
        $consoleApplication->resetComposer();
357
        $consoleApplication->setAutoExit(false);
358
        $this->consoleApplication = $consoleApplication;
359
    }
360
361
    /**
362
     * @param BaseInfo $BaseInfo
363
     *
364
     * @throws PluginException
365
     * @throws \Doctrine\ORM\NoResultException
366
     * @throws \Doctrine\ORM\NonUniqueResultException
367
     */
368
    public function configureRepository(BaseInfo $BaseInfo)
369
    {
370
        $this->init($BaseInfo);
371
    }
372
}
373