Completed
Push — dev/store-tests ( f77e94...4eab14 )
by Kiyotaka
15:00 queued 09:19
created

ComposerProcessService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 3
dl 0
loc 6
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\Entity\BaseInfo;
19
use Eccube\Exception\PluginException;
20
use Eccube\Service\SystemService;
21
22
/**
23
 * Class ComposerProcessService
24
 */
25
class ComposerProcessService implements ComposerServiceInterface
26
{
27
    /**
28
     * @var EccubeConfig config parameter
29
     */
30
    protected $eccubeConfig;
31
32
    /**
33
     * @var EntityManagerInterface
34
     */
35
    protected $entityManager;
36
37
    private $workingDir;
38
    private $composerFile;
39
    private $composerSetup;
40
    private $pathPHP;
41
42
    /**
43
     * ComposerProcessService constructor.
44
     *
45
     * @param EccubeConfig $eccubeConfig
46
     * @param EntityManagerInterface $entityManager
47
     * @param SystemService $systemService
48
     */
49
    public function __construct(EccubeConfig $eccubeConfig, EntityManagerInterface $entityManager, SystemService $systemService)
50
    {
51
        $this->eccubeConfig = $eccubeConfig;
52
        $this->entityManager = $entityManager;
53
        $this->pathPHP = $systemService->getPHP();
54
    }
55
56
    /**
57
     * This function to install a plugin by composer require
58
     *
59
     * @param string $packageName format "foo/bar foo/bar2:1.0.0"
60
     *
61
     * @throws PluginException
62
     */
63 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...
64
    {
65
        set_time_limit(0);
66
        $this->init();
67
68
        // Build command
69
        $command = $this->pathPHP.' '.$this->composerFile.' require '.$packageName;
70
        $command .= ' --prefer-dist --no-progress --no-suggest --no-scripts --ignore-platform-reqs --update-with-dependencies --profile --no-ansi --no-interaction -d ';
71
        $command .= $this->workingDir.' 2>&1';
72
        log_info($command);
73
        $this->runCommand($command);
74
    }
75
76
    /**
77
     * This function to remove a plugin by composer remove
78
     *
79
     * @param string $packageName format "foo/bar foo/bar2"
80
     *
81
     * @throws PluginException
82
     */
83 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...
84
    {
85
        set_time_limit(0);
86
        $this->init();
87
88
        // Build command
89
        $command = $this->pathPHP.' '.$this->composerFile.' remove '.$packageName;
90
        $command .= ' --no-progress --no-scripts --ignore-platform-reqs --profile --no-ansi --no-interaction -d ';
91
        $command .= $this->workingDir.' 2>&1';
92
        log_info($command);
93
94
        // Execute command
95
        $this->runCommand($command);
96
    }
97
98
    /**
99
     * Run command
100
     *
101
     * @throws PluginException
102
     *
103
     * @param string $command
104
     */
105
    public function runCommand($command)
106
    {
107
        $output = [];
108
        try {
109
            // Execute command
110
            $returnValue = -1;
111
            exec($command, $output, $returnValue);
112
113
            $outputString = implode(PHP_EOL, $output);
114
            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...
115
                throw new PluginException($outputString);
116
            }
117
            log_info(PHP_EOL.$outputString.PHP_EOL);
118
        } catch (\Exception $exception) {
119
            throw new PluginException($exception->getMessage());
120
        }
121
    }
122
123
    /**
124
     * Set working dir
125
     *
126
     * @param string $workingDir
127
     */
128
    public function setWorkingDir($workingDir)
129
    {
130
        $this->workingDir = $workingDir;
131
    }
132
133
    /**
134
     * Set init
135
     *
136
     * @throws PluginException
137
     */
138
    private function init()
