Failed Conditions
Push — experimental/3.1 ( e41384...3768d9 )
by Ryo
103:48 queued 80:20
created

PluginService::getRequirePluginName()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 2
nop 2
dl 0
loc 18
ccs 0
cts 0
cp 0
crap 30
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.co.jp/
9
 *
10
 * This program is free software; you can redistribute it and/or
11
 * modify it under the terms of the GNU General Public License
12
 * as published by the Free Software Foundation; either version 2
13
 * of the License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program; if not, write to the Free Software
22
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23
 */
24
25
namespace Eccube\Service;
26
27
use Doctrine\Common\Collections\Criteria;
28
use Doctrine\ORM\EntityManager;
29
use Eccube\Annotation\Inject;
30
use Eccube\Annotation\Service;
31
use Eccube\Application;
32
use Eccube\Common\Constant;
33
use Eccube\Entity\Plugin;
34
use Eccube\Exception\PluginException;
35
use Eccube\Plugin\ConfigManager;
36
use Eccube\Plugin\ConfigManager as PluginConfigManager;
37
use Eccube\Repository\PluginEventHandlerRepository;
38
use Eccube\Repository\PluginRepository;
39
use Eccube\Util\Cache;
40
use Eccube\Util\Str;
41
use Symfony\Component\Filesystem\Filesystem;
42
use Symfony\Component\Yaml\Yaml;
43
44
/**
45
 * @Service
46
 */
