Failed Conditions
Push — master ( 9ed6ac...d1d972 )
by chihiro
493:16 queued 481:52
created

PluginService::disable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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 Eccube\Common\Constant;
28
use Eccube\Exception\PluginException;
29
use Eccube\Util\Str;
30
use Symfony\Component\Filesystem\Filesystem;
31
use Symfony\Component\Yaml\Yaml;
32
33
class PluginService
0 ignored issues
show
introduced by
Missing class doc comment
Loading history...
34
{
35
    const CONFIG_YML = 'config.yml';
36
    const EVENT_YML = 'event.yml';
37
    private $app;
38
39 149
    public function __construct($app)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
40
    {
41 149
        $this->app = $app;
42 149
    }
43
44 9
    public function install($path, $source = 0)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
45
    {
46 9
        $pluginBaseDir = null;
47 9
        $tmp = null;
48
49
        try {
50 9
            $this->app->removePluginConfigCache();
51 9
            $tmp = $this->createTempDir();
52
53 9
            $this->unpackPluginArchive($path, $tmp); //一旦テンポラリに展開
54 9
            $this->checkPluginArchiveContent($tmp);
55
56 7
            $config = $this->readYml($tmp.'/'.self::CONFIG_YML);
57 7
            $event = $this->readYml($tmp.'/'.self::EVENT_YML);
58 7
            $this->deleteFile($tmp); // テンポラリのファイルを削除
59
60 7
            $this->checkSamePlugin($config['code']); // 重複していないかチェック
61
62 7
            $pluginBaseDir = $this->calcPluginDir($config['code']);
63 7
            $this->createPluginDir($pluginBaseDir); // 本来の置き場所を作成
64
65 7
            $this->unpackPluginArchive($path, $pluginBaseDir); // 問題なければ本当のplugindirへ
66
67 7
            $this->registerPlugin($config, $event, $source); // dbにプラグイン登録
68 6
            $this->app->writePluginConfigCache();
69 9
        } catch (PluginException $e) {
70 4
            $this->deleteDirs(array($tmp, $pluginBaseDir));
71 4
            throw $e;
72
        } catch (\Exception $e) { // インストーラがどんなExceptionを上げるかわからないので
73
74
            $this->deleteDirs(array($tmp, $pluginBaseDir));
75
            throw $e;
76
        }
77
78 6
        return true;
79
    }
80
81 9
    public function createTempDir()
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
82
    {
83 9
        @mkdir($this->app['config']['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...
84 9
        $d = ($this->app['config']['plugin_temp_realdir'].'/'.sha1(Str::random(16)));
85
86 9
        if (!mkdir($d, 0777)) {
87
            throw new PluginException($php_errormsg.$d);
88
        }
89
90 9
        return $d;
91
    }
92
93 4
    public function deleteDirs($arr)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
94
    {
95 4
        foreach ($arr as $dir) {
96 4
            if (file_exists($dir)) {
97 3
                $fs = new Filesystem();
98 3
                $fs->remove($dir);
99 3
            }
100 4
        }
101 4
    }
102
103 9
    public function unpackPluginArchive($archive, $dir)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
104
    {
105 9
        $extension = pathinfo($archive, PATHINFO_EXTENSION);
106
        try {
107 9
            if ($extension == 'zip') {
108
                $zip = new \ZipArchive();
109
                $zip->open($archive);
110
                $zip->extractTo($dir);
111
                $zip->close();
112
            } else {
113 9
                $phar = new \PharData($archive);
114 9
                $phar->extractTo($dir, null, true);
115
            }
116 9
        } catch (\Exception $e) {
117
            throw new PluginException('アップロードに失敗しました。圧縮ファイルを確認してください。');
118
        }
119 9
    }
120
121 149
    public function checkPluginArchiveContent($dir, array $config_cache = array())
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
122
    {
123
        try {
124 149
            if (!empty($config_cache)) {
125 141
                $meta = $config_cache;
126 141
            } else {
127 10
                $meta = $this->readYml($dir . '/config.yml');
0 ignored issues
show
Coding Style introduced by
Concat operator must not be surrounded by spaces
Loading history...
128
            }
129 149
        } catch (\Symfony\Component\Yaml\Exception\ParseException $e) {
130
            throw new PluginException($e->getMessage(), $e->getCode(), $e);
131
        }
132
133 149
        if (!is_array($meta)) {
134 2
            throw new PluginException('config.yml not found or syntax error');
135
        }
136 147 View Code Duplication
        if (!isset($meta['code']) || !$this->checkSymbolName($meta['code'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
137
            throw new PluginException('config.yml code empty or invalid_character(\W)');
138
        }
139 147
        if (!isset($meta['name'])) {
140
            // nameは直接クラス名やPATHに使われるわけではないため文字のチェックはなしし
141 1
            throw new PluginException('config.yml name empty');
142
        }
143 146 View Code Duplication
        if (isset($meta['event']) && !$this->checkSymbolName($meta['event'])) { // eventだけは必須ではない
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
144
            throw new PluginException('config.yml event empty or invalid_character(\W) ');
145
        }
146 146
        if (!isset($meta['version'])) {
147
            // versionは直接クラス名やPATHに使われるわけではないため文字のチェックはなしし
148
            throw new PluginException('config.yml version invalid_character(\W) ');
149
        }
150 146
        if (isset($meta['orm.path'])) {
151
            if (!is_array($meta['orm.path'])) {
152
                throw new PluginException('config.yml orm.path invalid_character(\W) ');
153
            }
154
        }
155 146
        if (isset($meta['service'])) {
156
            if (!is_array($meta['service'])) {
157
                throw new PluginException('config.yml service invalid_character(\W) ');
158
            }
159
        }
160 146
    }
161
162 10
    public function readYml($yml)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
163
    {
164 10
        if (file_exists($yml)) {
165 8
            return Yaml::parse(file_get_contents($yml));
166
        }
167
168 7
        return false;
169
    }
170
171 147
    public function checkSymbolName($string)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
172
    {
173 147
        return strlen($string) < 256 && preg_match('/^\w+$/', $string);
174
        // plugin_nameやplugin_codeに使える文字のチェック
175
        // a-z A-Z 0-9 _
176
        // ディレクトリ名などに使われれるので厳しめ
177
    }
178
179 7
    public function deleteFile($path)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
180
    {
181 7
        $f = new Filesystem();
182 7
        $f->remove($path);
183 7
    }
184
185 7
    public function checkSamePlugin($code)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
186
    {
187 7
        $repo = $this->app['eccube.repository.plugin']->findOneBy(array('code' => $code));
188 7
        if ($repo) {
189 1
            throw new PluginException('plugin already installed.');
190
        }
191 7
    }
192
193 7
    public function calcPluginDir($name)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
194
    {
195 7
        return $this->app['config']['plugin_realdir'].'/'.$name;
196
    }
197
198 7
    public function createPluginDir($d)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
199
    {
200 7
        $b = @mkdir($d);
201 7
        if (!$b) {
202
            throw new PluginException($php_errormsg);
203
        }
204 7
    }
205
206 7
    public function registerPlugin($meta, $event_yml, $source = 0)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
207
    {
208 7
        $em = $this->app['orm.em'];
209 7
        $em->getConnection()->beginTransaction();
210
        try {
211 7
            $p = new \Eccube\Entity\Plugin();
212
            // インストール直後はプラグインは有効にしない
213 7
            $p->setName($meta['name'])
214 7
                ->setEnable(Constant::DISABLED)
215 7
                ->setClassName(isset($meta['event']) ? $meta['event'] : '')
216 7
                ->setVersion($meta['version'])
217 7
                ->setDelflg(Constant::DISABLED)
218 7
                ->setSource($source)
219 7
                ->setCode($meta['code']);
220
221 7
            $em->persist($p);
222 7
            $em->flush();
223
224 7
            if (is_array($event_yml)) {
225 2
                foreach ($event_yml as $event => $handlers) {
226 2
                    foreach ($handlers as $handler) {
227 2
                        if (!$this->checkSymbolName($handler[0])) {
228
                            throw new PluginException('Handler name format error');
229
                        }
230 2
                        $peh = new \Eccube\Entity\PluginEventHandler();
231 2
                        $peh->setPlugin($p)
232 2
                            ->setEvent($event)
233 2
                            ->setdelFlg(Constant::DISABLED)
234 2
                            ->setHandler($handler[0])
235 2
                            ->setHandlerType($handler[1])
236 2
                            ->setPriority($this->app['eccube.repository.plugin_event_handler']->calcNewPriority($event, $handler[1]));
237 2
                        $em->persist($peh);
238 2
                        $em->flush();
239 2
                    }
240 2
                }
241 2
            }
242
243 7
            $em->persist($p);
244
245 7
            $this->callPluginManagerMethod($meta, 'install');
246
247 6
            $em->flush();
248 6
            $em->getConnection()->commit();
249 7
        } catch (\Exception $e) {
250 1
            $em->getConnection()->rollback();
251 1
            throw new PluginException($e->getMessage());
252
        }
253
254 6
        return $p;
255
    }
256
257 7
    public function callPluginManagerMethod($meta, $method)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
258
    {
259 7
        $class = '\\Plugin'.'\\'.$meta['code'].'\\'.'PluginManager';
260 7
        if (class_exists($class)) {
261 3
            $installer = new $class(); // マネージャクラスに所定のメソッドがある場合だけ実行する
262 3
            if (method_exists($installer, $method)) {
263 3
                $installer->$method($meta, $this->app);
264 2
            }
265 2
        }
266 6
    }
267
268 5
    public function uninstall(\Eccube\Entity\Plugin $plugin)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
269
    {
270 5
        $pluginDir = $this->calcPluginDir($plugin->getCode());
271 5
        $this->app->removePluginConfigCache();
272 5
        $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), 'disable');
273 5
        $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), 'uninstall');
274 5
        $this->unregisterPlugin($plugin);
275 5
        $this->deleteFile($pluginDir);
276 5
        $this->app->writePluginConfigCache();
277 5
        return true;
0 ignored issues
show
introduced by
Missing blank line before return statement
Loading history...
278
    }
279
280 5
    public function unregisterPlugin(\Eccube\Entity\Plugin $p)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
281
    {
282
        try {
283 5
            $em = $this->app['orm.em'];
284 5
            $em->getConnection()->beginTransaction();
285
286 5
            $p->setDelFlg(Constant::ENABLED)->setEnable(Constant::DISABLED);
287
288 5
            foreach ($p->getPluginEventHandlers()->toArray() as $peh) {
289 1
                $peh->setDelFlg(Constant::ENABLED);
290 5
            }
291
292 5
            $em->persist($p);
293 5
            $em->flush();
294 5
            $em->getConnection()->commit();
295 5
        } catch (\Exception $e) {
296
            $em->getConnection()->rollback();
297
            throw $e;
298
        }
299 5
    }
300
301 2
    public function disable(\Eccube\Entity\Plugin $plugin)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
302
    {
303 2
        return $this->enable($plugin, false);
304
    }
305
306 3
    public function enable(\Eccube\Entity\Plugin $plugin, $enable = true)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
307
    {
308 3
        $em = $this->app['orm.em'];
309
        try {
310 3
            $this->app->removePluginConfigCache();
311 3
            $pluginDir = $this->calcPluginDir($plugin->getCode());
312 3
            $em->getConnection()->beginTransaction();
313 3
            $plugin->setEnable($enable ? Constant::ENABLED : Constant::DISABLED);
314 3
            $em->persist($plugin);
315 3
            $this->callPluginManagerMethod(Yaml::parse(file_get_contents($pluginDir.'/'.self::CONFIG_YML)), $enable ? 'enable' : 'disable');
316 2
            $em->flush();
317 2
            $em->getConnection()->commit();
318 2
            $this->app->writePluginConfigCache();
319 3
        } catch (\Exception $e) {
320 1
            $em->getConnection()->rollback();
321 1
            throw $e;
322
        }
323
324 2
        return true;
325
    }
326
327 1
    public function update(\Eccube\Entity\Plugin $plugin, $path)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
328
    {
329 1
        $pluginBaseDir = null;
330 1
        $tmp = null;
331
        try {
332 1
            $this->app->removePluginConfigCache();
333 1
            $tmp = $this->createTempDir();
334
335 1
            $this->unpackPluginArchive($path, $tmp); //一旦テンポラリに展開
336 1
            $this->checkPluginArchiveContent($tmp);
337
338 1
            $config = $this->readYml($tmp.'/'.self::CONFIG_YML);
339 1
            $event = $this->readYml($tmp.'/event.yml');
340
341 1
            if ($plugin->getCode() != $config['code']) {
342
                throw new PluginException('new/old plugin code is different.');
343
            }
344 1
            if ($plugin->getName() != $config['name']) {
345
                throw new PluginException('new/old plugin name is different.');
346
            }
347
348 1
            $pluginBaseDir = $this->calcPluginDir($config['code']);
349 1
            $this->deleteFile($tmp); // テンポラリのファイルを削除
350
351 1
            $this->unpackPluginArchive($path, $pluginBaseDir); // 問題なければ本当のplugindirへ
352
353 1
            $this->updatePlugin($plugin, $config, $event); // dbにプラグイン登録
354 1
            $this->app->writePluginConfigCache();
355 1
        } catch (PluginException $e) {
356
            foreach (array($tmp) as $dir) {
357
                if (file_exists($dir)) {
358
                    $fs = new Filesystem();
359
                    $fs->remove($dir);
360
                }
361
            }
362
            throw $e;
363
        }
364
365 1
        return true;
366
    }
367
368 1
    public function updatePlugin(\Eccube\Entity\Plugin $plugin, $meta, $event_yml)
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
369
    {
370
        try {
371 1
            $em = $this->app['orm.em'];
372 1
            $em->getConnection()->beginTransaction();
373 1
            $plugin->setVersion($meta['version'])
374 1
                ->setName($meta['name']);
375
376 1
            if (isset($meta['event'])) {
377 1
                $plugin->setClassName($meta['event']);
378 1
            }
379
380 1
            $rep = $this->app['eccube.repository.plugin_event_handler'];
381
382 1
            if (is_array($event_yml)) {
383 1
                foreach ($event_yml as $event => $handlers) {
384 1
                    foreach ($handlers as $handler) {
385 1
                        if (!$this->checkSymbolName($handler[0])) {
386
                            throw new PluginException('Handler name format error');
387
                        }
388
                        // updateで追加されたハンドラかどうか調べる
389 1
                        $peh = $rep->findBy(array('del_flg' => Constant::DISABLED,
390 1
                            'plugin_id' => $plugin->getId(),
391 1
                            'event' => $event,
392 1
                            'handler' => $handler[0],
393 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...
394
395 1
                        if (!$peh) { // 新規にevent.ymlに定義されたハンドラなのでinsertする
396 1
                            $peh = new \Eccube\Entity\PluginEventHandler();
397 1
                            $peh->setPlugin($plugin)
398 1
                                ->setEvent($event)
399 1
                                ->setdelFlg(Constant::DISABLED)
400 1
                                ->setHandler($handler[0])
401 1
                                ->setHandlerType($handler[1])
402 1
                                ->setPriority($rep->calcNewPriority($event, $handler[1]));
403 1
                            $em->persist($peh);
404 1
                            $em->flush();
405 1
                        }
406 1
                    }
407 1
                }
408
409
                # アップデート後の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...
410 1
                foreach ($rep->findBy(array('del_flg' => Constant::DISABLED, 'plugin_id' => $plugin->getId())) as $peh) {
411 1
                    if (!isset($event_yml[$peh->getEvent()])) {
412
                        $em->remove($peh);
413
                        $em->flush();
414
                    } else {
415 1
                        $match = false;
416 1
                        foreach ($event_yml[$peh->getEvent()] as $handler) {
417 1
                            if ($peh->getHandler() == $handler[0] && $peh->getHandlerType() == $handler[1]) {
418 1
                                $match = true;
419 1
                            }
420 1
                        }
421 1
                        if (!$match) {
422 1
                            $em->remove($peh);
423 1
                            $em->flush();
424 1
                        }
425
                    }
426 1
                }
427 1
            }
428
429 1
            $em->persist($plugin);
430 1
            $this->callPluginManagerMethod($meta, 'update');
431 1
            $em->flush();
432 1
            $em->getConnection()->commit();
433 1
        } catch (\Exception $e) {
434
            $em->getConnection()->rollback();
435
            throw $e;
436
        }
437 1
    }
438
}
439