GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (130)

src/Executor/Server.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
/* (c) Anton Medvedev <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Deployer\Executor;
12
13
use Closure;
14
use Deployer\Exception\Exception;
15
use Symfony\Component\Console\Output\OutputInterface;
16
17
class Server
18
{
19
    private string $host;
20
    private int $port;
21
22
    private OutputInterface $output;
23
24
    private bool $stop = false;
25
26
    /**
27
     * @var ?resource
28
     */
29 12
    private $socket;
30
31
    /**
32
     * @var resource[]
33
     */
34
    private array $clientSockets = [];
35 12
36 12
    private Closure $afterCallback;
37 12
    private Closure $tickerCallback;
38 12
    private Closure $routerCallback;
39
40 12
    public function __construct($host, $port, OutputInterface $output)
41
    {
42 12
        self::checkRequiredExtensionsExists();
43
        $this->host = $host;
44
        $this->port = $port;
45 4
        $this->output = $output;
46
    }
47
48
    public static function checkRequiredExtensionsExists(): void
49
    {
50 12
        if (!function_exists('socket_import_stream')) {
51 12
            throw new Exception('Required PHP extension "sockets" is not loaded');
52 12
        }
53 12
        if (!function_exists('stream_set_blocking')) {
54 12
            throw new Exception('Required PHP extension "stream" is not loaded');
55 12
        }
56
    }
57 4
58
    public function run(): void
59 4
    {
60 4
        try {
61 4
            $this->socket = $this->createServerSocket();
62 4
            $this->updatePort();
63
            if ($this->output->isDebug()) {
64 4
                $this->output->writeln("[master] Starting server at http://{$this->host}:{$this->port}");
65 4
            }
66
67 4
            ($this->afterCallback)($this->port);
68
69 4
            while (true) {
70 4
                $this->acceptNewConnections();
71
                $this->handleClientRequests();
72 4
73 4
                // Prevent CPU exhaustion and 60fps ticker.
74
                usleep(16_000); // 16ms
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_STRING, expecting ',' or ')' on line 74 at column 25
Loading history...
75 4
76
                ($this->tickerCallback)();
77 1
78 1
                if ($this->stop) {
79
                    break;
80 1
                }
81 1
            }
82 1
83
            if ($this->output->isDebug()) {
84 1
                $this->output->writeln("[master] Stopping server at http://{$this->host}:{$this->port}");
85
            }
86
        } finally {
87
            if (isset($this->socket)) {
88
                fclose($this->socket);
89
            }
90
        }
91 4
    }
92
93 4
    /**
94 4
     * @return resource
95
     * @throws Exception
96 4
     */
97
    private function createServerSocket()
98 4
    {
99 4
        $server = stream_socket_server("tcp://{$this->host}:{$this->port}", $errno, $errstr);
100
        if (!$server) {
101 4
            throw new Exception("Socket creation failed: $errstr ($errno)");
102
        }
103 4
104 4
        if (!stream_set_blocking($server, false)) {
105
            throw new Exception("Failed to set server socket to non-blocking mode");
106 4
        }
107
108 4
        return $server;
109 4
    }
110
111 4
    private function updatePort(): void
112
    {
113 4
        $name = stream_socket_get_name($this->socket, false);
114 4
        if ($name) {
115
            list(, $port) = explode(':', $name);
116 4
            $this->port = (int) $port;
117
        } else {
118 4
            throw new Exception("Failed to get the assigned port");
119
        }
120
    }
121
122
    private function acceptNewConnections(): void
123
    {
124
        $newClientSocket = @stream_socket_accept($this->socket, 0);
125
        if ($newClientSocket) {
126
            if (!stream_set_blocking($newClientSocket, false)) {
127
                throw new Exception("Failed to set client socket to non-blocking mode");
128
            }
129
            $this->clientSockets[] = $newClientSocket;
130
        }
131
    }
132
133
    private function handleClientRequests(): void
134
    {
135
        foreach ($this->clientSockets as $key => $clientSocket) {
136
            if (feof($clientSocket)) {
137
                $this->closeClientSocket($clientSocket, $key);
138
                continue;
139
            }
140
141
            $request = $this->readClientRequest($clientSocket);
142
            list($path, $payload) = $this->parseRequest($request);
143
            $response = ($this->routerCallback)($path, $payload);
144
145
            $this->sendResponse($clientSocket, $response);
146
            $this->closeClientSocket($clientSocket, $key);
147
        }
148
    }
149
150
    private function readClientRequest($clientSocket)
151
    {
152
        $request = stream_get_contents($clientSocket);
153
154
        if ($request === false) {
155
            throw new Exception('Socket read failed');
156
        }
157
158
        return $request;
159
    }
160
161
    private function parseRequest($request)
162
    {
163
        $lines = explode("\r\n", $request);
164
        $requestLine = $lines[0];
165
        $parts = explode(' ', $requestLine);
166
        if (count($parts) !== 3) {
167
            throw new Exception("Malformed request line: $requestLine");
168
        }
169
        $path = $parts[1];
170
171
        $headers = [];
172
        for ($i = 1; $i < count($lines); $i++) {
173
            $line = $lines[$i];
174
            if (empty($line)) {
175
                break;
176
            }
177
            [$key, $value] = explode(':', $line, 2);
178
            $headers[$key] = trim($value);
179
        }
180
        if (empty($headers['Content-Type']) || $headers['Content-Type'] !== 'application/json') {
181
            throw new Exception("Malformed request: invalid Content-Type");
182
        }
183
184
        $payload = json_decode(implode("\n", array_slice($lines, $i + 1)), true, flags: JSON_THROW_ON_ERROR);
185
        return [$path, $payload];
186
    }
187
188
    private function sendResponse($clientSocket, Response $response)
189
    {
190
        $code = $response->getStatus();
191
        $content = json_encode($response->getBody(), flags: JSON_PRETTY_PRINT);
192
        $headers = "HTTP/1.1 $code OK\r\n" .
193
            "Content-Type: application/json\r\n" .
194
            "Content-Length: " . strlen($content) . "\r\n" .
195
            "Connection: close\r\n\r\n";
196
        fwrite($clientSocket, $headers . $content);
197
    }
198
199
    private function closeClientSocket($clientSocket, $key): void
200
    {
201
        fclose($clientSocket);
202
        unset($this->clientSockets[$key]);
203
    }
204
205
    public function afterRun(Closure $param): void
206
    {
207
        $this->afterCallback = $param;
208
    }
209
210
    public function ticker(Closure $param): void
211
    {
212
        $this->tickerCallback = $param;
213
    }
214
215
    public function router(Closure $param)
216
    {
217
        $this->routerCallback = $param;
218
    }
219
220
    public function stop(): void
221
    {
222
        $this->stop = true;
223
    }
224
}
225