Completed
Push — GearmanClient ( 413f25...9cbb32 )
by Vasily
03:56
created

Connection::onReady()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 3
eloc 6
nc 4
nop 0
dl 0
loc 10
rs 9.4285
1
<?php
2
namespace PHPDaemon\Clients\GearmanClient;
3
4
use PHPDaemon\Network\ClientConnection;
5
use PHPDaemon\Utils\Crypt;
6
7
8
/**
9
 * @package NetworkClients
10
 * @subpackage GearmanClient
11
 * @protocol http://gearman.org/protocol/
12
 *
13
 * @interface http://php.net/manual/ru/class.gearmanclient.php
14
 *
15
 * @author Popov Gennadiy <[email protected]>
16
 */
17
class  Connection extends ClientConnection {
0 ignored issues
show
Coding Style introduced by
Expected 1 space between class keyword and class name; 2 found
Loading history...
18
19
    /**
20
     * Magic code for request
21
     */
22
    const MAGIC_REQUEST         = "\0REQ";
23
24
    /**
25
     * Magic code for response
26
     */
27
    const MAGIC_RESPONSE        = "\0RES";
28
29
    /*
30
     * Byte length of header
31
     */
32
    const HEADER_LENGTH         = 12;
33
34
    /**
35
     * Header binary format
36
     */
37
    const HEADER_WRITE_FORMAT   = "a4NN";
38
39
    /**
40
     * Header read format
41
     */
42
    const HEADER_READ_FORMAT    = "a4magic/Ntype/Nsize";
43
44
    /**
45
     * Delimeter for function arguments
46
     */
47
    const ARGS_DELIMITER        = "\0";
48
49
50
51
    /**
52
     * Request codes
53
     *
54
     * @var array
55
     */
56
    protected static $requestCommandList = [
57
        'CAN_DO' => 1,
58
        'CANT_DO' => 2,
59
        'RESET_ABILITIES' => 3,
60
        'PRE_SLEEP' => 4,
61
        'SUBMIT_JOB' => 7,
62
        'GRAB_JOB' => 9,
63
        'WORK_STATUS' => 12,
64
        'WORK_COMPLETE' => 13,
65
        'WORK_FAIL' => 14,
66
        'GET_STATUS' => 15,
67
        'ECHO_REQ' => 16,
68
        'SUBMIT_JOB_BG' => 18,
69
        'SUBMIT_JOB_HIGH' => 21,
70
        'SET_CLIENT_ID' => 22,
71
        'CAN_DO_TIMEOUT' => 23,
72
        'ALL_YOURS' => 24,
73
        'WORK_EXCEPTION' => 25,
74
        'OPTION_REQ' => 26,
75
        'OPTION_RES' => 27,
76
        'WORK_DATA' => 28,
77
        'WORK_WARNING' => 29,
78
        'GRAB_JOB_UNIQ' => 30,
79
        'SUBMIT_JOB_HIGH_BG' => 32,
80
        'SUBMIT_JOB_LOW' => 33,
81
        'SUBMIT_JOB_LOW_BG' => 34,
82
        'SUBMIT_JOB_SCHED' => 35,
83
        'SUBMIT_JOB_EPOCH' => 36,
84
    ];
85
    protected static $requestCommandListFlipped;
86
87
    /**
88
     * Response codes
89
     *
90
     * @var array
91
     */
92
    protected static $responseCommandList = [
93
        'NOOP' => 6,
94
        'JOB_CREATED' => 8,
95
        'NO_JOB' => 10,
96
        'JOB_ASSIGN' => 11,
97
        'WORK_STATUS' => 12,
98
        'WORK_COMPLETE' => 13,
99
        'WORK_FAIL' => 14,
100
        'ECHO_RES' => 17,
101
        'ERROR' => 19,
102
        'STATUS_RES' => 20,
103
        'WORK_EXCEPTION' => 25,
104
        'OPTION_RES' => 27,
105
        'WORK_WARNING' => 29,
106
        'JOB_ASSIGN_UNIQ' => 31,
107
    ];
108
109
    protected static $responseCommandListFlipped;
110
111
    /**
112
     * @var \SplStack
113
     */
114
    protected $reqStack;
115
116
117
    /**
118
     * @var mixed
119
     */
120
    public $response;
121
122
    /**
123
     * @var string
124
     */
125
    public $responseType;
126
127
    /**
128
     * @var string
129
     */
130
    public $responseCommand;
131
132
    /**
133
     * Constructor
134
     * @return void
135
     */
136
    protected function init()
137
    {
138
        $this->reqStack = new \SplStack;
139
    }
140
    /**
141
     * Called when new data received
142
     *
143
     * @return void
144
     */
145
    public function onRead()
146
    {
147
        if (($head = $this->lookExact(static::HEADER_LENGTH)) === false) {
148
            return;
149
        }
150
151
        list($magic, $typeInt, $size) = unpack(static::HEADER_READ_FORMAT, $head);
152
153
        if ($this->getInputLength() < static::HEADER_LENGTH + $size) {
154
            return;
155
        }
156
157
        $this->drain(static::HEADER_LENGTH);
158
        $pct = $this->read($size);
159
160
        if ($magic === static::MAGIC_RESPONSE) {
161
            $this->responseType = static::responseCommandListFlipped[$typeInt];
162
            $this->responseCommand = $this->reqStack->isEmpty()
163
                ? ''
164
                : $this->reqStack->shift();
165
            if ($this->responseCommand === 'SUBMIT_JOB' && $this->responseType !== 'WORK_COMPLETE') {
166
                return;
167
            }
168
            $this->response = explode(static::ARGS_DELIMITER, $pct);
169
            $this->onResponse->executeOne($this);
170
            $this->responseType = null;
171
            $this->responseCommand = null;
172
            $this->responseType = null;
173
            $this->checkFree();
174
            return;
175
        } else {
176
            $type = static::$requestCommandListFlipped[$typeInt];
0 ignored issues
show
Unused Code introduced by
$type is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
177
            // @TODO
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
178
        }
179
    }
180
    
181
    /**
182
     * Called when the connection is handshaked (at low-level), and peer is ready to recv. data
183
     * @return void
184
     */
185
    public function onReady()
186
    {
187
        if (static::$requestCommandListFlipped === null) {
188
            static::$requestCommandListFlipped = array_flip(static::$requestCommandList);
189
        }
190
        if (static::$responseCommandListFlipped === null) {
191
            static::$responseCommandListFlipped = array_flip(static::$responseCommandList);
192
        }
193
        parent::onReady();
194
    }
195
196
197
    /**
198
     * Function send ECHO
199
     *
200
     * @param $payload
201
     * @param callable|null $cb
202
     */
203
    public function sendEcho ($payload, $cb = null)
204
    {
205
        $this->sendCommand('ECHO_REQ', $payload, $cb);
206
    }
207
208
    /**
209
     * Function run task and wait result in callback
210
     *
211
     * @param $params
212
     * @param callable $cb = null
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
213
     * @param boolean $unique
0 ignored issues
show
Bug introduced by
There is no parameter named $unique. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
214
     */
215
    public function submitJob($params, $cb = null)
216
    {
217
        $closure = function () use (&$params, $cb)
218
        {
219
            $this->sendCommand('SUBMIT_JOB'
220
                . (isset($params['pri']) ? '_ ' . strtoupper($params['pri']) : '')
221
                . (isset($params['bg']) && $params['bg'] ? '_BG' : ''),
222
                [$params['function'], $params['unique'], $params['payload']],
223
                $cb
224
            );
225
        };
226
        if (isset($params['unique'])) {
227
            $closure();
228
        } else {
229
            Crypt::randomString(10, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', function($random) use ($closure) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 136 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
230
                $params['unique'] = $random;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
231
                $closure();
232
            });
233
        }
234
    }