47
class PluginService
48
{
49
    /**
50
     * @Inject(PluginEventHandlerRepository::class)
51
     * @var PluginEventHandlerRepository
52
     */
53
    protected $pluginEventHandlerRepository;
54
55
    /**
56
     * @Inject("orm.em")
57
     * @var EntityManager
58
     */
59
    protected $entityManager;
60
61
    /**
62
     * @Inject(PluginRepository::class)
63
     * @var PluginRepository
64
     */
65
    protected $pluginRepository;
66
67
    /**
68
     * @Inject("config")
69
     * @var array
70
     */
71
    protected $appConfig;
72
73
    /**
74
     * @Inject(Application::class)
75
     * @var Application
76
     */
77
    protected $app;
78
79
    /**
80
     * @var EntityProxyService
81
     * @Inject(EntityProxyService::class)
82
     */
83
    protected $entityProxyService;
84
85
    /**
86
     * @Inject(SchemaService::class)
87
     * @var SchemaService
88
     */
89
    protected $schemaService;
90
91
    const CONFIG_YML = 'config.yml';
92
    const EVENT_YML = 'event.yml';
93 15
    const VENDOR_NAME = 'ec-cube';
94
95 15
    // ファイル指定してのプラグインインストール
96 15
    public function install($path, $source = 0)
0 ignored issues
show
introduced by
You must use "/**" style comments for a function comment
Loading history...
97
    {
98
        $pluginBaseDir = null;
99
        $tmp = null;
100 15
101 15
        try {
102
            // プラグイン配置前に実施する処理
103
            $this->preInstall();
104 15
105 15
            $tmp = $this->createTempDir();
106 15
107
            $this->unpackPluginArchive($path, $tmp); //一旦テンポラリに展開
108 15
            $this->checkPluginArchiveContent($tmp);
109 15
110
            $config = $this->readYml($tmp.'/'.self::CONFIG_YML);
111 13
            $event = $this->readYml($tmp.'/'.self::EVENT_YML);
112 13
            $this->deleteFile($tmp); // テンポラリのファイルを削除
113 13
114
            $this->checkSamePlugin($config['code']); // 重複していないかチェック
115 13
116
            $pluginBaseDir = $this->calcPluginDir($config['code']);
117 13
            $this->createPluginDir($pluginBaseDir); // 本来の置き場所を作成
118 13
119
            $this->unpackPluginArchive($path, $pluginBaseDir); // 問題なければ本当のplugindirへ
120 13
121
            // プラグイン配置後に実施する処理
122 13
            $this->postInstall($config, $event, $source);
123
        } catch (PluginException $e) {
124
            $this->deleteDirs(array($tmp, $pluginBaseDir));
125 12
            throw $e;
126 12
        } catch (\Exception $e) { // インストーラがどんなExceptionを上げるかわからないので
127
128 12
            $this->deleteDirs(array($tmp, $pluginBaseDir));
129 4
            throw $e;
130 4
        }
131 4
132
        return true;
133
    }
134
135
    // インストール事前処理
136 12
    public function preInstall()
0 ignored issues
show
introduced by
You must use "/**" style comments for a function comment
Loading history...
137 15
    {
138 12
        // キャッシュの削除
139
        PluginConfigManager::removePluginConfigCache();
140 15
        Cache::clear($this->app, false);
141
    }
142
143
    // インストール事後処理
144
    public function postInstall($config, $event, $source)
0 ignored issues
show
introduced by
You must use "/**" style comments for a function comment
Loading history...
145
    {
146
        // Proxyのクラスをロードせずにスキーマを更新するために、
147
        // インストール時には一時的なディレクトリにProxyを生成する
148 15
        $tmpProxyOutputDir = sys_get_temp_dir() . '/proxy_' . Str::random(12);
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
149 15
        @mkdir($tmpProxyOutputDir);
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...
150
151 15
        try {
152
            // dbにプラグイン登録
153
            $plugin = $this->registerPlugin($config, $event, $source);
154
155 15
            // インストール時には一時的に利用するProxyを生成してからスキーマを更新する
156
            $generatedFiles = $this->regenerateProxy($plugin, true, $tmpProxyOutputDir);
157
            $this->schemaService->updateSchema($generatedFiles, $tmpProxyOutputDir);
158
159
            ConfigManager::writePluginConfigCache();
160 4
        } finally {
161 4
            foreach (glob("${tmpProxyOutputDir}/*") as  $f) {
0 ignored issues
show
Coding Style introduced by
There should be 1 space after "as" as per the coding-style, but found 2.
Loading history...
162 3
                unlink($f);
163 4
            }
164
            rmdir($tmpProxyOutputDir);
165
        }
166
    }
167
168
    public function createTempDir()
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
169
    {
170 15
        @mkdir($this->appConfig['plugin_temp_realdir']);
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...
171
        $d = ($this->appConfig['plugin_temp_realdir'].'/'.sha1(Str::random(16)));
172 15
173
        if (!mkdir($d, 0777)) {
174
            throw new PluginException($php_errormsg.$d);
175
        }
176
177
        return $d;
178 15
    }
179 15
180
    public function deleteDirs($arr)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
181
    {
182
        foreach ($arr as $dir) {
183
            if (file_exists($dir)) {
184
                $fs = new Filesystem();
185
                $fs->remove($dir);
186
            }
187
        }
188
    }
189 1067
190 1067
    public function unpackPluginArchive($archive, $dir)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
191
    {
192 1067
        $extension = pathinfo($archive, PATHINFO_EXTENSION);
193
        try {
194
            if ($extension == 'zip') {
195
                $zip = new \ZipArchive();
196
                $zip->open($archive);
197
                $zip->extractTo($dir);
198 1067
                $zip->close();
199 2
            } else {
200
                $phar = new \PharData($archive);
201 1067
                $phar->extractTo($dir, null, true);
202
            }
203
        } catch (\Exception $e) {
204 1067
            throw new PluginException('アップロードに失敗しました。圧縮ファイルを確認してください。');
205
        }
206 1
    }
207
208 1067
    public function checkPluginArchiveContent($dir, array $config_cache = array())
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
209
    {
210
        try {
211 1067
            if (!empty($config_cache)) {
212
                $meta = $config_cache;
213
            } else {
214
                $meta = $this->readYml($dir . '/config.yml');
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
215 1067
            }
216
        } catch (\Symfony\Component\Yaml\Exception\ParseException $e) {
217
            throw new PluginException($e->getMessage(), $e->getCode(), $e);
218
        }
219
220 1067
        if (!is_array($meta)) {
221
            throw new PluginException('config.yml not found or syntax error');
222
        }
223 View Code Duplication
        if (!isset($meta['code']) || !$this->checkSymbolName($meta['code'])) {
224
            throw new PluginException('config.yml code empty or invalid_character(\W)');
225
        }
226
        if (!isset($meta['name'])) {
227
            // nameは直接クラス名やPATHに使われるわけではないため文字のチェックはなしし
228
            throw new PluginException('config.yml name empty');
229 16
        }
230 14 View Code Duplication
        if (isset($meta['event']) && !$this->checkSymbolName($meta['event'])) { // eventだけは必須ではない
231
            throw new PluginException('config.yml event empty or invalid_character(\W) ');
232
        }
233 13
        if (!isset($meta['version'])) {
234
            // versionは直接クラス名やPATHに使われるわけではないため文字のチェックはなしし
235
            throw new PluginException('config.yml version invalid_character(\W) ');
236
        }
237
        if (isset($meta['orm.path'])) {
238 1067
            if (!is_array($meta['orm.path'])) {
239
                throw new PluginException('config.yml orm.path invalid_character(\W) ');
240
            }
241
        }
242
        if (isset($meta['service'])) {
243
            if (!is_array($meta['service'])) {
244
                throw new PluginException('config.yml service invalid_character(\W) ');
245
            }
246 13
        }
247 13
    }
248
249
    public function readYml($yml)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
250
    {
251
        if (file_exists($yml)) {
252 13
            return Yaml::parse(file_get_contents($yml));
253 13
        }
254 1
255
        return false;
256
    }
257
258
    public function checkSymbolName($string)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
259
    {
260 13
        return strlen($string) < 256 && preg_match('/^\w+$/', $string);
261
        // plugin_nameやplugin_codeに使える文字のチェック
262
        // a-z A-Z 0-9 _
263
        // ディレクトリ名などに使われれるので厳しめ
264
    }
265 13
266 13
    public function deleteFile($path)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
267
    {
268
        $f = new Filesystem();
269
        $f->remove($path);
270
    }
271
272
    public function checkSamePlugin($code)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
273 13
    {
274 13
        $repo = $this->pluginRepository->findOneBy(array('code' => $code));
275
        if ($repo) {
276 13
            throw new PluginException('plugin already installed.');
277
        }
278 13
    }
279 13
280 13
    public function calcPluginDir($name)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
281 13
    {
282 13
        return $this->appConfig['plugin_realdir'].'/'.$name;
283 13
    }
284
285 13
    public function createPluginDir($d)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
286 13
    {
287
        $b = @mkdir($d);
288 13
        if (!$b) {
289 2
            throw new PluginException($php_errormsg);
290 2
        }
291 2
    }
292
293
    public function registerPlugin($meta, $event_yml, $source = 0)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
294 2
    {
295 2
        $em = $this->entityManager;
296 2
        $em->getConnection()->beginTransaction();
297 2
        try {
298 2
            $p = new \Eccube\Entity\Plugin();
299 2
            // インストール直後はプラグインは有効にしない
300 2
            $p->setName($meta['name'])
301 2
                ->setEnable(Constant::DISABLED)
302
                ->setClassName(isset($meta['event']) ? $meta['event'] : '')
303
                ->setVersion($meta['version'])
304
                ->setSource($source)
305
                ->setCode($meta['code']);
306 13
307
            $em->persist($p);
308 13
            $em->flush();
309
310 12
            if (is_array($event_yml)) {
311 12
                foreach ($event_yml as $event => $handlers) {
312 1
                    foreach ($handlers as $handler) {
313 1
                        if (!$this->checkSymbolName($handler[0])) {
314 1
                            throw new PluginException('Handler name format error');
315
                        }
316
                        $peh = new \Eccube\Entity\PluginEventHandler();
317 12
                        $peh->setPlugin($p)
318
                            ->setEvent($event)
319
                            ->setHandler($handler[0])
320
                            ->setHandlerType($handler[1])
321
                            ->setPriority($this->pluginEventHandlerRepository->calcNewPriority($event, $handler[1]));
322 13
                        $em->persist($peh);
323 13
                        $em->flush();
324 3
                    }
325 3
                }
326 3
            }
327
328
            $em->persist($p);
329
330
            $this->callPluginManagerMethod($meta, 'install');
331
332
            $em->flush();
333 7
            $em->getConnection()->commit();
334 7
        } catch (\Exception $e) {
335 7
            $em->getConnection()->rollback();
336 7
            throw new PluginException($e->getMessage());
337 7
        }
338 7
339 7
        return $p;
340 7
    }
341
342
    public function callPluginManagerMethod($meta, $method)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
343 7
    {
344
        $class = '\\Plugin'.'\\'.$meta['code'].'\\'.'PluginManager';
345 7
        if (class_exists($class)) {
346 7
            $installer = new $class(); // マネージャクラスに所定のメソッドがある場合だけ実行する
347
            if (method_exists($installer, $method)) {
348
                $installer->$method($meta, $this->app);
349
            }
350
        }
351
    }
352 7
353 7
    public function uninstall(\Eccube\Entity\Plugin $plugin)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
354 2
    {
355
        $pluginDir = $this->calcPluginDir($plugin->getCode());
356 7
        ConfigManager::removePluginConfigCache();
357 7
        Cache::clear($this->app, false);
358
        $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), 'disable');
