Completed
Push — 3.0 ( f23b72...fb09f5 )
by Daniel
04:42
created

Plugin::suggestComposerRepositories()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 20
ccs 0
cts 19
cp 0
rs 8.8571
cc 6
eloc 13
nc 8
nop 0
crap 42
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\Script\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
    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
        $this->deployManager = new DeployManager($eventManager);
103
        $this->deployManager->setSortPriority($this->getSortPriority($composer));
104
105
        $this->applyEvents($eventManager);
106
    }
107
108
    protected function applyEvents(EventManager $eventManager)
109
    {
110
111
        if ($this->config->hasAutoAppendGitignore()) {
112
            $gitIgnoreLocation = sprintf('%s/.gitignore', $this->config->getMagentoRootDir());
113
            $gitIgnore = new GitIgnoreListener(new GitIgnore($gitIgnoreLocation));
114
115
            $eventManager->listen('post-package-deploy', [$gitIgnore, 'addNewInstalledFiles']);
116
            $eventManager->listen('post-package-uninstall', [$gitIgnore, 'removeUnInstalledFiles']);
117
        }
118
119
        $io = $this->io;
120
        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
            });
124
        }
125
    }
126
127
    /**
128
     * get Sort Priority from extra Config
129
     *
130
     * @param \Composer\Composer $composer
131
     *
132
     * @return array
133
     */
134
    private function getSortPriority(Composer $composer)
135
    {
136
        $extra = $composer->getPackage()->getExtra();
137
138
        return isset($extra[ProjectConfig::SORT_PRIORITY_KEY])
139
            ? $extra[ProjectConfig::SORT_PRIORITY_KEY]
140
            : array();
141
    }
142
143
    /**
144
     * Apply plugin modifications to composer
145
     *
146
     * @param Composer    $composer
147
     * @param IOInterface $io
148
     */
149
    public function activate(Composer $composer, IOInterface $io)
150
    {
151
        $this->io = $io;
152
        $this->composer = $composer;
153
154
        $this->filesystem = new Filesystem();
155
        $this->config = new ProjectConfig($composer->getPackage()->getExtra(), $composer->getConfig()->all());
156
157
        if (!$this->config->skipSuggestComposerRepositories()) {
158
            $this->suggestComposerRepositories();
159
        }
160
161
        $this->entryFactory = new EntryFactory(
162
            $this->config,
163
            new DeploystrategyFactory($this->config),
164
            new PathTranslationParserFactory(new ParserFactory($this->config), $this->config)
165
        );
166
167
        $this->initDeployManager($composer, $io, $this->getEventManager());
168
        $this->writeDebug('activate magento plugin');
169
    }
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
    public function onNewCodeEvent(Event $event)
210
    {
211
212
        $packageTypeToMatch = static::PACKAGE_TYPE;
213
        $magentoModules = array_filter(
214
            $this->composer->getRepositoryManager()->getLocalRepository()->getPackages(),
215
            function (PackageInterface $package) use ($packageTypeToMatch) {
216
                if ($package instanceof AliasPackage) {
217
                    return false;
218
                }
219
                return $package->getType() === $packageTypeToMatch;
220
            }
221
        );
222
223
        if ($this->composer->getPackage()->getType() === static::PACKAGE_TYPE
224
            && $this->config->getIncludeRootPackage() === true
225
        ) {
226
            $magentoModules[] = $this->composer->getPackage();
227
        }
228
229
        $vendorDir = rtrim($this->composer->getConfig()->get(self::VENDOR_DIR_KEY), '/');
230
231
        Helper::initMagentoRootDir(
232
            $this->config,
233
            $this->io,
234
            $this->filesystem,
235
            $vendorDir
236
        );
237
238
        $this->applyEvents($this->getEventManager());
239
240
        if (in_array('--redeploy', $event->getArguments())) {
241
            $this->writeDebug('remove all deployed modules');
242
            $this->getModuleManager()->updateInstalledPackages(array());
243
        }
244
        $this->writeDebug('start magento module deploy via moduleManager');
245
        $this->getModuleManager()->updateInstalledPackages($magentoModules);
246
        $this->deployLibraries();
247
248
        $patcher = Bootstrap::fromConfig($this->config);
249
        $patcher->setIo($this->io);
250
        try {
251
            $patcher->patch();
252
        } catch (\DomainException $e) {
253
            $this->io->write('<comment>'.$e->getMessage().'</comment>');
254
        }
255
    }