139
    {
140
        if (!$this->isPhpCommandLine()) {
141
            throw new PluginException('Php cli not found.');
142
        }
143
144
        $composerMemory = $this->eccubeConfig['eccube_composer_memory_limit'];
145
        if (!$this->isSetCliMemoryLimit()) {
146
            $cliMemoryLimit = $this->getCliMemoryLimit();
147
            if ($cliMemoryLimit < $composerMemory && $cliMemoryLimit != -1) {
148
                throw new PluginException('Not enough memory limit.');
149
            }
150
        }
151
152
        /**
153
         * Mysql lock in transaction
154
         *
155
         * @see https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html
156
         *
157
         * @var EntityManagerInterface
158
         */
159
        $em = $this->entityManager;
160
        if ($em->getConnection()->isTransactionActive()) {
161
            $em->getConnection()->commit();
162
            $em->getConnection()->beginTransaction();
163
        }
164
165
        @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...
166
        // Config for some environment
167
        putenv('COMPOSER_HOME='.$this->eccubeConfig['plugin_realdir'].'/.composer');
168
        $this->workingDir = $this->workingDir ? $this->workingDir : $this->eccubeConfig['root_dir'];
169
        $this->setupComposer();
170
    }
171
172
    /**
173
     * Check composer file and setup it
174
     */
175
    private function setupComposer()
176
    {
177
        $this->composerFile = $this->workingDir.'/composer.phar';
178
        $this->composerSetup = $this->workingDir.'/composer-setup.php';
179
        if (!file_exists($this->composerFile)) {
180
            if (!file_exists($this->composerSetup)) {
181
                $result = copy('https://getcomposer.org/installer', $this->composerSetup);
182
                log_info($this->composerSetup.' : '.$result);
183
            }
184
            $command = $this->pathPHP.' '.$this->composerSetup;
185
            $this->runCommand($command);
186
187
            unlink($this->composerSetup);
188
        }
189
    }
190
191
    /**
192
     * Get grep memory_limit | Megabyte
193
     *
194
     * @return int|string
195
     */
196
    private function getCliMemoryLimit()
197
    {
198
        $grepMemory = exec($this->pathPHP.' -i | grep "memory_limit"');
199
        if ($grepMemory) {
200
            $grepMemory = explode('=>', $grepMemory);
201
202
            // -1 unlimited
203
            if (trim($grepMemory[2]) == -1) {
204
                return -1;
205
            }
206
207
            $exp = preg_split('#(?<=\d)(?=[a-z])#i', $grepMemory[2]);
208
            $memo = trim($exp[0]);
209
            if ($exp[1] == 'M') {
210
                return $memo;
211
            } else {
212
                if ($exp[1] == 'GB') {
213
                    return $memo * 1024;
214
                } else {
215
                    return 0;
216
                }
217
            }
218
        }
219
220
        return 0;
221
    }
222
223
    /**
224
     * Check to set new value grep "memory_limit"
225
     *
226
     * @return bool
227
     */
228
    private function isSetCliMemoryLimit()
229
    {
230
        $oldMemory = exec($this->pathPHP.' -i | grep "memory_limit"');
231
        $tmpMem = '1.5GB';
232
        if ($oldMemory) {
233
            $memory = explode('=>', $oldMemory);
234
            $originGrepMemmory = trim($memory[2]);
235
236
            if ($originGrepMemmory == $tmpMem) {
237
                $tmpMem = '1.49GB';
238
            }
239
240
            $newMemory = exec($this->pathPHP.' -d memory_limit='.$tmpMem.' -i | grep "memory_limit"');
241
            if ($newMemory) {
242
                $newMemory = explode('=>', $newMemory);
243
                $grepNewMemory = trim($newMemory[2]);
244
                if ($grepNewMemory != $originGrepMemmory) {
245
                    return true;
246
                }
247
            }
248
        }
249
250
        return false;
251
    }
252
253
    /**
254
     * Check php command line
255
     *
256
     * @return bool
257
     */
258
    private function isPhpCommandLine()
259
    {
260
        $php = exec('which php');
261
        if (null != $php) {
262
            if (strpos(strtolower($php), 'php') !== false) {
263
                return true;
264
            }
265
        }
266
267
        return false;
268
    }
269
270
    public function configureRepository(BaseInfo $BaseInfo)
271
    {
272
        // TODO: Implement configureRepository() method.
273
    }
274
}
275