359
        $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), 'uninstall');
360
        $this->disable($plugin);
361
        $this->unregisterPlugin($plugin);
362
        $this->deleteFile($pluginDir);
363
364
        // スキーマを更新する
365 8
        $this->schemaService->updateSchema([], $this->appConfig['root_dir'].'/app/proxy/entity');
366
367
        ConfigManager::writePluginConfigCache();
368
        return true;
0 ignored issues
show
introduced by
Missing blank line before return statement
Loading history...
369
    }
370
371
    public function unregisterPlugin(\Eccube\Entity\Plugin $p)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
372
    {
373
        try {
374
            $em = $this->entityManager;
375
            foreach ($p->getPluginEventHandlers()->toArray() as $peh) {
376
                $em->remove($peh);
377 12
            }
378 10
            $em->remove($p);
379
            $em->flush();
380 12
        } catch (\Exception $e) {
381
            throw $e;
382 12
        }
383
    }
384 12
385
    public function disable(\Eccube\Entity\Plugin $plugin)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
386
    {
387 12
        return $this->enable($plugin, false);
388 12
    }
389 12
390
    /**
391 8
     * Proxyを再生成します.
392 8
     * @param Plugin $plugin プラグイン
393 8
     * @param boolean $temporary プラグインが無効状態でも一時的に生成するかどうか
394 8
     * @param string|null $outputDir 出力先
395
     * @return array 生成されたファイルのパス
396
     */