256
257
    public function onPackageUpdate(PackageEvent $event)
258
    {
259
        /** @var Rule $rule */
260
        $rule = $event->getOperation()->getReason();
261
        if ($event->getOperation()->getJobType() === 'update') {
262
            if ($rule->getJob()['packageName'] === 'magento-hackathon/magento-composer-installer') {
263
                throw new \Exception(
264
                    'Dont update the "magento-hackathon/magento-composer-installer" with active plugins.' . PHP_EOL .
265
                    'Consult the documentation on how to update the Installer' . PHP_EOL .
266
                    'https://github.com/Cotya/magento-composer-installer#update-the-installer' . PHP_EOL
267
                );
268
            }
269
        }
270
        
271
    }
272
    
273
    /**
274
     * test configured repositories and give message about adding recommended ones
275
     */
276
    protected function suggestComposerRepositories()
277
    {
278
        $foundFiregento = false;
279
        $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...
280
281
        foreach ($this->config->getComposerRepositories() as $repository) {
282
            if (!isset($repository["type"]) || $repository["type"] !== "composer") {
283
                continue;
284
            }
285
            if (strpos($repository["url"], "packages.firegento.com") !== false) {
286
                $foundFiregento = true;
287
            }
288
        };
289
        $message1 = "<comment>you may want to add the %s repository to composer.</comment>";
290
        $message2 = "<comment>add it with:</comment> composer.phar config -g repositories.%s composer %s";
291
        if (!$foundFiregento) {
292
            $this->io->write(sprintf($message1, 'packages.firegento.com'));
293
            $this->io->write(sprintf($message2, 'firegento', 'https://packages.firegento.com'));
294
        }
295
    }
296
297
    /**
298
     * deploy Libraries
299
     */
300
    protected function deployLibraries()
301
    {
302
        $packages = $this->composer->getRepositoryManager()->getLocalRepository()->getPackages();
303
        $autoloadDirectories = array();
304
305
        $libraryPath = $this->config->getLibraryPath();
306
307
        if ($libraryPath === null) {
308
            $this->writeDebug('jump over deployLibraries as no Magento libraryPath is set');
309
310
            return;
311
        }
312
313
        $vendorDir = rtrim($this->composer->getConfig()->get(self::VENDOR_DIR_KEY), '/');
314
315
        $this->filesystem->removeDirectory($libraryPath);
316
        $this->filesystem->ensureDirectoryExists($libraryPath);
317
318
        foreach ($packages as $package) {
319
            /** @var PackageInterface $package */
320
            $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...
321
            if ($packageConfig === null) {
322
                continue;
323
            }
324
            if (!isset($packageConfig['autoload'])) {
325
                $packageConfig['autoload'] = array('/');
326
            }
327
            foreach ($packageConfig['autoload'] as $path) {
328
                $autoloadDirectories[] = $libraryPath . '/' . $package->getName() . "/" . $path;
329
            }
330
            $this->writeDebug(sprintf('Magento deployLibraries executed for %s', $package->getName()));
331
332
            $libraryTargetPath = $libraryPath . '/' . $package->getName();
333
            $this->filesystem->removeDirectory($libraryTargetPath);
334
            $this->filesystem->ensureDirectoryExists($libraryTargetPath);
335
            $this->copyRecursive($vendorDir . '/' . $package->getPrettyName(), $libraryTargetPath);
336
        }
337
338
        if (false !== ($executable = $this->getTheseerAutoloadExecutable())) {
339
            $this->writeDebug('Magento deployLibraries executes autoload generator');
340
341
            $params = $this->getTheseerAutoloadParams($libraryPath, $autoloadDirectories);
342
343
            $process = new Process($executable . $params);
344
            $process->run();
345
        }
346
    }
