Passed
Push — master ( 05cc9e...6196c7 )
by Alec
02:39
created

Stream   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 96
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 23
dl 0
loc 96
rs 10
c 2
b 0
f 0
ccs 14
cts 14
cp 1
wmc 15

5 Methods

Rating   Name   Duplication   Size   Complexity  
A checkStream() 0 5 2
A hasColorSupport() 0 26 5
A assertStream() 0 5 2
A refineStream() 0 5 1
A checkWindowsColorSupport() 0 7 5
1
<?php declare(strict_types=1);
2
3
namespace AlecRabbit\Cli\Tools;
4
5
use function AlecRabbit\onWindows;
6
use const AlecRabbit\ENV_ANSICON;
7
use const AlecRabbit\ENV_CON_EMU_ANSI;
8
use const AlecRabbit\ENV_TERM;
9
use const AlecRabbit\ENV_TERM_PROGRAM;
10
use const AlecRabbit\XTERM;
11
12
class Stream
13
{
14
15
    /**
16
     * Returns true if the stream supports colorization.
17
     *
18
     * Colorization is disabled if not supported by the stream:
19
     *
20
     * This is tricky on Windows, because Cygwin, Msys2 etc emulate pseudo
21
     * terminals via named pipes, so we can only check the environment.
22
     *
23
     * Reference: Composer\XdebugHandler\Process::supportsColor
24
     * https://github.com/composer/xdebug-handler
25
     *
26
     * Reference: Symfony\Component\Console\Output\StreamOutput::hasColorSupport()
27
     * https://github.com/symfony/console
28
     *
29
     * @param null|bool|resource $stream
30
     * @return bool true if the stream supports colorization, false otherwise
31
     */
32 6
    public static function hasColorSupport($stream = null): bool
33
    {
34 6
        $stream = self::refineStream($stream);
35 5
        if ('Hyper' === getenv(ENV_TERM_PROGRAM)) {
36
            // @codeCoverageIgnoreStart
37
            return true;
38
            // @codeCoverageIgnoreEnd
39
        }
40
41
        // @codeCoverageIgnoreStart
42
        if (onWindows()) {
43
            return static::checkWindowsColorSupport($stream);
44
        }
45
        // @codeCoverageIgnoreEnd
46
47 5
        if (\function_exists('stream_isatty')) {
48 5
            return @stream_isatty($stream);
49
        }
50
51
        // @codeCoverageIgnoreStart
52
        if (\function_exists('posix_isatty')) {
53
            /** @noinspection PhpComposerExtensionStubsInspection */
54
            return @posix_isatty($stream);
55
        }
56
57
        return static::checkStream($stream);
58
        // @codeCoverageIgnoreEnd
59
    }
60
61
    /**
62
     * @param null|bool|resource $stream
63
     * @return resource
64
     */
65 6
    protected static function refineStream($stream = null)
66
    {
67 6
        $stream = $stream ?? STDOUT;
68 6
        self::assertStream($stream);
69 5
        return $stream;
70
    }
71
72
    /**
73
     * @param mixed $stream
74
     */
75 6
    protected static function assertStream($stream): void
76
    {
77 6
        if (!\is_resource($stream)) {
78 1
            throw new \RuntimeException(
79 1
                'Expecting parameter 1 to be resource, ' . \gettype($stream) . ' given'
80
            );
81
        }
82 5
    }
83
84
    /**
85
     * @param resource $stream
86
     * @return bool
87
     * @codeCoverageIgnore
88
     */
89
    protected static function checkWindowsColorSupport($stream): bool
90
    {
91
        return (\function_exists('sapi_windows_vt100_support')
92
                && @sapi_windows_vt100_support($stream))
93
            || false !== getenv(ENV_ANSICON)
94
            || 'ON' === getenv(ENV_CON_EMU_ANSI)
95
            || XTERM === getenv(ENV_TERM);
96
    }
97
98
    /**
99
     * @param resource $stream
100
     * @return bool
101
     * @codeCoverageIgnore
102
     */
103
    protected static function checkStream($stream): bool
104
    {
105
        $stat = @fstat($stream);
106
        // Check if formatted mode is S_IFCHR
107
        return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
108
    }
109
}
110