397
    private function regenerateProxy(Plugin $plugin, $temporary, $outputDir = null)
398
    {
399 12
        if (is_null($outputDir)) {
400 12
            $outputDir = $this->appConfig['root_dir'].'/app/proxy/entity';
401
        }
402 12
        @mkdir($outputDir);
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...
403 12
404 12
        $enabledPluginCodes = array_map(
405 12
            function($p) { return $p->getCode(); },
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
Coding Style introduced by
Opening brace must be the last content on the line
Loading history...
introduced by
Missing blank line before return statement
Loading history...
406
            $this->pluginRepository->findAllEnabled()
407
        );
408
409
        $excludes = [];
410
        if ($temporary || $plugin->getEnable() === Constant::ENABLED) {
411 11
            $enabledPluginCodes[] = $plugin->getCode();
412
        } else {
413 11
            $index = array_search($plugin->getCode(), $enabledPluginCodes);
414 11
            if ($index >= 0) {
415 11
                array_splice($enabledPluginCodes, $index, 1);
416 11
                $excludes = [$this->appConfig['root_dir']."/app/Plugin/".$plugin->getCode()."/Entity"];
417 11
            }
418 11
        }
419
420 11
        $enabledPluginEntityDirs = array_map(function($code) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
421
            return $this->appConfig['root_dir']."/app/Plugin/${code}/Entity";
422
        }, $enabledPluginCodes);
423 10
424
        return $this->entityProxyService->generate(
425 10
            array_merge([$this->appConfig['root_dir'].'/app/Acme/Entity'], $enabledPluginEntityDirs),
426 10
            $excludes,
427 10
            $outputDir
428 1
        );
429 1
    }
430 1
431
    public function enable(\Eccube\Entity\Plugin $plugin, $enable = true)
