Completed
Push — master ( aa9ebf...10f3e1 )
by Valentin
22s queued 12s
created

NativeSocket::recv()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 7
ccs 0
cts 5
cp 0
rs 10
cc 2
nc 2
nop 0
crap 6
1
<?php
2
3
namespace Pheanstalk\Socket;
4
5
use Pheanstalk\Exception;
6
use Pheanstalk\Socket;
7
8
/**
9
 * A Socket implementation around a fsockopen() stream.
10
 *
11
 * @author  Paul Annesley
12
 * @package Pheanstalk
13
 * @license http://www.opensource.org/licenses/mit-license.php
14
 */
15
class NativeSocket implements Socket
16
{
17
    /**
18
     * The default timeout for a blocking read on the socket.
19
     */
20
    const SOCKET_TIMEOUT = 1;
21
22
    /**
23
     * Number of retries for attempted writes which return zero length.
24
     */
25
    const WRITE_RETRIES = 8;
26
27
    private $_socket;
28
29
    /**
30
     * @param string $host
31
     * @param int    $port
32
     * @param int    $connectTimeout
33
     * @param bool   $connectPersistent
34
     */
35 8
    public function __construct($host, $port, $connectTimeout, $connectPersistent)
36
    {
37 8
        if ($connectPersistent) {
38 1
            $this->_socket = $this->_wrapper()
39 1
                ->pfsockopen($host, $port, $errno, $errstr, $connectTimeout);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $errstr seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $errno seems to be never defined.
Loading history...
40
        } else {
41 7
            $this->_socket = $this->_wrapper()
42 7
                ->fsockopen($host, $port, $errno, $errstr, $connectTimeout);
43
        }
44
45 8
        if (!$this->_socket) {
46 2
            throw new Exception\ConnectionException($errno, $errstr." (connecting to $host:$port)");
47
        }
48
49 6
        $this->_wrapper()
50 6
            ->stream_set_timeout($this->_socket, self::SOCKET_TIMEOUT);
51
    }
52
53
    /* (non-phpdoc)
54
     * @see Socket::write()
55
     */
56 6
    public function write($data)
57
    {
58 6
        $history = new WriteHistory(self::WRITE_RETRIES);
59
60 6
        for ($written = 0, $fwrite = 0; $written < strlen($data); $written += $fwrite) {
0 ignored issues
show
Unused Code introduced by
The assignment to $fwrite is dead and can be removed.
Loading history...
61 6
            $fwrite = $this->_wrapper()
62 6
                ->fwrite($this->_socket, substr($data, $written));
63
64 6
            $history->log($fwrite);
65
66 6
            if ($history->isFullWithNoWrites()) {
67
                throw new Exception\SocketException(sprintf(
68
                    'fwrite() failed to write data after %u tries',
69
                    self::WRITE_RETRIES
70
                ));
71
            }
72
        }
73
    }
74
75
    /* (non-phpdoc)
76
     * @see Socket::write()
77
     */
78
    public function read($length)
79
    {
80
        $read = 0;
81
        $parts = '';
82
83
        while ($read < $length && !$this->_wrapper()->feof($this->_socket)) {
84
            $data = $this->_wrapper()
85
                ->fread($this->_socket, $length - $read);
86
87
            if ($data === false) {
88
                throw new Exception\SocketException('fread() returned false');
89
            }
90
91
            $read += strlen($data);
92
            $parts .= $data;
93
        }
94
95
        return $parts;
96
    }
97
98
    /**
99
     * @return \SimpleXMLElement
100
     */
101
    public function recv()
102
    {
103
        $response = "";
104
        while ($recv = $this->_wrapper()->fread($this->_socket, 1600)) {
105
            $response .= $recv;
106
        }
107
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response returns the type string which is incompatible with the documented return type SimpleXMLElement.
Loading history...
108
    }
109
110
    /**
111
     * Request a socket until the returned datas are a valid xml string
112
     * @param null $length
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $length is correct as it would always require null to be passed?
Loading history...
113
     *
114
     * @return string
115
     * @throws Exception\SocketException
116
     */
117 6
    public function getLine($length = null)
118
    {
119 6
        $timeout = (int) 5;
120 6
        $timer   = microtime(true);
121 6
        $data = '';
122 6
        libxml_use_internal_errors(true);
123
        do {
124 6
            libxml_clear_errors();
125 6
                $data .= isset($length) ?
126 6
                    $this->_wrapper()->fgets($this->_socket, $length) : $this->_wrapper()->fgets($this->_socket);
127 6
            simplexml_load_string($data);
128 6
            if (!empty(libxml_get_errors()) && microtime(true) - $timer > $timeout) {
129
                $this->disconnect();
130
                throw new Exception\SocketException('Socket timed out!');
131 6
            } elseif (empty(libxml_get_errors())) {
132
                try {
133 6
                    $xml = new \SimpleXMLElement($data);
134
                } catch (\Exception $e) {
135
                    $error = (string) (isset($xml['error'])) ? $xml['error'] : 'Socket closed by server!';
136
                    throw new Exception\SocketException($error);
137
                }
138
            }
139 6
        } while (!empty(libxml_get_errors()));
140
141 6
        libxml_clear_errors();
142
143 6
        return rtrim($data);
144
    }
145
146 1
    public function disconnect()
147
    {
148 1
        $this->_wrapper()->fclose($this->_socket);
149
    }
150
151
    // ----------------------------------------
152
153
    /**
154
     * Wrapper class for all stream functions.
155
     * Facilitates mocking/stubbing stream operations in unit tests.
156
     */
157 8
    private function _wrapper()
158
    {
159 8
        return StreamFunctions::instance();
160
    }
161
}
162