AMQPAbstractCollection::encodeBool()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
crap 2
1
<?php
2
namespace PhpAmqpLib\Wire;
3
4
use PhpAmqpLib\Channel\AbstractChannel;
5
use PhpAmqpLib\Exception;
6
7
8
/**
9
 * Iterator implemented for transparent integration with AMQPWriter::write_[array|table]()
10
 */
11
abstract class AMQPAbstractCollection implements \Iterator
12
{
13
14
    //protocol defines available field types and their corresponding symbols
15
    const PROTOCOL_080 = AbstractChannel::PROTOCOL_080;
16
    const PROTOCOL_091 = AbstractChannel::PROTOCOL_091;
17
    const PROTOCOL_RBT = 'rabbit'; //pseudo proto
18
19
    //Abstract data types
20
    const T_INT_SHORTSHORT = 1;
21
    const T_INT_SHORTSHORT_U = 2;
22
    const T_INT_SHORT = 3;
23
    const T_INT_SHORT_U = 4;
24
    const T_INT_LONG = 5;
25
    const T_INT_LONG_U = 6;
26
    const T_INT_LONGLONG = 7;
27
    const T_INT_LONGLONG_U = 8;
28
29
    const T_DECIMAL = 9;
30
    const T_TIMESTAMP = 10;
31
    const T_VOID = 11;
32
33
    const T_BOOL = 12;
34
35
    const T_STRING_SHORT = 13;
36
    const T_STRING_LONG = 14;
37
38
    const T_ARRAY = 15;
39
    const T_TABLE = 16;
40
41
    /**
42
     * @var string
43
     */
44
    private static $_protocol = null;
45
46
    /*
47
     * Field types messy mess http://www.rabbitmq.com/amqp-0-9-1-errata.html#section_3
48
     * Default behaviour is to use rabbitMQ compatible field-set
49
     * Define AMQP_STRICT_FLD_TYPES=true to use strict AMQP instead
50
     */
51
    private static $_types_080 = array(
52
        self::T_INT_LONG => 'I',
53
        self::T_DECIMAL => 'D',
54
        self::T_TIMESTAMP => 'T',
55
        self::T_STRING_LONG => 'S',
56
        self::T_TABLE => 'F'
57
    );
58
59
    /**
60
     * @var array
61
     */
62
    private static $_types_091 = array(
63
        self::T_INT_SHORTSHORT => 'b',
64
        self::T_INT_SHORTSHORT_U => 'B',
65
        self::T_INT_SHORT => 'U',
66
        self::T_INT_SHORT_U => 'u',
67
        self::T_INT_LONG => 'I',
68
        self::T_INT_LONG_U => 'i',
69
        self::T_INT_LONGLONG => 'L',
70
        self::T_INT_LONGLONG_U => 'l',
71
        self::T_DECIMAL => 'D',
72
        self::T_TIMESTAMP => 'T',
73
        self::T_VOID => 'V',
74
        self::T_BOOL => 't',
75
        self::T_STRING_SHORT => 's',
76
        self::T_STRING_LONG => 'S',
77
        self::T_ARRAY => 'A',
78
        self::T_TABLE => 'F'
79
    );
80
81
    /**
82
     * @var array
83
     */
84
    private static $_types_rabbit = array(
85
        self::T_INT_SHORTSHORT => 'b',
86
        self::T_INT_SHORT => 's',
87
        self::T_INT_LONG => 'I',
88
        self::T_INT_LONGLONG => 'l',
89
        self::T_DECIMAL => 'D',
90
        self::T_TIMESTAMP => 'T',
91
        self::T_VOID => 'V',
92
        self::T_BOOL => 't',
93
        self::T_STRING_LONG => 'S',
94
        self::T_ARRAY => 'A',
95
        self::T_TABLE => 'F'
96
    );
97
98
    /**
99
     * @var array
100
     */
101
    protected $data = array();
102
103 190
    public function __construct(array $data = null)
104
    {
105 190
        if (!empty($data)) {
106 110
            $this->data = $this->encodeCollection($data);
107 84
        }
108 185
    }
109
110
    /**
111
     * @return int
112
     */
113
    abstract public function getType();
114
115
    /**
116
     * @param mixed $val
117
     * @param int $type
118
     * @param string $key
119
     */
120 105
    final protected function setValue($val, $type = null, $key = null)
121
    {
122 105
        if ($val instanceof self) {
123 35
            if ($type && ($type != $val->getType())) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type 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...
124 5
                throw new Exception\AMQPInvalidArgumentException(
125 5
                    'Attempted to add instance of ' . get_class($val) . ' representing type [' . $val->getType() . '] as mismatching type [' . $type . ']'
126 4
                );
127
            }
128 30
            $type = $val->getType();
129 99
        } elseif ($type) { //ensuring data integrity and that all members are properly validated
0 ignored issues
show
Bug Best Practice introduced by
The expression $type 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...
130
            switch ($type) {
131 90
                case self::T_ARRAY:
132 5
                    throw new Exception\AMQPInvalidArgumentException('Arrays must be passed as AMQPArray instance');
133
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
134 85
                case self::T_TABLE:
135 5
                    throw new Exception\AMQPInvalidArgumentException('Tables must be passed as AMQPTable instance');
136
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
137 80
                case self::T_DECIMAL:
138 5
                    if (!($val instanceof AMQPDecimal)) {
139 5
                        throw new Exception\AMQPInvalidArgumentException('Decimal values must be instance of AMQPDecimal');
140
                    }
141
                    break;
142
            }
143 60
        }
144
145 85
        if ($type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type 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...
146 80
            self::checkDataTypeIsSupported($type, false);
147 65
            $val = array($type, $val);
148 52
        } else {
149 5
            $val = $this->encodeValue($val);
150
        }
151
152 70
        if ($key === null) {
153 25
            $this->data[] = $val;
154 20
        } else {
155 60
            $this->data[$key] = $val;
156
        }
157 70
    }
