Mt940Parser   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 249
Duplicated Lines 12.05 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 31
c 1
b 0
f 0
lcom 1
cbo 0
dl 30
loc 249
rs 9.8

9 Methods

Rating   Name   Duplication   Size   Complexity  
A parse() 0 7 1
B prepareFile() 0 35 6
C parseContent() 0 54 12
A parseStatementIdentifier() 10 10 2
A parseAccountNumber() 10 10 2
A parseStatementNumber() 10 10 2
A parseBalance() 0 13 2
B parseTransaction() 0 28 3
A parseTransactionDetails() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Ksdev\Mt940Parser;
4
5
class Mt940Parser
6
{
7
    /**
8
     * Parse the MT940 file format and return it as an readable array
9
     *
10
     * @param string $filePath
11
     *
12
     * @return array
13
     *
14
     * @throws \Exception
15
     */
16
    public function parse($filePath)
17
    {
18
        $preparedArray = $this->prepareFile($filePath);
19
        $statement = $this->parseContent($preparedArray);
20
21
        return $statement;
22
    }
23
24
    /**
25
     * Convert the file content into an array structure
26
     *
27
     * @param string $filePath
28
     *
29
     * @return array
30
     *
31
     * @throws \RuntimeException  If the file cannot be opened
32
     */
33
    private function prepareFile($filePath)
34
    {
35
        $file = new \SplFileObject($filePath);
36
37
        $i = 0;
38
        $isBlockOpen = false;
39
        $preparedArray = [];
40
        foreach ($file as $line) {
41
            $strippedLine = str_replace(["\r", "\n"], '', $line);
42
43
            if ($strippedLine == chr(45) . chr(3)) {
44
                $i++;
45
                $isBlockOpen = false;
46
                continue;
47
            }
48
49
            if ($strippedLine == chr(1)) {
50
                $isBlockOpen = true;
51
                continue;
52
            }
53
54
            if ($isBlockOpen) {
55
                if (preg_match('/^:.{2,3}:/', $line)) {
56
                    $preparedArray[$i][] = $line;
57
                }
58
                else {
59
                    $arrayKeys = array_keys($preparedArray[$i]);
60
                    $lastKey = end($arrayKeys);
61
                    $preparedArray[$i][$lastKey] .= $line;
62
                }
63
            }
64
        }
65
66
        return $preparedArray;
67
    }
68
69
    /**
70
     * Convert the prepared array into an easily readable form
71
     *
72
     * @param array $preparedArray
73
     *
74
     * @return array
75
     *
76
     * @throws \Exception
77
     */
78
    private function parseContent($preparedArray)
79
    {
80
        $statement = [];
81
        foreach ($preparedArray as $key => $accountBlock) {
82
            foreach ($accountBlock as $tagLine) {
83
                if (preg_match('/^:(.{2,3}):(.*)/s', $tagLine, $matches)) {
84
                    $tagNum = $matches[1];
85
                    $tagContent = $matches[2];
86
87
                    switch ($tagNum) {
88
                        case '20':
89
                            $generationDate = $this->parseStatementIdentifier($tagContent);
90
                            $statement[$key]['generationDate'] = $generationDate;
91
                            break;
92
                        case '25':
93
                            $accountNumber = $this->parseAccountNumber($tagContent);
94
                            $statement[$key]['accountNumber'] = $accountNumber;
95
                            break;
96
                        case '28C':
97
                            $statementNumber = $this->parseStatementNumber($tagContent);
98
                            $statement[$key]['statementNumber'] = $statementNumber;
99
                            break;
100
                        case '60F':
101
                            $openingBalance = $this->parseBalance($tagContent);
102
                            $statement[$key]['openingBalance'] = $openingBalance;
103
                            break;
104
                        case '62F':
105
                            $closingBalance = $this->parseBalance($tagContent);
106
                            $statement[$key]['closingBalance'] = $closingBalance;
107
                            break;
108
                        case '64':
109
                            $availableBalance = $this->parseBalance($tagContent);
110
                            $statement[$key]['availableBalance'] = $availableBalance;
111
                            break;
112
                        case '61':
113
                            $transaction = $this->parseTransaction($tagContent);
114
                            $statement[$key]['transactions'][] = $transaction;
115
                            break;
116
                        case '86':
117
                            $details = $this->parseTransactionDetails($tagContent);
118
                            $arrayKeys = array_keys($statement[$key]['transactions']);
119
                            $lastKey = end($arrayKeys);
120
                            $statement[$key]['transactions'][$lastKey]['details'] = $details;
121
                            break;
122
                    }
123
                }
124
                else {
125
                    throw new \Exception('Invalid format of tag line');
126
                }
127
            }
128
        }
129
130
        return $statement;
131
    }
132
133
    /**
134
     * @param string $tagContent
135
     *
136
     * @return string
137
     *
138
     * @throws \Exception
139
     */
140 View Code Duplication
    private function parseStatementIdentifier($tagContent)
141
    {
142
        if (preg_match('/ST(\d{6})/', $tagContent, $matches)) {
143
            $generationDate = $matches[1];
144
145
            return $generationDate;
146
        }
147
148
        throw new \Exception('Invalid format of statement identifier');
149
    }
150
151
    /**
152
     * @param string $tagContent
153
     *
154
     * @return string
155
     *
156
     * @throws \Exception
157
     */
158 View Code Duplication
    private function parseAccountNumber($tagContent)
159
    {
160
        if (preg_match('/(\d{26})/', $tagContent, $matches)) {
161
            $accountNumber = $matches[1];
162
163
            return $accountNumber;
164
        }
165
166
        throw new \Exception('Invalid format of account number');
167
    }
168
169
    /**
170
     * @param string $tagContent
171
     *
172
     * @return string
173
     *
174
     * @throws \Exception
175
     */
176 View Code Duplication
    private function parseStatementNumber($tagContent)
177
    {
178
        if (preg_match('/(\d+)\//', $tagContent, $matches)) {
179
            $statementNumber = $matches[1];
180
181
            return $statementNumber;
182
        }
183
184
        throw new \Exception('Invalid format of statement number');
185
    }
186
187
    /**
188
     * @param string $tagContent
189
     *
190
     * @return array
191
     *
192
     * @throws \Exception
193
     */
194
    private function parseBalance($tagContent)
195
    {
196
        if (preg_match('/([CD])(\d{6})(\w{3})([\d,]+)/', $tagContent, $matches)) {
197
            $balance  = $matches[1];
198
            $date     = $matches[2];
199
            $currency = $matches[3];
200
            $amount   = $matches[4];
201
202
            return compact('balance', 'date', 'currency', 'amount');
203
        }
204
205
        throw new \Exception('Invalid format of balance');
206
    }
207
208
    /**
209
     * @param string $tagContent
210
     *
211
     * @return array
212
     *
213
     * @throws \Exception
214
     */
215
    private function parseTransaction($tagContent)
216
    {
217
        if (preg_match('/(\d{6})(\d{4})([CD])([A-Z])([\d,]+)N(\w{3}).*\n(.+)/', $tagContent, $matches)) {
218
            $valueDate      = $matches[1];
219
            $bookingDate    = $matches[2];
220
            $balance        = $matches[3];
221
            $currencyLetter = $matches[4];
222
            $amount         = $matches[5];
223
            $code           = $matches[6];
224
            $description    = mb_convert_encoding($matches[7], 'UTF-8', 'ISO-8859-2');
225
226
            $currencies = [
227
                'N' => 'PLN',
228
                'R' => 'EUR',
229
                'D' => 'USD'
230
            ];
231
            if (isset($currencies[$currencyLetter])) {
232
                $currency = $currencies[$currencyLetter];
233
            }
234
            else {
235
                throw new \Exception('Invalid format of transaction currency');
236
            }
237
238
            return compact('valueDate', 'bookingDate', 'balance', 'currency', 'amount', 'code', 'description');
239
        }
240
241
        throw new \Exception('Invalid format of transaction');
242
    }
243
244
    /**
245
     * @param string $tagContent
246
     *
247
     * @return string
248
     */
249
    private function parseTransactionDetails($tagContent)
250
    {
251
        return mb_convert_encoding(str_replace(["\r", "\n"], '', $tagContent), 'UTF-8', 'ISO-8859-2');
252
    }
253
}
254