bragento/bragento-composer-installer

View on GitHub
src/Deploy/Manager.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php
/**
 * Manager.php
 *
 * PHP Version 5
 *
 * @category  Bragento_MagentoComposerInstaller
 * @package   Bragento\Magento\Composer\Installer\Deploy
 * @author    David Verholen <david.verholen@brandung.de>
 * @copyright 2014 Brandung GmbH & Co. KG
 * @license   http://opensource.org/licenses/OSL-3.0 OSL-3.0
 * @link      http://www.brandung.de
 */

namespace Bragento\Magento\Composer\Installer\Deploy;

use Bragento\Magento\Composer\Installer\Deploy\Manager\Actions;
use Bragento\Magento\Composer\Installer\Deploy\Manager\Entry;
use Bragento\Magento\Composer\Installer\Deploy\Manager\PackageTypes;
use Bragento\Magento\Composer\Installer\Deploy\Strategy\AbstractStrategy;
use Bragento\Magento\Composer\Installer\Deploy\Strategy\Factory;
use Bragento\Magento\Composer\Installer\Exception\NotInitializedException;
use Bragento\Magento\Composer\Installer\Project\Config;
use Bragento\Magento\Composer\Installer\Util\Filesystem;
use Composer\Composer;
use Composer\DependencyResolver\Operation\UninstallOperation;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Installer\PackageEvents;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\Script\Event;
use Symfony\Component\Finder\SplFileInfo;

/**
 * Class Manager
 *
 * @category  Bragento_MagentoComposerInstaller
 * @package   Bragento\Magento\Composer\Installer\Deploy
 * @author    David Verholen <david.verholen@brandung.de>
 * @copyright 2014 Brandung GmbH & Co. KG
 * @license   http://opensource.org/licenses/OSL-3.0 OSL-3.0
 * @link      http://www.brandung.de
 */
class Manager implements EventSubscriberInterface
{
    /**
     * instance
     *
     * @var Manager
     */
    protected static $instance;

    /**
     * _composer
     *
     * @var Composer
     */
    protected static $composer;

    /**
     * @var IOInterface
     */
    protected static $io;

    /**
     * entries
     *
     * @var Entry[]
     */
    protected $moduleEntries;

    /**
     * entries
     *
     * @var Entry[]
     */
    protected $themeEntries;

    /**
     * _coreEntry
     *
     * @var Entry
     */
    protected $coreEntry;

    /**
     * @var PackageInterface[]
     */
    protected $deployedPackages;

    /**
     * _fs
     *
     * @var Filesystem
     */
    protected $fs;

    /**
     * private construct for singleton
     */
    private function __construct()
    {
        $this->moduleEntries = array();
        $this->themeEntries = array();
        $this->deployedPackages = array();
    }

    /**
     * init
     *
     * @param Composer    $composer
     * @param IOInterface $io
     *
     * @return void
     */
    public static function init(Composer $composer, IOInterface $io)
    {
        self::$composer = $composer;
        self::$io = $io;
    }

    /**
     * getInstance
     *
     * @return Manager
     */
    public static function getInstance()
    {
        if (null === self::$instance) {
            self::$instance = new Manager();
        }

        return self::$instance;
    }

    /**
     * Returns an array of event names this subscriber wants to listen to.
     *
     * The array keys are event names and the value can be:
     *
     * * The method name to call (priority defaults to 0)
     * * An array composed of the method name to call and the priority
     * * An array of arrays composed of the method names to call and respective
     *   priorities, or 0 if unset
     *
     * For instance:
     *
     * * array('eventName' => 'methodName')
     * * array('eventName' => array('methodName', $priority))
     *
     * @return array The event names to listen to
     */
    public static function getSubscribedEvents()
    {
        return array(
            PackageEvents::POST_PACKAGE_UNINSTALL => 'onPostPackageUninstall',
        );
    }

    /**
     * doDeploy
     *
     * deploy core, then modules, then themes
     *
     * @return void
     */
    public function doDeploy()
    {
        $this->addAllPackages();
        $this->dispatchEvent(Events::PRE_DEPLOY);
        if (null !== $this->coreEntry) {
            $this->coreEntry = $this->deployEntry($this->coreEntry);
        }
        $this->moduleEntries = $this->deployEntriesArray($this->moduleEntries);
        $this->themeEntries = $this->deployEntriesArray($this->themeEntries);
        $this->dispatchEvent(Events::POST_DEPLOY);
    }

    /**
     * addAllPackages
     *
     * @return void
     */
    protected function addAllPackages()
    {
        foreach ($this->getPackages() as $package) {
            if (!in_array($package->getType(), $this->supports())) {
                continue;
            }

            $action = $this->isUndeployedPackage($package)
                ? Actions::INSTALL
                : Actions::UPDATE;

            $this->addEntry(
                $this->getDeployManagerEntry(
                    $package,
                    $action
                )
            );
        }
    }

    /**
     * getPackages
     *
     * @return PackageInterface[]
     */
    protected function getPackages()
    {
        return $this->getComposer()
            ->getRepositoryManager()
            ->getLocalRepository()
            ->getCanonicalPackages();
    }

    /**
     * getComposer
     *
     * @return Composer
     * @throws NotInitializedException
     */
    protected function getComposer()
    {
        if (null === self::$composer) {
            throw new NotInitializedException($this);
        }

        return self::$composer;
    }

    /**
     * supports
     *
     * @return array
     */
    public function supports()
    {
        return array(
            PackageTypes::MAGENTO_CORE,
            PackageTypes::MAGENTO_MODULE,
            PackageTypes::MAGENTO_THEME
        );
    }