0 ignored issues
show
introduced by
Declare public methods first, then protected ones and finally private ones
Loading history...
introduced by
Missing function doc comment
Loading history...
432
    {
433 10
        $em = $this->entityManager;
434
        try {
435
            PluginConfigManager::removePluginConfigCache();
436
            Cache::clear($this->app, false);
437
            $pluginDir = $this->calcPluginDir($plugin->getCode());
438 1
            $em->getConnection()->beginTransaction();
439 1
            $plugin->setEnable($enable ? Constant::ENABLED : Constant::DISABLED);
440
            $em->persist($plugin);
441 1
442 1
            $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), $enable ? 'enable' : 'disable');
443 1
444
            // Proxyだけ再生成してスキーマは更新しない
445 1
            $this->regenerateProxy($plugin, false);
446 1
447
            $em->flush();
448 1
            $em->getConnection()->commit();
449 1
            PluginConfigManager::writePluginConfigCache();
450
        } catch (\Exception $e) {
451 1
            $em->getConnection()->rollback();
452
            throw $e;
453
        }
454
455 1
        return true;
456 1
    }
457
458 1
    public function update(\Eccube\Entity\Plugin $plugin, $path)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
459 1
    {
460
        $pluginBaseDir = null;
461 1
        $tmp = null;
462
        try {
463
            PluginConfigManager::removePluginConfigCache();
464
            Cache::clear($this->app, false);
465
            $tmp = $this->createTempDir();
466
467
            $this->unpackPluginArchive($path, $tmp); //一旦テンポラリに展開
468
            $this->checkPluginArchiveContent($tmp);
469
470
            $config = $this->readYml($tmp.'/'.self::CONFIG_YML);
471
            $event = $this->readYml($tmp.'/event.yml');
472 1
473
            if ($plugin->getCode() != $config['code']) {
474
                throw new PluginException('new/old plugin code is different.');
475
            }
476
477
            $pluginBaseDir = $this->calcPluginDir($config['code']);
478 1
            $this->deleteFile($tmp); // テンポラリのファイルを削除
479 1
480 1
            $this->unpackPluginArchive($path, $pluginBaseDir); // 問題なければ本当のplugindirへ
481 1
            $this->updatePlugin($plugin, $config, $event); // dbにプラグイン登録
482
483 1
            PluginConfigManager::writePluginConfigCache();
484 1
        } catch (PluginException $e) {
485
            foreach (array($tmp) as $dir) {
486
                if (file_exists($dir)) {
487 1
                    $fs = new Filesystem();
488
                    $fs->remove($dir);
489 1
                }
490 1
            }
491 1
            throw $e;
492 1
        }
493
494
        return true;
495
    }
496 1
497 1
    public function updatePlugin(\Eccube\Entity\Plugin $plugin, $meta, $event_yml)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
498 1
    {
499 1
        try {
500 1
            $em = $this->entityManager;
501
            $em->getConnection()->beginTransaction();
502 1
            $plugin->setVersion($meta['version'])
503 1
                ->setName($meta['name']);
504 1
505 1
            if (isset($meta['event'])) {
506 1
                $plugin->setClassName($meta['event']);
507 1
            }
508 1
509 1
            $rep = $this->pluginEventHandlerRepository;
510 1
511
            if (is_array($event_yml)) {
512
                foreach ($event_yml as $event => $handlers) {
513
                    foreach ($handlers as $handler) {
514
                        if (!$this->checkSymbolName($handler[0])) {
515
                            throw new PluginException('Handler name format error');
516 1
                        }
517 1
                        // updateで追加されたハンドラかどうか調べる
518
                        $peh = $rep->findBy(array(
519
                            'plugin_id' => $plugin->getId(),
520
                            'event' => $event,
521 1
                            'handler' => $handler[0],
522 1
                            'handler_type' => $handler[1],));
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 24 spaces, but found 28.
Loading history...
introduced by
Add a single space after each comma delimiter
Loading history...
523 1
524 1
                        if (!$peh) { // 新規にevent.ymlに定義されたハンドラなのでinsertする
0 ignored issues
show
Bug Best Practice introduced by
The expression $peh of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
525
                            $peh = new \Eccube\Entity\PluginEventHandler();
526
                            $peh->setPlugin($plugin)
527 1
                                ->setEvent($event)
528 1
                                ->setHandler($handler[0])
529 1
                                ->setHandlerType($handler[1])
530
                                ->setPriority($rep->calcNewPriority($event, $handler[1]));
531
                            $em->persist($peh);
532
                            $em->flush();
533
                        }
534
                    }
535 1
                }
536 1
537 1
                # アップデート後のevent.ymlで削除されたハンドラをdtb_plugin_event_handlerから探して削除
0 ignored issues
show
Coding Style introduced by
Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.
Loading history...
538 1
                foreach ($rep->findBy(array('plugin_id' => $plugin->getId())) as $peh) {
539
                    if (!isset($event_yml[$peh->getEvent()])) {
540
                        $em->remove($peh);
541
                        $em->flush();
542
                    } else {
543
                        $match = false;
544
                        foreach ($event_yml[$peh->getEvent()] as $handler) {
545
                            if ($peh->getHandler() == $handler[0] && $peh->getHandlerType() == $handler[1]) {
546
                                $match = true;
547
                            }
548
                        }
549
                        if (!$match) {
550
                            $em->remove($peh);
551
                            $em->flush();
552
                        }
553
                    }
554
                }
555
            }
556
557
            $em->persist($plugin);
558
            $this->callPluginManagerMethod($meta, 'update');
559
            $em->flush();
560
            $em->getConnection()->commit();
561
        } catch (\Exception $e) {
562
            $em->getConnection()->rollback();
563
            throw $e;
564
        }
565
    }
