php-yaoi/php-yaoi

View on GitHub
src/Cli/Command/Runner.php

Summary

Maintainability
B
6 hrs
Test Coverage
<?php
/**
 * @see \Yaoi\Cli\Command\Runner equals
 * @see \Yaoi\Router
 * TODO merge them
 */

namespace Yaoi\Cli\Command;

use Yaoi\BaseClass;
use Yaoi\Cli\Response;
use Yaoi\Cli\View\Table;
use Yaoi\Command;
use Yaoi\Command\Exception;
use Yaoi\Io\Content\Heading;
use Yaoi\Io\Content\SubContent;
use Yaoi\Io\Request;

/**
 * Class Runner
 * @package Yaoi\Cli\Command
 * @todo move embedded actions (bash-completion, help, install ...) to separate commands
 */
class Runner extends BaseClass implements \Yaoi\Command\RunnerContract
{
    const OPTION_NAME = '--';
    const OPTION_SHORT = '-';

    const HELP = 'help';
    const VERSION = 'version';
    const BASH_COMPLETION = 'bash-completion';
    const INSTALL = 'install';

    const GROUP_MISC = 'Misc';
    const GROUP_DEFAULT = 'Options';

    /** @var Command */
    protected $command;

    /** @var \Yaoi\Command\Option[]  */
    protected $optionsArray;

    protected $commandName;
    protected $commandDescription;
    protected $commandVersion;

    public function __construct(Command $command) {
        $this->command = $command;
        $definition = $command->definition();
        $this->commandName = $definition->name;
        $this->commandVersion = $definition->version;
        $this->commandDescription = $definition->description;
        $this->optionsArray = $this->command->optionsArray();
        $this->response = new Response();
        $command->setResponse($this->response);
    }

    protected $showHelp;
    protected $showVersion;
    protected $showBashCompletion;

    /** @var Request */
    protected $request;

    /** @var Response */
    protected $response;

    /** @var RequestMapper */
    protected $reader;

    /**
     * @var int Skips specified count of tokens at `argv` head, for embedding in application runner
     * TODO refactor this to basePath (in array form here) from @see \Yaoi\Router
     */
    protected $skipFirstTokens = 0;


    public function run(Request $request = null)
    {
        if (null === $request) {
            $request = Request::createAuto();
        }

        $this->request = $request;

        try {
            $this->reader = new RequestMapper();
            $this->reader->skipFirstTokens = $this->skipFirstTokens;
            $this->reader->read($request, $this->command->optionsArray());
        } catch (Exception $exception) {
            $this->response->error($exception->getMessage());
            $this->response->addContent('Use --help to show information.');
            return $this;
        }

        if (!empty($this->reader->values[self::HELP])) {
            $this->showHelp();
            return $this;
        } elseif (!empty($this->reader->values[self::VERSION])) {
            $this->showVersion();
            return $this;
        } elseif (!empty($this->reader->values[self::BASH_COMPLETION])) {
            $this->showBashCompletion();
            return $this;
        } elseif (!empty($this->reader->values[self::INSTALL])) {
            // @codeCoverageIgnoreStart
            $this->install();
            return $this;
            // @codeCoverageIgnoreEnd
        } else {
            foreach ($this->reader->values as $name => $value) {
                $this->command->$name = $value;
            }
            $this->command->performAction();
            return $this;
        }
    }


    public function showVersion()
    {
        if ($this->commandName) {

            $versionText = '';
            if ($this->commandVersion) {
                $versionText .= $this->commandVersion . ' ';
            }

            $versionText .= $this->commandName;
            $this->response->addContent(new Heading($versionText));
        }
        if ($this->commandDescription) {
            $this->response->addContent(new Heading($this->commandDescription));
        }
    }

    public function showBashCompletion()
    {
        $completion = new Completion($this->command);
        $completion->render();
    }

    public function showHelp()
    {
        $this->showVersion();

        try {
            $def = new PrepareDefinition($this->optionsArray);
        } catch (Exception $exception) {
            $this->response->error('Command definition error: ' . $exception->getMessage());
            return;
        }

        $def->initOptions();


        $this->response->addContent(new Heading('Usage: '));
        $this->response->addContent(new SubContent($this->commandName . $def->usage));

        if ($def->argumentsDescription) {
            $this->response->addContent(new SubContent(Table::create(new \ArrayIterator($def->argumentsDescription))));
        }

        if ($def->optionsDescription) {
            foreach ($def->optionsDescription as $group => $descriptions) {
                $this->response->addContent(new Heading($group . ": "));
                $this->response->addContent(new SubContent(Table::create(new \ArrayIterator($descriptions))));
            }
        }
    }

    /**
     * @codeCoverageIgnore
     */
    public function install()
    {
        $this->response->addContent('Installing');

        $request = Request::createAuto();
        if (!$request->isCli()) {
            $this->response->error('CLI mode required');
            return;
        }

        $scriptFilename = realpath($request->server()->SCRIPT_NAME);
        $basename = basename($scriptFilename);

        ob_start();
        $this->showBashCompletion();
        $completion = ob_get_clean();

        $completionDirs = array(
            '/usr/local/etc/bash_completion.d/',
            '/etc/bash_completion.d/',
        );

        $completionDir = null;
        foreach ($completionDirs as $dir) {
            if (file_exists($dir)) {
                $completionDir = $dir;
                break;
            }
        }

        if (null === $completionDir) {
            $this->response->error('bash_completion.d not found');
            return;
        }

        $result = file_put_contents($completionDir . $basename, $completion)
            && chmod($completionDir . $basename, 0755);

        if (!$result) {
            $this->response->error('Unable to save bash completion');
            return;
        }

        $scriptFilenameInstall = '/usr/local/bin/' . $basename;
        if (!file_exists($scriptFilenameInstall)) {
            $cmd = 'ln -s ' . $scriptFilename . ' ' . $scriptFilenameInstall;
            $this->response->addContent($cmd);
            system($cmd, $result);
            if ($result) {
                $this->response->error('Unable to create symlink to ' . $scriptFilenameInstall);
            }
        }

        $this->response->success('OK');
        system('exec bash');
    }

}