Passed
Push — master ( fba68d...d13eac )
by Anton
14:18 queued 07:08
created

Plugin::remove()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 60
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 39
c 2
b 0
f 0
dl 0
loc 60
rs 8.3626
cc 7
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
/**
4
 * Bluz composer plugin
5
 *
6
 * @copyright Bluz PHP Team
7
 * @link https://github.com/bluzphp/composer-plugin
8
 */
9
10
/**
11
 * @namespace
12
 */
13
namespace Bluz\Composer\Installers;
14
15
use Composer\Composer;
16
use Composer\DependencyResolver\Operation\UpdateOperation;
17
use Composer\EventDispatcher\EventSubscriberInterface;
18
use Composer\Installer\PackageEvent;
19
use Composer\Installer\PackageEvents;
20
use Composer\IO\IOInterface;
21
use Composer\Package\PackageInterface;
22
use Composer\Plugin\PluginInterface;
23
use Composer\Script\Event;
24
use Composer\Script\ScriptEvents;
25
use Symfony\Component\Filesystem\Exception\IOException;
26
use Symfony\Component\Filesystem\Filesystem;
27
use Symfony\Component\Finder\Finder;
28
29
/**
30
 * Class Plugin
31
 *
32
 * @package Bluz\Composer\Installers
33
 */