158
159
    /**
160
     * @return array
161
     */
162 80
    final public function getNativeData()
163
    {
164 80
        return $this->decodeCollection($this->data);
165
    }
166
167
    /**
168
     * @param array $val
169
     * @return array
170
     */
171 110
    final protected function encodeCollection(array $val)
172
    {
173 110
        foreach ($val as $k=>$v) {
174 110
            $val[$k] = $this->encodeValue($v);
175 84
        }
176
177 105
        return $val;
178
    }
179
180
    /**
181
     * @param array $val
182
     * @return array
183
     */
184 80
    final protected function decodeCollection(array $val)
185
    {
186 80
        foreach ($val as $k=>$v) {
187 80
            $val[$k] = $this->decodeValue($v[1], $v[0]);
188 64
        }
189
190 80
        return $val;
191
    }
192
193
    /**
194
     * @param mixed $val
195
     * @return mixed
196
     * @throws Exception\AMQPOutOfBoundsException
197
     */
198 115
    protected function encodeValue($val)
199
    {
200 115
        if (is_string($val)) {
201 105
            $val = $this->encodeString($val);
202 110
        } elseif (is_float($val)) {
203 40
            $val = $this->encodeFloat($val);
204 90
        } elseif (is_int($val)) {
205 75
            $val = $this->encodeInt($val);
206 90
        } elseif (is_bool($val)) {
207 75
            $val = $this->encodeBool($val);
208 88
        } elseif (is_null($val)) {
209 35
            $val = $this->encodeVoid();
210 64
        } elseif ($val instanceof \DateTime) {
211
            $val = array(self::T_TIMESTAMP, $val->getTimestamp());
212 61
        } elseif ($val instanceof AMQPDecimal) {
213
            $val = array(self::T_DECIMAL, $val);
214 75
        } elseif ($val instanceof self) {
215
            //avoid silent type correction of strictly typed values
216
            self::checkDataTypeIsSupported($val->getType(), false);
217
            $val = array($val->getType(), $val);
218 75
        } elseif (is_array($val)) {
219
            //AMQP specs says "Field names MUST start with a letter, '$' or '#'"
220
            //so beware, some servers may raise an exception with 503 code in cases when indexed array is encoded as table
221 70
            if (self::isProtocol(self::PROTOCOL_080)) {
222
                //080 doesn't support arrays, forcing table
223 15
                $val = array(self::T_TABLE, new AMQPTable($val));
224 67
            } elseif (empty($val) || (array_keys($val) === range(0, count($val) - 1))) {
225 50
                $val = array(self::T_ARRAY, new AMQPArray($val));
226 40
            } else {
227 50
                $val = array(self::T_TABLE, new AMQPTable($val));
228
            }
229 56
        } else {
230 5
            throw new Exception\AMQPOutOfBoundsException(sprintf('Encountered value of unsupported type: %s', gettype($val)));
231
        }
232
233 110
        return $val;
234
    }
235
236
    /**
237
     * @param mixed $val
238
     * @param integer $type
239
     * @return array|bool|\DateTime|null
240
     */
241 80
    protected function decodeValue($val, $type)
242
    {
243 80
        if ($val instanceof self) {
244
            //covering arrays and tables
245 50
            $val = $val->getNativeData();
246 40
        } else {
247
            switch ($type) {
248 75
                case self::T_BOOL:
249 30
                    $val = (bool) $val;
250 30
                    break;
251 75
                case self::T_TIMESTAMP:
252
                    $val = \DateTime::createFromFormat('U', $val);
253
                    break;
254 75
                case self::T_VOID:
255 25
                    $val = null;
256 25
                    break;
257 70
                case self::T_ARRAY:
258 70
                case self::T_TABLE:
259
                    throw new Exception\AMQPLogicException(
260
                        'Encountered an array/table struct which is not an instance of AMQPCollection. ' .
261
                        'This is considered a bug and should be fixed, please report'
262
                    );
263
            }
264
        }
265
266 80
        return $val;
267
    }
268
269
    /**
270
     * @param string $val
271
     * @return array
272
     */
273 105
    protected function encodeString($val)
274
    {
275 105
        return array(self::T_STRING_LONG, $val);
276
    }
277
278
    /**
279
     * @param int $val
280
     * @return array
281
     */
