UDP::setDefaultPort()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
nc 1
nop 1
1
<?php
2
namespace PHPDaemon\BoundSocket;
3
4
use PHPDaemon\Core\Daemon;
5
use PHPDaemon\Core\Debug;
6
use PHPDaemon\Core\Timer;
7
use PHPDaemon\Core\EventLoop;
8
9
/**
10
 * UDP
11
 *
12
 * @package Core
13
 *
14
 * @author  Vasily Zorin <[email protected]>
15
 */
16
class UDP extends Generic
17
{
18
    /**
19
     * Hostname
20
     * @var string
21
     */
22
    protected $host;
23
24
    /**
25
     * Port
26
     * @var integer
27
     */
28
    protected $port;
29
30
    /**
31
     * Listener mode?
32
     * @var boolean
33
     */
34
    protected $listenerMode = true;
35
36
    /**
37
     * Default port
38
     * @var integer
39
     */
40
    protected $defaultPort;
41
42
    /**
43
     * Reuse?
44
     * @var boolean
45
     */
46
    protected $reuse = true;
47
48
    /**
49
     * Ports map
50
     * @var array [portNumber => Connection]
51
     */
52
    protected $portsMap = [];
53
54
    /**
55
     * Sets default port
56
     * @param integer $port Port
57
     * @return void
58
     */
59
    public function setDefaultPort($port)
60
    {
61
        $this->defaultPort = $port;
62
    }
63
64
    /**
65
     * Send UDP packet
66
     * @param string $data Data
67
     * @param integer $flags Flags
68
     * @param string $host Host
69
     * @param integer $port Port
70
     * @return integer
71
     */
72
    public function sendTo($data, $flags, $host, $port)
73
    {
74
        return socket_sendto($this->fd, $data, mb_orig_strlen($data), $flags, $host, $port);
75
    }
76
77
    /**
78
     * Unassigns addr
79
     * @param string $addr Address
80
     * @return void
81
     */
82
    public function unassignAddr($addr)
83
    {
84
        unset($this->portsMap[$addr]);
85
    }
86
87
    /**
88
     * Sets reuse
89
     * @param integer $reuse Port
0 ignored issues
show
Documentation introduced by
Should the type for parameter $reuse not be boolean|integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
90
     * @return void
91
     */
92
    public function setReuse($reuse = true)
93
    {
94
        $this->reuse = $reuse;
0 ignored issues
show
Documentation Bug introduced by
It seems like $reuse can also be of type integer. However, the property $reuse is declared as type boolean. 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...
95
    }
96
97
    /**
98
     * Bind given addreess
99
     * @return boolean Success.
100
     */
101
    public function bindSocket()
102
    {
103
        $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
104 View Code Duplication
        if (!$sock) {
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...
105
            $errno = socket_last_error();
106
            Daemon::$process->log(get_class($this) . ': Couldn\'t create UDP-socket (' . $errno . ' - ' . socket_strerror($errno) . ').');
107
            return false;
108
        }
109
        if (!isset($this->port)) {
110
            if (isset($this->defaultPort)) {
111
                $this->port = $this->defaultPort;
112 View Code Duplication
            } else {
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...
113
                Daemon::log(get_class($this) . ' (' . get_class($this->pool) . '): no port defined for \'' . $this->uri['uri'] . '\'');
114
            }
115
        }
116 View Code Duplication
        if ($this->reuse) {
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...
117
            if (!socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1)) {
118
                $errno = socket_last_error();
119
                Daemon::$process->log(get_class($this) . ': Couldn\'t set option REUSEADDR to socket (' . $errno . ' - ' . socket_strerror($errno) . ').');
120
                return false;
121
            }
122
            if (defined('SO_REUSEPORT') && !@socket_set_option($sock, SOL_SOCKET, SO_REUSEPORT, 1)) {
123
                $errno = socket_last_error();
124
                Daemon::$process->log(get_class($this) . ': Couldn\'t set option REUSEPORT to socket (' . $errno . ' - ' . socket_strerror($errno) . ').');
125
                return false;
126
            }
127
        }
128 View Code Duplication
        if (!@socket_bind($sock, $this->host, $this->port)) {
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...
129
            $errno = socket_last_error();
130
            $addr = $this->host . ':' . $this->port;
131
            Daemon::$process->log(get_class($this) . ': Couldn\'t bind UDP-socket \'' . $addr . '\' (' . $errno . ' - ' . socket_strerror($errno) . ').');
132
            return false;
133
        }
134
        socket_getsockname($sock, $this->host, $this->port);
135
        $addr = $this->host . ':' . $this->port;
0 ignored issues
show
Unused Code introduced by
$addr 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...
136
        socket_set_nonblock($sock);
137
        $this->setFd($sock);
138
        return true;
139
    }
