GenericContent::has()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 2
eloc 2
nc 2
nop 1
crap 6
1
<?php
2
namespace PhpAmqpLib\Wire;
3
4
use PhpAmqpLib\Channel\AMQPChannel;
5
6
/**
7
 * Abstract base class for AMQP content.  Subclasses should override
8
 * the propertyDefinitions attribute.
9
 */
10
abstract class GenericContent
11
{
12
    /** @var AMQPChannel[] */
13
    public $delivery_info = array();
14
15
    /** @var array Final property definitions */
16
    protected $prop_types;
17
18
    /** @var array Properties content */
19
    private $properties = array();
20
21
    /** @var string Compiled properties */
22
    private $serialized_properties;
23
24
    /**
25
     * @var array
26
     */
27
    protected static $propertyDefinitions = array(
28
        'dummy' => 'shortstr'
29
    );
30
31
    /**
32
     * @param array $props Message property content
33
     * @param array $prop_types Message property definitions
34
     */
35 130
    public function __construct($props = array(), $prop_types = array())
36
    {
37 130
        $this->prop_types = self::$propertyDefinitions;
38
39 130
        if (!empty($prop_types)) {
40 130
            $this->prop_types = $prop_types;
41 104
        }
42
43 130
        if (!empty($props)) {
44 120
            $this->properties = array_intersect_key($props, $this->prop_types);
45 96
        }
46 130
    }
47
48
    /**
49
     * Check whether a property exists in the 'properties' dictionary
50
     * or if present - in the 'delivery_info' dictionary.
51
     *
52
     * @param string $name
53
     * @return bool
54
     */
55
    public function has($name)
56
    {
57
        return isset($this->properties[$name]) || isset($this->delivery_info[$name]);
58
    }
59
60
    /**
61
     * Look for additional properties in the 'properties' dictionary,
62
     * and if present - the 'delivery_info' dictionary.
63
     *
64
     * @param string $name
65
     * @throws \OutOfBoundsException
66
     * @return mixed|AMQPChannel
67
     */
68 15
    public function get($name)
69
    {
70 15
        if (isset($this->properties[$name])) {
71 15
            return $this->properties[$name];
72
        }
73
74 10
        if (isset($this->delivery_info[$name])) {
75
            return $this->delivery_info[$name];
76
        }
77
78 10
        throw new \OutOfBoundsException(sprintf(
79 10
            'No "%s" property',
80
            $name
81 8
        ));
82
    }
83
84
    /**
85
     * Returns the properties content
86
     *
87
     * @return array
88
     */
89 75
    public function get_properties()
90
    {
91 75
        return $this->properties;
92
    }
93
94
    /**
95
     * Sets a property value
96
     *
97
     * @param string $name The property name (one of the property definition)
98
     * @param mixed $value The property value
99
     * @throws \OutOfBoundsException
100
     */
101 5
    public function set($name, $value)
102
    {
103 5
        if (!array_key_exists($name, $this->prop_types)) {
104
            throw new \OutOfBoundsException(sprintf(
105
                'No "%s" property',
106
                $name
107
            ));
108
        }
109
110 5
        $this->properties[$name] = $value;
111 5
    }
112
113
    /**
114
     * Given the raw bytes containing the property-flags and
115
     * property-list from a content-frame-header, parse and insert
116
     * into a dictionary stored in this object as an attribute named
117
     * 'properties'.
118
     *
119
     * @param AMQPReader $reader
120
     * NOTE: do not mutate $reader
121
     * @return $this
122
     */
123 120
    public function load_properties($reader)
124
    {
125
        // Read 16-bit shorts until we get one with a low bit set to zero
126 120
        $flags = array();
127
128 120
        while (true) {
129 120
            $flag_bits = $reader->read_short();
130 120
            $flags[] = $flag_bits;
131
132 120
            if (($flag_bits & 1) === 0) {
133 120
                break;
134
            }
135
        }
136
137 120
        $shift = 0;
138 120
        $data = array();
139
140 120
        foreach ($this->prop_types as $key => $proptype) {
141 120
            if ($shift === 0) {
142 120
                if (!$flags) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $flags of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
143
                    break;
144
                }
145 120
                $flag_bits = array_shift($flags);
146 120
                $shift = 15;
147 96
            }
148
149 120
            if ($flag_bits & (1 << $shift)) {
0 ignored issues
show
Bug introduced by
The variable $flag_bits does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
150 105
                $data[$key] = $reader->{'read_' . $proptype}();
151 84
            }
152
153 120
            $shift -= 1;
154 96
        }
155
156 120
        $this->properties = $data;
157
158 120
        return $this;
159
    }
160
161
162
    /**
163
     * Serializes the 'properties' attribute (a dictionary) into the
164
     * raw bytes making up a set of property flags and a property
165
     * list, suitable for putting into a content frame header.
166
     *
167
     * @return string
168
     * @todo Inject the AMQPWriter to make the method easier to test
169
     */
170 130
    public function serialize_properties()
171
    {
172 130
        if (!empty($this->serialized_properties)) {
173 10
            return $this->serialized_properties;
174
        }
175
176 130
        $shift = 15;
177 130
        $flag_bits = 0;
178 130
        $flags = array();
179 130
        $raw_bytes = new AMQPWriter();
180
181 130
        foreach ($this->prop_types as $key => $prototype) {
182 130
            $val = isset($this->properties[$key]) ? $this->properties[$key] : null;
183
184
            // Very important: PHP type eval is weak, use the === to test the
185
            // value content. Zero or false value should not be removed
186 130
            if ($val === null) {
187 130
                $shift -= 1;
188 130
                continue;
189
            }
190
191 115
            if ($shift === 0) {
192
                $flags[] = $flag_bits;
193
                $flag_bits = 0;
194
                $shift = 15;
195
            }
196
197 115
            $flag_bits |= (1 << $shift);
198 115
            if ($prototype != 'bit') {
199 115
                $raw_bytes->{'write_' . $prototype}($val);
200 92
            }
201
202 115
            $shift -= 1;
203 104
        }
204
205 130
        $flags[] = $flag_bits;
206 130
        $result = new AMQPWriter();
207 130
        foreach ($flags as $flag_bits) {
208 130
            $result->write_short($flag_bits);
209 104
        }
210
211 130
        $result->write($raw_bytes->getvalue());
212
213 130
        $this->serialized_properties = $result->getvalue();
214
215 130
        return $this->serialized_properties;
216
    }
217
}
218