566
567
    /**
568
     * Do check dependency plugin
569
     *
570
     * @param array $arrPlugin
571
     * @param array $plugin
572
     * @param array $arrDependency
573
     * @return array|mixed
574
     */
575
    public function getDependency($arrPlugin, $plugin, $arrDependency = array())
576
    {
577
        // Prevent infinity loop
578
        if (empty($arrDependency)) {
579
            $arrDependency[] = $plugin;
580
        }
581
582
        // Check dependency
583
        if (!isset($plugin['require']) || empty($plugin['require'])) {
584
            return $arrDependency;
585
        }
586
587
        $require = $plugin['require'];
588
        // Check dependency
589
        foreach ($require as $pluginName => $version) {
590
            $dependPlugin = $this->buildInfo($arrPlugin, $pluginName);
591
            // Prevent call self
592
            if (!$dependPlugin || $dependPlugin['product_code'] == $plugin['product_code']) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $dependPlugin of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
593
                continue;
594
            }
595
596
            // Check duplicate in dependency
597
            $index = array_search($dependPlugin['product_code'], array_column($arrDependency, 'product_code'));
598
            if ($index === false) {
599
                $arrDependency[] = $dependPlugin;
600
                // Check child dependency
601
                $arrDependency = $this->getDependency($arrPlugin, $dependPlugin, $arrDependency);
602
            }
603
        }
604
605
        return $arrDependency;
606
    }
607
608
    /**
609
     * Get plugin information
610
     *
611
     * @param array  $arrPlugin
612
     * @param string $pluginCode
613
     * @return array|null
614
     */
615
    public function buildInfo($arrPlugin, $pluginCode)
616
    {
617
        $plugin = [];
618
        $index = $this->checkPluginExist($arrPlugin, $pluginCode);
619
        if ($index === false) {
620
            return $plugin;
621
        }
622
        // Get target plugin in return of api
623
        $plugin = $arrPlugin[$index];
624
625
        // Check the eccube version that the plugin supports.
626
        $plugin['is_supported_eccube_version'] = 0;
627
        if (in_array(Constant::VERSION, $plugin['eccube_version'])) {
628
            // Match version
629
            $plugin['is_supported_eccube_version'] = 1;
630
        }
631
632
        $plugin['depend'] = $this->getRequirePluginName($arrPlugin, $plugin);
633
634
        return $plugin;
635
    }
636
637
    /**
638
     * Get dependency name and version only
639
     *
640
     * @param array $arrPlugin
641
     * @param array $plugin
642
     * @return mixed
643
     */
644
    public function getRequirePluginName($arrPlugin, $plugin)
