Completed
Pull Request — master (#100)
by Maxime
02:21
created

AbstractConnection::getHandler()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 0
cts 12
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 0
crap 12
1
<?php
2
3
/**
4
 * This file is a part of Woketo package.
5
 *
6
 * (c) Nekland <[email protected]>
7
 *
8
 * For the full license, take a look to the LICENSE file
9
 * on the root directory of this project
10
 */
11
12
namespace Nekland\Woketo\Core;
13
14
use Nekland\Woketo\Exception\NoHandlerException;
15
use Nekland\Woketo\Exception\WebsocketException;
16
use Nekland\Woketo\Message\MessageHandlerInterface;
17
use Nekland\Woketo\Rfc6455\Frame;
18
use Nekland\Woketo\Rfc6455\Handshake\HandshakeInterface;
19
use Nekland\Woketo\Rfc6455\Message;
20
use Nekland\Woketo\Rfc6455\MessageProcessor;
21
use Nekland\Woketo\Utils\SimpleLogger;
22
use Psr\Log\LoggerAwareTrait;
23
use Psr\Log\LoggerInterface;
24
use React\EventLoop\LoopInterface;
25
use React\EventLoop\Timer\TimerInterface;
26
use React\Socket\Connection;
27
28
abstract class AbstractConnection
29
{
30
    /**
31
     * 5 seconds
32
     */
33
    const DEFAULT_TIMEOUT = 5;
34
35
    use LoggerAwareTrait;
36
37
    /**
38
     * @var Connection
39
     */
40
    protected $stream;
41
42
    /**
43
     * @var MessageProcessor
44
     */
45
    protected $messageProcessor;
46
47
    /**
48
     * @var \Nekland\Woketo\Rfc6455\Handshake\ServerHandshake|\Nekland\Woketo\Rfc6455\Handshake\ClientHandshake
49
     */
50
    protected $handshake;
51
52
    /**
53
     * @var bool
54
     */
55
    protected $handshakeDone;
56
57
    /**
58
     * @var string
59
     */
60
    protected $uri;
61
62
    /**
63
     * @var MessageHandlerInterface|\Closure
64
     */
65
    protected $handler;
66
67
    /**
68
     * @var TimerInterface
69
     */
70
    protected $timeout;
71
72
    /**
73
     * @var Message
74
     */
75
    protected $currentMessage;
76
77
    /**
78
     * @var LoopInterface
79
     */
80
    protected $loop;
81
82
    public function __construct(MessageProcessor $messageProcessor, HandshakeInterface $handshake = null)
83
    {
84
        $this->handshake = $handshake;
0 ignored issues
show
Documentation Bug introduced by
It seems like $handshake can also be of type object<Nekland\Woketo\Rf...ake\HandshakeInterface>. However, the property $handshake is declared as type object<Nekland\Woketo\Rf...dshake\ClientHandshake>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
85
        $this->messageProcessor = $messageProcessor;
86
    }
87
88
    protected function onMessage(string $data)
89
    {
90
        try {
91
            if (!$this->handshakeDone) {
92
                $this->processHandshake($data);
93
            } else {
94
                $this->processMessage($data);
95
            }
96
97
            return;
98
        } catch (WebsocketException $e) {
99
            $this->messageProcessor->close($this->stream);
100
            $this->getLogger()->notice('Connection to ' . $this->getIp() . ' closed with error : ' . $e->getMessage());
101
            $this->handler->onError($e, $this);
0 ignored issues
show
Bug introduced by
The method onError does only exist in Nekland\Woketo\Message\MessageHandlerInterface, but not in Closure.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
102
        }
103
    }
104
105
    protected abstract function processHandshake(string $data);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
106
    protected abstract function processMessage(string $data);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
107
108
    /**
109
     * @return string
110
     */
111
    public abstract function getIp();
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
112
113
    /**
114
     * @return \Psr\Log\LoggerInterface
115
     */
116
    public function getLogger()
117
    {
118
        return $this->logger ?: $this->logger = new SimpleLogger();
119
    }
120
121
    /**
122
     * @return MessageHandlerInterface
123
     * @throws NoHandlerException
124
     */
125
    protected function getHandler() : MessageHandlerInterface
126
    {
127
        if ($this->handler instanceof \Closure) {
128
            $handler = $this->handler;
129
            $handler = $handler($this->uri, $this);
130
131
            if (null === $handler) {
132
                throw new NoHandlerException(sprintf('No handler for request URI %s.', $this->uri));
133
            }
134
135
            $this->handler = $handler;
136
        }
137
138
        return $this->handler;
139
    }
140
141
    /**
142
     * Close the connection with normal close.
143
     */
144
    public function close()
145
    {
146
        $this->messageProcessor->close($this->stream);
147
    }
148
149
    /**
150
     * @param string|Frame  $frame
151
     * @param int           $opCode
152
     * @throws \Nekland\Woketo\Exception\RuntimeException
153
     */
154
    public abstract function write($frame, int $opCode = Frame::OP_TEXT);
0 ignored issues
show
Coding Style introduced by
The abstract declaration must precede the visibility declaration
Loading history...
155
}
156