Failed Conditions
Push — experimental/3.1 ( 7f7c1e...7af380 )
by Ryo
68:08 queued 67:47
created

ComposerProcessService::composerVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 9.4285
1
<?php
2
/*
3
 * This file is part of EC-CUBE
4
 *
5
 * Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved.
6
 *
7
 * http://www.lockon.co.jp/
8
 *
9
 * This program is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU General Public License
11
 * as published by the Free Software Foundation; either version 2
12
 * of the License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
 */
23
namespace Eccube\Service\Composer;
24
25
use Doctrine\ORM\EntityManagerInterface;
26
use Eccube\Annotation\Service;
27
28
/**
29
 * Class ComposerProcessService
30
 * @package Eccube\Service\Composer
31
 * @Service
32
 */
33
class ComposerProcessService implements ComposerServiceInterface
34
{
35
    /**
36
     * @var array config parameter
37
     */
38
    protected $appConfig;
39
40
    /**
41
     * @var EntityManagerInterface
42
     */
43
    protected $entityManager;
44
45
    private $workingDir;
46
    private $composerFile;
47
    private $composerSetup;
48
    private $pathPHP;
49
50
    /**
51
     * ComposerProcessService constructor.
52
     *
53
     * @param array                  $appConfig
54
     * @param EntityManagerInterface $entityManager
55
     * @param string                 $pathPHP
56
     */
57
    public function __construct($appConfig, $entityManager, $pathPHP)
58
    {
59
        $this->appConfig = $appConfig;
60
        $this->entityManager = $entityManager;
61
        $this->pathPHP = $pathPHP;
62
    }
63
64
    /**
65
     * This function to install a plugin by composer require
66
     *
67
     * @param string $packageName format "foo/bar foo/bar2:1.0.0"
68
     * @return bool
69
     */
70 View Code Duplication
    public function execRequire($packageName)
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...
71
    {
72
        set_time_limit(0);
73
        if (false === $this->init()) {
74
            return false;
75
        }
76
        // Build command
77
        $command = $this->pathPHP.' '.$this->composerFile.' require '.$packageName;
78
        $command .= ' --prefer-dist --no-progress --no-suggest --no-scripts --ignore-platform-reqs --profile --no-ansi --no-interaction -d ';
79
        $command .= $this->workingDir.' 2>&1';
80
        log_info($command);
81
        $this->runCommand($command);
82
83
        return true;
84
    }
85
86
    /**
87
     * This function to remove a plugin by composer remove
88
     *
89
     * @param string $packageName format "foo/bar foo/bar2"
90
     * @return bool
91
     */
92 View Code Duplication
    public function execRemove($packageName)
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...
93
    {
94
        set_time_limit(0);
95
        if (false === $this->init()) {
96
            return false;
97
        }
98
        // Build command
99
        $command = $this->pathPHP.' '.$this->composerFile.' remove '.$packageName;
100
        $command .= ' --no-progress --no-scripts --ignore-platform-reqs --profile --no-ansi --no-interaction -d ';
101
        $command .= $this->workingDir.' 2>&1';
102
        log_info($command);
103
104
        // Execute command
105
        $this->runCommand($command);
106
107
        return true;
108
    }
109
110
    /**
111
     * Run command
112
     *
113
     * @param string $command
114
     * @return void
115
     */
116
    public function runCommand($command)
117
    {
118
        // Execute command
119
        $output = array();
120
        exec($command, $output);
121
        log_info(PHP_EOL.implode(PHP_EOL, $output).PHP_EOL);
122
    }
123
124
    /**
125
     * Set working dir
126
     * @param string $workingDir
127
     */
128
    public function setWorkingDir($workingDir)
129
    {
130
        $this->workingDir = $workingDir;
131
    }
132
133
    /**
134
     * Set init
135
     * @return bool
136
     */
137
    private function init()
138
    {
139
        if (!$this->isPhpCommandLine()) {
140
            return false;
141
        }
142
143
        if (!$this->isSetCliMemoryLimit()) {
144
            $composerMemory = $this->appConfig['composer_memory_limit'];
145
            if ($this->getCliMemoryLimit() < $composerMemory && $this->getCliMemoryLimit() != -1) {
146
                return false;
147
            }
148
        }
149
150
        /**
151
         * Mysql lock in transaction
152
         * @link https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html
153
         * @var EntityManagerInterface $em
154
         */
155
        $em = $this->entityManager;
156
        if ($em->getConnection()->isTransactionActive()) {
157
            $em->getConnection()->commit();
158
            $em->getConnection()->beginTransaction();
159
        }
160
161
        @ini_set('memory_limit', '1536M');
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...
162
        // Config for some environment
163
        putenv('COMPOSER_HOME='.$this->appConfig['plugin_realdir'].'/.composer');
164
        $this->workingDir = $this->workingDir ? $this->workingDir : $this->appConfig['root_dir'];
165
        $this->setupComposer();
166
167
        return true;
168
    }
169
170
    /**
171
     * Check composer file and setup it
172
     */
173
    private function setupComposer()
174
    {
175
        $this->composerFile = $this->workingDir.'/composer.phar';
176
        $this->composerSetup = $this->workingDir.'/composer-setup.php';
177
        if (!file_exists($this->composerFile)) {
178
            if (!file_exists($this->composerSetup)) {
179
                $result = copy('https://getcomposer.org/installer', $this->composerSetup);
180
                log_info($this->composerSetup.' : '.$result);
181
            }
182
            $command = $this->pathPHP.' '.$this->composerSetup;
183
            $this->runCommand($command);
184
185
            unlink($this->composerSetup);
186
        }
187
    }
188
189
    /**
190
     * Get grep memory_limit | Megabyte
191
     * @return int|string
192
     */
193
    private function getCliMemoryLimit()
194
    {
195
        $grepMemory = exec($this->pathPHP.' -i | grep "memory_limit"');
196
        if ($grepMemory) {
197
            $grepMemory = explode('=>', $grepMemory);
198
199
            // -1 unlimited
200
            if (trim($grepMemory[2]) == -1) {
201
                return -1;
202
            }
203
204
            $exp = preg_split('#(?<=\d)(?=[a-z])#i', $grepMemory[2]);
205
            $memo = trim($exp[0]);
206
            if ($exp[1] == 'M') {
207
                return $memo;
208
            } else {
209
                if ($exp[1] == 'GB') {
210
                    return $memo * 1024;
211
                } else {
212
                    return 0;
213
                }
214
            }
215
        }
216
217
        return 0;
218
    }
219
220
    /**
221
     * Check to set new value grep "memory_limit"
222
     * @return bool
223
     */
224
    private function isSetCliMemoryLimit()
225
    {
226
        $oldMemory = exec($this->pathPHP.' -i | grep "memory_limit"');
227
        $tmpMem = '1.5GB';
228
        if ($oldMemory) {
229
            $memory = explode('=>', $oldMemory);
230
            $originGrepMemmory = trim($memory[2]);
231
232
            if ($originGrepMemmory == $tmpMem) {
233
                $tmpMem = '1.49GB';
234
            }
235
236
            $newMemory = exec($this->pathPHP.' -d memory_limit='.$tmpMem.' -i | grep "memory_limit"');
237
            if ($newMemory) {
238
                $newMemory = explode('=>', $newMemory);
239
                $grepNewMemory = trim($newMemory[2]);
240
                if ($grepNewMemory != $originGrepMemmory) {
241
                    return true;
242
                }
243
            }
244
        }
245
246
        return false;
247
    }
248
249
    /**
250
     * Check php command line
251
     * @return bool
252
     */
253
    private function isPhpCommandLine()
254
    {
255
        $php = exec('which php');
256
        if (null != $php) {
257
            if (strpos(strtolower($php), 'php') !== false) {
258
                return true;
259
            }
260
        }
261
262
        return false;
263
    }
264
265
    /**
266
     * Get version of composer
267
     * @return null|string
268
     */
269
    public function composerVersion()
0 ignored issues
show
introduced by
Declare public methods first, then protected ones and finally private ones
Loading history...
270
    {
271
        $this->init();
272
        $command = $this->pathPHP . ' ' . $this->composerFile . ' -V';
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
273
        return exec($command);
0 ignored issues
show
introduced by
Missing blank line before return statement
Loading history...
274
    }
275
276
    /**
277
     * Get mode
278
     * @return mixed|string
279
     */
280
    public function getMode()
281
    {
282
        return 'EXEC';
283
    }
284
}
285