Completed
Pull Request — master (#7)
by Anton
01:30
created

Plugin::remove()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 62
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 62
rs 7.3333
c 0
b 0
f 0
cc 7
eloc 39
nc 7
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Bluz composer plugin
4
 *
5
 * @copyright Bluz PHP Team
6
 * @link https://github.com/bluzphp/composer-plugin
7
 */
8
9
/**
10
 * @namespace
11
 */
12
namespace Bluz\Composer\Installers;
13
14
use Composer\Composer;
15
use Composer\EventDispatcher\EventSubscriberInterface;
16
use Composer\Installer\PackageEvents;
17
use Composer\IO\IOInterface;
18
use Composer\Plugin\PluginInterface;
19
use Composer\Script\Event;
20
use Composer\Script\ScriptEvents;
21
use Symfony\Component\Filesystem\Exception\IOException;
22
use Symfony\Component\Filesystem\Filesystem;
23
use Symfony\Component\Finder\Finder;
24
25
/**
26
 * Class Plugin
27
 *
28
 * @package Bluz\Composer\Installers
29
 */
30
class Plugin implements PluginInterface, EventSubscriberInterface
31
{
32
    const PERMISSION_CODE = 0755;
33
    const REPEAT = 5;
34
    const DIRECTORIES = [
35
        'application',
36
        'data',
37
        'public',
38
        'tests'
39
    ];
40
41
    /**
42
     * @var Installer
43
     */
44
    protected $installer;
45
46
    /**
47
     * @var string
48
     */
49
    protected $environment;
50
51
    /**
52
     * @var Filesystem
53
     */
54
    protected $filesystem;
55
56
    /**
57
     * Create instance, define constants
58
     */
59
    public function __construct()
60
    {
61
        defined('PATH_ROOT') ?: define('PATH_ROOT', realpath($_SERVER['DOCUMENT_ROOT']));
62
        defined('DS') ?: define('DS', DIRECTORY_SEPARATOR);
63
    }
64
65
    /**
66
     * Called after the plugin is loaded
67
     *
68
     * It setup composer installer
69
     *
70
     * {@inheritDoc}
71
     */
72
    public function activate(Composer $composer, IOInterface $io)
73
    {
74
        $this->installer = new Installer($io, $composer);
75
        $composer->getInstallationManager()->addInstaller($this->installer);
76
    }
77
78
    /**
79
     * Registered events after the plugin is loaded
80
     *
81
     * {@inheritDoc}
82
     */
83
    public static function getSubscribedEvents(): array
84
    {
85
        return [
86
            // copy files to working directory
87
            PackageEvents::POST_PACKAGE_INSTALL => 'copyFiles',
88
            // copy new files
89
            PackageEvents::POST_PACKAGE_UPDATE => 'copyFiles',
90
            // removed unchanged files
91
            PackageEvents::PRE_PACKAGE_UPDATE => 'removeFiles',
92
            // removed all files
93
            PackageEvents::PRE_PACKAGE_UNINSTALL => 'removeFiles',
94
            // copy extra files from root composer.json
95
            ScriptEvents::POST_UPDATE_CMD => 'copyExtraFiles',
96
            // remove extra files from root composer.json
97
            // ScriptEvents::PRE_UPDATE_CMD => 'removeExtraFiles'
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
98
        ];
99
    }
100
101
102
    /**
103
     * Hook which is called after install package
104
     * It copies bluz module
105
     *
106
     * @throws \InvalidArgumentException
107
     */
108
    public function copyFiles()
109
    {
110
        if (file_exists($this->installer->getVendorPath())) {
111
            $this->copyModule();
112
        }
113
    }
114
115
    /**
116
     * Hook which is called before update package
117
     * It checks bluz module
118
     */
119
    public function removeFiles()
120
    {
121
        if (file_exists($this->installer->getVendorPath())) {
122
            $this->removeModule();
123
        }
124
    }
125
126
    /**
127
     * Copy extra files from compose.json of project
128
     *
129
     * @param Event $event
130
     *
131
     * @return void
132
     * @throws \InvalidArgumentException
133
     */
134
    public function copyExtraFiles(Event $event)
135
    {
136
        $extras = $event->getComposer()->getPackage()->getExtra();
137
        if (array_key_exists('copy-files', $extras)) {
138
            $this->installer->getIo()->write(
139
                sprintf('  - Copied additional file(s)'),
140
                true
141
            );
142
            $this->copyExtras($extras['copy-files']);
143
        }
144
    }
145
146
    /**
147
     * Remove extra files from compose.json of project
148
     *
149
     * @param Event $event
150
     *
151
     * @return void
152
     */
153
    public function removeExtraFiles(Event $event)
154
    {
155
        $extras = $event->getComposer()->getPackage()->getExtra();
156
        if (array_key_exists('copy-files', $extras)) {
157
            $this->removeExtras($extras['copy-files']);
158
        }
159
    }
160
161
    /**
162
     * Get Filesystem
163
     *
164
     * @return Filesystem
165
     */
166
    protected function getFilesystem()
167
    {
168
        if (!$this->filesystem) {
169
            $this->filesystem = new Filesystem();
170
        }
171
        return $this->filesystem;
172
    }
173
174
    /**
175
     * getExtra
176
     *
177
     * @return array
178
     */
179
    protected function getExtraFiles() : array
180
    {
181
        $moduleJson = json_decode(file_get_contents($this->installer->getVendorPath() . DS . 'composer.json'), true);
182
183
        if (isset($moduleJson, $moduleJson['extra'], $moduleJson['extra']['copy-files'])) {
184
            return $moduleJson['extra']['copy-files'];
185
        }
186
        return [];
187
    }
188
189
    /**
190
     * Copy Module files
191
     *
192
     * @return void
193
     * @throws \InvalidArgumentException
194
     */
195
    protected function copyModule()
196
    {
197
        $this->copyExtras($this->getExtraFiles());
198
199
        foreach (self::DIRECTORIES as $directory) {
200
            $this->copy(
201
                $this->installer->getVendorPath() . DS . $directory . DS,
202
                PATH_ROOT . DS . $directory . DS
203
            );
204
        }
205
206
        $this->installer->getIo()->write(
207
            sprintf(
208
                '  - Copied <comment>%s</comment> module to application',
209
                basename($this->installer->getVendorPath())
210
            ),
211
            true
212
        );
213
    }
214
215
    /**
216
     * copyExtras
217
     *
218
     * @param  array $files
219
     *
220
     * @return void
221
     * @throws \InvalidArgumentException
222
     */
223
    protected function copyExtras($files)
224
    {
225
        foreach ($files as $source => $target) {
226
            $this->copy(
227
                dirname($this->installer->getVendorPath(), 2) . DS . $source,
228
                PATH_ROOT . DS . $target
229
            );
230
        }
231
    }
232
233
    /**
234
     * It recursively copies the files and directories
235
     *
236
     * @param $source
237
     * @param $target
238
     *
239
     * @return void
240
     * @throws \InvalidArgumentException
241
     */
242
    protected function copy($source, $target)
243
    {
244
        // skip, if not exists
245
        if (!file_exists($source)) {
246
            return;
247
        }
248
        // skip, if target exists
249
        if (is_file($target)) {
250
            $this->installer->getIo()->write(
251
                sprintf('  - File <comment>%s</comment> already exists', $target),
252
                true,
253
                IOInterface::VERBOSE
254
            );
255
            return;
256
        }
257
258
        // Check the renaming of file for direct moving (file-to-file)
259
        $isRenameFile = substr($target, -1) !== '/' && !is_dir($source);
260
261
        if (file_exists($target) && !is_dir($target) && !$isRenameFile) {
262
            throw new \InvalidArgumentException('Destination directory is not a directory');
263
        }
264
265
        try {
266
            if ($isRenameFile) {
267
                $this->getFilesystem()->mkdir(dirname($target));
268
            } else {
269
                $this->getFilesystem()->mkdir($target);
270
            }
271
        } catch (IOException $e) {
272
            throw new \InvalidArgumentException(
273
                sprintf('Could not create directory `%s`', $target)
274
            );
275
        }
276
277
        if (false === file_exists($source)) {
278
            throw new \InvalidArgumentException(
279
                sprintf('Source directory or file `%s` does not exist', $source)
280
            );
281
        }
282
283
        if (is_dir($source)) {
284
            $finder = new Finder;
285
            $finder->files()->in($source);
286
287
            foreach ($finder as $file) {
288
                try {
289
                    $this->getFilesystem()->copy($file, $target . DS . $file->getRelativePathname());
290
                } catch (IOException $e) {
291
                    throw new \InvalidArgumentException(
292
                        sprintf('Could not copy `%s`', $file->getBaseName())
293
                    );
294
                }
295
            }
296
        } else {
297
            try {
298
                if ($isRenameFile) {
299
                    $this->getFilesystem()->copy($source, $target);
300
                } else {
301
                    $this->getFilesystem()->copy($source, $target . '/' . basename($source));
302
                }
303
            } catch (IOException $e) {
304
                throw new \InvalidArgumentException(sprintf('Could not copy `%s`', $source));
305
            }
306
        }
307
308
        $this->installer->getIo()->write(
309
            sprintf('  - Copied file(s) from <comment>%s</comment> to <comment>%s</comment>', $source, $target),
310
            true,
311
            IOInterface::VERBOSE
312
        );
313
    }
314
315
    /**
316
     * It recursively removes the files and empty directories
317
     * @return void
318
     */
319
    protected function removeModule()
320
    {
321
        $this->removeExtras($this->getExtraFiles());
322
323
        foreach (self::DIRECTORIES as $directory) {
324
            $this->remove($directory);
325
        }
326
327
        $this->installer->getIo()->write(
328
            sprintf(
329
                '  - Removed <comment>%s</comment> module from application',
330
                basename($this->installer->getVendorPath())
331
            ),
332
            true
333
        );
334
    }
335
336
    /**
337
     * removeExtras
338
     *
339
     * @param  array $files
340
     *
341
     * @return void
342
     */
343
    protected function removeExtras($files)
344
    {
345
        foreach ($files as $source => $target) {
346
            $this->installer->getIo()->write(
347
                sprintf('  - Skipped additional file(s) <comment>%s</comment>', $target),
348
                true
349
            );
350
        }
351
    }
352
353
    /**
354
     * It recursively removes the files and directories
355
     * @param $directory
356
     * @return void
357
     */
358
    protected function remove($directory)
359
    {
360
        $sourcePath = $this->installer->getVendorPath() . DS . $directory;
361
362
        if (!is_dir($sourcePath)) {
363
            return;
364
        }
365
        foreach ($iterator = new \RecursiveIteratorIterator(
366
            new \RecursiveDirectoryIterator(
367
                $sourcePath,
368
                \RecursiveDirectoryIterator::SKIP_DOTS
369
            ),
370
            \RecursiveIteratorIterator::CHILD_FIRST
371
        ) as $item) {
372
            // path to copied file
373
            $current = PATH_ROOT . DS . $directory . DS . $iterator->getSubPathName();
374
375
            // remove empty directories
376
            if (is_dir($current)) {
377
                if (count(scandir($current, SCANDIR_SORT_ASCENDING)) === 2) {
378
                    rmdir($current);
379
                    $this->installer->getIo()->write(
380
                        "  - Removed directory `{$iterator->getSubPathName()}`",
381
                        true,
382
                        IOInterface::VERBOSE
383
                    );
384
                } else {
385
                    $this->installer->getIo()->write(
386
                        sprintf(
387
                            '  - <comment>Skipped directory `%s`</comment>',
388
                            $directory . DS . $iterator->getSubPathName()
389
                        ),
390
                        true,
391
                        IOInterface::VERBOSE
392
                    );
393
                }
394
                continue;
395
            }
396
397
            // skip already removed files
398
            if (!is_file($current)) {
399
                continue;
400
            }
401
402
            if (md5_file($item) === md5_file($current)) {
403
                // remove file
404
                unlink($current);
405
                $this->installer->getIo()->write(
406
                    "  - Removed file `{$iterator->getSubPathName()}`",
407
                    true,
408
                    IOInterface::VERBOSE
409
                );
410
            } else {
411
                // or skip changed files
412
                $this->installer->getIo()->write(
413
                    "  - <comment>File `{$iterator->getSubPathName()}` has changed</comment>",
414
                    true,
415
                    IOInterface::VERBOSE
416
                );
417
            }
418
        }
419
    }
420
}
421