Lines 97.82% 45 / 46
Methods 88.88% 8 / 9
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 __construct 100.00% 5 / 5 100.00% 1 / 1 3
 registerParser 100.00% 1 / 1 100.00% 1 / 1 1
 prependParser 100.00% 1 / 1 100.00% 1 / 1 1
 parseAll 100.00% 11 / 11 100.00% 1 / 1 6
 parserAcceptsSender 88.88% 8 / 9 0.00% 0 / 1 6.05
 parse 100.00% 6 / 6 100.00% 1 / 1 3
 tryParse 100.00% 5 / 5 100.00% 1 / 1 3
 getParsers 100.00% 1 / 1 100.00% 1 / 1 1
 registerDefaultParsers 100.00% 7 / 7 100.00% 1 / 1 2
25final class BankMailsParser
26{
27    /**
28     * @var list<ParserInterface>
29     */
30    private array $parsers = [];
31
32    /**
33     * @param list<ParserInterface>|null $parsers Parsers to use. Null registers the built-in parsers.
34     *        The statement parser is included only when $decryptor is set.
35     */
36    public function __construct(
37        ?array $parsers = null,
38        private readonly ?TatraBankaMailDecryptor $decryptor = null,
39    ) {
40        if ($parsers === null) {
41            $this->registerDefaultParsers();
42
43            return;
44        }
45
46        foreach ($parsers as $parser) {
47            $this->registerParser($parser);
48        }
49    }
50
51    public function registerParser(ParserInterface $parser): void
52    {
53        $this->parsers[] = $parser;
54    }
55
56    public function prependParser(ParserInterface $parser): void
57    {
58        array_unshift($this->parsers, $parser);
59    }
60
61    /**
62     * @return list<MailContent>
63     */
64    public function parseAll(string $content, ?string $sender = null, ?string $subject = null): array
65    {
66        $matched = [];
67        foreach ($this->parsers as $parser) {
68            if (!$this->parserAcceptsSender($parser, $sender)) {
69                continue;
70            }
71            if ($parser->supports($content, $sender, $subject)) {
72                $matched[] = $parser;
73            }
74        }
75
76        if (count($matched) > 1) {
77            throw AmbiguousBankException::forParsers(...array_map(static fn(ParserInterface $parser): string => $parser::class, $matched));
78        }
79
80        if ($matched === []) {
81            throw UnsupportedBankException::withSender($sender);
82        }
83
84        return $matched[0]->parseAll($content);
85    }
86
87    private function parserAcceptsSender(ParserInterface $parser, ?string $sender): bool
88    {
89        if ($sender === null || trim($sender) === '') {
90            return true;
91        }
92
93        $domains = $parser->bankDomains();
94        if ($domains === []) {
95            return false;
96        }
97
98        foreach ($domains as $domain) {
99            if (SenderHost::matches($sender, $domain)) {
100                return true;
101            }
102        }
103
104        return false;
105    }
106
107    public function parse(string $content, ?string $sender = null, ?string $subject = null): MailContent
108    {
109        $parsed = $this->parseAll($content, $sender, $subject);
110        if ($parsed === []) {
111            throw InvalidEmailFormatException::malformedContent(self::class, 'Parser returned no transactions');
112        }
113        if (count($parsed) > 1) {
114            throw MultipleTransactionsException::useParseAll(count($parsed));
115        }
116
117        return $parsed[0];
118    }
119
120    public function tryParse(string $content, ?string $sender = null, ?string $subject = null): ?MailContent
121    {
122        try {
123            return $this->parse($content, $sender, $subject);
124        } catch (ConfigurationException $exception) {
125            throw $exception;
126        } catch (ParseException|\InvalidArgumentException|\ValueError|\DateMalformedStringException) {
127            return null;
128        }
129    }
130
131    /**
132     * @return list<ParserInterface>
133     */
134    public function getParsers(): array
135    {
136        return $this->parsers;
137    }
138
139    private function registerDefaultParsers(): void
140    {
141        $this->registerParser(new Parser\TatraBanka\TatraBankaMailParser());
142        $this->registerParser(new Parser\TatraBanka\TatraBankaSimpleMailParser());
143        if ($this->decryptor instanceof TatraBankaMailDecryptor) {
144            $this->registerParser(new Parser\TatraBanka\TatraBankaStatementMailParser($this->decryptor));
145        }
146        $this->registerParser(new Parser\Csob\CsobMailParser());
147        $this->registerParser(new Parser\Csob\SkCsobMailParser());
148        $this->registerParser(new Parser\Vub\VubMailParser());
149    }
150}