Completed
Push — master ( 5b24b6...4b8dcd )
by Anton
04:26 queued 02:19
created

Builder::isColorsSupported()   B

Complexity

Conditions 11
Paths 21

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 31
rs 7.3166
c 0
b 0
f 0
cc 11
nc 21
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
/**
4
 * RoadRunner.
5
 *
6
 * @license   MIT
7
 * @author    Anton Titov (Wolfy-J)
8
 */
9
10
namespace Spiral\RoadRunner\QuickBuild;
11
12
final class Builder
13
{
14
    const DOCKER = 'spiralscout/rr-build';
15
16
    /**
17
     * Coloring.
18
     *
19
     * @var array
20
     */
21
    protected static $colors = [
22
        "reset"  => "\e[0m",
23
        "white"  => "\033[1;38m",
24
        "red"    => "\033[0;31m",
25
        "green"  => "\033[0;32m",
26
        "yellow" => "\033[1;93m",
27
        "gray"   => "\033[0;90m"
28
    ];
29
30
    /** @var array */
31
    private $config;
32
33
    /**
34
     * @param array $config
35
     */
36
    protected function __construct(array $config)
37
    {
38
        $this->config = $config;
39
    }
40
41
    /**
42
     * Validate the build configuration.
43
     *
44
     * @return array
45
     */
46
    public function configErrors(): array
47
    {
48
        $errors = [];
49
        if (!isset($this->config["commands"])) {
50
            $errors[] = "Directive 'commands' missing";
51
        }
52
53
        if (!isset($this->config["packages"])) {
54
            $errors[] = "Directive 'packages' missing";
55
        }
56
57
        if (!isset($this->config["register"])) {
58
            $errors[] = "Directive 'register' missing";
59
        }
60
61
        return $errors;
62
    }
63
64
    /**
65
     * Build the application.
66
     *
67
     * @param string $directory
68
     * @param string $template
69
     * @param string $output
70
     * @param string $version
71
     */
72
    public function build(string $directory, string $template, string $output, string $version)
73
    {
74
        $filename = $directory . "/main.go";
75
        $output = $output . ($this->getOS() == 'windows' ? '.exe' : '');
76
77
        // step 1, generate template
78
        $this->generate($template, $filename);
79
80
        $command = sprintf(
81
            'docker run --rm -v "%s":/mnt -e RR_VERSION=%s -e GOARCH=amd64 -e GOOS=%s %s /bin/bash -c "mv /mnt/main.go main.go; bash compile.sh; cp rr /mnt/%s;"',
82
            $directory,
83
            $version,
84
            $this->getOS(),
85
            self::DOCKER,
86
            $output
87
        );
88
89
        self::cprintf("<yellow>%s</reset>\n", $command);
90
91
        // run the build
92
        $this->run($command, true);
93
94
        if (!file_exists($directory . '/' . $output)) {
95
            self::cprintf("<red>Build has failed!</reset>");
96
            return;
97
        }
98
99
        self::cprintf("<green>Build complete!</reset>\n");
100
        $this->run($directory . '/' . $output, false);
101
    }
102
103
    /**
104
     * @param string $command
105
     * @param bool   $shadow
106
     */
107
    protected function run(string $command, bool $shadow = false)
108
    {
109
        $shadow && self::cprintf("<gray>");
110
        passthru($command);
111
        $shadow && self::cprintf("</reset>");
112
    }
113
114
    /**
115
     * @param string $template
116
     * @param string $filename
117
     */
118
    protected function generate(string $template, string $filename)
119
    {
120
        $body = file_get_contents($template);
121
122
        $replace = [
123
            '// -packages- //' => '"' . join("\"\n\"", $this->config['packages']) . '"',
124
            '// -commands- //' => '_ "' . join("\"\n_ \"", $this->config['commands']) . '"',
125
            '// -register- //' => join("\n", $this->config['register'])
126
        ];
127
128
        // compile the template
129
        $result = str_replace(array_keys($replace), array_values($replace), $body);
130
        file_put_contents($filename, $result);
131
    }
132
133
    /**
134
     * @return string
135
     */
136
    protected function getOS(): string
137
    {
138
        $os = strtolower(PHP_OS);
139
140
        if (strpos($os, 'win') !== false) {
141
            return 'windows';
142
        }
143
144
        if (strpos($os, 'darwin') !== false) {
145
            return 'darwin';
146
        }
147
148
        return "linux";
149
    }
150
151
    /**
152
     * Create new builder using given config.
153
     *
154
     * @param string $config
155
     * @return Builder|null
156
     */
157
    public static function loadConfig(string $config): ?Builder
158
    {
159
        if (!file_exists($config)) {
160
            return null;
161
        }
162
163
        $configData = json_decode(file_get_contents($config), true);
164
        if (!is_array($configData)) {
165
            return null;
166
        }
167
168
        return new Builder($configData);
169
    }
170
171
    /**
172
     * Make colored output.
173
     *
174
     * @param string $format
175
     * @param mixed  ...$args
176
     */
177
    public static function cprintf(string $format, ...$args)
178
    {
179
        if (self::isColorsSupported()) {
180
            $format = preg_replace_callback("/<\/?([^>]+)>/", function ($value) {
181
                return self::$colors[$value[1]];
182
            }, $format);
183
        } else {
184
            $format = preg_replace("/<[^>]+>/", "", $format);
185
        }
186
187
        echo sprintf($format, ...$args);
188
    }
189
190
    /**
191
     * @return bool
192
     */
193
    public static function isWindows(): bool
194
    {
195
        return \DIRECTORY_SEPARATOR === '\\';
196
    }
197
198
    /**
199
     * Returns true if the STDOUT supports colorization.
200
     *
201
     * @codeCoverageIgnore
202
     * @link https://github.com/symfony/Console/blob/master/Output/StreamOutput.php#L94
203
     * @param mixed $stream
204
     * @return bool
205
     */
206
    public static function isColorsSupported($stream = STDOUT): bool
207
    {
208
        if ('Hyper' === getenv('TERM_PROGRAM')) {
209
            return true;
210
        }
211
212
        try {
213
            if (\DIRECTORY_SEPARATOR === '\\') {
214
                return (
215
                        function_exists('sapi_windows_vt100_support')
216
                        && @sapi_windows_vt100_support($stream)
217
                    ) || getenv('ANSICON') !== false
218
                    || getenv('ConEmuANSI') == 'ON'
219
                    || getenv('TERM') == 'xterm';
220
            }
221
222
            if (\function_exists('stream_isatty')) {
223
                return (bool)@stream_isatty($stream);
224
            }
225
226
            if (\function_exists('posix_isatty')) {
227
                return (bool)@posix_isatty($stream);
228
            }
229
230
            $stat = @fstat($stream);
231
            // Check if formatted mode is S_IFCHR
232
            return $stat ? 0020000 === ($stat['mode'] & 0170000) : false;
233
        } catch (\Throwable $e) {
234
            return false;
235
        }
236
    }
237
}