235
236
    /**
237
     * Get job status
238
     * 
239
     * @param mixed $jobHandle Job handle that was given in JOB_CREATED packet.
240
     * @param callable $cb = null
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
241
     *
242
     */
243
    public function getStatus($jobHandle, $cb = null) {
244
        $this->sendCommand('GET_STATUS', [$jobHandle], $cb);
245
    }
246
247
    /**
248
     * Function set settings for current connection
249
     * Available settings
250
     * 'exceptions' - Forward WORK_EXCEPTION packets to the client.
251
     *
252
     * @url http://gearman.org/protocol/
253
     *
254
     *
255
     * @param int $optionName
256
     * @param callable $cb = null
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
257
     */
258
    public function setConnectionOption($optionName, $cb = null) {
259
        $this->sendCommand('OPTION_RES', [$optionName], $cb);
260
    }
261
262
    /**
263
     * Send a command
264
     *
265
     * @param $commandName
266
     * @param $payload
267
     * @param callable $cb = null
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
268
     */
269
    public function sendCommand($commandName, $payload, $cb = null) {
270
271
        $pct = implode(
272
            static::ARGS_DELIMITER,
273
            array_map(function($item){ return !is_scalar($item) ? serialize($item) : $item; }, (array) $payload)
274
        );
275
        $this->reqStack->push($commandName);
276
        $this->onResponse->push($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by parameter $cb on line 269 can also be of type null; however, PHPDaemon\Structures\StackCallbacks::push() does only seem to accept callable, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
277
        $this->write(pack(
278
            static::HEADER_WRITE_FORMAT,
279
            static::MAGIC_REQUEST, $this->requestCommandList[$commandName], mb_orig_strlen($pct)));
280
        $this->write($pct);
281
    }
282
}