HeaderFactory::getClassFor()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 10
c 0
b 0
f 0
cc 4
nc 4
nop 1
crap 4
1
<?php
2
/**
3
 * This file is part of the ZBateson\MailMimeParser project.
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
8
namespace ZBateson\MailMimeParser\Header;
9
10
use Psr\Log\LoggerInterface;
11
use ReflectionClass;
12
use ZBateson\MailMimeParser\Header\Consumer\AddressBaseConsumerService;
13
use ZBateson\MailMimeParser\Header\Consumer\DateConsumerService;
14
use ZBateson\MailMimeParser\Header\Consumer\GenericConsumerMimeLiteralPartService;
15
use ZBateson\MailMimeParser\Header\Consumer\IConsumerService;
16
use ZBateson\MailMimeParser\Header\Consumer\IdBaseConsumerService;
17
use ZBateson\MailMimeParser\Header\Consumer\ParameterConsumerService;
18
use ZBateson\MailMimeParser\Header\Consumer\ReceivedConsumerService;
19
use ZBateson\MailMimeParser\Header\Consumer\SubjectConsumerService;
20
use ZBateson\MailMimeParser\Header\Part\MimeTokenPartFactory;
21
22
/**
23
 * Constructs various IHeader types depending on the type of header passed.
24
 *
25
 * If the passed header resolves to a specific defined header type, it is parsed
26
 * as such.  Otherwise, a GenericHeader is instantiated and returned.  Headers
27
 * are mapped as follows:
28
 *
29
 *  - {@see AddressHeader}: From, To, Cc, Bcc, Sender, Reply-To, Resent-From,
30
 *    Resent-To, Resent-Cc, Resent-Bcc, Resent-Reply-To, Return-Path,
31
 *    Delivered-To
32
 *  - {@see DateHeader}: Date, Resent-Date, Delivery-Date, Expires, Expiry-Date,
33
 *    Reply-By
34
 *  - {@see ParameterHeader}: Content-Type, Content-Disposition, Received-SPF,
35
 *    Authentication-Results, DKIM-Signature, Autocrypt
36
 *  - {@see SubjectHeader}: Subject
37
 *  - {@see IdHeader}: Message-ID, Content-ID, In-Reply-To, References
38
 *  - {@see ReceivedHeader}: Received
39
 *
40
 * @author Zaahid Bateson
41
 */
