Completed
Push — dev/store-tests ( 5bbfa9...f77e94 )
by Kiyotaka
10:02 queued 04:31
created

ComposerProcessService::composerVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 7
rs 10
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 Doctrine\ORM\EntityManagerInterface;
17
use Eccube\Common\EccubeConfig;
18
use Eccube\Exception\PluginException;
19
use Eccube\Service\SystemService;
20
21
/**
22
 * Class ComposerProcessService
23
 */
24
class ComposerProcessService implements ComposerServiceInterface
0 ignored issues
show
Bug introduced by
There is one abstract method configureRepository in this class; you could implement it, or declare this class as abstract.
Loading history...
25
{
26
    /**
27
     * @var EccubeConfig config parameter
28
     */
29
    protected $eccubeConfig;
30
31
    /**
32
     * @var EntityManagerInterface
33
     */
34
    protected $entityManager;
35
36
    private $workingDir;
37
    private $composerFile;
38
    private $composerSetup;
39
    private $pathPHP;
40
41
    /**
42
     * ComposerProcessService constructor.
43
     *
44
     * @param EccubeConfig $eccubeConfig
45
     * @param EntityManagerInterface $entityManager
46
     * @param SystemService $systemService
47
     */
48
    public function __construct(EccubeConfig $eccubeConfig, EntityManagerInterface $entityManager, SystemService $systemService)
49
    {
50
        $this->eccubeConfig = $eccubeConfig;
51
        $this->entityManager = $entityManager;
52
        $this->pathPHP = $systemService->getPHP();
53
    }
54
55
    /**
56
     * This function to install a plugin by composer require
57
     *
58
     * @param string $packageName format "foo/bar foo/bar2:1.0.0"
59
     *
60
     * @throws PluginException
61
     */
62 View Code Duplication
    public function execRequire($packageName, $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...
63
    {
64
        set_time_limit(0);
65
        $this->init();
66
67
        // Build command
68
        $command = $this->pathPHP.' '.$this->composerFile.' require '.$packageName;
69
        $command .= ' --prefer-dist --no-progress --no-suggest --no-scripts --ignore-platform-reqs --update-with-dependencies --profile --no-ansi --no-interaction -d ';
70
        $command .= $this->workingDir.' 2>&1';
71
        log_info($command);
72
        $this->runCommand($command);
73
    }
74
75
    /**
76
     * This function to remove a plugin by composer remove
77
     *
78
     * @param string $packageName format "foo/bar foo/bar2"
79
     *
80
     * @throws PluginException
81
     */
82 View Code Duplication
    public function execRemove($packageName, $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...
83
    {
84
        set_time_limit(0);
85
        $this->init();
86
87
        // Build command
88
        $command = $this->pathPHP.' '.$this->composerFile.' remove '.$packageName;
89
        $command .= ' --no-progress --no-scripts --ignore-platform-reqs --profile --no-ansi --no-interaction -d ';
90
        $command .= $this->workingDir.' 2>&1';
91
        log_info($command);
92
93
        // Execute command
94
        $this->runCommand($command);
95
    }
96
97
    /**
98
     * Run command
99
     *
100
     * @throws PluginException
101
     *
102
     * @param string $command
103
     */
104
    public function runCommand($command)
105
    {
106
        $output = [];
107
        try {
108
            // Execute command
109
            $returnValue = -1;
110
            exec($command, $output, $returnValue);
111
112
            $outputString = implode(PHP_EOL, $output);
113
            if ($returnValue) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $returnValue of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
114
                throw new PluginException($outputString);
115
            }
116
            log_info(PHP_EOL.$outputString.PHP_EOL);
117
        } catch (\Exception $exception) {
118
            throw new PluginException($exception->getMessage());
119
        }
120
    }
121
122
    /**
123
     * Set working dir
124
     *
125
     * @param string $workingDir
126
     */
127
    public function setWorkingDir($workingDir)
128
    {
129
        $this->workingDir = $workingDir;
130
    }
131
132
    /**
133
     * Set init
134
     *
135
     * @throws PluginException
136
     */
137
    private function init()
138
    {
139
        if (!$this->isPhpCommandLine()) {
140
            throw new PluginException('Php cli not found.');
141
        }
142
143
        $composerMemory = $this->eccubeConfig['eccube_composer_memory_limit'];
144
        if (!$this->isSetCliMemoryLimit()) {
145
            $cliMemoryLimit = $this->getCliMemoryLimit();
146
            if ($cliMemoryLimit < $composerMemory && $cliMemoryLimit != -1) {
147
                throw new PluginException('Not enough memory limit.');
148
            }
149
        }
150
151
        /**
152
         * Mysql lock in transaction
153
         *
154
         * @see https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html
155
         *
156
         * @var EntityManagerInterface
157
         */
158
        $em = $this->entityManager;
159
        if ($em->getConnection()->isTransactionActive()) {
160
            $em->getConnection()->commit();
161
            $em->getConnection()->beginTransaction();
162
        }
163
164
        @ini_set('memory_limit', $composerMemory.'M');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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...
165
        // Config for some environment
166
        putenv('COMPOSER_HOME='.$this->eccubeConfig['plugin_realdir'].'/.composer');
167
        $this->workingDir = $this->workingDir ? $this->workingDir : $this->eccubeConfig['root_dir'];
168
        $this->setupComposer();
169
    }
170
171
    /**
172
     * Check composer file and setup it
173
     */
174
    private function setupComposer()
175
    {
176
        $this->composerFile = $this->workingDir.'/composer.phar';
177
        $this->composerSetup = $this->workingDir.'/composer-setup.php';
178
        if (!file_exists($this->composerFile)) {
179
            if (!file_exists($this->composerSetup)) {
180
                $result = copy('https://getcomposer.org/installer', $this->composerSetup);
181
                log_info($this->composerSetup.' : '.$result);
182
            }
183
            $command = $this->pathPHP.' '.$this->composerSetup;
184
            $this->runCommand($command);
185
186
            unlink($this->composerSetup);
187
        }
188
    }
189
190
    /**
191
     * Get grep memory_limit | Megabyte
192
     *
193
     * @return int|string
194
     */
195
    private function getCliMemoryLimit()
196
    {
197
        $grepMemory = exec($this->pathPHP.' -i | grep "memory_limit"');
198
        if ($grepMemory) {
199
            $grepMemory = explode('=>', $grepMemory);
200
201
            // -1 unlimited
202
            if (trim($grepMemory[2]) == -1) {
203
                return -1;
204
            }
205
206
            $exp = preg_split('#(?<=\d)(?=[a-z])#i', $grepMemory[2]);
207
            $memo = trim($exp[0]);
208
            if ($exp[1] == 'M') {
209
                return $memo;
210
            } else {
211
                if ($exp[1] == 'GB') {
212
                    return $memo * 1024;
213
                } else {
214
                    return 0;
215
                }
216
            }
217
        }
218
219
        return 0;
220
    }
221
222
    /**
223
     * Check to set new value grep "memory_limit"
224
     *
225
     * @return bool
226
     */
227
    private function isSetCliMemoryLimit()
228
    {
229
        $oldMemory = exec($this->pathPHP.' -i | grep "memory_limit"');
230
        $tmpMem = '1.5GB';
231
        if ($oldMemory) {
232
            $memory = explode('=>', $oldMemory);
233
            $originGrepMemmory = trim($memory[2]);
234
235
            if ($originGrepMemmory == $tmpMem) {
236
                $tmpMem = '1.49GB';
237
            }
238
239
            $newMemory = exec($this->pathPHP.' -d memory_limit='.$tmpMem.' -i | grep "memory_limit"');
240
            if ($newMemory) {
241
                $newMemory = explode('=>', $newMemory);
242
                $grepNewMemory = trim($newMemory[2]);
243
                if ($grepNewMemory != $originGrepMemmory) {
244
                    return true;
245
                }
246
            }
247
        }
248
249
        return false;
250
    }
251
252
    /**
253
     * Check php command line
254
     *
255
     * @return bool
256
     */
257
    private function isPhpCommandLine()
258
    {
259
        $php = exec('which php');
260
        if (null != $php) {
261
            if (strpos(strtolower($php), 'php') !== false) {
262
                return true;
263
            }
264
        }
265
266
        return false;
267
    }
268
}
269