Completed
Push — master ( a7af36...1cb211 )
by Evgenij
02:42
created

StreamedClientIo::resolveRemoteAddress()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * Async sockets
4
 *
5
 * @copyright Copyright (c) 2015, Efimov Evgenij <[email protected]>
6
 *
7
 * This source file is subject to the MIT license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
namespace AsyncSockets\Socket\Io;
11
12
use AsyncSockets\Frame\FramePickerInterface;
13
14
/**
15
 * Class StreamedClientIo
16
 */
17
class StreamedClientIo extends AbstractClientIo
18
{
19
    /**
20
     * Read attempts count
21
     */
22
    const READ_ATTEMPTS = 2;
23
24
    /**
25
     * Amount of read attempts
26
     *
27
     * @var int
28
     */
29
    private $readAttempts = self::READ_ATTEMPTS;
30
31
    /**
32
     * Remote socket address
33
     *
34
     * @var string
35
     */
36
    private $remoteAddress;
37
38
    /** {@inheritdoc} */
39 18
    protected function readRawDataIntoPicker(FramePickerInterface $picker)
40
    {
41
        // work-around https://bugs.php.net/bug.php?id=52602
42 18
        $resource         = $this->socket->getStreamResource();
43
        $readContext      = [
44 18
            'countCycles'       => 0,
45 18
            'dataBeforeIo'      => $this->getDataInSocket(),
46 18
            'isStreamDataEmpty' => false,
47 18
        ];
48
49
        do {
50 18
            $data = fread($resource, self::SOCKET_BUFFER_SIZE);
51 18
            $this->throwNetworkSocketExceptionIf($data === false, 'Failed to read data.');
52 17
            $isDataEmpty = $data === '';
53 17
            $result      = $picker->pickUpData($data, $this->getRemoteAddress());
54
55 17
            $readContext['countCycles']      += 1;
56 17
            $readContext['isStreamDataEmpty'] = $this->isReadDataActuallyEmpty($data);
57 17
            $this->readAttempts               = $this->resolveReadAttempts($readContext, $this->readAttempts);
58 17
        } while (!$this->isEndOfFrameReached($picker, false) && !$isDataEmpty);
59
60 17
        return $result;
61
    }
62
63
    /**
64
     * Calculate attempts value
65
     *
66
     * @param array $context Read context
67
     * @param int   $currentAttempts Current attempts counter
68
     *
69
     * @return int
70
     */
71 17
    private function resolveReadAttempts(array $context, $currentAttempts)
72
    {
73 17
        return ($context['countCycles'] === 1 && empty($context['dataBeforeIo'])) ||
74 8
               ($context['countCycles'] > 1   && $context['isStreamDataEmpty']) ?
75 17
            $currentAttempts - 1 :
76 17
            self::READ_ATTEMPTS;
77
78
    }
79
80
    /** {@inheritdoc} */
81 4
    protected function writeRawData($data)
82
    {
83 4
        $resource = $this->socket->getStreamResource();
84 4
        $test     = stream_socket_sendto($resource, '');
85 4
        $this->throwNetworkSocketExceptionIf($test !== 0, 'Failed to send data.');
86
87 3
        $written = fwrite($resource, $data, strlen($data));
88 3
        $this->throwNetworkSocketExceptionIf($written === false, 'Failed to send data.');
89
90 2
        if ($written === 0) {
91 2
            $this->throwExceptionIfNotConnected('Remote connection has been lost.');
92 1
        }
93
94 1
        return $written;
95
    }
96
97
    /** {@inheritdoc} */
98 24
    protected function isConnected()
99
    {
100 24
        return $this->resolveRemoteAddress() !== false;
101
    }
102
103
    /** {@inheritdoc} */
104 17
    protected function getRemoteAddress()
105
    {
106 17
        if ($this->remoteAddress === null) {
107 17
            $this->remoteAddress = $this->resolveRemoteAddress();
108 17
        }
109
110 17
        return $this->remoteAddress;
111
    }
112
113
    /**
114
     * Checks whether data read from stream buffer can be filled later
115
     *
116
     * @param string $data Read data
117
     *
118
     * @return bool
119
     */
120 17
    private function isReadDataActuallyEmpty($data)
121
    {
122 17
        $result = false;
123 17
        if ($data === '') {
124 8
            $dataInSocket = $this->getDataInSocket();
125 8
            $result       = $dataInSocket === '' || $dataInSocket === false;
126 8
        }
127
128 17
        return $result;
129
    }
130
131
    /** {@inheritdoc} */
132 17
    protected function isEndOfTransfer()
133
    {
134 17
        return $this->getDataInSocket() === '';
135
    }
136
137
    /** {@inheritdoc} */
138 4
    protected function canReachFrame()
139
    {
140 4
        return $this->readAttempts > 0 && $this->isConnected();
141
    }
142
143
    /**
144
     * Return first byte from socket buffer
145
     *
146
     * @return string
147
     */
148 18
    private function getDataInSocket()
149
    {
150 18
        return stream_socket_recvfrom($this->socket->getStreamResource(), 1, STREAM_PEEK);
151
    }
152
153
    /**
154
     * Return remote address if we connected or false otherwise
155
     *
156
     * @return string|bool
157
     */
158 24
    private function resolveRemoteAddress()
159
    {
160 24
        return stream_socket_get_name($this->socket->getStreamResource(), true);
161
    }
162
}
163