140
141
    /**
142
     * Called when socket is bound
143
     * @return boolean Success
144
     */
145
    protected function onBound()
146
    {
147
        if (!$this->ev) {
148
            Daemon::log(get_class($this) . '::' . __METHOD__ . ': Couldn\'t set event on bound socket: ' . Debug::dump($this->fd));
149
            return false;
150
        }
151
        return true;
152
    }
153
154
    /**
155
     * Enable socket events
156
     * @return void
157
     */
158 View Code Duplication
    public function enable()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
159
    {
160
        if ($this->enabled) {
161
            return;
162
        }
163
        if (!$this->fd) {
164
            return;
165
        }
166
        $this->enabled = true;
167
168
        if ($this->ev === null) {
169
            if ($this->eventLoop === null) {
170
                // @TODO нужно перенести куда-то выше, не годиться тут инициировать
171
                $this->eventLoop = EventLoop::$instance;
172
            }
173
174
            $this->ev = $this->eventLoop->event($this->fd, \Event::READ | \Event::PERSIST, [$this, 'onReadUdp']);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->eventLoop->event(...ay($this, 'onReadUdp')) of type object<Event> is incompatible with the declared type object<EventListener\Event> of property $ev.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
175
            $this->onBound();
176
        } else {
177
            $this->onAcceptEv();
0 ignored issues
show
Bug introduced by
The call to onAcceptEv() misses some required arguments starting with $listener.
Loading history...
178
        }
179
        $this->ev->add();
180
    }
181
182
    /**
183
     * Called when we got UDP packet
184
     * @param resource $stream Descriptor
0 ignored issues
show
Documentation introduced by
Should the type for parameter $stream not be resource|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
185
     * @param integer $events Events
186
     * @param mixed $arg Attached variable
187
     * @return boolean Success.
188
     */
189
    public function onReadUdp($stream = null, $events = 0, $arg = null)
0 ignored issues
show
Unused Code introduced by
The parameter $stream is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $events is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $arg is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
190
    {
191
        if (Daemon::$process->reload) {
192
            return false;
193
        }
194
195
        if ($this->pool->maxConcurrency) {
196
            if ($this->pool->count() >= $this->pool->maxConcurrency) {
197
                $this->overload = true;
0 ignored issues
show
Bug introduced by
The property overload does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
198
                return false;
199
            }
200
        }
201
202
        $host = null;
203
        do {
204
            $l = @socket_recvfrom($this->fd, $buf, 10240, MSG_DONTWAIT, $host, $port);
205
            if (!$l) {
206
                break;
207
            }
208
            $key = '[' . $host . ']:' . $port;
209
            if (!isset($this->portsMap[$key])) {
210
                if ($this->pool->allowedClients !== null) {
211
                    if (!self::netMatch($this->pool->allowedClients, $host)) {
0 ignored issues
show
Bug introduced by
The property allowedClients does not seem to exist in PHPDaemon\Network\Pool.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
212
                        Daemon::log('Connection is not allowed (' . $host . ')');
213
                    }
214
                    continue;
215
                }
216
                $class = $this->pool->connectionClass;
217
                $conn = new $class(null, $this->pool);
218
                $conn->setDgram(true);
219
                $conn->onWriteEv(null);
220
                $conn->setPeername($host, $port);
221
                $conn->setParentSocket($this);
222
                $this->portsMap[$key] = $conn;
223
                $conn->timeoutRef = setTimeout(function ($timer) use ($conn) {
224
                    $conn->finish();
225
                    $timer->finish();
226
                }, $conn->timeout * 1e6);
227
                $conn->onUdpPacket($buf);
228
            } else {
229
                $conn = $this->portsMap[$key];
230
                $conn->onUdpPacket($buf);
231
                Timer::setTimeout($conn->timeoutRef);
232
            }
233
        } while (true);
234
        return $host !== null;
235
    }
236
}
237