StreamSocket::getAddress()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * \AppserverIo\Server\Sockets\StreamSocket
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Johann Zelger <[email protected]>
15
 * @copyright 2015 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io/server
18
 * @link      http://www.appserver.io
19
 */
20
21
namespace AppserverIo\Server\Sockets;
22
23
use AppserverIo\Psr\Socket\SocketInterface;
24
use AppserverIo\Psr\Socket\SocketReadException;
25
use AppserverIo\Psr\Socket\SocketReadTimeoutException;
26
use AppserverIo\Psr\Socket\SocketServerException;
27
use AppserverIo\Psr\Socket\AppserverIo\Psr\Socket;
28
29
/**
30
 * Class StreamSocket
31
 *
32
 * @author    Johann Zelger <[email protected]>
33
 * @copyright 2015 TechDivision GmbH <[email protected]>
34
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
35
 * @link      https://github.com/appserver-io/server
36
 * @link      http://www.appserver.io
37
 */
38
class StreamSocket implements SocketInterface
39
{
40
41
    /**
42
     * Holds the connection resource itself.
43
     *
44
     * @var resource
45
     */
46
    protected $connectionResource;
47
48
    /**
49
     * Holds the actual resource id
50
     *
51
     * @var int
52
     */
53
    protected $connectionResourceId;
54
55
    /**
56
     * Holds the peer name of the client who connected
57
     *
58
     * @var string
59
     */
60
    protected $connectionPeername;
61
62
    /**
63
     * Creates a stream socket server and returns a instance of Stream implementation with server socket in it.
64
     *
65
     * @param string   $socket  The address incl. transport the server should be listening to. For example 0.0.0.0:8080
66
     * @param string   $flags   The flags to be set on server create
67
     * @param resource $context The context to be set on stream create
68
     *
69
     * @return \AppserverIo\Server\Sockets\StreamSocket The Stream instance with a server socket created.
70
     *
71
     * @throws \AppserverIo\Psr\Socket\SocketServerException
72
     */
73
    public static function getServerInstance($socket, $flags = null, $context = null)
74
    {
75
        // init flags if none were given
76
        if (is_null($flags)) {
77
            $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
78
        }
79
80
        // init context if none was given
81
        if (is_null($context)) {
82
            // set socket backlog to 1024 for perform many concurrent connections
83
            $opts = array(
84
                'socket' => array(
85
                    'backlog' => 1024
86
                ),
87
                'ssl' => array(
88
                    'crypto_method' => STREAM_CRYPTO_METHOD_TLS_SERVER
89
                )
90
            );
91
            // get default stream context for server connection with socket backlog preset
92
            $context = stream_context_get_default($opts);
93
        }
94
95
        // create stream socket server resource
96
        $serverResource = stream_socket_server($socket, $errno, $errstr, $flags, $context);
97
98
        // throw exception if it was not possible to create server socket binding
99
        if (!$serverResource) {
100
            throw new SocketServerException($errstr, $errno);
101
        }
102
103
        // set blocking mode
104
        stream_set_blocking($serverResource, 1);
105
        // create instance and return it.
106
        return self::getInstance($serverResource);
107
    }
108
109
    /**
110
     * Creates a stream socket client and returns a instance of Stream implementation with client socket in it.
111
     *
112
     * @param string   $socket  The address incl. transport the server should be listening to. For example 0.0.0.0:8080
113
     * @param string   $flags   The flags to be set on client create
114
     * @param resource $context The context to be set on stream create
115
     *
116
     * @return \AppserverIo\Server\Sockets\StreamSocket The Stream instance with a client socket created.
117
     *
118
     * @throws \AppserverIo\Psr\Socket\SocketServerException
119
     */
120
    public static function getClientInstance($socket, $flags = null, $context = null)
121
    {
122
        // init flags if none were given
123
        if (is_null($flags)) {
124
            $flags = STREAM_CLIENT_CONNECT;
125
        }
126
127
        // init context if none was given
128
        if (is_null($context)) {
129
            // create default stream context object
130
            $context = stream_context_get_default();
131
        }
132
133
        // create a stream socket client resource
134
        $clientResource = stream_socket_client($socket, $errno, $errstr, ini_get('default_socket_timeout'), $flags, $context);
135
136
        // throw exception if it was not possible to create server socket binding
137
        if (!$clientResource) {
138
            throw new SocketServerException($errstr, $errno);
139
        }
140
141
        // set blocking mode
142
        stream_set_blocking($clientResource, 1);
143
        // create instance and return it
144
        return self::getInstance($clientResource);
145
    }
146
147
    /**
148
     * Returns an instance of Stream with preset resource in it.
149
     *
150
     * @param resource $connectionResource The resource to use
151
     *
152
     * @return \AppserverIo\Server\Sockets\StreamSocket
153
     */
154
    public static function getInstance($connectionResource)
155
    {
156
        $connection = new self();
157
        $connection->setConnectionResource($connectionResource);
158
        return $connection;
159
    }
160
161
    /**
162
     * Accepts connections from clients and build up a instance of Stream with connection resource in it.
163
     *
164
     * @param int $acceptTimeout  The timeout in seconds to wait for accepting connections.
165
     * @param int $receiveTimeout The timeout in seconds to wait for read a line.
166
     *
167
     * @return \AppserverIo\Server\Sockets\StreamSocket|bool The Stream instance with the connection socket
168
     *                                                           accepted or bool false if timeout or error occurred.
169
     */
170
    public function accept($acceptTimeout = 600, $receiveTimeout = 60)
171
    {
172
        // init local ref vars
173
        $peername = null;
174
175
        $connectionResource = @stream_socket_accept($this->getConnectionResource(), $acceptTimeout, $peername);
176
        // if timeout or error occurred return false as accept function does
177
        if ($connectionResource === false) {
178
            return false;
179
        }
180
        // set timeout for read data fom client
181
        stream_set_timeout($connectionResource, $receiveTimeout);
182
        $connection = $this->getInstance($connectionResource);
183
        $connection->setPeername($peername);
184
        return $connection;
185
    }
186
187
    /**
188
     * Returns the line read from connection resource
189
     *
190
     * @param int $readLength     The max length to read for a line.
191
     * @param int $receiveTimeout The max time to wait for read the next line
192
     *
193
     * @return string
194
     * @throws \AppserverIo\Psr\Socket\SocketReadTimeoutException
195
     */
196 View Code Duplication
    public function readLine($readLength = 1024, $receiveTimeout = null)
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...
197
    {
198
        if ($receiveTimeout) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $receiveTimeout of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
199
            // set timeout for read data fom client
200
            @stream_set_timeout($this->getConnectionResource(), $receiveTimeout);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
201
        }
202
        $line = @fgets($this->getConnectionResource(), $readLength);
203
204
        // check if read error occured
205
        if ($line === false) {
206
            throw new SocketReadException();
207
        }
208
209
        // check if timeout occurred
210
        if (strlen($line) === 0) {
211
            throw new SocketReadTimeoutException();
212
        }
213
214
        return $line;
215
    }
