Plugin::suggestComposerRepositories()   B
last analyzed

Complexity

Conditions 6
Paths 8

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.8395

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 10
cts 14
cp 0.7143
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 13
nc 8
nop 0
crap 6.8395
1
<?php
2
/**
3
 *
4
 *
5
 *
6
 *
7
 */
8
9
namespace MagentoHackathon\Composer\Magento;
10
11
use Composer\Config;
12
use Composer\DependencyResolver\Rule;
13
use Composer\Installer;
14
use Composer\Package\AliasPackage;
15
use Composer\Script\Event;
16
use Composer\Installer\PackageEvent;
17
use MagentoHackathon\Composer\Helper;
18
use MagentoHackathon\Composer\Magento\Event\EventManager;
19
use MagentoHackathon\Composer\Magento\Event\PackageDeployEvent;
20
use MagentoHackathon\Composer\Magento\Factory\DeploystrategyFactory;
21
use MagentoHackathon\Composer\Magento\Factory\EntryFactory;
22
use MagentoHackathon\Composer\Magento\Factory\ParserFactory;
23
use MagentoHackathon\Composer\Magento\Factory\PathTranslationParserFactory;
24
use MagentoHackathon\Composer\Magento\Patcher\Bootstrap;
25
use MagentoHackathon\Composer\Magento\Repository\InstalledPackageFileSystemRepository;
26
use MagentoHackathon\Composer\Magento\UnInstallStrategy\UnInstallStrategy;
27
use MagentoHackathon\Composer\Magento\Factory\InstallStrategyFactory;
28
use RecursiveDirectoryIterator;
29
use RecursiveIteratorIterator;
30
use Composer\Composer;
31
use Composer\IO\IOInterface;
32
use Composer\Package\PackageInterface;
33
use Composer\Plugin\PluginInterface;
34
use Composer\EventDispatcher\EventSubscriberInterface;
35
use Composer\Script\ScriptEvents;
36
use Composer\Util\Filesystem;
37
use Symfony\Component\Process\Process;
38
39
class Plugin implements PluginInterface, EventSubscriberInterface
40
{
41
    /**
42
     * The type of packages this plugin supports
43
     */
44
    const PACKAGE_TYPE = 'magento-module';
45
46
    const VENDOR_DIR_KEY = 'vendor-dir';
47
48
    const BIN_DIR_KEY = 'bin-dir';
49
50
    const THESEER_AUTOLOAD_EXEC_BIN_PATH = '/phpab';
51
52
    const THESEER_AUTOLOAD_EXEC_REL_PATH = '/theseer/autoload/composer/bin/phpab';
53
54
    /**
55
     * @var IOInterface
56
     */
57
    protected $io;
58
59
    /**
60
     * @var ProjectConfig
61
     */
62
    protected $config;
63
64
    /**
65
     * @var DeployManager
66
     */
67
    protected $deployManager;
68
69
    /**
70
     * @var Composer
71
     */
72
    protected $composer;
73
74
    /**
75
     * @var Filesystem
76
     */
77
    protected $filesystem;
78
79
    /**
80
     * @var EntryFactory
81
     */
82
    protected $entryFactory;
83
84
    /**
85
     * @var EventManager
86
     */
87
    private $eventManager;
88
89
    /**
90
     * @var ModuleManager
91
     */
92
    private $moduleManager;
93
94
    /**
95
     * init the DeployManager
96
     *
97
     * @param Composer    $composer
98
     * @param IOInterface $io
99
     */
100 6
    protected function initDeployManager(Composer $composer, IOInterface $io, EventManager $eventManager)
0 ignored issues
show
Unused Code introduced by
The parameter $io is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
101
    {
102 6
        $this->deployManager = new DeployManager($eventManager);
103 6
        $this->deployManager->setSortPriority($this->getSortPriority($composer));
104
105 6
        $this->applyEvents($eventManager);
106 6
    }
107
108 6
    protected function applyEvents(EventManager $eventManager)
109
    {
110
111 6
        if ($this->config->hasAutoAppendGitignore()) {
112 1
            $gitIgnoreLocation = sprintf('%s/.gitignore', $this->config->getMagentoRootDir());
113 1
            $gitIgnore = new GitIgnoreListener(new GitIgnore($gitIgnoreLocation));
114
115 1
            $eventManager->listen('post-package-deploy', [$gitIgnore, 'addNewInstalledFiles']);
116 1
            $eventManager->listen('post-package-uninstall', [$gitIgnore, 'removeUnInstalledFiles']);
117
        }
118
119 6
        $io = $this->io;
120 6
        if ($this->io->isDebug()) {
121
            $eventManager->listen('pre-package-deploy', function (PackageDeployEvent $event) use ($io) {
122
                $io->write('Start magento deploy for ' . $event->getDeployEntry()->getPackageName());
123 1
            });
124
        }
125 6
    }
126
127
    /**
128
     * get Sort Priority from extra Config
129
     *
130
     * @param \Composer\Composer $composer
131
     *
132
     * @return array
133
     */
134 6
    private function getSortPriority(Composer $composer)
135
    {
136 6
        $extra = $composer->getPackage()->getExtra();
137
138 6
        return isset($extra[ProjectConfig::SORT_PRIORITY_KEY])
139
            ? $extra[ProjectConfig::SORT_PRIORITY_KEY]
140 6
            : array();
141
    }
142
143
    /**
144
     * Apply plugin modifications to composer
145
     *
146
     * @param Composer    $composer
147
     * @param IOInterface $io
148
     */
149 6
    public function activate(Composer $composer, IOInterface $io)
150
    {
151 6
        $this->io = $io;
152 6
        $this->composer = $composer;
153
154 6
        $this->filesystem = new Filesystem();
155 6
        $this->config = new ProjectConfig($composer->getPackage()->getExtra(), $composer->getConfig()->all());
156
157 6
        if (!$this->config->skipSuggestComposerRepositories()) {
158 5
            $this->suggestComposerRepositories();
159
        }
160
161 6
        $this->entryFactory = new EntryFactory(
162 6
            $this->config,
163 6
            new DeploystrategyFactory($this->config),
164 6
            new PathTranslationParserFactory(new ParserFactory($this->config), $this->config)
165
        );
166
167 6
        $this->initDeployManager($composer, $io, $this->getEventManager());
168 6
        $this->writeDebug('activate magento plugin');
169 6
    }
170
171
    /**
172
     * Returns an array of event names this subscriber wants to listen to.
173
     *
174
     * The array keys are event names and the value can be:
175
     *
176
     * * The method name to call (priority defaults to 0)
177
     * * An array composed of the method name to call and the priority
178
     * * An array of arrays composed of the method names to call and respective
179
     *   priorities, or 0 if unset
180
     *
181
     * For instance:
182
     *
183
     * * array('eventName' => 'methodName')
184
     * * array('eventName' => array('methodName', $priority))
185
     * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
186
     *
187
     * @return array The event names to listen to
188
     */
189
    public static function getSubscribedEvents()
190
    {
191
        return array(
192
            Installer\PackageEvents::PRE_PACKAGE_UPDATE => array(
193
                array('onPackageUpdate', 0),
194
            ),
195
            ScriptEvents::POST_INSTALL_CMD => array(
196
                array('onNewCodeEvent', 0),
197
            ),
198
            ScriptEvents::POST_UPDATE_CMD  => array(
199
                array('onNewCodeEvent', 0),
200
            ),
201
        );
202
    }
203
204
    /**
205
     * event listener is named this way, as it listens for events leading to changed code files
206
     *
207
     * @param Event $event
208
     */
209 3
    public function onNewCodeEvent(Event $event)
210
    {
211
212 3
        $packageTypeToMatch = static::PACKAGE_TYPE;
213 3
        $magentoModules = array_filter(
214 3
            $this->composer->getRepositoryManager()->getLocalRepository()->getPackages(),
215 3
            function (PackageInterface $package) use ($packageTypeToMatch) {
216 3
                if ($package instanceof AliasPackage) {
217 1
                    return false;
218
                }
219 3
                return $package->getType() === $packageTypeToMatch;
220 3
            }
221
        );
222
223 3
        if ($this->composer->getPackage()->getType() === static::PACKAGE_TYPE
224 3
            && $this->config->getIncludeRootPackage() === true
225
        ) {
226 1
            $magentoModules[] = $this->composer->getPackage();
227
        }
228
229 3
        $vendorDir = rtrim($this->composer->getConfig()->get(self::VENDOR_DIR_KEY), '/');
230
231 3
        Helper::initMagentoRootDir(
232 3
            $this->config,
233 3
            $this->io,
234 3
            $this->filesystem,
235
            $vendorDir
236
        );
237
238 3
        $this->applyEvents($this->getEventManager());
239
240 3
        if (in_array('--redeploy', $event->getArguments())) {
241
            $this->writeDebug('remove all deployed modules');
242
            $this->getModuleManager()->updateInstalledPackages(array());
243
        }
244 3
        $this->writeDebug('start magento module deploy via moduleManager');
245 3
        $this->getModuleManager()->updateInstalledPackages($magentoModules);
246 3
        $this->deployLibraries();
247
248 3
        $patcher = Bootstrap::fromConfig($this->config);
249 3
        $patcher->setIo($this->io);
250
        try {
251 3
            $patcher->patch();
252 3
        } catch (\DomainException $e) {
253 3
            $this->io->write('<comment>'.$e->getMessage().'</comment>');
254
        }
255 3
    }
256
257
    public function onPackageUpdate(PackageEvent $event)
258
    {
259
        /** @var Rule $rule */
260
        $rule = $event->getOperation()->getReason();
261
        if ($rule instanceof Rule) {
262
            if ($event->getOperation()->getJobType() === 'update') {
263
                if ($rule->getJob()['packageName'] === 'magento-hackathon/magento-composer-installer') {
264
                    throw new \Exception(
265
                        'Dont update the "magento-hackathon/magento-composer-installer" with active plugins.' . PHP_EOL .
266
                        'Consult the documentation on how to update the Installer' . PHP_EOL .
267
                        'https://github.com/Cotya/magento-composer-installer#update-the-installer' . PHP_EOL
268
                    );
269
                }
270
            }
271
        } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
272
            
273
        }
274
        
275
    }
