Passed
Push — master ( 2a3d19...5e9e4e )
by Caen
05:36 queued 13s
created

InputStreamHandler::__invoke()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Hyde\Publications\Commands\Helpers;
6
7
use Hyde\Hyde;
8
9
use function trim;
10
use function fgets;
11
use function sprintf;
12
use function explode;
13
use function array_shift;
14
use function str_contains;
15
16
/**
17
 * Collects an array of lines from the standard input stream. Feed is terminated by a blank line.
18
 *
19
 * @internal
20
 *
21
 * @see \Hyde\Publications\Testing\Feature\InputStreamHandlerTest
22
 */
23
class InputStreamHandler
24
{
25
    public const TERMINATION_SEQUENCE = '<<<';
26
    public const END_OF_TRANSMISSION = "\x04";
27
28
    private static ?array $mockedStreamBuffer = null;
29
30
    public static function call(): array
31
    {
32
        return (new self())->__invoke();
33
    }
34
35
    public function __invoke(): array
36
    {
37
        return $this->getLinesFromInputStream();
38
    }
39
40
    protected function getLinesFromInputStream(): array
41
    {
42
        $lines = [];
43
        do {
44
            $line = Hyde::stripNewlines($this->readInputStream());
45
            if ($this->shouldTerminate($line)) {
46
                break;
47
            }
48
            $lines[] = trim($line);
49
        } while (true);
50
51
        return $lines;
52
    }
53
54
    protected function shouldTerminate(string $line): bool
55
    {
56
        // TODO add option to terminate with two blank lines (useful for tags input, but is default to false, for example to accept paragraphs)
57
58
        return $line === self::TERMINATION_SEQUENCE || str_contains($line, self::END_OF_TRANSMISSION);
59
    }
60
61
    /** @codeCoverageIgnore Allows for mocking of the standard input stream */
62
    protected function readInputStream(): string
63
    {
64
        if (self::$mockedStreamBuffer !== null) {
65
            return array_shift(self::$mockedStreamBuffer) ?? '';
66
        }
67
68
        return fgets(STDIN) ?: self::END_OF_TRANSMISSION;
69
    }
70
71
    /** @internal Allows for mocking of the standard input stream */
72
    public static function mockInput(string $input): void
73
    {
74
        self::$mockedStreamBuffer = explode("\n", $input);
75
    }
76
77
    public static function terminationMessage(): string
78
    {
79
        return sprintf('Terminate with <comment>%s</comment> or press %s to finish', self::TERMINATION_SEQUENCE, self::getShortcut());
80
    }
81
82
    protected static function getShortcut(): string
83
    {
84
        return '<comment>Ctrl+D</comment>'.(PHP_OS_FAMILY === 'Windows' ? ' then <comment>Enter</comment>' : '');
85
    }
86
}
87