    /**
     * isUndeployedPackage
     *
     * @param PackageInterface $package
     *
     * @return bool
     */
    public function isUndeployedPackage(PackageInterface $package)
    {
        return false === $this->getFs()
            ->exists($this->getStateFilePath($package));
    }

    /**
     * getFs
     *
     * @return Filesystem
     */
    protected function getFs()
    {
        if (null == $this->fs) {
            $this->fs = new Filesystem();
        }

        return $this->fs;
    }

    /**
     * getStateFilePath
     *
     * @param PackageInterface $package
     *
     * @return string
     */
    public function getStateFilePath(PackageInterface $package)
    {
        return $this->getFs()->joinFileUris(
            $this->getStateDir(),
            str_replace('/', '_', $package->getName())
        );
    }

    /**
     * getStateDir
     *
     * @return string
     */
    public function getStateDir()
    {
        return $this->getFs()->joinFileUris(
            Config::getInstance()->getVendorDir(),
            State::STATE_DIR
        );
    }

    /**
     * addEntry
     *
     * only the first package with type
     * magento-core will be added
     *
     * @param Entry $entry the Entry to add
     *
     * @return void
     */
    public function addEntry(Entry $entry)
    {
        switch ($entry->getDeployStrategy()->getPackage()->getType()) {
            case PackageTypes::MAGENTO_CORE:
                $this->coreEntry = $entry;
                break;

            case PackageTypes::MAGENTO_MODULE:
                $this->moduleEntries[] = $entry;
                break;

            case PackageTypes::MAGENTO_THEME:
                $this->themeEntries[] = $entry;
                break;

            default:
        }
    }

    /**
     * getDeployManagerEntry
     *
     * @param PackageInterface $package package to deploy
     * @param string           $action
     *
     * @return Entry
     */
    protected function getDeployManagerEntry(PackageInterface $package, $action)
    {
        return new Entry(
            $this->getDeployStrategy($package, $action)
        );
    }

    /**
     * getDeployStrategy
     *
     * @param PackageInterface $package
     * @param string           $action
     *
     * @return AbstractStrategy
     */
    protected function getDeployStrategy(PackageInterface $package, $action)
    {
        return Factory::get(
            $package,
            $action,
            $this->getSourceDir($package),
            $this->getTargetDir()
        );
    }

    /**
     * getSourceDir
     *
     * @param PackageInterface $package
     *
     * @return SplFileInfo
     */
    protected function getSourceDir(PackageInterface $package)
    {
        return $this->getFs()->getDir($this->getInstallPath($package));
    }

    /**
     * getInstallPath
     *
     * @param PackageInterface $package
     *
     * @return string
     */
    public function getInstallPath(PackageInterface $package)
    {
        $targetDir = $package->getTargetDir();
        return $this->getPackageBasePath($package)
        . ($targetDir ? '/' . $targetDir : '');
    }

    /**
     * getPackageBasePath
     *
     * @param PackageInterface $package
     *
     * @return string
     */
    protected function getPackageBasePath(PackageInterface $package)
    {
        return $this->getFs()->joinFileUris(
            Config::getInstance()->getVendorDir(),
            $package->getPrettyName()
        );
    }

    /**
     * getTargetDir
     *
     * @return SplFileInfo
     */
    protected function getTargetDir()
    {
        return $this->getDeploymentDir();
    }

    private function dispatchEvent($name)
    {
        $event = new Event(
            $name,
            $this->getComposer(),
            self::$io
        );

        $this->getComposer()->getEventDispatcher()
            ->dispatch($name, $event);
    }

    /**
     * addDeployedPackage
     *
     * @param PackageInterface $package
     *
     * @return void
     */
    protected function addDeployedPackage(PackageInterface $package)
    {
        array_push($this->deployedPackages, $package);
    }

    /**
     * deployEntriesArray
     *
     * @param Entry[] $entries
     *
     * @return Entry[]
     * @throws Exception\UnknownActionException
     */
    protected function deployEntriesArray(array $entries)
    {
        while (count($entries)) {
            /** @var Entry $entry */
            $entry = array_shift($entries);
            $this->deployEntry($entry);
        }

        return $entries;
    }

    /**
     * deployCoreEntry
     *
     * @param Entry $entry
     *
     * @throws Exception\UnknownActionException
     * @return Entry
     */
    protected function deployEntry(Entry &$entry)
    {
        if (null !== $entry) {
            $entry->getDeployStrategy()->doDeploy();
            $this->addDeployedPackage(
                $entry->getDeployStrategy()->getPackage()
            );
            $entry = null;
        }

        return $entry;
    }

    /**
     * getDeploymentDir
     *
     * @return SplFileInfo
     */
    protected function getDeploymentDir()
    {
        return Config::getInstance()->getMagentoRootDir();
    }

    /**
     * onPostPackageUninstall
     *
     * add Entries of uninstalled packages, since they are not
     * in local repository anymore
     *
     * @param \Composer\EventDispatcher\Event|Event $event
     */
    public function onPostPackageUninstall(\Composer\EventDispatcher\Event $event)
    {
        /** @var UninstallOperation $operation */
        $operation = $event->getOperation();
        $this->addEntry(
            $this->getDeployManagerEntry(
                $operation->getPackage(),
                Actions::UNINSTALL
            )
        );
    }

    /**
     * getDeployedPackages
     *
     * @return array|\Composer\Package\PackageInterface[]
     */
    public function getDeployedPackages()
    {
        return $this->deployedPackages;
    }
}