276
    
277
    /**
278
     * test configured repositories and give message about adding recommended ones
279
     */
280 5
    protected function suggestComposerRepositories()
281
    {
282 5
        $foundFiregento = false;
283 5
        $foundMagento   = false;
0 ignored issues
show
Unused Code introduced by
$foundMagento is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
284
285 5
        foreach ($this->config->getComposerRepositories() as $repository) {
286
            if (!isset($repository["type"]) || $repository["type"] !== "composer") {
287
                continue;
288
            }
289
            if (strpos($repository["url"], "packages.firegento.com") !== false) {
290
                $foundFiregento = true;
291
            }
292
        };
293 5
        $message1 = "<comment>you may want to add the %s repository to composer.</comment>";
294 5
        $message2 = "<comment>add it with:</comment> composer.phar config -g repositories.%s composer %s";
295 5
        if (!$foundFiregento) {
296 5
            $this->io->write(sprintf($message1, 'packages.firegento.com'));
297 5
            $this->io->write(sprintf($message2, 'firegento', 'https://packages.firegento.com'));
298
        }
299 5
    }
300
301
    /**
302
     * deploy Libraries
303
     */
304 3
    protected function deployLibraries()
305
    {
306 3
        $packages = $this->composer->getRepositoryManager()->getLocalRepository()->getPackages();
307 3
        $autoloadDirectories = array();
308
309 3
        $libraryPath = $this->config->getLibraryPath();
310
311 3
        if ($libraryPath === null) {
312 3
            $this->writeDebug('jump over deployLibraries as no Magento libraryPath is set');
313
314 3
            return;
315
        }
316
317
        $vendorDir = rtrim($this->composer->getConfig()->get(self::VENDOR_DIR_KEY), '/');
318
319
        $this->filesystem->removeDirectory($libraryPath);
320
        $this->filesystem->ensureDirectoryExists($libraryPath);
321
322
        foreach ($packages as $package) {
323
            /** @var PackageInterface $package */
324
            $packageConfig = $this->config->getLibraryConfigByPackagename($package->getName());
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $packageConfig is correct as $this->config->getLibrar...me($package->getName()) (which targets MagentoHackathon\Compose...ryConfigByPackagename()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
325
            if ($packageConfig === null) {
326
                continue;
327
            }
328
            if (!isset($packageConfig['autoload'])) {
329
                $packageConfig['autoload'] = array('/');
330
            }
331
            foreach ($packageConfig['autoload'] as $path) {
332
                $autoloadDirectories[] = $libraryPath . '/' . $package->getName() . "/" . $path;
333
            }
334
            $this->writeDebug(sprintf('Magento deployLibraries executed for %s', $package->getName()));
335
336
            $libraryTargetPath = $libraryPath . '/' . $package->getName();
337
            $this->filesystem->removeDirectory($libraryTargetPath);
338
            $this->filesystem->ensureDirectoryExists($libraryTargetPath);
339
            $this->copyRecursive($vendorDir . '/' . $package->getPrettyName(), $libraryTargetPath);
340
        }
341
342
        if (false !== ($executable = $this->getTheseerAutoloadExecutable())) {
343
            $this->writeDebug('Magento deployLibraries executes autoload generator');
344
345
            $params = $this->getTheseerAutoloadParams($libraryPath, $autoloadDirectories);
346
347
            $process = new Process($executable . $params);
348
            $process->run();
349
        }
350
    }
351
352
    /**
353
     * return the autoload generator binary path or false if not found
354
     *
355
     * @return bool|string
356
     */
357
    protected function getTheseerAutoloadExecutable()
358
    {
359
        $executable = $this->composer->getConfig()->get(self::BIN_DIR_KEY)
360
            . self::THESEER_AUTOLOAD_EXEC_BIN_PATH;
361
362
        if (!file_exists($executable)) {
363
            $executable = $this->composer->getConfig()->get(self::VENDOR_DIR_KEY)
364
                . self::THESEER_AUTOLOAD_EXEC_REL_PATH;
365
        }
366
367
        if (!file_exists($executable)) {
368
            $this->writeDebug(
369
                'Magento deployLibraries autoload generator not available, you should require "theseer/autoload"',
370
                $executable
371
            );
372
373
            return false;
374
        }
375
376
        return $executable;
377
    }
378
379
    /**
380
     * get Theseer Autoload Generator Params
381
     *
382
     * @param string $libraryPath
383
     * @param array  $autoloadDirectories
384
     *
385
     * @return string
386
     */
387
    protected function getTheseerAutoloadParams($libraryPath, $autoloadDirectories)
388
    {
389
        // @todo  --blacklist 'test\\\\*'
390
        return " -b {$libraryPath} -o {$libraryPath}/autoload.php  " . implode(' ', $autoloadDirectories);
391
    }
392
393
    /**
394
     * Copy then delete is a non-atomic version of {@link rename}.
395
     *
396
     * Some systems can't rename and also don't have proc_open,
397
     * which requires this solution.
398
     *
399
     * copied from \Composer\Util\Filesystem::copyThenRemove and removed the remove part
400
     *
401
     * @param string $source
402
     * @param string $target
403
     */
404
    protected function copyRecursive($source, $target)
405
    {
406
        $it = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS);
407
        $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::SELF_FIRST);