216
217
    /**
218
     * Reads the given length from connection resource
219
     *
220
     * @param int $readLength     The max length to read for a line.
221
     * @param int $receiveTimeout The max time to wait for read the next line
222
     *
223
     * @return string
224
     *
225
     * @throws \AppserverIo\Psr\Socket\SocketReadTimeoutException
226
     * @throws \AppserverIo\Psr\Socket\SocketReadException
227
     */
228 View Code Duplication
    public function read($readLength = 1024, $receiveTimeout = null)
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...
229
    {
230
        if ($receiveTimeout) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $receiveTimeout of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
231
            // set timeout for read data fom client
232
            @stream_set_timeout($this->getConnectionResource(), $receiveTimeout);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
233
        }
234
        // read in line from client
235
        $line = @fread($this->getConnectionResource(), $readLength);
236
        // check if false is response
237
        if (false === $line) {
238
            // throw new socket exception
239
            throw new SocketReadException();
240
        }
241
        // check if timeout occurred
242
        if (strlen($line) === 0) {
243
            throw new SocketReadTimeoutException();
244
        }
245
        return $line;
246
    }
247
248
    /**
249
     * Writes the given message to the connection resource.
250
     *
251
     * @param string $message The message to write to the connection resource.
252
     *
253
     * @return int
254
     */
255
    public function write($message)
256
    {
257
        return @fwrite($this->getConnectionResource(), $message, strlen($message));
258
    }
259
260
    /**
261
     * Receives data from a socket, connected or not.
262
     *
263
     * This method HAS to be used when working with an UDP socket.
264
     *
265
     * @param integer $length The number of bytes to receive from the
266
     * @param integer $flags  The value of flags, can be STREAM_OOB or STREAM_PEEK
267
     *
268
     * @return string Returns the read data, as a string
269
     * @see http://docs.php.net/manual/de/function.stream-socket-recvfrom.php
270
     */
