Completed
Push — master ( 9cb483...15ad7c )
by Maik
03:24
created

SecureSocket::isWriteable()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 28
Code Lines 15

Duplication

Lines 28
Ratio 100 %

Code Coverage

Tests 11
CRAP Score 5.246

Importance

Changes 0
Metric Value
dl 28
loc 28
ccs 11
cts 14
cp 0.7856
rs 8.439
c 0
b 0
f 0
cc 5
eloc 15
nc 5
nop 0
crap 5.246
1
<?php
2
namespace Generics\Socket;
3
4
use Composer\CaBundle\CaBundle;
5
use Generics\ResetException;
6
use Generics\Streams\SocketStream;
7
use Countable;
8
use Exception;
9
10
abstract class SecureSocket implements SocketStream
11
{
12
13
    /**
14
     * The socket handle
15
     *
16
     * @var resource
17
     */
18
    protected $handle;
19
20
    /**
21
     * The socket endpoint
22
     *
23
     * @var Endpoint
24
     */
25
    protected $endpoint;
26
27
    /**
28
     * The stream context
29
     *
30
     * @var resource
31
     */
32
    private $streamContext;
33
34
    /**
35
     * Create a new socket
36
     *
37
     * @param Endpoint $endpoint
38
     *            The endpoint for the socket
39
     */
40 11
    public function __construct(Endpoint $endpoint)
41
    {
42 11
        $this->endpoint = $endpoint;
43 11
        $this->open();
44 11
    }
45
46
    /**
47
     *
48
     * {@inheritdoc}
49
     * @see \Generics\Streams\InputStream::read()
50
     */
51 9
    public function read($length = 1, $offset = null): string
52
    {
53 9
        return stream_get_contents($this->handle, $length, $offset == null ? - 1 : intval($offset));
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $offset of type integer|null against null; this is ambiguous if the integer can be zero. Consider using a strict comparison === instead.
Loading history...
54
    }
55
56
    /**
57
     *
58
     * {@inheritdoc}
59
     * @see \Generics\Streams\Stream::isOpen()
60
     */
61
    public function isOpen(): bool
62
    {
63
        return is_resource($this->handle) && ! feof($this->handle);
64
    }
65
66
    /**
67
     *
68
     * {@inheritdoc}
69
     * @see \Generics\Streams\OutputStream::flush()
70
     */
71
    public function flush()
72
    {
73
        // flush not available on streams
74
    }
75
76
    /**
77
     *
78
     * {@inheritdoc}
79
     * @see \Generics\Streams\Stream::ready()
80
     */
81 10 View Code Duplication
    public function ready(): bool
82
    {
83 10
        if (! is_resource($this->handle)) {
84
            return false;
85
        }
86
        
87
        $read = array(
88 10
            $this->handle
89
        );
90 10
        $write = null;
91 10
        $except = null;
92
        
93 10
        $num = @stream_select($read, $write, $except, 0);
94
        
95 10
        if ($num === false) {
96
            throw new SocketException("Could not determine the stream client status");
97
        }
98
        
99 10
        if ($num < 1) {
100 10
            return false;
101
        }
102
        
103 9
        if (! in_array($this->handle, $read)) {
104
            return false;
105
        }
106
        
107 9
        return true;
108
    }
109
110
    /**
111
     *
112
     * {@inheritdoc}
113
     * @see Countable::count()
114
     */
115
    public function count()
116
    {
117
        $meta = stream_get_meta_data($this->handle);
118
        
119
        foreach ($meta as $data) {
120
            if (strstr($data, 'Content-Length:')) {
121
                return intval(trim(substr($data, $start, 15)));
0 ignored issues
show
Bug introduced by
The variable $start does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
122
            }
123
        }
124
        throw new SocketException("Cannot count elements of stream client");
125
    }
126
127
    /**
128
     *
129
     * {@inheritdoc}
130
     * @see \Generics\Resettable::reset()
131
     */
132 View Code Duplication
    public function reset()
133
    {
134
        try {
135
            $this->close();
136
            $this->open();
137
        } catch (Exception $ex) {
138
            throw new ResetException($ex->getMessage(), array(), $ex->getCode(), $ex);
139
        }
140
    }
141
142
    /**
143
     *
144
     * {@inheritdoc}
145
     * @see \Generics\Streams\Stream::close()
146
     */
147 6
    public function close()
148
    {
149 6
        if (is_resource($this->handle)) {
150 6
            fclose($this->handle);
151 6
            $this->handle = null;
152
        }
153 6
    }
154
155
    /**
156
     *
157
     * {@inheritdoc}
158
     * @see \Generics\Streams\OutputStream::write()
159
     */
160 11
    public function write($buffer)
161
    {
162 11
        if(!$this->isWriteable()) {
163 1
            throw new SocketException("Stream is not ready for writing");
164
        }
165 10
        $len = strlen($buffer);
166 10
        $written = 0;
167
        do {
168 10
            $bytes = fwrite($this->handle, $buffer);
169 10
            if ($bytes === false) {
170
                throw new SocketException("Could not write {len} bytes to stream (at least {written} written)", array(
171
                    'len' => $len,
172
                    'written' => $written
173
                ));
174
            }
175 10
            $written += $bytes;
176 10
        } while ($written != $len);
177 10
    }
178
179
    /**
180
     *
181
     * {@inheritdoc}
182
     * @see \Generics\Streams\OutputStream::isWriteable()
183
     */
184 11 View Code Duplication
    public function isWriteable(): bool
185
    {
186 11
        if (! is_resource($this->handle)) {
187 1
            return false;
188
        }
189
        
190 10
        $read = null;
191
        $write = array(
192 10
            $this->handle
193
        );
194 10
        $except = null;
195
        
196 10
        $num = @stream_select($read, $write, $except, 0, 0);
197
        
198 10
        if ($num === false) {
199
            throw new SocketException("Could not determine the stream client status");
200
        }
201
        
202 10
        if ($num < 1) {
203
            return false;
204
        }
205
        
206 10
        if (! in_array($this->handle, $write)) {
207
            return false;
208
        }
209
        
210 10
        return true;
211
    }
212
213 11
    private function open()
214
    {
215 11
        $this->prepareStreamContext();
216
        
217 11
        $this->handle = stream_socket_client(sprintf('ssl://%s:%d', $this->endpoint->getAddress(), $this->endpoint->getPort()), //
218 11
$error, $errorString, 2, STREAM_CLIENT_CONNECT, $this->streamContext);
219
        
220 11
        if ($error) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $error 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...
221
            throw new SocketException($errorString, array(), $error);
222
        }
223 11
    }
224
225 11
    private function prepareStreamContext()
226
    {
227
        $opts = array(
228 11
            'http' => array(
229
                'method' => "GET"
230
            )
231
        );
232
        
233 11
        $caPath = CaBundle::getSystemCaRootBundlePath();
234
        
235 11
        if (is_dir($caPath)) {
236
            $opts['ssl']['capath'] = $caPath;
237
        } else {
238 11
            $opts['ssl']['cafile'] = $caPath;
239
        }
240
        
241 11
        $this->streamContext = stream_context_create($opts);
242 11
    }
243
244
    /**
245
     * Retrieve end point object
246
     *
247
     * @return Endpoint
248
     */
249 11
    public function getEndPoint(): Endpoint
250
    {
251 11
        return $this->endpoint;
252
    }
253
}