42
class HeaderFactory
43
{
44
    protected LoggerInterface $logger;
45
46
    /**
47
     * @var IConsumerService[] array of available consumer service classes
48
     */
49
    protected array $consumerServices;
50
51
    /**
52
     * @var MimeTokenPartFactory for mime decoding.
53
     */
54
    protected MimeTokenPartFactory $mimeTokenPartFactory;
55
56
    /**
57
     * @var string[][] maps IHeader types to headers.
58
     */
59
    protected $types = [
60
        AddressHeader::class => [
61
            'from',
62
            'to',
63
            'cc',
64
            'bcc',
65
            'sender',
66
            'replyto',
67
            'resentfrom',
68
            'resentto',
69
            'resentcc',
70
            'resentbcc',
71
            'resentreplyto',
72
            'returnpath',
73
            'deliveredto',
74
        ],
75
        DateHeader::class => [
76
            'date',
77
            'resentdate',
78
            'deliverydate',
79
            'expires',
80
            'expirydate',
81
            'replyby',
82
        ],
83
        ParameterHeader::class => [
84
            'contenttype',
85
            'contentdisposition',
86
            'receivedspf',
87
            'authenticationresults',
88
            'dkimsignature',
89
            'autocrypt',
90
        ],
91
        SubjectHeader::class => [
92
            'subject',
93
        ],
94
        IdHeader::class => [
95
            'messageid',
96
            'contentid',
97
            'inreplyto',
98
            'references'
99
        ],
100
        ReceivedHeader::class => [
101
            'received'
102
        ]
103
    ];
104
105
    /**
106
     * @var string Defines the generic IHeader type to use for headers that
107
     *      aren't mapped in $types
108
     */
109
    protected $genericType = GenericHeader::class;
110
111 14
    public function __construct(
112
        LoggerInterface $logger,
113
        MimeTokenPartFactory $mimeTokenPartFactory,
114
        AddressBaseConsumerService $addressBaseConsumerService,
115
        DateConsumerService $dateConsumerService,
116
        GenericConsumerMimeLiteralPartService $genericConsumerMimeLiteralPartService,
117
        IdBaseConsumerService $idBaseConsumerService,
118
        ParameterConsumerService $parameterConsumerService,
119
        ReceivedConsumerService $receivedConsumerService,
120
        SubjectConsumerService $subjectConsumerService
121
    ) {
122 14
        $this->logger = $logger;
123 14
        $this->mimeTokenPartFactory = $mimeTokenPartFactory;
124 14
        $this->consumerServices = [
125 14
            AddressBaseConsumerService::class => $addressBaseConsumerService,
126 14
            DateConsumerService::class => $dateConsumerService,
127 14
            GenericConsumerMimeLiteralPartService::class => $genericConsumerMimeLiteralPartService,
128 14
            IdBaseConsumerService::class => $idBaseConsumerService,
129 14
            ParameterConsumerService::class => $parameterConsumerService,
130 14
            ReceivedConsumerService::class => $receivedConsumerService,
131 14
            SubjectConsumerService::class => $subjectConsumerService
132 14
        ];
133
    }
134
135
    /**
136
     * Returns the string in lower-case, and with non-alphanumeric characters
137
     * stripped out.
138
     *
139
     * @param string $header The header name
140
     * @return string The normalized header name
141
     */
142 118
    public function getNormalizedHeaderName(string $header) : string
143
    {
144 118
        return \preg_replace('/[^a-z0-9]/', '', \strtolower($header));
145
    }
146
147
    /**
148
     * Returns the name of an IHeader class for the passed header name.
149
     *
150
     * @param string $name The header name.
151
     * @return string The Fully Qualified class name.
152
     */
153 118
    private function getClassFor(string $name) : string
154
    {
155 118
        $test = $this->getNormalizedHeaderName($name);
156 118
        foreach ($this->types as $class => $matchers) {
157 118
            foreach ($matchers as $matcher) {
158 118
                if ($test === $matcher) {
159 116
                    return $class;
160
                }
161
            }
162
        }
163 102
        return $this->genericType;
164
    }
165
166
    /**
167
     * Creates an IHeader instance for the passed header name and value, and
168
     * returns it.
169
     *
170
     * @param string $name The header name.
171
     * @param string $value The header value.
172
     * @return IHeader The created header object.
173
     */
174 118
    public function newInstance(string $name, string $value) : IHeader
175
    {
176 118
        $class = $this->getClassFor($name);
177 118
        $this->logger->debug(
178 118
            'Creating {class} for header with name "{name}" and value "{value}"',
179 118
            ['class' => $class, 'name' => $name, 'value' => $value]
180 118
        );
181 118
        return $this->newInstanceOf($name, $value, $class);
182
    }
183
184
    /**
185
     * Creates an IHeader instance for the passed header name and value using
186
     * the passed IHeader class, and returns it.
187
     *
188
     * @param string $name The header name.
189
     * @param string $value The header value.
190
     * @param string $iHeaderClass The class to use for header creation
191
     * @return IHeader The created header object.
192
     */
193 120
    public function newInstanceOf(string $name, string $value, string $iHeaderClass) : IHeader
194
    {
195 120
        $ref = new ReflectionClass($iHeaderClass);
196 120
        $params = $ref->getConstructor()->getParameters();
197 120
        if ($ref->isSubclassOf(MimeEncodedHeader::class)) {
198 90
            return new $iHeaderClass(
199 90
                $name,
200 90
                $value,
201 90
                $this->logger,
202 90
                $this->mimeTokenPartFactory,
203 90
                $this->consumerServices[$params[4]->getType()->getName()]
0 ignored issues
show
Bug introduced by
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

203
                $this->consumerServices[$params[4]->getType()->/** @scrutinizer ignore-call */ getName()]
Loading history...
204 90
            );
205
        }
206 120
        return new $iHeaderClass(
207 120
            $name,
208 120
            $value,
209 120
            $this->logger,
210 120
            $this->consumerServices[$params[3]->getType()->getName()]
211 120
        );
212
    }
213
}
214