Completed
Push — master ( aaeb57...138031 )
by Marco
11:42
created

SocketTransport   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 243
Duplicated Lines 2.88 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 69.31%

Importance

Changes 0
Metric Value
wmc 34
lcom 1
cbo 7
dl 7
loc 243
ccs 70
cts 101
cp 0.6931
rs 9.2
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
B performCall() 0 48 4
A create() 0 5 1
B connect() 0 37 5
A close() 0 5 1
A send() 0 15 2
A write() 0 12 1
A read() 0 19 3
A readGreeter() 0 19 3
B rawRead() 7 15 5
A can() 0 19 3
A uncan() 0 17 4
A checkEncryptedResponseConsistency() 0 5 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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
Documentation introduced by
The property status does not exist on object<Comodojo\Daemon\Socket\Greeter>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
100
            throw new SocketException("Socket connect failed: ".$greeter->status);
0 ignored issues
show
Documentation introduced by
The property status does not exist on object<Comodojo\Daemon\Socket\Greeter>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
101
        }
102
103 5
        if ( $greeter->version != self::VERSION ) {
0 ignored issues
show
Documentation introduced by
The property version does not exist on object<Comodojo\Daemon\Socket\Greeter>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

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);
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
$sent is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
120
121
        // TODO: manage exceptions!
122
123 5
        $received = $this->read();
124
125 5
        if ( $received->status === false ) {
0 ignored issues
show
Documentation introduced by
The property status does not exist on object<Comodojo\Daemon\Socket\Response>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
126
            throw new Exception($received->message);
0 ignored issues
show
Documentation introduced by
The property message does not exist on object<Comodojo\Daemon\Socket\Response>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
127
        }
128
129 5
        return $received->message;
0 ignored issues
show
Documentation introduced by
The property message does not exist on object<Comodojo\Daemon\Socket\Response>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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
Documentation introduced by
The property content_type does not exist on object<Comodojo\Daemon\Socket\Request>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
138 5
        $request->message = $data;
0 ignored issues
show
Documentation introduced by
The property message does not exist on object<Comodojo\Daemon\Socket\Request>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

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
Documentation introduced by
The property status does not exist on object<Comodojo\Daemon\Socket\Response>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
154
            $response->message = "Server has gone away";
0 ignored issues
show
Documentation introduced by
The property message does not exist on object<Comodojo\Daemon\Socket\Response>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
155 5
        } else if ( empty($datagram) ) {
156
            $response->status = false;
0 ignored issues
show
Documentation introduced by
The property status does not exist on object<Comodojo\Daemon\Socket\Response>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
157
            $response->message = "No response received";
0 ignored issues
show
Documentation introduced by
The property message does not exist on object<Comodojo\Daemon\Socket\Response>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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
Documentation introduced by
The property status does not exist on object<Comodojo\Daemon\Socket\Greeter>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
174
            return $greeter;
175
        }
176
177 5
        if ( $datagram === false ) {
178
            $greeter->status = 'server has gone away';
0 ignored issues
show
Documentation introduced by
The property status does not exist on object<Comodojo\Daemon\Socket\Greeter>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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 View Code Duplication
        while (true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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