Completed
Push — master ( bc6bcb...97b2de )
by Craig
02:22
created

PostmarkTransport::getAttachments()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 9
cts 9
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 1
crap 2
1
<?php
2
3
namespace Coconuts\Mail;
4
5
use Swift_Mime_Message;
6
use GuzzleHttp\ClientInterface;
7
use Illuminate\Mail\Transport\Transport;
8
9
class PostmarkTransport extends Transport
10
{
11
    /**
12
     * Guzzle client instance.
13
     *
14
     * @var \GuzzleHttp\ClientInterface
15
     */
16
    protected $client;
17
18
    /**
19
     * The Postmark API key.
20
     *
21
     * @var string
22
     */
23
    protected $key;
24
25
    /**
26
     * The Postmark API end-point.
27
     *
28
     * @var string
29
     */
30
    protected $url = 'https://api.postmarkapp.com/email';
31
32
    /**
33
     * Create a new Postmark transport instance.
34
     *
35
     * @param \GuzzleHttp\ClientInterface $client
36
     * @param string $key
37
     *
38
     * @return void
39
     */
40 5
    public function __construct(ClientInterface $client, $key)
41
    {
42 5
        $this->key = $key;
43 5
        $this->client = $client;
44 5
    }
45
46
    /**
47
     * Send the given Message.
48
     *
49
     * Recipient/sender data will be retrieved from the Message API.
50
     * The return value is the number of recipients who were accepted for delivery.
51
     *
52
     * @param Swift_Mime_Message $message
53
     * @param string[] $failedRecipients An array of failures by-reference
54
     *
55
     * @return int
56
     */
57 1
    public function send(Swift_Mime_Message $message, &$failedRecipients = null)
58
    {
59 1
        $this->beforeSendPerformed($message);
60
61 1
        $this->client->post($this->url, $this->payload($message));
62
63 1
        $this->sendPerformed($message);
64
65 1
        return $this->numberOfRecipients($message);
66
    }
67
68
    /**
69
     * Get all attachments for the given message.
70
     *
71
     * @param \Swift_Mime_Message $message
72
     *
73
     * @return array
74
     */
75 2
    protected function getAttachments(Swift_Mime_Message $message)
76
    {
77 2
        $attachments = [];
78
79 2
        $children = $message->getChildren();
80
81 2
        foreach ($children as $child) {
82 2
            $attachments[] = [
83 2
                'Name' => $child->getFilename(),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Swift_Mime_MimeEntity as the method getFilename() does only exist in the following implementations of said interface: Swift_Attachment, Swift_EmbeddedFile, Swift_Image, Swift_Mime_Attachment, Swift_Mime_EmbeddedFile.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
84 2
                'Content' => base64_encode($child->getBody()),
85 2
                'ContentType' => $child->getContentType(),
86
            ];
87
        }
88
89 2
        return $attachments;
90
    }
91
92
    /**
93
     * Format the contacts for the API request
94
     *
95
     * @param array $contacts
96
     *
97
     * @return string
98
     */
99 3
    protected function getContacts($contacts)
100
    {
101 3
        return collect($contacts)
102
            ->map(function ($display, $address) {
103 3
                return $display ? $display . " <{$address}>" : $address;
104 3
            })
105 3
            ->values()
106 3
            ->implode(',');
107
    }
108
109
    /**
110
     * Get the "From" payload field for the API request.
111
     *
112
     * @param \Swift_Mime_Message $message
113
     *
114
     * @return string
115
     */
116 3
    protected function getFrom(Swift_Mime_Message $message)
117
    {
118 3
        return collect($message->getFrom())
119 3
            ->map(function ($display, $address) {
120 3
                return $display ? $display . " <$address>" : $address;
121 3
            })
122 3
            ->values()
123 3
            ->implode(',');
124
    }
125
126
    /**
127
     * Get the HTTP payload for sending the Postmark message.
128
     *
129
     * @param \Swift_Mime_Message $message
130
     *
131
     * @return array
132
     */
133 2
    protected function payload(Swift_Mime_Message $message)
134
    {
135 2
        $headers = $message->getHeaders();
136
137 2
        $to = $this->getContacts($message->getTo());
138 2
        $cc = $this->getContacts($message->getCc());
139 2
        $bcc = $this->getContacts($message->getBcc());
140 2
        $replyTo = $this->getContacts($message->getReplyTo());
141 2
        $attachments = $this->getAttachments($message);
142
143
        return [
144
            'headers' => [
145 2
                'Accept' => 'application/json',
146 2
                'X-Postmark-Server-Token' => $this->key,
147
            ],
148
            'json' => [
149 2
                'From' => $this->getFrom($message),
150 2
                'To' => $to,
151 2
                'Cc' => $cc,
152 2
                'Bcc' => $bcc,
153 2
                'Tag' => $headers->has('tag') ? $headers->get('tag')->getFieldBody() : '',
154 2
                'Subject' => $message->getSubject(),
155 2
                'HtmlBody' => $message->getBody(),
156 2
                'ReplyTo' => $replyTo,
157 2
                'Attachments' => $attachments,
158
            ],
159
        ];
160
    }
161
}
162