Passed
Pull Request — master (#95)
by
unknown
02:42
created
lib/Fhp/Parser/MT940.php 1 patch
Indentation   +233 added lines, -233 removed lines patch added patch discarded remove patch
@@ -13,237 +13,237 @@
 block discarded – undo
13 13
  */
14 14
 class MT940
15 15
 {
16
-	const TARGET_ARRAY = 0;
17
-
18
-	const CD_CREDIT = 'credit';
19
-	const CD_DEBIT = 'debit';
20
-	const CD_CREDIT_CANCELLATION = 'credit_cancellation';
21
-	const CD_DEBIT_CANCELLATION = 'debit_cancellation';
22
-
23
-	// The divider can be either \r\n or @@
24
-	const LINE_DIVIDER = "(@@|\r\n)";
25
-
26
-	/** @var string */
27
-	protected $rawData;
28
-	/** @var string */
29
-	protected $soaDate;
30
-
31
-	/**
32
-	 * MT940 constructor.
33
-	 *
34
-	 * @param string $rawData
35
-	 */
36
-	public function __construct($rawData)
37
-	{
38
-		$this->rawData = (string) $rawData;
39
-	}
40
-
41
-	/**
42
-	 * @param string $target
43
-	 * @return array
44
-	 * @throws MT940Exception
45
-	 */
46
-	public function parse($target)
47
-	{
48
-		switch ($target) {
49
-		case static::TARGET_ARRAY:
50
-			return $this->parseToArray();
51
-			break;
52
-		default:
53
-			throw new MT940Exception('Invalid parse type provided');
54
-		}
55
-	}
56
-
57
-	/**
58
-	 * @return array
59
-	 * @throws MT940Exception
60
-	 */
61
-	protected function parseToArray()
62
-	{
63
-		$result = array();
64
-
65
-		// split at every :20: ("Die Felder ":20:" bis ":28:" müssen vor jedem Zwischensaldo ausgegeben werden.")
66
-		$statementBlocks = preg_split('/' . self::LINE_DIVIDER . ':20:.*?' . self::LINE_DIVIDER . '/', $this->rawData);
67
-
68
-		foreach ($statementBlocks as $statementBlock) {
69
-			$parts = preg_split('/' . self::LINE_DIVIDER . ':/', $statementBlock);
70
-			$statement = array();
71
-			$transactions = array();
72
-			$cnt = 0;
73
-			for ($i = 0, $cnt = count($parts); $i < $cnt; $i++) {
74
-				// handle start balance
75
-				// 60F:C160401EUR1234,56
76
-				if (preg_match('/^60[FM]:/', $parts[$i])) {
77
-					$parts[$i] = substr($parts[$i], 4);
78
-					$this->soaDate = $this->getDate(substr($parts[$i], 1, 6));
79
-
80
-					$amount = str_replace(',', '.', substr($parts[$i], 10));
81
-					$statement['start_balance'] = array(
82
-									'amount' => $amount,
83
-									'credit_debit' => (substr($parts[$i], 0, 1) == 'C') ? static::CD_CREDIT : static::CD_DEBIT
84
-							);
85
-					$statement['date'] = $this->soaDate;
86
-				} elseif (
87
-							// found transaction
88
-							// trx:61:1603310331DR637,39N033NONREF
89
-							0 === strpos($parts[$i], '61:')
90
-							&& isset($parts[$i + 1])
91
-							&& 0 === strpos($parts[$i + 1], '86:')
92
-					) {
93
-					$transaction = substr($parts[$i], 3);
94
-					$description = substr($parts[$i + 1], 3);
95
-
96
-					$currentTrx = array();
97
-
98
-					preg_match('/^\d{6}(\d{4})?(C|D|RC|RD)[A-Z]?([^N]+)N/', $transaction, $matches);
99
-
100
-					switch ($matches[2]) {
101
-							case 'C':
102
-									$currentTrx['credit_debit'] = static::CD_CREDIT;
103
-									break;
104
-							case 'D':
105
-									$currentTrx['credit_debit'] = static::CD_DEBIT;
106
-									break;
107
-							case 'RC':
108
-									$currentTrx['credit_debit'] = static::CD_CREDIT_CANCELLATION;
109
-									break;
110
-							case 'RD':
111
-									$currentTrx['credit_debit'] = static::CD_DEBIT_CANCELLATION;
112
-									break;
113
-							default:
114
-									throw new MT940Exception('c/d/rc/rd mark not found in: ' . $transaction);
115
-							}
116
-
117
-					$amount = $matches[3];
118
-					$amount = str_replace(',', '.', $amount);
119
-					$currentTrx['amount'] = floatval($amount);
120
-
121
-					$currentTrx['transaction_code'] = substr($description, 0, 3);
122
-
123
-					$description = $this->parseDescription($description);
124
-					$currentTrx['description'] = $description;
125
-
126
-					// :61:1605110509D198,02NMSCNONREF
127
-					// 16 = year
128
-					// 0511 = valuta date
129
-					// 0509 = booking date
130
-					$year = substr($transaction, 0, 2);
131
-					$valutaDate = $this->getDate($year . substr($transaction, 2, 4));
132
-
133
-					$bookingDate = substr($transaction, 6, 4);
134
-					if (preg_match('/^\d{4}$/', $bookingDate)) {
135
-						// if valuta date is earlier than booking date, then it must be in the new year.
136
-						if (substr($transaction, 2, 2) == '12' && substr($transaction, 6, 2) == '01') {
137
-							$year++;
138
-						} elseif (substr($transaction, 2, 2) == '01' && substr($transaction, 6, 2) == '12') {
139
-							$year--;
140
-						}
141
-						$bookingDate = $this->getDate($year . $bookingDate);
142
-					} else {
143
-						// if booking date not set in :61, then we have to take it from :60F
144
-						$bookingDate = $this->soaDate;
145
-					}
146
-
147
-					$currentTrx['booking_date'] = $bookingDate;
148
-					$currentTrx['valuta_date'] = $valutaDate;
149
-
150
-					$transactions[] = $currentTrx;
151
-				}
152
-			}
153
-			$statement['transactions'] = $transactions;
154
-			if (count($transactions) > 0) {
155
-				$result[] = $statement;
156
-			}
157
-		}
158
-
159
-		return $result;
160
-	}
161
-
162
-	/**
163
-	 * @param string $descr
164
-	 * @return array
165
-	 */
166
-	protected function parseDescription($descr)
167
-	{
168
-		$prepared = array();
169
-		$result = array();
170
-
171
-		// prefill with empty values
172
-		for ($i = 0; $i <= 63; $i++) {
173
-			$prepared[$i] = null;
174
-		}
175
-
176
-		$descr = preg_replace('/' . self::LINE_DIVIDER . '/', '', $descr);
177
-		$descr = preg_replace('/  +/', ' ', $descr);
178
-		$descr = str_replace('? ', '?', $descr);
179
-		preg_match_all('/\?[\r\n]*(\d{2})([^\?]+)/', $descr, $matches, PREG_SET_ORDER);
180
-
181
-		$descriptionLines = array();
182
-		$description1 = ''; // Legacy, could be removed.
183
-		$description2 = ''; // Legacy, could be removed.
184
-		foreach ($matches as $m) {
185
-			$index = (int) $m[1];
186
-			if ((20 <= $index && $index <= 29) || (60 <= $index && $index <= 63)) {
187
-				if (20 <= $index && $index <= 29) {
188
-					$description1 .= $m[2];
189
-				} else {
190
-					$description2 .= $m[2];
191
-				}
192
-				$m[2] = trim($m[2]);
193
-				if (!empty($m[2])) {
194
-					$descriptionLines[] = $m[2];
195
-				}
196
-			} else {
197
-				$prepared[$index] = $m[2];
198
-			}
199
-		}
200
-
201
-		$description = array();
202
-		if (empty($descriptionLines) || strlen($descriptionLines[0]) < 5 || $descriptionLines[0][4] !== '+') {
203
-			$description['SVWZ'] = implode('', $descriptionLines);
204
-		} else {
205
-			$lastType = null;
206
-			foreach ($descriptionLines as $line) {
207
-				if (strlen($line) > 5 && $line[4] === '+') {
208
-					if ($lastType != null) {
209
-						$description[$lastType] = trim($description[$lastType]);
210
-					}
211
-					$lastType = substr($line, 0, 4);
212
-					$description[$lastType] = substr($line, 5);
213
-				} else {
214
-					$description[$lastType] .= $line;
215
-				}
216
-				if (strlen($line) < 27) {
217
-					// Usually, lines are 27 characters long. In case characters are missing, then it's either the end
218
-					// of the current type or spaces have been trimmed from the end. We want to collapse multiple spaces
219
-					// into one and we don't want to leave trailing spaces behind. So add a single space here to make up
220
-					// for possibly missing spaces, and if it's the end of the type, it will be trimmed off later.
221
-					$description[$lastType] .= ' ';
222
-				}
223
-			}
224
-			$description[$lastType] = trim($description[$lastType]);
225
-		}
226
-
227
-		$result['description']       = $description;
228
-		$result['booking_text']      = trim($prepared[0]);
229
-		$result['primanoten_nr']     = trim($prepared[10]);
230
-		$result['description_1']     = trim($description1);
231
-		$result['bank_code']         = trim($prepared[30]);
232
-		$result['account_number']    = trim($prepared[31]);
233
-		$result['name']              = trim($prepared[32] . $prepared[33]);
234
-		$result['text_key_addition'] = trim($prepared[34]);
235
-		$result['description_2']     = trim($description2);
236
-		return $result;
237
-	}
238
-
239
-	/**
240
-	 * @param string $val
241
-	 * @return string
242
-	 */
243
-	protected function getDate($val)
244
-	{
245
-		$val = '20' . $val;
246
-		preg_match('/(\d{4})(\d{2})(\d{2})/', $val, $m);
247
-		return $m[1] . '-' . $m[2] . '-' . $m[3];
248
-	}
16
+    const TARGET_ARRAY = 0;
17
+
18
+    const CD_CREDIT = 'credit';
19
+    const CD_DEBIT = 'debit';
20
+    const CD_CREDIT_CANCELLATION = 'credit_cancellation';
21
+    const CD_DEBIT_CANCELLATION = 'debit_cancellation';
22
+
23
+    // The divider can be either \r\n or @@
24
+    const LINE_DIVIDER = "(@@|\r\n)";
25
+
26
+    /** @var string */
27
+    protected $rawData;
28
+    /** @var string */
29
+    protected $soaDate;
30
+
31
+    /**
32
+     * MT940 constructor.
33
+     *
34
+     * @param string $rawData
35
+     */
36
+    public function __construct($rawData)
37
+    {
38
+        $this->rawData = (string) $rawData;
39
+    }
40
+
41
+    /**
42
+     * @param string $target
43
+     * @return array
44
+     * @throws MT940Exception
45
+     */
46
+    public function parse($target)
47
+    {
48
+        switch ($target) {
49
+        case static::TARGET_ARRAY:
50
+            return $this->parseToArray();
51
+            break;
52
+        default:
53
+            throw new MT940Exception('Invalid parse type provided');
54
+        }
55
+    }
56
+
57
+    /**
58
+     * @return array
59
+     * @throws MT940Exception
60
+     */
61
+    protected function parseToArray()
62
+    {
63
+        $result = array();
64
+
65
+        // split at every :20: ("Die Felder ":20:" bis ":28:" müssen vor jedem Zwischensaldo ausgegeben werden.")
66
+        $statementBlocks = preg_split('/' . self::LINE_DIVIDER . ':20:.*?' . self::LINE_DIVIDER . '/', $this->rawData);
67
+
68
+        foreach ($statementBlocks as $statementBlock) {
69
+            $parts = preg_split('/' . self::LINE_DIVIDER . ':/', $statementBlock);
70
+            $statement = array();
71
+            $transactions = array();
72
+            $cnt = 0;
73
+            for ($i = 0, $cnt = count($parts); $i < $cnt; $i++) {
74
+                // handle start balance
75
+                // 60F:C160401EUR1234,56
76
+                if (preg_match('/^60[FM]:/', $parts[$i])) {
77
+                    $parts[$i] = substr($parts[$i], 4);
78
+                    $this->soaDate = $this->getDate(substr($parts[$i], 1, 6));
79
+
80
+                    $amount = str_replace(',', '.', substr($parts[$i], 10));
81
+                    $statement['start_balance'] = array(
82
+                                    'amount' => $amount,
83
+                                    'credit_debit' => (substr($parts[$i], 0, 1) == 'C') ? static::CD_CREDIT : static::CD_DEBIT
84
+                            );
85
+                    $statement['date'] = $this->soaDate;
86
+                } elseif (
87
+                            // found transaction
88
+                            // trx:61:1603310331DR637,39N033NONREF
89
+                            0 === strpos($parts[$i], '61:')
90
+                            && isset($parts[$i + 1])
91
+                            && 0 === strpos($parts[$i + 1], '86:')
92
+                    ) {
93
+                    $transaction = substr($parts[$i], 3);
94
+                    $description = substr($parts[$i + 1], 3);
95
+
96
+                    $currentTrx = array();
97
+
98
+                    preg_match('/^\d{6}(\d{4})?(C|D|RC|RD)[A-Z]?([^N]+)N/', $transaction, $matches);
99
+
100
+                    switch ($matches[2]) {
101
+                            case 'C':
102
+                                    $currentTrx['credit_debit'] = static::CD_CREDIT;
103
+                                    break;
104
+                            case 'D':
105
+                                    $currentTrx['credit_debit'] = static::CD_DEBIT;
106
+                                    break;
107
+                            case 'RC':
108
+                                    $currentTrx['credit_debit'] = static::CD_CREDIT_CANCELLATION;
109
+                                    break;
110
+                            case 'RD':
111
+                                    $currentTrx['credit_debit'] = static::CD_DEBIT_CANCELLATION;
112
+                                    break;
113
+                            default:
114
+                                    throw new MT940Exception('c/d/rc/rd mark not found in: ' . $transaction);
115
+                            }
116
+
117
+                    $amount = $matches[3];
118
+                    $amount = str_replace(',', '.', $amount);
119
+                    $currentTrx['amount'] = floatval($amount);
120
+
121
+                    $currentTrx['transaction_code'] = substr($description, 0, 3);
122
+
123
+                    $description = $this->parseDescription($description);
124
+                    $currentTrx['description'] = $description;
125
+
126
+                    // :61:1605110509D198,02NMSCNONREF
127
+                    // 16 = year
128
+                    // 0511 = valuta date
129
+                    // 0509 = booking date
130
+                    $year = substr($transaction, 0, 2);
131
+                    $valutaDate = $this->getDate($year . substr($transaction, 2, 4));
132
+
133
+                    $bookingDate = substr($transaction, 6, 4);
134
+                    if (preg_match('/^\d{4}$/', $bookingDate)) {
135
+                        // if valuta date is earlier than booking date, then it must be in the new year.
136
+                        if (substr($transaction, 2, 2) == '12' && substr($transaction, 6, 2) == '01') {
137
+                            $year++;
138
+                        } elseif (substr($transaction, 2, 2) == '01' && substr($transaction, 6, 2) == '12') {
139
+                            $year--;
140
+                        }
141
+                        $bookingDate = $this->getDate($year . $bookingDate);
142
+                    } else {
143
+                        // if booking date not set in :61, then we have to take it from :60F
144
+                        $bookingDate = $this->soaDate;
145
+                    }
146
+
147
+                    $currentTrx['booking_date'] = $bookingDate;
148
+                    $currentTrx['valuta_date'] = $valutaDate;
149
+
150
+                    $transactions[] = $currentTrx;
151
+                }
152
+            }
153
+            $statement['transactions'] = $transactions;
154
+            if (count($transactions) > 0) {
155
+                $result[] = $statement;
156
+            }
157
+        }
158
+
159
+        return $result;
160
+    }
161
+
162
+    /**
163
+     * @param string $descr
164
+     * @return array
165
+     */
166
+    protected function parseDescription($descr)
167
+    {
168
+        $prepared = array();
169
+        $result = array();
170
+
171
+        // prefill with empty values
172
+        for ($i = 0; $i <= 63; $i++) {
173
+            $prepared[$i] = null;
174
+        }
175
+
176
+        $descr = preg_replace('/' . self::LINE_DIVIDER . '/', '', $descr);
177
+        $descr = preg_replace('/  +/', ' ', $descr);
178
+        $descr = str_replace('? ', '?', $descr);
179
+        preg_match_all('/\?[\r\n]*(\d{2})([^\?]+)/', $descr, $matches, PREG_SET_ORDER);
180
+
181
+        $descriptionLines = array();
182
+        $description1 = ''; // Legacy, could be removed.
183
+        $description2 = ''; // Legacy, could be removed.
184
+        foreach ($matches as $m) {
185
+            $index = (int) $m[1];
186
+            if ((20 <= $index && $index <= 29) || (60 <= $index && $index <= 63)) {
187
+                if (20 <= $index && $index <= 29) {
188
+                    $description1 .= $m[2];
189
+                } else {
190
+                    $description2 .= $m[2];
191
+                }
192
+                $m[2] = trim($m[2]);
193
+                if (!empty($m[2])) {
194
+                    $descriptionLines[] = $m[2];
195
+                }
196
+            } else {
197
+                $prepared[$index] = $m[2];
198
+            }
199
+        }
200
+
201
+        $description = array();
202
+        if (empty($descriptionLines) || strlen($descriptionLines[0]) < 5 || $descriptionLines[0][4] !== '+') {
203
+            $description['SVWZ'] = implode('', $descriptionLines);
204
+        } else {
205
+            $lastType = null;
206
+            foreach ($descriptionLines as $line) {
207
+                if (strlen($line) > 5 && $line[4] === '+') {
208
+                    if ($lastType != null) {
209
+                        $description[$lastType] = trim($description[$lastType]);
210
+                    }
211
+                    $lastType = substr($line, 0, 4);
212
+                    $description[$lastType] = substr($line, 5);
213
+                } else {
214
+                    $description[$lastType] .= $line;
215
+                }
216
+                if (strlen($line) < 27) {
217
+                    // Usually, lines are 27 characters long. In case characters are missing, then it's either the end
218
+                    // of the current type or spaces have been trimmed from the end. We want to collapse multiple spaces
219
+                    // into one and we don't want to leave trailing spaces behind. So add a single space here to make up
220
+                    // for possibly missing spaces, and if it's the end of the type, it will be trimmed off later.
221
+                    $description[$lastType] .= ' ';
222
+                }
223
+            }
224
+            $description[$lastType] = trim($description[$lastType]);
225
+        }
226
+
227
+        $result['description']       = $description;
228
+        $result['booking_text']      = trim($prepared[0]);
229
+        $result['primanoten_nr']     = trim($prepared[10]);
230
+        $result['description_1']     = trim($description1);
231
+        $result['bank_code']         = trim($prepared[30]);
232
+        $result['account_number']    = trim($prepared[31]);
233
+        $result['name']              = trim($prepared[32] . $prepared[33]);
234
+        $result['text_key_addition'] = trim($prepared[34]);
235
+        $result['description_2']     = trim($description2);
236
+        return $result;
237
+    }
238
+
239
+    /**
240
+     * @param string $val
241
+     * @return string
242
+     */
243
+    protected function getDate($val)
244
+    {
245
+        $val = '20' . $val;
246
+        preg_match('/(\d{4})(\d{2})(\d{2})/', $val, $m);
247
+        return $m[1] . '-' . $m[2] . '-' . $m[3];
248
+    }
249 249
 }
Please login to merge, or discard this patch.