347
348
    /**
349
     * return the autoload generator binary path or false if not found
350
     *
351
     * @return bool|string
352
     */
353
    protected function getTheseerAutoloadExecutable()
354
    {
355
        $executable = $this->composer->getConfig()->get(self::BIN_DIR_KEY)
356
            . self::THESEER_AUTOLOAD_EXEC_BIN_PATH;
357
358
        if (!file_exists($executable)) {
359
            $executable = $this->composer->getConfig()->get(self::VENDOR_DIR_KEY)
360
                . self::THESEER_AUTOLOAD_EXEC_REL_PATH;
361
        }
362
363
        if (!file_exists($executable)) {
364
            $this->writeDebug(
365
                'Magento deployLibraries autoload generator not available, you should require "theseer/autoload"',
366
                $executable
367
            );
368
369
            return false;
370
        }
371
372
        return $executable;
373
    }
374
375
    /**
376
     * get Theseer Autoload Generator Params
377
     *
378
     * @param string $libraryPath
379
     * @param array  $autoloadDirectories
380
     *
381
     * @return string
382
     */
383
    protected function getTheseerAutoloadParams($libraryPath, $autoloadDirectories)
384
    {
385
        // @todo  --blacklist 'test\\\\*'
386
        return " -b {$libraryPath} -o {$libraryPath}/autoload.php  " . implode(' ', $autoloadDirectories);
387
    }
388
389
    /**
390
     * Copy then delete is a non-atomic version of {@link rename}.
391
     *
392
     * Some systems can't rename and also don't have proc_open,
393
     * which requires this solution.
394
     *
395
     * copied from \Composer\Util\Filesystem::copyThenRemove and removed the remove part
396
     *
397
     * @param string $source
398
     * @param string $target
399
     */
400
    protected function copyRecursive($source, $target)
401
    {
402
        $it = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS);
403
        $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::SELF_FIRST);
404
        $this->filesystem->ensureDirectoryExists($target);
405
406
        foreach ($ri as $file) {
407
            $targetPath = $target . DIRECTORY_SEPARATOR . $ri->getSubPathName();
408
            if ($file->isDir()) {
409
                $this->filesystem->ensureDirectoryExists($targetPath);
410
            } else {
411
                copy($file->getPathname(), $targetPath);
412
            }
413
        }
414
    }
415
416
    /**
417
     * print Debug Message
418
     *
419
     * @param $message
420
     */
421
    private function writeDebug($message, $varDump = null)
422
    {
423
        if ($this->io->isDebug()) {
424
            $this->io->write($message);
425
426
            if (!is_null($varDump)) {
427
                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...
428
            }
429
        }
430
    }
431
432
    /**
433
     * @param PackageInterface $package
434
     * @return string
435
     */
436
    public function getPackageInstallPath(PackageInterface $package)
437
    {
438
        $vendorDir = realpath(rtrim($this->composer->getConfig()->get('vendor-dir'), '/'));
439
        return sprintf('%s/%s', $vendorDir, $package->getPrettyName());
440
    }
441
442
    /**
443
     * @return EventManager
444
     */
445
    protected function getEventManager()
446
    {
447
        if (null === $this->eventManager) {
448
            $this->eventManager = new EventManager;
449
        }
450
451
        return $this->eventManager;
452
    }
453
454
    /**
455
     * @return ModuleManager
456
     */
457
    protected function getModuleManager()
458
    {
459
        if (null === $this->moduleManager) {
460
            $this->moduleManager = new ModuleManager(
461
                new InstalledPackageFileSystemRepository(
462
                    rtrim($this->composer->getConfig()->get(self::VENDOR_DIR_KEY), '/') . '/installed.json',
463
                    new InstalledPackageDumper()
464
                ),
465
                $this->getEventManager(),
466
                $this->config,
467
                new UnInstallStrategy($this->filesystem, $this->config->getMagentoRootDir()),
468
                new InstallStrategyFactory($this->config, new ParserFactory($this->config))
469
            );
470
        }
471
472
        return $this->moduleManager;
473
    }
474
}
475