SocketTransport   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 239
Duplicated Lines 0 %

Test Coverage

Coverage 69.31%

Importance

Changes 0
Metric Value
eloc 95
dl 0
loc 239
ccs 70
cts 101
cp 0.6931
rs 9.68
c 0
b 0
f 0
wmc 34

12 Methods

Rating   Name   Duplication   Size   Complexity  
A readGreeter() 0 17 3
A checkEncryptedResponseConsistency() 0 3 2
A can() 0 17 3
A rawRead() 0 13 5
A send() 0 13 2
A close() 0 3 1
A write() 0 10 1
A uncan() 0 15 4
A create() 0 3 1
A read() 0 17 3
A performCall() 0 46 4
A connect() 0 35 5
1
<?php namespace Comodojo\Daemon\Socket;
2
3
use \Comodojo\RpcClient\Interfaces\Transport as TransportInterface;
4
use \Comodojo\Httprequest\Httprequest;
5
use \phpseclib\Crypt\AES;
6
use \Psr\Log\LoggerInterface;
7
use \Comodojo\Exception\RpcException;
8
use \Comodojo\Exception\SocketException;
9
use \Exception;
10
11
class SocketTransport extends AbstractSocket implements TransportInterface {
12
13
    private $aes = null;
14
15
    /**
16
     * {@inheritdoc}
17
     */
18 5
    public function performCall(
19
        LoggerInterface $logger,
20
        $data,
21
        $content_type,
22
        $encrypt = false
23
    ) {
24
25
        try {
26
27 5
            $logger->debug("Connecting to socket");
28
29 5
            $this->connect();
30
31 5
            $logger->debug("Sending RPC data");
32
33 5
            $data = $this->can($data, $encrypt);
34
35 5
            $response = $this->send($content_type, $data);
36
37 5
            $this->close();
38
39 5
            $logger->debug("Decoding RPC response");
40
41 5
            $return = $this->uncan($response, $encrypt);
42
43
        } catch (SocketException $se) {
44
45
            $logger->error("Socket Transport error: ".$se->getMessage());
46
47
            throw $se;
48
49
        } catch (RpcException $re) {
50
51
            $logger->error("RPC Client error: ".$re->getMessage());
52
53
            throw $re;
54
55
        } catch (Exception $e) {
56
57
            $logger->critical("Generic Client error: ".$e->getMessage());
58
59
            throw $e;
60
61
        }
62
63 5
        return $return;
64
65
    }
66
67 5
    public static function create($handler, $read_buffer = null) {
68
69 5
        return new SocketTransport($handler, $read_buffer);
70
71
    }
72
73 5
    public function connect() {
74
75 5
        $this->socket = @socket_create(
76 5
            $this->socket_domain,
77 5
            $this->socket_type,
78 5
            $this->socket_protocol
79
        );
80
81 5
        if ( $this->socket === false ) {
82
            $error = self::getSocketError();
83
            throw new SocketException("Socket unavailable: $error");
84
        }
85
86 5
        $connect = @socket_connect(
87 5
            $this->socket,
88 5
            $this->socket_resource,
89 5
            $this->socket_port
90
        );
91
92 5
        if ( $connect === false ) {
93
            $error = self::getSocketError($this->socket);
94
            throw new SocketException("Cannot connect to socket: $error");
95
        }
96
97 5
        $greeter = $this->readGreeter();
98
99 5
        if ( $greeter->status != 'connected' ) {
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist on Comodojo\Daemon\Socket\Greeter. Since you implemented __get, consider adding a @property annotation.
Loading history...
100
            throw new SocketException("Socket connect failed: ".$greeter->status);
101
        }
102
103 5
        if ( $greeter->version != self::VERSION ) {
0 ignored issues
show
Bug Best Practice introduced by
The property version does not exist on Comodojo\Daemon\Socket\Greeter. Since you implemented __get, consider adding a @property annotation.
Loading history...
104
            throw new SocketException("Socket connect failed: socket interface version mismatch");
105
        }
106
107 5
        return $this;
108
109
    }
110
111 5
    public function close() {
112
113 5
        return socket_close($this->socket);
0 ignored issues
show
Bug introduced by
Are you sure the usage of socket_close($this->socket) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
114
115
    }
116
117 5
    protected function send($content_type, $data) {
118
119 5
        $sent = $this->write($content_type, $data);
0 ignored issues
show
Unused Code introduced by
The assignment to $sent is dead and can be removed.
Loading history...
120
121
        // TODO: manage exceptions!
122
123 5
        $received = $this->read();
124
125 5
        if ( $received->status === false ) {
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist on Comodojo\Daemon\Socket\Response. Since you implemented __get, consider adding a @property annotation.
Loading history...
126
            throw new Exception($received->message);
0 ignored issues
show
Bug Best Practice introduced by
The property message does not exist on Comodojo\Daemon\Socket\Response. Since you implemented __get, consider adding a @property annotation.
Loading history...
127
        }
128
129 5
        return $received->message;
130
131
    }
132
133 5
    protected function write($content_type, $data) {
134
135 5
        $request = new Request();
136
137 5
        $request->content_type = $content_type;
0 ignored issues
show
Bug Best Practice introduced by
The property content_type does not exist on Comodojo\Daemon\Socket\Request. Since you implemented __set, consider adding a @property annotation.
Loading history...
138 5
        $request->message = $data;
0 ignored issues
show
Bug Best Practice introduced by
The property message does not exist on Comodojo\Daemon\Socket\Request. Since you implemented __set, consider adding a @property annotation.
Loading history...
139
140 5
        $datagram = $request->serialize()."\r\n";
141
142 5
        return socket_write($this->socket, $datagram, strlen($datagram));
143
144
    }
145
146 5
    protected function read() {
147
148 5
        $response = new Response();
149
150 5
        $datagram = $this->rawRead();
151
152 5
        if ( is_null($datagram) ) {
153
            $response->status = false;
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist on Comodojo\Daemon\Socket\Response. Since you implemented __set, consider adding a @property annotation.
Loading history...
154
            $response->message = "Server has gone away";
0 ignored issues
show
Bug Best Practice introduced by
The property message does not exist on Comodojo\Daemon\Socket\Response. Since you implemented __set, consider adding a @property annotation.
Loading history...
155 5
        } else if ( empty($datagram) ) {
156
            $response->status = false;
157
            $response->message = "No response received";
158
        } else {
159 5
            $response->unserialize($datagram);
160
        }
161
162 5
        return $response;
163
164
    }
165
166 5
    protected function readGreeter() {
167
168 5
        $greeter = new Greeter();
169
170 5
        $datagram = $this->rawRead();
171
172 5
        if ( is_null($datagram) ) {
173
            $greeter->status = 'greeter not received';
0 ignored issues
show
Bug Best Practice introduced by
The property status does not exist on Comodojo\Daemon\Socket\Greeter. Since you implemented __set, consider adding a @property annotation.
Loading history...
174
            return $greeter;
175
        }
176
177 5
        if ( $datagram === false ) {
178
            $greeter->status = 'server has gone away';
179
            return $greeter;
180
        }
181
182 5
        return $greeter->unserialize($datagram);
183
184
    }
185
186 5
    protected function rawRead() {
187
188 5
        $datagram = '';
189
190 5
        while ( true ) {
191 5
            $recv = @socket_read($this->socket, $this->read_buffer, PHP_NORMAL_READ);
192 5
            if ( $recv === false ) break;
193 5
            if ( $recv === 0 ) return null;
194 5
            $datagram .= $recv;
195 5
            if ( strstr($recv, PHP_EOL) ) break;
196
        }
197
198 5
        return trim($datagram);
199
200
    }
201
202 5
    private function can($data, $key) {
203
204 5
        if ( !empty($key) && is_string($key) ) {
205
206
            $this->aes = new AES();
207
208
            $this->aes->setKey($key);
209
210
            $return = 'comodojo_encrypted_request-'.base64_encode($this->aes->encrypt($data));
211
212
        } else {
213
214 5
            $return = $data;
215
216
        }
217
218 5
        return $return;
219
220
    }
221
222 5
    private function uncan($data, $key) {
223
224 5
        if ( !empty($key) && is_string($key) ) {
225
226
            if ( self::checkEncryptedResponseConsistency($data) === false ) throw new RpcException("Inconsistent encrypted response received");
227
228
            $return = $this->aes->decrypt(base64_decode(substr($data, 28)));
229
230
        } else {
231
232 5
            $return = $data;
233
234
        }
235
236 5
        return $return;
237
238
    }
239
240
    /**
241
     * Check if an encrypted envelope is consisent or not
242
     *
243
     * @param   string    $data
244
     *
245
     * @return  bool
246
     */
247
    private static function checkEncryptedResponseConsistency($data) {
248
249
        return substr($data, 0, 27) == 'comodojo_encrypted_response' ? true : false;
250
251
    }
252
253
}
254