tflori/hugga

View on GitHub
src/Input/Readline.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

namespace Hugga\Input;

use Hugga\Console;
use Hugga\InteractiveInputInterface;

class Readline extends AbstractInput implements InteractiveInputInterface
{
    public static function isCompatible($resource): bool
    {
        if (parent::isCompatible($resource) && Console::isTty($resource) && STDIN === $resource) {
            // @codeCoverageIgnoreStart
            // can not be executed in CI (no tty)
            return !self::isEditline();
            // @codeCoverageIgnoreEnd
        }
        return false;
    }

    public function readLine(string $prompt = null): string
    {
        return $this->phpReadline('', $prompt ?? ' ');
    }

    public function read(int $count = 1, string $prompt = null): string
    {
        $str = $this->readConditional(function ($str) use ($count) {
            return mb_strlen($str) >= $count;
        }, $prompt);
        return $str;
    }

    public function readUntil(string $sequence, string $prompt = null): string
    {
        $seqLen = strlen($sequence);
        $str = $this->readConditional(function ($str) use ($sequence, $seqLen) {
            return substr($str, -$seqLen) === $sequence;
        }, $prompt);
        return substr($str, 0, -$seqLen);
    }

    /**
     * Check if readline uses editline library
     *
     * @return bool
     * @codeCoverageIgnore Can not be executed in CI (no tty)
     */
    protected static function isEditline()
    {
        return self::phpReadline('info', 'library_version') === 'EditLine wrapper';
    }

    protected function readConditional(callable $conditionMet, string $prompt = null): string
    {
        $str = $previous = '';
        $this->phpReadline('callback_handler_install', $prompt ?? ' ', function ($str) use (&$previous) {
            $previous .= $str . PHP_EOL;
        });
        do {
            $r = array($this->resource);
            $n = stream_select($r, $w, $e, null);
            if ($n && in_array($this->resource, $r)) {
                $this->phpReadline('callback_read_char');
                $str = $previous . $this->phpReadline('info', 'line_buffer');
            }
        } while (!$conditionMet($str));
        $this->phpReadline('callback_handler_remove');

        if (substr($str, -strlen(PHP_EOL)) !== PHP_EOL) {
            $this->console->write(PHP_EOL);
        }
        return $str;
    }

    /**
     * Calls phps readline_* methods
     *
     * @param string $method
     * @param mixed ...$args
     * @return mixed
     * @codeCoverageIgnore we can not test this
     */
    protected static function phpReadline(string $method, ...$args)
    {
        $method = 'readline' . (strlen($method) ? '_' . $method : '');
        return call_user_func_array($method, $args);
    }
}