Completed
Push — 3.0 ( 46e3af...f23b72 )
by Daniel
03:20
created

Plugin::initDeployManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 3
crap 1
1
<?php
2
/**
3
 *
4
 *
5
 *
6
 *
7
 */
8
9
namespace MagentoHackathon\Composer\Magento;
10
11
use Composer\Config;
12
use Composer\Installer;
13
use Composer\Package\AliasPackage;
14
use Composer\Script\Event;
15
use MagentoHackathon\Composer\Helper;
16
use MagentoHackathon\Composer\Magento\Event\EventManager;
17
use MagentoHackathon\Composer\Magento\Event\PackageDeployEvent;
18
use MagentoHackathon\Composer\Magento\Factory\DeploystrategyFactory;
19
use MagentoHackathon\Composer\Magento\Factory\EntryFactory;
20
use MagentoHackathon\Composer\Magento\Factory\ParserFactory;
21
use MagentoHackathon\Composer\Magento\Factory\PathTranslationParserFactory;
22
use MagentoHackathon\Composer\Magento\Patcher\Bootstrap;
23
use MagentoHackathon\Composer\Magento\Repository\InstalledPackageFileSystemRepository;
24
use MagentoHackathon\Composer\Magento\UnInstallStrategy\UnInstallStrategy;
25
use MagentoHackathon\Composer\Magento\Factory\InstallStrategyFactory;
26
use RecursiveDirectoryIterator;
27
use RecursiveIteratorIterator;
28
use Composer\Composer;
29
use Composer\IO\IOInterface;
30
use Composer\Package\PackageInterface;
31
use Composer\Plugin\PluginInterface;
32
use Composer\EventDispatcher\EventSubscriberInterface;
33
use Composer\Script\ScriptEvents;
34
use Composer\Util\Filesystem;
35
use Symfony\Component\Process\Process;
36
37
class Plugin implements PluginInterface, EventSubscriberInterface
38
{
39
    /**
40
     * The type of packages this plugin supports
41
     */
42
    const PACKAGE_TYPE = 'magento-module';
43
44
    const VENDOR_DIR_KEY = 'vendor-dir';
45
46
    const BIN_DIR_KEY = 'bin-dir';
47
48
    const THESEER_AUTOLOAD_EXEC_BIN_PATH = '/phpab';
49
50
    const THESEER_AUTOLOAD_EXEC_REL_PATH = '/theseer/autoload/composer/bin/phpab';
51
52
    /**
53
     * @var IOInterface
54
     */
55
    protected $io;
56
57
    /**
58
     * @var ProjectConfig
59
     */
60
    protected $config;
61
62
    /**
63
     * @var DeployManager
64
     */
65
    protected $deployManager;
66
67
    /**
68
     * @var Composer
69
     */
70
    protected $composer;
71
72
    /**
73
     * @var Filesystem
74
     */
75
    protected $filesystem;
76
77
    /**
78
     * @var EntryFactory
79
     */
80
    protected $entryFactory;
81
82
    /**
83
     * @var EventManager
84
     */
85
    private $eventManager;
86
87
    /**
88
     * @var ModuleManager
89
     */
90
    private $moduleManager;
91
92
    /**
93
     * init the DeployManager
94
     *
95
     * @param Composer    $composer
96
     * @param IOInterface $io
97
     */
98 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...
99
    {
100 6
        $this->deployManager = new DeployManager($eventManager);
101 6
        $this->deployManager->setSortPriority($this->getSortPriority($composer));
102
103 6
        $this->applyEvents($eventManager);
104 6
    }
105
106 6
    protected function applyEvents(EventManager $eventManager)
107
    {
108
109 6
        if ($this->config->hasAutoAppendGitignore()) {
110 1
            $gitIgnoreLocation = sprintf('%s/.gitignore', $this->config->getMagentoRootDir());
111 1
            $gitIgnore = new GitIgnoreListener(new GitIgnore($gitIgnoreLocation));
112
113 1
            $eventManager->listen('post-package-deploy', [$gitIgnore, 'addNewInstalledFiles']);
114 1
            $eventManager->listen('post-package-uninstall', [$gitIgnore, 'removeUnInstalledFiles']);
115 1
        }
116
117 6
        $io = $this->io;
118 6
        if ($this->io->isDebug()) {
119
            $eventManager->listen('pre-package-deploy', function (PackageDeployEvent $event) use ($io) {
120
                $io->write('Start magento deploy for ' . $event->getDeployEntry()->getPackageName());
121 1
            });
122 1
        }
123 6
    }
124
125
    /**
126
     * get Sort Priority from extra Config
127
     *
128
     * @param \Composer\Composer $composer
129
     *
130
     * @return array
131
     */
132 6
    private function getSortPriority(Composer $composer)
133
    {
134 6
        $extra = $composer->getPackage()->getExtra();
135
136 6
        return isset($extra[ProjectConfig::SORT_PRIORITY_KEY])
137 6
            ? $extra[ProjectConfig::SORT_PRIORITY_KEY]
138 6
            : array();
139
    }
140
141
    /**
142
     * Apply plugin modifications to composer
143
     *
144
     * @param Composer    $composer
145
     * @param IOInterface $io
146
     */
147 6
    public function activate(Composer $composer, IOInterface $io)
148
    {
149 6
        $this->io = $io;
150 6
        $this->composer = $composer;
151
152 6
        $this->filesystem = new Filesystem();
153 6
        $this->config = new ProjectConfig($composer->getPackage()->getExtra(), $composer->getConfig()->all());
154
155 6
        if (!$this->config->skipSuggestComposerRepositories()) {
156 5
            $this->suggestComposerRepositories();
157 5
        }
158
159 6
        $this->entryFactory = new EntryFactory(
160 6
            $this->config,
161 6
            new DeploystrategyFactory($this->config),
162 6
            new PathTranslationParserFactory(new ParserFactory($this->config), $this->config)
163 6
        );
164
165 6
        $this->initDeployManager($composer, $io, $this->getEventManager());
166 6
        $this->writeDebug('activate magento plugin');
167 6
    }
168
169
    /**
170
     * Returns an array of event names this subscriber wants to listen to.
171
     *
172
     * The array keys are event names and the value can be:
173
     *
174
     * * The method name to call (priority defaults to 0)
175
     * * An array composed of the method name to call and the priority
176
     * * An array of arrays composed of the method names to call and respective
177
     *   priorities, or 0 if unset
178
     *
179
     * For instance:
180
     *
181
     * * array('eventName' => 'methodName')
182
     * * array('eventName' => array('methodName', $priority))
183
     * * array('eventName' => array(array('methodName1', $priority), array('methodName2'))
184
     *
185
     * @return array The event names to listen to
186
     */
187
    public static function getSubscribedEvents()
188
    {
189
        return array(
190
            ScriptEvents::POST_INSTALL_CMD => array(
191
                array('onNewCodeEvent', 0),
192
            ),
193
            ScriptEvents::POST_UPDATE_CMD  => array(
194
                array('onNewCodeEvent', 0),
195
            ),
196
        );
197
    }
198
199
    /**
200
     * event listener is named this way, as it listens for events leading to changed code files
201
     *
202
     * @param Event $event
203
     */
204 3
    public function onNewCodeEvent(Event $event)
205
    {
206
207 3
        $packageTypeToMatch = static::PACKAGE_TYPE;
208 3
        $magentoModules = array_filter(
209 3
            $this->composer->getRepositoryManager()->getLocalRepository()->getPackages(),
210 3
            function (PackageInterface $package) use ($packageTypeToMatch) {
211 3
                if ($package instanceof AliasPackage) {
212 1
                    return false;
213
                }
214 3
                return $package->getType() === $packageTypeToMatch;
215
            }
216 3
        );
217
218 3
        if ($this->composer->getPackage()->getType() === static::PACKAGE_TYPE
219 3
            && $this->config->getIncludeRootPackage() === true
220 3
        ) {
221 1
            $magentoModules[] = $this->composer->getPackage();
222 1
        }
223
224 3
        $vendorDir = rtrim($this->composer->getConfig()->get(self::VENDOR_DIR_KEY), '/');
225
226 3
        Helper::initMagentoRootDir(
227 3
            $this->config,
228 3
            $this->io,
229 3
            $this->filesystem,
230
            $vendorDir
231 3
        );
232
233 3
        $this->applyEvents($this->getEventManager());
234
235 3
        if (in_array('--redeploy', $event->getArguments())) {
236
            $this->writeDebug('remove all deployed modules');
237
            $this->getModuleManager()->updateInstalledPackages(array());
238
        }
239 3
        $this->writeDebug('start magento module deploy via moduleManager');
240 3
        $this->getModuleManager()->updateInstalledPackages($magentoModules);
241 3
        $this->deployLibraries();
242
243 3
        $patcher = Bootstrap::fromConfig($this->config);
244 3
        $patcher->setIo($this->io);
245
        try {
246 3
            $patcher->patch();
247 3
        } catch (\DomainException $e) {
248 3
            $this->io->write('<comment>'.$e->getMessage().'</comment>');
249
        }
250 3
    }
251
252
    /**
253
     * test configured repositories and give message about adding recommended ones
254
     */
255 5
    protected function suggestComposerRepositories()
256
    {
257 5
        $foundFiregento = false;
258 5
        $foundMagento   = false;
259
260 5
        foreach ($this->config->getComposerRepositories() as $repository) {
261
            if (!isset($repository["type"]) || $repository["type"] !== "composer") {
262
                continue;
263
            }
264
            if (strpos($repository["url"], "packages.firegento.com") !== false) {
265
                $foundFiregento = true;
266
            }
267
            if (strpos($repository["url"], "packages.magento.com") !== false) {
268
                $foundMagento = true;
269
            }
270 5
        };
271 5
        $message1 = "<comment>you may want to add the %s repository to composer.</comment>";
272 5
        $message2 = "<comment>add it with:</comment> composer.phar config -g repositories.%s composer %s";
273 5
        if (!$foundFiregento) {
274 5
            $this->io->write(sprintf($message1, 'packages.firegento.com'));
275 5
            $this->io->write(sprintf($message2, 'firegento', 'https://packages.firegento.com'));
276 5
        }
277 5
        if (!$foundMagento) {
278 5
            $this->io->write(sprintf($message1, 'packages.magento.com'));
279 5
            $this->io->write(sprintf($message2, 'magento', 'https://packages.magento.com'));
280 5
        }
281
282 5
    }
283
284
    /**
285
     * deploy Libraries
286
     */
287 3
    protected function deployLibraries()
288
    {
289 3
        $packages = $this->composer->getRepositoryManager()->getLocalRepository()->getPackages();
290 3
        $autoloadDirectories = array();
291
292 3
        $libraryPath = $this->config->getLibraryPath();
293
294 3
        if ($libraryPath === null) {
295 3
            $this->writeDebug('jump over deployLibraries as no Magento libraryPath is set');
296
297 3
            return;
298
        }
299
300
        $vendorDir = rtrim($this->composer->getConfig()->get(self::VENDOR_DIR_KEY), '/');
301
302
        $this->filesystem->removeDirectory($libraryPath);
303
        $this->filesystem->ensureDirectoryExists($libraryPath);
304
305
        foreach ($packages as $package) {
306
            /** @var PackageInterface $package */
307
            $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...
308
            if ($packageConfig === null) {
309
                continue;
310
            }
311
            if (!isset($packageConfig['autoload'])) {
312
                $packageConfig['autoload'] = array('/');
313
            }
314
            foreach ($packageConfig['autoload'] as $path) {
315
                $autoloadDirectories[] = $libraryPath . '/' . $package->getName() . "/" . $path;
316
            }
317
            $this->writeDebug(sprintf('Magento deployLibraries executed for %s', $package->getName()));
318
319
            $libraryTargetPath = $libraryPath . '/' . $package->getName();
320
            $this->filesystem->removeDirectory($libraryTargetPath);
321
            $this->filesystem->ensureDirectoryExists($libraryTargetPath);
322
            $this->copyRecursive($vendorDir . '/' . $package->getPrettyName(), $libraryTargetPath);
323
        }
324
325
        if (false !== ($executable = $this->getTheseerAutoloadExecutable())) {
326
            $this->writeDebug('Magento deployLibraries executes autoload generator');
327
328
            $params = $this->getTheseerAutoloadParams($libraryPath, $autoloadDirectories);
329
330
            $process = new Process($executable . $params);
331
            $process->run();
332
        }
333
    }
334
335
    /**
336
     * return the autoload generator binary path or false if not found
337
     *
338
     * @return bool|string
339
     */
340
    protected function getTheseerAutoloadExecutable()
341
    {
342
        $executable = $this->composer->getConfig()->get(self::BIN_DIR_KEY)
343
            . self::THESEER_AUTOLOAD_EXEC_BIN_PATH;
344
345
        if (!file_exists($executable)) {
346
            $executable = $this->composer->getConfig()->get(self::VENDOR_DIR_KEY)
347
                . self::THESEER_AUTOLOAD_EXEC_REL_PATH;
348
        }
349
350
        if (!file_exists($executable)) {
351
            $this->writeDebug(
352
                'Magento deployLibraries autoload generator not available, you should require "theseer/autoload"',
353
                $executable
354
            );
355
356
            return false;
357
        }
358
359
        return $executable;
360
    }
361
362
    /**
363
     * get Theseer Autoload Generator Params
364
     *
365
     * @param string $libraryPath
366
     * @param array  $autoloadDirectories
367
     *
368
     * @return string
369
     */
370
    protected function getTheseerAutoloadParams($libraryPath, $autoloadDirectories)
371
    {
372
        // @todo  --blacklist 'test\\\\*'
373
        return " -b {$libraryPath} -o {$libraryPath}/autoload.php  " . implode(' ', $autoloadDirectories);
374
    }
375
376
    /**
377
     * Copy then delete is a non-atomic version of {@link rename}.
378
     *
379
     * Some systems can't rename and also don't have proc_open,
380
     * which requires this solution.
381
     *
382
     * copied from \Composer\Util\Filesystem::copyThenRemove and removed the remove part
383
     *
384
     * @param string $source
385
     * @param string $target
386
     */
387
    protected function copyRecursive($source, $target)
388
    {
389
        $it = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS);
390
        $ri = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::SELF_FIRST);
