Completed
Push — master ( e54759...0c780c )
by Valentin
03:37
created

Connection   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 227
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 21
eloc 95
c 2
b 0
f 0
dl 0
loc 227
ccs 81
cts 81
cp 1
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A hasSocket() 0 3 1
A disconnect() 0 4 1
A __construct() 0 12 3
A isServiceListening() 0 8 2
A dispatchCommand() 0 37 3
A getPort() 0 3 1
A build_query() 0 16 4
A getHost() 0 3 1
A setSocket() 0 3 1
A getSocket() 0 21 3
A getConnectTimeout() 0 3 1
1
<?php
2
3
namespace Pheanstalk;
4
5
use DOMXPath as DOMXPath;
6
use Pheanstalk\Socket\NativeSocket;
7
8
/**
9
 * A connection to a beanstalkd server.
10
 *
11
 * @author  Paul Annesley
12
 * @package Pheanstalk
13
 * @license http://www.opensource.org/licenses/mit-license.php
14
 */
15
class Connection
16
{
17
    const CRLF = "\r\n";
18
    const CRLF_LENGTH = 2;
19
    const DEFAULT_CONNECT_TIMEOUT = 2;
20
21
    // responses which are global errors, mapped to their exception short-names
22
    private static $_errorResponses = array(
0 ignored issues
show
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
23
        Response::RESPONSE_OUT_OF_MEMORY                    => 'OutOfMemory',
24
        Response::RESPONSE_INTERNAL_ERROR                   => 'InternalError',
25
        Response::RESPONSE_DRAINING                         => 'Draining',
26
        Response::RESPONSE_BAD_FORMAT                       => 'BadFormat',
27
        Response::RESPONSE_UNKNOWN_COMMAND                  => 'UnknownCommand',
28
        Response::RESPONSE_WORKFLOW_ALREADY_EXISTS          => 'DuplicateEntry',
29
        Response::RESPONSE_SERVER_ERROR                     => '',
30
    );
31
32
    // responses which are followed by data
33
    private static $_dataResponses = array(
0 ignored issues
show
introduced by
The private property $_dataResponses is not used, and could be removed.
Loading history...
Coding Style introduced by
Short array syntax must be used to define arrays
Loading history...
34
        Response::RESPONSE_RESERVED,
35
        Response::RESPONSE_FOUND,
36
        Response::RESPONSE_OK,
37
    );
38
39
    private $_socket;
40
    private $_hostname;
41
    private $_port;
42
    private $_connectTimeout;
43
    private $_connectPersistent;
44
    protected $user;
45
    protected $password;
46
47
    /**
48
     * @param string $hostname
49
     * @param string $user
50
     * @param string $password
51
     * @param int    $port
52
     * @param float  $connectTimeout
53
     * @param bool   $connectPersistent
54
     */
55 9
    public function __construct($hostname, $user = null, $password = null, $port = 5000, $connectTimeout = null, $connectPersistent = false)
56
    {
57 9
        if (is_null($connectTimeout) || !is_numeric($connectTimeout)) {
58 6
            $connectTimeout = self::DEFAULT_CONNECT_TIMEOUT;
59
        }
60
61 9
        $this->_hostname = $hostname;
62 9
        $this->_port = $port;
63 9
        $this->user = $user;
64 9
        $this->password = $password;
65 9
        $this->_connectTimeout = $connectTimeout;
66 9
        $this->_connectPersistent = $connectPersistent;
67
    }
68
69
    /**
70
     * @return bool
71
     */
72 1
    public function hasSocket()
73
    {
74 1
        return isset($this->_socket);
75
    }
76
77
    /**
78
     * Disconnect the socket.
79
     * Subsequent socket operations will create a new connection.
80
     */
81 1
    public function disconnect()
82
    {
83 1
        $this->getSocket()->disconnect();
84 1
        $this->_socket = null;
85
    }
86
87
    /**
88
     * @param Command $command
89
     *
90
     * @throws Exception\ClientException
91
     *
92
     * @return object Response
93
     */
94 7
    public function dispatchCommand(Command $command)
95
    {
96 7
        $socket = $this->getSocket();
97
98 6
        $dom = $this->build_query($command->getGroup(), $command->getAction(), $command->getFilters(), $command->getParameters());
0 ignored issues
show
Bug introduced by
$command->getAction() of type string is incompatible with the type boolean expected by parameter $action of Pheanstalk\Connection::build_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

98
        $dom = $this->build_query($command->getGroup(), /** @scrutinizer ignore-type */ $command->getAction(), $command->getFilters(), $command->getParameters());
Loading history...
99 6
        $xml = $dom->saveXML();
100 6
        $socket->write($xml);
101
102 6
        $responseLine = $socket->getLine();
103 6
        if ($command->getGroup() . ':' . $command->getAction() === 'instance:query') {
104
//            dump($responseLine, $command->getGroup() . ':' . $command->getAction());
105
        }
106 6
        $xml = new \SimpleXMLElement($responseLine);
107 6
        $json = json_encode($xml);
108 6
        $responseLine = json_decode($json,TRUE);
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected true, but found TRUE.
Loading history...
109 6
        $responseName = preg_replace('#^(\S+).*$#s', '$1', $responseLine["@attributes"]['status']);
110 6
        if ($responseName === "KO") {
111 2
            $exceptionType = $responseLine['@attributes']['error-code'] ?? Response::RESPONSE_SERVER_ERROR;
112 2
            $exception = sprintf(
113 2
                '\Pheanstalk\Exception\Server%sException',
114 2
                self::$_errorResponses[$exceptionType] ?? ''
115
            );
116 2
            throw new $exception(sprintf(
117 2
                "%s while executing %s:%s",
118 2
                $responseLine['@attributes']['error'],
119 2
                $command->getGroup(),
120 2
                $command->getAction()
121
            ));
122
        }
123
124
125 4
        $data = $responseLine;
126
127
128
        return $command
129 4
            ->getResponseParser()
130 4
            ->parseResponse($responseLine["@attributes"]['status'], $data);
131
    }
132
133
    /**
134
     * Returns the connect timeout for this connection.
135
     *
136
     * @return float
137
     */
138 1
    public function getConnectTimeout()
139
    {
140 1
        return $this->_connectTimeout;
141
    }
142
143
    /**
144
     * Returns the host for this connection.
145
     *
146
     * @return string
147
     */
148 1
    public function getHost()
149
    {
150 1
        return $this->_hostname;
151
    }
152
153
    /**
154
     * Returns the port for this connection.
155
     *
156
     * @return int
157
     */
158 1
    public function getPort()
159
    {
160 1
        return $this->_port;
161
    }
162
163
    // ----------------------------------------
164
165
    /**
166
     * Socket handle for the connection to beanstalkd.
167
     *
168
     * @throws Exception\ConnectionException
169
     *
170
     * @return Socket
171
     */
172 9
    public function getSocket()
173
    {
174 9
        if (!isset($this->_socket)) {
175 8
            $this->_socket = new NativeSocket(
176 8
                $this->_hostname,
177 8
                $this->_port,
178 8
                $this->_connectTimeout,
0 ignored issues
show
Bug introduced by
It seems like $this->_connectTimeout can also be of type double; however, parameter $connectTimeout of Pheanstalk\Socket\NativeSocket::__construct() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
                /** @scrutinizer ignore-type */ $this->_connectTimeout,
Loading history...
179 8
                $this->_connectPersistent
180
            );
181 6
            $response = $this->_socket->getLine();
182 6
            $xml = new \SimpleXMLElement($response);
183 6
            $challenge = (string) $xml['challenge'];
184 6
            if (!empty($challenge)) {
185 6
                $hmac = hash_hmac("sha1", hex2bin($challenge), sha1($this->password, true));
186 6
                $dom = $this->build_query('auth', false, ["response" => $hmac, "user" => $this->user]);
187 6
                $this->_socket->write($dom->saveXML());
188 6
                $recv = $this->_socket->getLine();
0 ignored issues
show
Unused Code introduced by
The assignment to $recv is dead and can be removed.
Loading history...
189
            }
190
        }
191
192 7
        return $this->_socket;
193
    }
194
195
    /**
196
     * @param NativeSocket $socket
197
     */
198 2
    public function setSocket(NativeSocket $socket)
199
    {
200 2
        $this->_socket = $socket;
201
    }
202
203
    /**
204
     * @param       $name
205
     * @param bool  $action
206
     * @param array $attributes
207
     * @param array $parameters
208
     *
209
     * @return \DOMDocument
210
     */
211 7
    protected function build_query($name, $action = false, $attributes = [], $parameters = []){
0 ignored issues
show
Coding Style introduced by
Method name "Connection::build_query" is not in camel caps format
Loading history...
212 7
        $dom = new \DOMDocument("1.0", "utf-8");
213 7
        $root = $dom->createElement($name);
214 7
        if($action)
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after IF keyword; 0 found
Loading history...
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
215 6
            $root->setAttribute('action', $action);
0 ignored issues
show
Bug introduced by
$action of type true is incompatible with the type string expected by parameter $value of DOMElement::setAttribute(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

215
            $root->setAttribute('action', /** @scrutinizer ignore-type */ $action);
Loading history...
216 7
        foreach ($attributes as $key => $value) {
217 7
            $root->setAttribute($key, $value);
218
        }
219 7
        foreach ($parameters as $parameter => $value) {
220 1
            $param = $dom->createElement('parameter');
221 1
            $param->setAttribute('name', $parameter);
222 1
            $param->setAttribute('value', $value);
223 1
            $root->appendChild($param);
224
        }
225 7
        $dom->appendChild($root);
226 7
        return $dom;
227
    }
228
229
    /**
230
     * Checks connection to the beanstalkd socket.
231
     *
232
     * @return true|false
233
     */
234 2
    public function isServiceListening()
235
    {
236
        try {
237 2
            $this->getSocket();
238
239 1
            return true;
240 1
        } catch (Exception\ConnectionException $e) {
241 1
            return false;
242
        }
243
    }
244
}
245