408
        $this->filesystem->ensureDirectoryExists($target);
409
410
        foreach ($ri as $file) {
411
            $targetPath = $target . DIRECTORY_SEPARATOR . $ri->getSubPathName();
412
            if ($file->isDir()) {
413
                $this->filesystem->ensureDirectoryExists($targetPath);
414
            } else {
415
                copy($file->getPathname(), $targetPath);
416
            }
417
        }
418
    }
419
420
    /**
421
     * print Debug Message
422
     *
423
     * @param $message
424
     */
425 6
    private function writeDebug($message, $varDump = null)
426
    {
427 6
        if ($this->io->isDebug()) {
428 1
            $this->io->write($message);
429
430 1
            if (!is_null($varDump)) {
431
                var_dump($varDump);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($varDump); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
432
            }
433
        }
434 6
    }
435
436
    /**
437
     * @param PackageInterface $package
438
     * @return string
439
     */
440
    public function getPackageInstallPath(PackageInterface $package)
441
    {
442
        $vendorDir = realpath(rtrim($this->composer->getConfig()->get('vendor-dir'), '/'));
443
        return sprintf('%s/%s', $vendorDir, $package->getPrettyName());
444
    }
445
446
    /**
447
     * @return EventManager
448
     */
449
    protected function getEventManager()
450
    {
451
        if (null === $this->eventManager) {
452
            $this->eventManager = new EventManager;
453
        }
454
455
        return $this->eventManager;
456
    }
457
458
    /**
459
     * @return ModuleManager
460
     */
461
    protected function getModuleManager()
462
    {
463
        if (null === $this->moduleManager) {
464
            $this->moduleManager = new ModuleManager(
465
                new InstalledPackageFileSystemRepository(
466
                    rtrim($this->composer->getConfig()->get(self::VENDOR_DIR_KEY), '/') . '/installed.json',
467
                    new InstalledPackageDumper()
468
                ),
469
                $this->getEventManager(),
470
                $this->config,
471
                new UnInstallStrategy($this->filesystem, $this->config->getMagentoRootDir()),
472
                new InstallStrategyFactory($this->config, new ParserFactory($this->config))
473
            );
474
        }
475
476
        return $this->moduleManager;
477
    }
478
}
479