Completed
Push — master ( 2f27ef...3ff097 )
by Frederik
07:37
created

PhpMailTransport::send()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 28
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2.0393

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 11
cts 14
cp 0.7856
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 20
nc 2
nop 1
crap 2.0393
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Transport;
5
6
use Genkgo\Mail\Header\HeaderLine;
7
use Genkgo\Mail\HeaderInterface;
8
use Genkgo\Mail\MessageInterface;
9
use Genkgo\Mail\TransportInterface;
10
11
final class PhpMailTransport implements TransportInterface
12
{
13
14
    /**
15
     * @var EnvelopeFactory
16
     */
17
    private $envelopFactory;
18
    /**
19
     * @var array
20
     */
21
    private $parameters;
22
    /**
23
     * @var \Closure
24
     */
25
    private $replacedMailMethod;
26
27
    /**
28
     * PhpMailTransport constructor.
29
     * @param EnvelopeFactory $envelopFactory
30
     * @param array $parameters
31
     */
32 4
    public function __construct(EnvelopeFactory $envelopFactory, array $parameters = [])
33
    {
34 4
        $this->envelopFactory = $envelopFactory;
35 4
        $this->parameters = $parameters;
36 4
    }
37
38
    /**
39
     * @param MessageInterface $message
40
     * @return void
41
     */
42 4
    public function send(MessageInterface $message): void
43
    {
44 4
        $to = $this->extractSingleHeader($message, 'to');
45 3
        $subject = $this->extractSingleHeader($message, 'subject');
46 2
        $headers = $this->extractHeaders($message);
47 2
        $parameters = $this->constructParameters($message);
48
49 1
        if ($this->replacedMailMethod === null) {
50
            // @codeCoverageIgnoreStart
51
            mail(
52
                $to,
53
                $subject,
54
                (string)$message->getBody(),
55
                $headers,
56
                $parameters
57
            );
58
            // @codeCoverageIgnoreEnd
59
        } else {
60 1
            $callback = $this->replacedMailMethod;
61 1
            $callback(
62
                $to,
63
                $subject,
64 1
                (string)$message->getBody(),
65
                $headers,
66 1
                $parameters
67
            );
68
        }
69 1
    }
70
71
    /**
72
     * @param MessageInterface $message
73
     * @param string $name
74
     * @return string
75
     */
76 4
    private function extractSingleHeader(MessageInterface $message, string $name): string
77
    {
78 4
        $headers = $message->getHeader($name);
79 4
        if (count($headers) === 0) {
80 2
            throw new \InvalidArgumentException(
81 2
                'Cannot transport message without header ' . $name
82
            );
83
        }
84
85 3
        return (string)reset($headers)->getValue();
86
    }
87
88
    /**
89
     * @param MessageInterface $message
90
     * @return string
91
     */
92 2
    private function extractHeaders(MessageInterface $message): string
93
    {
94 2
        return implode("\r\n",
95 2
            array_values(
96 2
                array_filter(
97 2
                    array_map(
98
                        function (array $headers) {
99 2
                            return implode(
100 2
                                "\r\n",
101 2
                                array_map(
102 2
                                    function (HeaderInterface $header) {
103 2
                                        return (string)(new HeaderLine($header));
104 2
                                    },
105 2
                                    $headers
106
                                )
107
                            );
108 2
                        },
109
                        $message
110 2
                            ->withoutHeader('to')
111 2
                            ->withoutHeader('subject')
112 2
                            ->getHeaders()
113
                    )
114
                )
115
            )
116
        );
117
    }
118
119
    /**
120
     * @param MessageInterface $message
121
     * @return string
122
     */
123 2
    private function constructParameters(MessageInterface $message): string
124
    {
125 2
        $envelop = $this->envelopFactory->make($message);
126 2
        if (preg_match('/\"/', $envelop->getAddress())) {
127 1
            throw new \RuntimeException(
128 1
                'Unable to guarantee injection-free envelop'
129
            );
130
        }
131
132 1
        return implode(' ', array_merge($this->parameters, ['-f' . (string)$envelop]));
133
    }
134
135
    /**
136
     * @param \Closure $callback
137
     * @param EnvelopeFactory $envelopFactory
138
     * @param array $parameters
139
     * @return PhpMailTransport
140
     */
141 4
    public static function newReplaceMailMethod(
142
        \Closure $callback,
143
        EnvelopeFactory $envelopFactory,
144
        array $parameters = []
145
    ): PhpMailTransport
146
    {
147 4
        $transport = new self($envelopFactory, $parameters);
148 4
        $transport->replacedMailMethod = $callback;
149 4
        return $transport;
150
    }
151
}