645
    {
646
        $depend = [];
647
        if (isset($plugin['require']) && !empty($plugin['require'])) {
648
            foreach ($plugin['require'] as $name => $version) {
649
                $ret = $this->checkPluginExist($arrPlugin, $name);
650
                if ($ret === false) {
651
                    continue;
652
                }
653
                $depend[] = [
654
                    'name' => $arrPlugin[$ret]['name'],
655
                    'version' => $version,
656
                ];
657
            }
658
        }
659
660
        return $depend;
661
    }
662
663
    /**
664
     * Check require plugin in enable
665
     *
666
     * @param string $pluginCode
667
     * @return array
668
     */
669
    public function findRequirePluginNeedEnable($pluginCode)
670
    {
671
        $dir = $this->appConfig['plugin_realdir'].'/'.$pluginCode;
672
        $composerFile = $dir.'/composer.json';
673
        $requires = [];
674
        if (!file_exists($composerFile)) {
675
            return $requires;
676
        }
677
        $jsonText = file_get_contents($composerFile);
678
        if ($jsonText) {
679
            $json = json_decode($jsonText, true);
680
            $require = $json['require'];
681
682
            // Remove vendor plugin
683
            if (isset($require[self::VENDOR_NAME.'/plugin-installer'])) {
684
                unset($require[self::VENDOR_NAME.'/plugin-installer']);
685
            }
686
            foreach ($require as $name => $version) {
687
                // Check plugin of ec-cube only
688
                if (strpos($name, self::VENDOR_NAME.'/') !== false) {
689
                    $requireCode = str_replace(self::VENDOR_NAME.'/', '', $name);
690
                    $ret = $this->isEnable($requireCode);
691
                    if ($ret) {
692
                        continue;
693
                    }
694
                    $requires[] = $requireCode;
695
                }
696
            }
697
        }
698
699
        return $requires;
700
    }
701
    /**
702
     * Find the dependent plugins that need to be disabled
703
     *
704
     * @param string $pluginCode
705
     * @return array
706
     */
707
    public function findDependentPluginNeedDisable($pluginCode)
708
    {
709
        $criteria = Criteria::create()
710
            ->where(Criteria::expr()->eq('enable', Constant::ENABLED))
711
            ->andWhere(Criteria::expr()->neq('code', $pluginCode));
712
713
        /**
714
         * @var Plugin[] $enabledPlugins
715
         */
716
        $enabledPlugins = $this->pluginRepository->matching($criteria);
717
        $dependents = [];
718
        foreach ($enabledPlugins as $plugin) {
719
            $dir = $this->appConfig['plugin_realdir'].'/'.$plugin->getCode();
720
            $fileName = $dir.'/composer.json';
721
            if (!file_exists($fileName)) {
722
                continue;
723
            }
724
            $jsonText = file_get_contents($fileName);
725
            if ($jsonText) {
726
                $json = json_decode($jsonText, true);
727
                if (!isset($json['require'])) {
728
                    continue;
729
                }
730
                if (array_key_exists(self::VENDOR_NAME.'/'.$pluginCode, $json['require'])) {
731
                    $dependents[] = $plugin->getName();
732
                }
733
            }
734
        }
735
736
        return $dependents;
737
    }
738
739
    /**
740
     * @param $arrPlugin
741
     * @param $pluginCode
742
     * @return false|int|string
743
     */
744
    private function checkPluginExist($arrPlugin, $pluginCode)
745
    {
746
        if (strpos($pluginCode, self::VENDOR_NAME.'/') !== false) {
747
            $pluginCode = str_replace(self::VENDOR_NAME.'/', '', $pluginCode);
748
        }
749
        // Find plugin in array
750
        $index = array_search($pluginCode, array_column($arrPlugin, 'product_code'));
751
752
        return $index;
753
    }
754
755
    /**
756
     * @param string $code
757
     * @return bool
758
     */
759
    private function isEnable($code)
760
    {
761
        $Plugin = $this->pluginRepository->findOneBy([
0 ignored issues
show
introduced by
Add a comma after each item in a multi-line array
Loading history...
762
            'enable' => Constant::ENABLED,
763
            'code' => $code
764
        ]);
765
        if ($Plugin) {
766
            return true;
767
        }
768
769
        return false;
770
    }
771
}
772