34
class Plugin implements PluginInterface, EventSubscriberInterface
35
{
36
    public const PERMISSION_CODE = 0755;
37
    public const REPEAT = 5;
38
    public const DIRECTORIES = [
39
        'application',
40
        'data',
41
        'public',
42
        'tests'
43
    ];
44
45
    /**
46
     * @var Installer
47
     */
48
    protected $installer;
49
50
    /**
51
     * @var string
52
     */
53
    protected $vendorPath;
54
55
    /**
56
     * @var string
57
     */
58
    protected $packagePath;
59
60
    /**
61
     * @var string
62
     */
63
    protected $environment;
64
65
    /**
66
     * @var Filesystem
67
     */
68
    protected $filesystem;
69
70
    /**
71
     * Create instance, define constants
72
     */
73
    public function __construct()
74
    {
75
        \defined('PATH_ROOT') ?: \define('PATH_ROOT', realpath($_SERVER['DOCUMENT_ROOT']));
76
        \defined('DS') ?: \define('DS', DIRECTORY_SEPARATOR);
77
    }
78
79
    /**
80
     * Called after the plugin is loaded
81
     *
82
     * It setup composer installer
83
     *
84
     * {@inheritDoc}
85
     */
86
    public function activate(Composer $composer, IOInterface $io): void
87
    {
88
        $this->installer = new Installer($io, $composer);
89
        $this->vendorPath = $composer->getConfig()->get('vendor-dir');
90
        $composer->getInstallationManager()->addInstaller($this->installer);
91
    }
92
93
    /**
94
     * Registered events after the plugin is loaded
95
     *
96
     * {@inheritDoc}
97
     */
98
    public static function getSubscribedEvents(): array
99
    {
100
        return [
101
            // copy extra files from root composer.json
102
            // do it only once after create project
103
            ScriptEvents::POST_CREATE_PROJECT_CMD => 'copyProjectExtraFiles',
104
            // copy module's files to working directory
105
            PackageEvents::POST_PACKAGE_INSTALL => 'copyModuleFiles',
106
            PackageEvents::POST_PACKAGE_UPDATE => 'copyModuleFiles',
107
            // removed unchanged module's files
108
            PackageEvents::PRE_PACKAGE_UPDATE => 'removeModuleFiles',
109
            PackageEvents::PRE_PACKAGE_UNINSTALL => 'removeModuleFiles',
110
        ];
111
    }
112
113
    /**
114
     * extractPackage
115
     *
116
     * @param PackageEvent $event
117
     *
118
     * @return PackageInterface
119
     */
120
    protected function extractPackage(PackageEvent $event): PackageInterface
121
    {
122
        if ($event->getOperation() instanceof UpdateOperation) {
123
            return $event->getOperation()->getTargetPackage();
124
        }
125
        return $event->getOperation()->getPackage();
0 ignored issues
show
Bug introduced by
The method getPackage() does not exist on Composer\DependencyResol...tion\OperationInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Composer\DependencyResol...eration\UpdateOperation or Composer\DependencyResol...eration\SolverOperation or Composer\DependencyResol...eration\UpdateOperation. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
        return $event->getOperation()->/** @scrutinizer ignore-call */ getPackage();
Loading history...
126
    }
127
128
    /**
129
     * Copy extra files from compose.json of project
130
     *
131
     * @param Event $event
132
     *
133
     * @return void
134
     * @throws \InvalidArgumentException
135
     */
136
    public function copyProjectExtraFiles(Event $event): void
137
    {
138
        $extras = $event->getComposer()->getPackage()->getExtra();
139
        if (array_key_exists('copy-files', $extras)) {
140
            $this->installer->getIo()->write(
141
                sprintf('  - Copied additional file(s)'),
142
                true
143
            );
144
            $this->copyExtras($extras['copy-files']);
145
        }
146
    }
147
148
    /**
149
     * Hook which is called after install package
150
     * It copies bluz module
151
     *
152
     * @param PackageEvent $event
153
     *
154
     * @throws \InvalidArgumentException
155
     */
156
    public function copyModuleFiles(PackageEvent $event): void
157
    {
158
        $package = $this->extractPackage($event);
159
        $this->packagePath = $this->vendorPath . DS . $package->getName();
160
        if ($package->getType() === 'bluz-module' && file_exists($this->packagePath)) {
161
            if ($package->getExtra() && isset($package->getExtra()['copy-files'])) {
162
                $this->copyExtras($package->getExtra()['copy-files']);
163
            }
164
            $this->copyModule();
165
        }
166
    }
167
168
    /**
169
     * Hook which is called before update package
170
     * It checks bluz module
171
     *
172
     * @param PackageEvent $event
173
     */
174
    public function removeModuleFiles(PackageEvent $event): void
175
    {
176
        $package = $this->extractPackage($event);
177
        $this->packagePath = $this->vendorPath . DS . $package->getName();
178
        if ($package->getType() === 'bluz-module' && file_exists($this->packagePath)) {
179
            if ($package->getExtra() && isset($package->getExtra()['copy-files'])) {
180
                $this->removeExtras($package->getExtra()['copy-files']);
181
            }
182
            $this->removeModule();
183
        }
184
    }
185
186
    /**
187
     * Get Filesystem
188
     *
189
     * @return Filesystem
190
     */
191
    protected function getFilesystem(): Filesystem
192
    {
193
        if (!$this->filesystem) {
194
            $this->filesystem = new Filesystem();
195
        }
196
        return $this->filesystem;
197
    }
198
199
    /**
200
     * Copy Module files
201
     *
202
     * @return void
203
     * @throws \InvalidArgumentException
204
     */
205
    protected function copyModule(): void
206
    {
207
        foreach (self::DIRECTORIES as $directory) {
208
            $this->copy(
209
                $this->packagePath . DS . $directory . DS,
210
                PATH_ROOT . DS . $directory . DS
211
            );
212
        }
213
214
        $this->installer->getIo()->write(
215
            sprintf(
216
                '  - Copied <comment>%s</comment> module to application',
217
                basename($this->packagePath)
218
            ),
219
            true
220
        );
221
    }
222
223
    /**
224
     * copyExtras
225
     *
226
     * @param array $files
227
     *
228
     * @return void
229
     * @throws \InvalidArgumentException
230
     */
231
    protected function copyExtras(array $files): void
232
    {
233
        foreach ($files as $source => $target) {
234
            $this->copy(
235
                $this->vendorPath . DS . $source,
236
                PATH_ROOT . DS . $target
237
            );
238
        }
239
    }
240
241
    /**
242
     * It recursively copies the files and directories
243
     *
244
     * @param string $source
245
     * @param string $target
246
     *
247
     * @return void
248
     * @throws \InvalidArgumentException
249
     */
250
    protected function copy(string $source, string $target): void
251
    {
252
        // skip, if not exists
253
        if (!file_exists($source)) {
254
            return;
255
        }
256
        // skip, if target exists
257
        if (is_file($target) && !is_dir($target)) {
258
            $this->installer->getIo()->write(
259
                sprintf('  - File <comment>%s</comment> already exists', $target),
260
                true,
261
                IOInterface::VERBOSE
262
            );
263
            return;
264
        }
265
266
        // Check the renaming of file for direct moving (file-to-file)
267
        $isRenameFile = substr($target, -1) !== '/' && !is_dir($source);
268
269
        if (file_exists($target) && !is_dir($target) && !$isRenameFile) {
270
            throw new \InvalidArgumentException('Destination directory is not a directory');
271
        }
272
273
        try {
274
            if ($isRenameFile) {
275
                $this->getFilesystem()->mkdir(\dirname($target));
276
            } else {
277
                $this->getFilesystem()->mkdir($target);
278
            }
279
        } catch (IOException $e) {
280
            throw new \InvalidArgumentException(
281
                sprintf('Could not create directory `%s`', $target)
282
            );
283
        }
284
285
        if (false === file_exists($source)) {
286
            throw new \InvalidArgumentException(
287
                sprintf('Source directory or file `%s` does not exist', $source)
288
            );
289
        }
290
291
        if (is_dir($source)) {
292
            $finder = new Finder();
293
            $finder->files()->in($source);
294
295
            foreach ($finder as $file) {
296
                try {
297
                    $this->getFilesystem()->copy($file, $target . DS . $file->getRelativePathname());
298
                } catch (IOException $e) {
299
                    throw new \InvalidArgumentException(
300
                        sprintf('Could not copy `%s`', $file->getBaseName())
301
                    );
302
                }
303
            }
304
        } else {
305
            try {
306
                if ($isRenameFile) {
307
                    $this->getFilesystem()->copy($source, $target);
308
                } else {
309
                    $this->getFilesystem()->copy($source, $target . '/' . basename($source));
310
                }
311
            } catch (IOException $e) {
312
                throw new \InvalidArgumentException(sprintf('Could not copy `%s`', $source));
313
            }
314
        }
315
316
        $this->installer->getIo()->write(
317
            sprintf('  - Copied file(s) from <comment>%s</comment> to <comment>%s</comment>', $source, $target),
318
            true,
319
            IOInterface::VERBOSE
320
        );
321
    }
322
323
    /**
324
     * It recursively removes the files and empty directories
325
     * @return void
326
     */
327
    protected function removeModule(): void
328
    {
329
        foreach (self::DIRECTORIES as $directory) {
330
            $this->remove($directory);
331
        }
332
333
        $this->installer->getIo()->write(
334
            sprintf(
335
                '  - Removed <comment>%s</comment> module from application',
336
                basename($this->packagePath)
337
            ),
338
            true
339
        );
340
    }
341
342
    /**
343
     * removeExtras
344
     *
345
     * @param array $files
346
     *
347
     * @return void
348
     */
349
    protected function removeExtras(array $files): void
350
    {
351
        foreach ($files as $source => $target) {
352
            $this->installer->getIo()->write(
353
                sprintf('  - Skipped additional file(s) <comment>%s</comment>', $target),
354
                true
355
            );
356
        }
357
    }
358
359
    /**
360
     * It recursively removes the files and directories
361
     * @param $directory
362
     * @return void
363
     */
364
    protected function remove($directory): void
365
    {
366
        $sourcePath = $this->packagePath . DS . $directory;
367
368
        if (!is_dir($sourcePath)) {
369
            return;
370
        }
371
        foreach (
372
            $iterator = new \RecursiveIteratorIterator(
373
                new \RecursiveDirectoryIterator(
374
                    $sourcePath,
375
                    \RecursiveDirectoryIterator::SKIP_DOTS
376
                ),
377
                \RecursiveIteratorIterator::CHILD_FIRST
378
            ) as $item
379
        ) {
380
            // path to copied file
381
            $current = PATH_ROOT . DS . $directory . DS . $iterator->getSubPathName();
0 ignored issues
show
Bug introduced by
The method getSubPathName() does not exist on RecursiveIteratorIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

381
            $current = PATH_ROOT . DS . $directory . DS . $iterator->/** @scrutinizer ignore-call */ getSubPathName();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
382
383
            // remove empty directories
384
            if (is_dir($current)) {
385
                if (\count(scandir($current, SCANDIR_SORT_ASCENDING)) === 2) {
386
                    rmdir($current);
387
                    $this->installer->getIo()->write(
388
                        "  - Removed directory `{$iterator->getSubPathName()}`",
389
                        true,
390
                        IOInterface::VERBOSE
391
                    );
392
                } else {
393
                    $this->installer->getIo()->write(
394
                        sprintf(
395
                            '  - <comment>Skipped directory `%s`</comment>',
396
                            $directory . DS . $iterator->getSubPathName()
397
                        ),
398
                        true,
399
                        IOInterface::VERBOSE
400
                    );
401
                }
402
                continue;
403
            }
404
405
            // skip already removed files
406
            if (!is_file($current)) {
407
                continue;
408
            }
409
410
            if (md5_file($item) === md5_file($current)) {
411
                // remove file
412
                unlink($current);
413
                $this->installer->getIo()->write(
414
                    "  - Removed file `{$iterator->getSubPathName()}`",
415
                    true,
416
                    IOInterface::VERBOSE
417
                );
418
            } else {
419
                // or skip changed files
420
                $this->installer->getIo()->write(
421
                    "  - <comment>File `{$iterator->getSubPathName()}` has changed</comment>",
422
                    true,
423
                    IOInterface::VERBOSE
424
                );
425
            }
426
        }
427
    }
428
429
    public function deactivate(Composer $composer, IOInterface $io)
430
    {
431
        // TODO: Implement deactivate() method.
432
    }
433
434
    public function uninstall(Composer $composer, IOInterface $io)
435
    {
436
        // TODO: Implement uninstall() method.
437
    }
438
}
439