Completed
Pull Request — master (#14)
by Nicolas
03:05
created

Message::packHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Puzzle\AMQP\Messages;
4
5
use Psr\Log\InvalidArgumentException;
6
use Puzzle\AMQP\WritableMessage;
7
use Puzzle\AMQP\ReadableMessage;
8
use Puzzle\AMQP\Messages\Bodies\NullBody;
9
10
class Message implements WritableMessage
11
{
12
    use BodySetter;
13
    
14
    protected
15
        $body;
16
    
17
    private
18
        $canBeDroppedSilently,
1 ignored issue
show
Coding Style introduced by
The visibility should be declared for property $canBeDroppedSilently.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
19
        $headers,
20
        $attributes;
21
22 23
    public function __construct($routingKey = '')
23
    {
24 23
        $this->body = new NullBody();
25 23
        $this->canBeDroppedSilently = true;
26 23
        $this->headers = array();
27 23
        $this->initializeAttributes();
28 23
        $this->setAttribute('routing_key', $routingKey);
29 23
    }
30
    
31 23
    private function initializeAttributes()
32
    {
33 23
        $this->attributes = array(
34 23
            'routing_key' => null,
35 23
            'content_type' => $this->getContentType(),
36 23
            'content_encoding' => 'utf8',
37
            'message_id' => function($timestamp) {
38 8
                return sha1($this->getRoutingKey() . $timestamp . $this->generateBodyId() . mt_rand());
39 23
            },
40 23
            'user_id' => null,
41 23
            'app_id' => null,
42 23
            'delivery_mode' => self::PERSISTENT,
43 23
            'priority' => null,
44
            'timestamp' => function($timestamp) {
45 9
                return $timestamp;
46 23
            },
47 23
            'expiration' => null,
48 23
            'type' => null,
49 23
            'reply_to' => null,
50 23
            'correlation_id' => null,
51
            'headers' => function($timestamp) {
52 10
                return $this->packHeaders($timestamp);
53 23
            },
54
        );
55 23
    }
56
    
57 8
    private function generateBodyId()
58
    {
59 8
        if($this->body instanceof Footprintable)
60 8
        {
61 1
            return $this->body->footprint();
62
        }
63
        
64 7
        return uniqid(true);
65
    }
66
    
67 1
    public function canBeDroppedSilently()
68
    {
69 1
        return $this->canBeDroppedSilently;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->canBeDroppedSilently; (boolean) is incompatible with the return type declared by the interface Puzzle\AMQP\WritableMessage::canBeDroppedSilently of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
70
    }
71
    
72 1
    public function disallowSilentDropping()
73
    {
74 1
        $this->canBeDroppedSilently = false;
75
        
76 1
        return $this;
77
    }
78
79 23
    public function getContentType()
80
    {
81 23
        return $this->body->getContentType();
82
    }
83
84 19
    public function getRoutingKey()
85
    {
86 19
        return $this->getAttribute('routing_key');
87
    }
88
89 1
    public function getBodyInTransportFormat()
90
    {
91 1
        return $this->body->asTransported();
92
    }
93
94 5
    public function setBody(Body $body)
95
    {
96 5
        $this->body = $body;
97 5
        $this->updateContentType();
98
        
99 5
        return $this;
100
    }
101
    
102 12
    private function updateContentType()
103
    {
104 12
        $this->attributes['content_type'] = $this->body->getContentType();
105 12
    }
106
107 13
    public function addHeader($headerName, $value)
108
    {
109 13
        $this->headers[$headerName] = $value;
110
111 13
        return $this;
112
    }
113
114 3
    public function addHeaders(array $headers)
115
    {
116 3
        foreach($headers as $name => $value)
117
        {
118 3
            $this->addHeader($name, $value);
119 3
        }
120
121 3
        return $this;
122
    }
123
124 1
    public function setAuthor($author)
125
    {
126 1
        $this->addHeader('author', $author);
127
128 1
        return $this;
129
    }
130
131 10
    public function packAttributes($timestamp = false)
132
    {
133 10
        $this->updateContentType();
134
        
135 10
        if($timestamp === false)
136 10
        {
137 8
            $timestamp = (new \DateTime("now"))->getTimestamp();
138 8
        }
139
140 10
        return array_map(function($value) use($timestamp) {
141
142 10
            if($value instanceof \Closure)
143 10
            {
144 10
                $value = $value($timestamp);
145 10
            }
146
147 10
            return $value;
148
149 10
        }, $this->attributes);
150
    }
151
152 10
    private function packHeaders($timestamp)
153
    {
154 10
        $this->headers['message_datetime'] = date('Y-m-d H:i:s', $timestamp);
155
156 10
        return $this->headers;
157
    }
158
159 23
    public function setAttribute($attributeName, $value)
160
    {
161 23
        if($attributeName !== 'headers')
162 23
        {
163 23
            if(array_key_exists($attributeName, $this->attributes))
164 23
            {
165 23
                $this->attributes[$attributeName] = $value;
166 23
            }
167 23
        }
168
169 23
        return $this;
170
    }
171
172
    public function getAppId()
173
    {
174
        return $this->getAttribute('app_id');
175
    }
176
177 3
    public function getHeaders()
178
    {
179 3
        $attributes = $this->packAttributes();
180
181 3
        return $attributes['headers'];
182
    }
183
184 21
    public function getAttribute($attributeName)
185
    {
186 21
        if(array_key_exists($attributeName, $this->attributes))
187 21
        {
188 20
            return $this->attributes[$attributeName];
189
        }
190
191 1
        throw new InvalidArgumentException(sprintf('Property "%s" is unknown or is not a message property', $attributeName));
192
    }
193
194 1
    public function __toString()
195
    {
196 1
        return json_encode(array(
197 1
            'routing_key' => $this->getRoutingKey(),
198 1
            'body' => (string) $this->body,
199 1
            'attributes' => $this->attributes,
200 1
            'can be dropped silently' => $this->canBeDroppedSilently
201 1
        ));
202
    }
203
204 1
    public function setExpiration($expirationInSeconds)
205
    {
206 1
        $ttlInMs = 1000 * (int) $expirationInSeconds;
207
208 1
        $this->setAttribute('expiration', (string) $ttlInMs);
209
210 1
        return $this;
211
    }
212
213 2
    public static function buildFromReadableMessage(ReadableMessage $readableMessage, $newRoutingKey = false)
214
    {
215 2
        $routingKey = $readableMessage->getRoutingKey();
216
217 2
        if($newRoutingKey !== false)
218 2
        {
219 1
            $routingKey = $newRoutingKey;
220 1
        }
221
222 2
        $writableMessage = new static($routingKey);
0 ignored issues
show
Bug introduced by
It seems like $routingKey defined by $newRoutingKey on line 219 can also be of type boolean; however, Puzzle\AMQP\Messages\Message::__construct() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
223 2
        $writableMessage->setBody($readableMessage->getBody());
224
225 2
        $writableMessage->addHeaders($readableMessage->getHeaders());
226
227 2
        $attributes = $readableMessage->getAttributes();
228 2
        $skippedAttributes = array('timestamp', 'headers', 'app_id', 'routing_key');
229 2
        foreach($attributes as $attributeName => $value)
230
        {
231 2
            if(! in_array($attributeName, $skippedAttributes))
232 2
            {
233 2
                $writableMessage->setAttribute($attributeName, $value);
234 2
            }
235 2
        }
236
237 2
        return $writableMessage;
238
    }
239
}
240