SecureSocket::count()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 6
cp 0
rs 9.9
c 0
b 0
f 0
cc 3
nc 3
nop 0
crap 12
1
<?php
2
/**
3
 * This file is part of the PHP Generics package.
4
 *
5
 * @package Generics
6
 */
7
namespace Generics\Socket;
8
9
use Composer\CaBundle\CaBundle;
10
use Generics\ResetException;
11
use Generics\Streams\SocketStream;
12
use Countable;
13
use Exception;
14
15
/**
16
 * This abstract class provides basic secure socket functionality
17
 *
18
 * @author Maik Greubel <[email protected]>
19
 */
20
abstract class SecureSocket implements SocketStream
21
{
22
23
    /**
24
     * The socket handle
25
     *
26
     * @var resource
27
     */
28
    protected $handle;
29
30
    /**
31
     * The socket endpoint
32
     *
33
     * @var Endpoint
34
     */
35
    protected $endpoint;
36
37
    /**
38
     * The stream context
39
     *
40
     * @var resource
41
     */
42
    private $streamContext;
43
44
    /**
45
     * Create a new socket
46
     *
47
     * @param Endpoint $endpoint
48
     *            The endpoint for the socket
49
     */
50 12
    public function __construct(Endpoint $endpoint)
51
    {
52 12
        $this->endpoint = $endpoint;
53 12
        $this->open();
54 12
    }
55
56
    /**
57
     *
58
     * {@inheritdoc}
59
     * @see \Generics\Streams\InputStream::read()
60
     */
61 9
    public function read($length = 1, $offset = null): string
62
    {
63 9
        return stream_get_contents($this->handle, $length, $offset === null ? - 1 : intval($offset));
64
    }
65
66
    /**
67
     *
68
     * {@inheritdoc}
69
     * @see \Generics\Streams\Stream::isOpen()
70
     */
71
    public function isOpen(): bool
72
    {
73
        return is_resource($this->handle) && ! feof($this->handle);
74
    }
75
76
    /**
77
     *
78
     * {@inheritdoc}
79
     * @see \Generics\Streams\OutputStream::flush()
80
     */
81
    public function flush()
82
    {
83
        // flush not available on streams
84
    }
85
86
    /**
87
     *
88
     * {@inheritdoc}
89
     * @see \Generics\Streams\Stream::ready()
90
     */
91 10 View Code Duplication
    public function ready(): bool
92
    {
93 10
        if (! is_resource($this->handle)) {
94
            return false;
95
        }
96
        
97
        $read = array(
98 10
            $this->handle
99
        );
100 10
        $write = null;
101 10
        $except = null;
102
        
103 10
        $num = @stream_select($read, $write, $except, 0);
104
        
105 10
        if ($num === false) {
106
            throw new SocketException("Could not determine the stream client status");
107
        }
108
        
109 10
        if ($num < 1) {
110 10
            return false;
111
        }
112
        
113 9
        if (! in_array($this->handle, $read)) {
114
            return false;
115
        }
116
        
117 9
        return true;
118
    }
119
120
    /**
121
     *
122
     * {@inheritdoc}
123
     * @see Countable::count()
124
     */
125
    public function count()
126
    {
127
        $meta = stream_get_meta_data($this->handle);
128
        
129
        foreach ($meta as $data) {
130
            if (strstr($data, 'Content-Length:')) {
131
                return intval(trim(substr($data, 15)));
132
            }
133
        }
134
        throw new SocketException("Cannot count elements of stream client");
135
    }
136
137
    /**
138
     *
139
     * {@inheritdoc}
140
     * @see \Generics\Resettable::reset()
141
     */
142 View Code Duplication
    public function reset()
143
    {
144
        try {
145
            $this->close();
146
            $this->open();
147
        } catch (Exception $ex) {
148
            throw new ResetException($ex->getMessage(), array(), $ex->getCode(), $ex);
149
        }
150
    }
151
152
    /**
153
     *
154
     * {@inheritdoc}
155
     * @see \Generics\Streams\Stream::close()
156
     */
157 6
    public function close()
158
    {
159 6
        if (is_resource($this->handle)) {
160 6
            fclose($this->handle);
161 6
            $this->handle = null;
162
        }
163 6
    }
164
165
    /**
166
     *
167
     * {@inheritdoc}
168
     * @see \Generics\Streams\OutputStream::write()
169
     */
170 11
    public function write($buffer)
171
    {
172 11
        if (!$this->isWriteable()) {
173 1
            throw new SocketException("Stream is not ready for writing");
174
        }
175 10
        $len = strlen($buffer);
176 10
        $written = 0;
177
        do {
178 10
            $bytes = fwrite($this->handle, $buffer);
179 10
            if ($bytes === false) {
180
                throw new SocketException("Could not write {len} bytes to stream (at least {written} written)", array(
181
                    'len' => $len,
182
                    'written' => $written
183
                ));
184
            }
185 10
            $written += $bytes;
186 10
        } while ($written != $len);
187 10
    }
188
189
    /**
190
     *
191
     * {@inheritdoc}
192
     * @see \Generics\Streams\OutputStream::isWriteable()
193
     */
194 11 View Code Duplication
    public function isWriteable(): bool
195
    {
196 11
        if (! is_resource($this->handle)) {
197 1
            return false;
198
        }
199
        
200 10
        $read = null;
201
        $write = array(
202 10
            $this->handle
203
        );
204 10
        $except = null;
205
        
206 10
        $num = @stream_select($read, $write, $except, 0, 0);
207
        
208 10
        if ($num === false) {
209
            throw new SocketException("Could not determine the stream client status");
210
        }
211
        
212 10
        if ($num < 1) {
213
            return false;
214
        }
215
        
216 10
        if (! in_array($this->handle, $write)) {
217
            return false;
218
        }
219
        
220 10
        return true;
221
    }
222
223 12
    private function open()
224
    {
225 12
        $this->prepareStreamContext();
226
        
227 12
        $this->handle = stream_socket_client(
228 12
            sprintf('ssl://%s:%d', $this->endpoint->getAddress(), $this->endpoint->getPort()), //
229 12
            $error,
230 12
            $errorString,
231 12
            2,
232 12
            STREAM_CLIENT_CONNECT,
233 12
            $this->streamContext
234
        );
235
        
236 12
        if ($error > 0) {
237
            throw new SocketException($errorString, array(), $error);
238
        }
239 12
    }
240
241 12
    private function prepareStreamContext()
242
    {
243
        $opts = array(
244 12
            'http' => array(
245
                'method' => "GET"
246
            )
247
        );
248
        
249 12
        $caPath = CaBundle::getSystemCaRootBundlePath();
250
        
251 12
        if (is_dir($caPath)) {
252
            $opts['ssl']['capath'] = $caPath;
253
        } else {
254 12
            $opts['ssl']['cafile'] = $caPath;
255
        }
256
        
257 12
        $this->streamContext = stream_context_create($opts);
258 12
    }
259
260
    /**
261
     * Retrieve end point object
262
     *
263
     * @return Endpoint
264
     */
265 11
    public function getEndPoint(): Endpoint
266
    {
267 11
        return $this->endpoint;
268
    }
269
}
270