282 75
    protected function encodeInt($val)
283
    {
284 75
        if (($val >= -2147483648) && ($val <= 2147483647)) {
285 75
            $ev = array(self::T_INT_LONG, $val);
286 71
        } elseif (self::isProtocol(self::PROTOCOL_080)) {
287
            //080 doesn't support longlong
288 15
            $ev = $this->encodeString((string) $val);
289 12
        } else {
290 40
            $ev = array(self::T_INT_LONGLONG, $val);
291
        }
292
293 75
        return $ev;
294
    }
295
296
    /**
297
     * @param float $val
298
     * @return array
299
     */
300 40
    protected function encodeFloat($val)
301
    {
302 40
        return static::encodeString((string) $val);
303
    }
304
305
    /**
306
     * @param bool $val
307
     * @return array
308
     */
309 75
    protected function encodeBool($val)
310
    {
311 75
        $val = (bool) $val;
312
313 75
        return self::isProtocol(self::PROTOCOL_080) ? array(self::T_INT_LONG, (int) $val) : array(self::T_BOOL, $val);
314
    }
315
316
    /**
317
     * @return array
318
     */
319 35
    protected function encodeVoid()
320
    {
321 35
        return self::isProtocol(self::PROTOCOL_080) ? $this->encodeString('') : array(self::T_VOID, null);
322
    }
323
324
    /**
325
     * @return string
326
     */
327 215
    final public static function getProtocol()
328
    {
329 215
        if (self::$_protocol === null) {
330 5
            self::$_protocol = defined('AMQP_STRICT_FLD_TYPES') && AMQP_STRICT_FLD_TYPES ?
331 4
                AbstractChannel::getProtocolVersion() :
332 5
                self::PROTOCOL_RBT;
333 4
        }
334
335 215
        return self::$_protocol;
336
    }
337
338
    /**
339
     * @param string $proto
340
     * @return bool
341
     */
342 85
    final public static function isProtocol($proto)
343
    {
344 85
        return self::getProtocol() == $proto;
345
    }
346
347
    /**
348
     * @return array  [dataTypeConstant => dataTypeSymbol]
349
     */
350 185
    final public static function getSupportedDataTypes()
351
    {
352 185
        switch ($proto = self::getProtocol()) {
353 185
            case self::PROTOCOL_080:
354 10
                $types = self::$_types_080;
355 10
                break;
356 175
            case self::PROTOCOL_091:
357 40
                $types = self::$_types_091;
358 40
                break;
359 140
            case self::PROTOCOL_RBT:
360 140
                $types = self::$_types_rabbit;
361 140
                break;
362
            default:
363
                throw new Exception\AMQPOutOfRangeException(sprintf('Unknown protocol: %s', $proto));
364 148
        }
365
366 185
        return $types;
367
    }
368
369
    /**
370
     * @param string $type
371
     * @param bool $return Whether to return or raise AMQPOutOfRangeException
372
     * @return boolean
373
     */
374 80
    final public static function checkDataTypeIsSupported($type, $return = true)
375
    {
376
        try {
377 80
            $supported = self::getSupportedDataTypes();
378 80
            if (!isset($supported[$type])) {
379 15
                throw new Exception\AMQPOutOfRangeException(sprintf('AMQP-%s doesn\'t support data of type [%s]', self::getProtocol(), $type));
380
            }
381 65
            return true;
382
383 15
        } catch (Exception\AMQPOutOfRangeException $ex) {
384 15
            if (!$return) {
385 15
                throw $ex;
386
            }
387
388
            return false;
389
        }
390
    }
391
392
    /**
393
     * @param integer $type
394
     * @return string
395
     */
396 135 View Code Duplication
    final public static function getSymbolForDataType($type)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
397
    {
398 135
        $types = self::getSupportedDataTypes();
399 135
        if (!isset($types[$type])) {
400
            throw new Exception\AMQPOutOfRangeException(sprintf('AMQP-%s doesn\'t support data of type [%s]', self::getProtocol(), $type));
401
        }
402
403 135
        return $types[$type];
404
    }
405
406
    /**
407
     * @param string $symbol
408
     * @return integer
409
     */
410 155 View Code Duplication
    final public static function getDataTypeForSymbol($symbol)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
411
    {
412 155
        $symbols = array_flip(self::getSupportedDataTypes());
413 155
        if (!isset($symbols[$symbol])) {
414 5
            throw new Exception\AMQPOutOfRangeException(sprintf('AMQP-%s doesn\'t define data of type [%s]', self::getProtocol(), $symbol));
415
        }
416
417 150
        return $symbols[$symbol];
418
    }
419
420 55
    public function current()
421
    {
422 55
        return current($this->data);
423
    }
424
425 25
    public function key()
426
    {
427 25
        return key($this->data);
428
    }
429
430 55
    public function next()
431
    {
432 55
        next($this->data);
433 55
    }
434
435 60
    public function rewind()
436
    {
437 60
        reset($this->data);
438 60
    }
439
440 60
    public function valid()
441
    {
442 60
        return key($this->data) !== null;
443
    }
444
}
445