391
        $this->filesystem->ensureDirectoryExists($target);
392
393
        foreach ($ri as $file) {
394
            $targetPath = $target . DIRECTORY_SEPARATOR . $ri->getSubPathName();
395
            if ($file->isDir()) {
396
                $this->filesystem->ensureDirectoryExists($targetPath);
397
            } else {
398
                copy($file->getPathname(), $targetPath);
399
            }
400
        }
401
    }
402
403
    /**
404
     * print Debug Message
405
     *
406
     * @param $message
407
     */
408 6
    private function writeDebug($message, $varDump = null)
409
    {
410 6
        if ($this->io->isDebug()) {
411 1
            $this->io->write($message);
412
413 1
            if (!is_null($varDump)) {
414
                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...
415
            }
416 1
        }
417 6
    }
418
419
    /**
420
     * @param PackageInterface $package
421
     * @return string
422
     */
423
    public function getPackageInstallPath(PackageInterface $package)
424
    {
425
        $vendorDir = realpath(rtrim($this->composer->getConfig()->get('vendor-dir'), '/'));
426
        return sprintf('%s/%s', $vendorDir, $package->getPrettyName());
427
    }
428
429
    /**
430
     * @return EventManager
431
     */
432
    protected function getEventManager()
433
    {
434
        if (null === $this->eventManager) {
435
            $this->eventManager = new EventManager;
436
        }
437
438
        return $this->eventManager;
439
    }
440
441
    /**
442
     * @return ModuleManager
443
     */
444
    protected function getModuleManager()
445
    {
446
        if (null === $this->moduleManager) {
447
            $this->moduleManager = new ModuleManager(
448
                new InstalledPackageFileSystemRepository(
449
                    rtrim($this->composer->getConfig()->get(self::VENDOR_DIR_KEY), '/') . '/installed.json',
450
                    new InstalledPackageDumper()
451
                ),
452
                $this->getEventManager(),
453
                $this->config,
454
                new UnInstallStrategy($this->filesystem, $this->config->getMagentoRootDir()),
455
                new InstallStrategyFactory($this->config, new ParserFactory($this->config))
456
            );
457
        }
458
459
        return $this->moduleManager;
460
    }
461
}
462