Passed
Push — master ( 1c6547...ceb2be )
by Sam
04:03
created

SocketSocket::__construct()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 4.016

Importance

Changes 0
Metric Value
eloc 20
dl 0
loc 34
ccs 18
cts 20
cp 0.9
rs 9.6
c 0
b 0
f 0
cc 4
nc 5
nop 3
crap 4.016
1
<?php
2
3
4
namespace Pheanstalk\Socket;
5
6
use Pheanstalk\Contract\SocketInterface;
7
use Pheanstalk\Exception\ConnectionException;
8
use Pheanstalk\Exception\SocketException;
9
10
/**
11
 * A Socket implementation using the Sockets extension
12
 */
13
class SocketSocket implements SocketInterface
14
{
15
    /** @var resource */
16
    private $socket;
17
18 27
    public function __construct(
19
        string $host,
20
        int $port,
21
        int $connectTimeout
22
    ) {
23 27
        if (!extension_loaded('sockets')) {
24
            throw new \Exception('Sockets extension not found');
25
        }
26
27 27
        $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
28 27
        if ($this->socket === false) {
0 ignored issues
show
introduced by Sam Mousa
The condition $this->socket is always false. If $this->socket can have other possible types, add them to src/Socket/SocketSocket.php:15
Loading history...
29
            $this->throwException();
30
        }
31
32
        $timeout = [
33 27
            'sec' => $connectTimeout,
34 27
            'usec' => 0
35
        ];
36
37 27
        $sendTimeout = socket_get_option($this->socket, SOL_SOCKET, SO_SNDTIMEO);
38 27
        $receiveTimeout = socket_get_option($this->socket, SOL_SOCKET, SO_RCVTIMEO);
39 27
        socket_set_option($this->socket, SOL_TCP, SO_KEEPALIVE, 1);
40 27
        socket_set_option($this->socket, SOL_SOCKET,SO_SNDTIMEO, $timeout);
41 27
        socket_set_option($this->socket, SOL_SOCKET,SO_RCVTIMEO, $timeout);
42 27
        socket_set_block($this->socket);
43
44 27
        $address = gethostbyname($host);
45 27
        if (@socket_connect($this->socket, $address, $port) === false) {
46 1
            $error = socket_last_error($this->socket);
47 1
            throw new ConnectionException($error, socket_strerror($error));
48
        };
49
50 26
        socket_set_option($this->socket, SOL_SOCKET,SO_SNDTIMEO, $sendTimeout);
51 26
        socket_set_option($this->socket, SOL_SOCKET,SO_RCVTIMEO, $receiveTimeout);
52
    }
53
54
    /**
55
     * Writes data to the socket.
56
     *
57
     * @param string $data
58
     *
59
     * @return void
60
     */
61 25
    public function write(string $data): void
62
    {
63 25
        $this->checkClosed();
64 25
        while (!empty($data)) {
65 25
            $written = socket_write($this->socket, $data);
66 25
            if ($written === false) {
67
                $this->throwException();
68
            }
69 25
            $data = substr($data, $written);
70
        }
71
    }
72
73
    private function throwException()
74
    {
75
        $error = socket_last_error($this->socket);
76
        throw new SocketException(socket_strerror($error), $error);
77
    }
78
79 25
    private function checkClosed()
80
    {
81 25
        if (!isset($this->socket)) {
82
            throw new SocketException('The connection was closed');
83
        }
84
    }
85
86
    /**
87
     * Reads up to $length bytes from the socket.
88
     *
89
     * @return string
90
     */
91 24
    public function read(int $length): string
92
    {
93 24
        $this->checkClosed();
94
95 24
        $buffer = '';
96 24
        while (mb_strlen($buffer, '8BIT') < $length) {
97 24
            $result = socket_read($this->socket, $length - mb_strlen($buffer, '8BIT'));
98 24
            if ($result === false) {
99
                $this->throwException();
100
            }
101 24
            $buffer .= $result;
102
        }
103
104 24
        return $buffer;
105
    }
106
107 25
    public function getLine(): string
108
    {
109 25
        $this->checkClosed();
110
111 25
        $buffer = '';
112
        // Reading stops at \r or \n. In case it stopped at \r we must continue reading.
113 25
        while(substr($buffer, -1, 1) !== "\n") {
114 25
            $result = socket_read($this->socket, 1024, PHP_NORMAL_READ);
115 25
            if ($result === false) {
116
                $this->throwException();
117
            }
118 25
            $buffer .= $result;
119
        }
120
121
122
123 25
        return rtrim($buffer);
124
    }
125
126
    /**
127
     * Disconnect the socket; subsequent usage of the socket will fail.
128
     */
129 1
    public function disconnect(): void
130
    {
131 1
        $this->checkClosed();
132 1
        socket_close($this->socket);
133 1
        unset($this->socket);
134
    }
135
}