271
    public function receiveFrom($length = 512, $flags = null)
272
    {
273
274
        // initialize the peername
275
        $peername = '';
276
277
        // receive the data from the socket
278
        $buffer = stream_socket_recvfrom($this->getConnectionResource(), $length, $flags, $peername);
279
280
        // check if receiving the data has been succeeded
281
        if ($buffer === false) {
282
            // throw new socket exception
283
            throw new SocketReadException();
284
        }
285
286
        // set the peername
287
        $this->setPeername($peername);
288
289
        // return the received data
290
        return $buffer;
291
    }
292
293
    /**
294
     * Sends a message to a socket, whether it is connected or not.
295
     *
296
     *This method HAS to be used when working with an UDP socket.
297
     *
298
     * @param string  $message The data to be sent
299
     * @param integer $flags   The value of flags can be STREAM_OOB
300
     *
301
     * @return integer Returns a result code, as an integer
302
     * @see http://docs.php.net/manual/de/function.stream-socket-sendto.php
303
     */
304
    public function sendTo($message, $flags = 0)
0 ignored issues
show
Unused Code introduced by
The parameter $flags 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...
305
    {
306
        return @stream_socket_sendto($this->getConnectionResource(), $message, 0, $this->getPeername());
307
    }
308
309
    /**
310
     * Copies data from a stream
311
     *
312
     * @param resource $sourceResource The source stream
313
     *
314
     * @return int The total count of bytes copied.
315
     */
316
    public function copyStream($sourceResource)
317
    {
318
        // first try to rewind source resource stream
319
        @rewind($sourceResource);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
320
        // call internal stream copy function
321
        return @stream_copy_to_stream($sourceResource, $this->getConnectionResource());
322
    }
323
324
    /**
325
     * Closes the connection resource
326
     *
327
     * @return bool
328
     */
329
    public function close()
330
    {
331
        // check if resource still exists
332
        if (is_resource($this->getConnectionResource())) {
333
            return @fclose($this->getConnectionResource());
334
        }
335
        return false;
336
    }
337
338
    /**
339
     * Returns the stream socket's status
340
     *
341
     * @return bool|array The status information as array or false in case of an invalid stream socket resource
342
     */
343
    public function getStatus()
344
    {
345
        return @socket_get_status($this->getConnectionResource());
346
    }
347
348
    /**
349
     * Returns the meta information of the stream socket
350
     *
351
     * @return bool|array The meta informations of the stream socket
352
     */
353
    public function getMetaInfo()
354
    {
355
        return @stream_get_meta_data($this->getConnectionResource());
356
    }
357
358
    /**
359
     * Sets the connection resource
360
     *
361
     * @param resource $connectionResource The resource to socket file descriptor
362
     *
363
     * @return void
364
     */
365
    public function setConnectionResource($connectionResource)
366
    {
367
        $this->connectionResourceId = (int)$connectionResource;
368
        $this->connectionResource = $connectionResource;
369
    }
370
371
    /**
372
     * Sets the peer name
373
     *
374
     * @param string $peername The peername in format ip:port
375
     *
376
     * @return void
377
     */
378
    public function setPeername($peername)
379
    {
380
        $this->connectionPeername = $peername;
381
    }
382
383
    /**
384
     * Returns the peer name in format ip:port (e.g. 10.20.30.40:57128)
385
     *
386
     * @return string
387
     */
388
    public function getPeername()
389
    {
390
        return $this->connectionPeername;
391
    }
392
393
    /**
394
     * Returns the address of connection
395
     *
396
     * @return string
397
     */
398
    public function getAddress()
399
    {
400
        return strstr($this->getPeername(), ':', true);
401
    }
402
403
    /**
404
     * Returns the port of connection
405
     *
406
     * @return string
407
     */
408
    public function getPort()
409
    {
410
        return str_replace(':', '', strstr($this->getPeername(), ':'));
411
    }
412
413
    /**
414
     * Returns the connection resource
415
     *
416
     * @return mixed
417
     */
418
    public function getConnectionResource()
419
    {
420
        return $this->connectionResource;
421
    }
422
423
    /**
424
     * Returns connection resource id
425
     *
426
     * @return int
427
     */
428
    public function getConnectionResourceId()
429
    {
430
        return $this->connectionResourceId;
431
    }
432
}
433