FinTs::createStateOfAccountMessage()   B
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 89

Duplication

Lines 16
Ratio 17.98 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 16
loc 89
ccs 0
cts 43
cp 0
rs 7.9288
c 0
b 0
f 0
cc 5
nc 5
nop 5
crap 30

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Fhp;
4
5
use Fhp\Adapter\AdapterInterface;
6
use Fhp\Adapter\Curl;
7
use Fhp\DataTypes\Kik;
8
use Fhp\DataTypes\Kti;
9
use Fhp\DataTypes\Ktv;
10
use Fhp\Dialog\Dialog;
11
use Fhp\Message\AbstractMessage;
12
use Fhp\Message\Message;
13
use Fhp\Model\SEPAAccount;
14
use Fhp\Response\GetAccounts;
15
use Fhp\Response\GetSaldo;
16
use Fhp\Response\GetSEPAAccounts;
17
use Fhp\Response\GetStatementOfAccount;
18
use Fhp\Segment\HKKAZ;
19
use Fhp\Segment\HKSAL;
20
use Fhp\Segment\HKSPA;
21
use Psr\Log\LoggerInterface;
22
use Psr\Log\NullLogger;
23
24
/**
25
 * Class FinTs.
26
 *
27
 * @package Fhp
28
 */
29
class FinTs
30
{
31
    const DEFAULT_COUNTRY_CODE = 280;
32
33
    /** @var LoggerInterface */
34
    protected $logger;
35
    /** @var  string */
36
    protected $server;
37
    /** @var int */
38
    protected $port;
39
    /** @var string */
40
    protected $bankCode;
41
    /** @var string */
42
    protected $username;
43
    /** @var string */
44
    protected $pin;
45
    /** @var  Connection */
46
    protected $connection;
47
    /** @var  AdapterInterface */
48
    protected $adapter;
49
    /** @var int */
50
    protected $systemId = 0;
51
    /** @var string */
52
    protected $bankName;
53
54
    /**
55
     * FinTs constructor.
56
     * @param string $server
57
     * @param int $port
58
     * @param string $bankCode
59
     * @param string $username
60
     * @param string $pin
61
     * @param LoggerInterface|null $logger
62
     */
63
    public function __construct(
64
        $server,
65
        $port,
66
        $bankCode,
67
        $username,
68
        $pin,
69
        LoggerInterface $logger = null
70
    ) {
71
        $this->server = $server;
72
        $this->port = $port;
73
        $this->logger = null == $logger ? new NullLogger() : $logger;
74
75
        // escaping of bank code not really needed here as it should
76
        // never have special chars. But we just do it to ensure
77
        // that the request will not get messed up and the user
78
        // can receive a valid error response from the HBCI server.
79
        $this->bankCode = $this->escapeString($bankCode);
80
81
        // Here, escaping is needed for usernames or pins with
82
        // HBCI special chars.
83
        $this->username = $this->escapeString($username);
84
        $this->pin = $this->escapeString($pin);
85
86
        $this->adapter = new Curl($this->server, $this->port);
87
        $this->connection = new Connection($this->adapter);
88
    }
89
90
    /**
91
     * Sets the adapter to use.
92
     *
93
     * @param AdapterInterface $adapter
94
     */
95
    public function setAdapter(AdapterInterface $adapter)
96
    {
97
        $this->adapter = $adapter;
98
        $this->connection = new Connection($this->adapter);
99
    }
100
101
    /**
102
     * Gets array of all accounts.
103
     *
104
     * @return Model\Account[]
105
     */
106
    public function getAccounts()
107
    {
108
        $dialog = $this->getDialog();
109
        $result = $dialog->syncDialog();
110
        $this->bankName = $dialog->getBankName();
111
        $accounts = new GetAccounts($result);
112
113
        return $accounts->getAccountsArray();
114
    }
115
116
    /**
117
     * Gets array of all SEPA Accounts.
118
     *
119
     * @return Model\SEPAAccount[]
120
     * @throws Adapter\Exception\AdapterException
121
     * @throws Adapter\Exception\CurlException
122
     */
123
    public function getSEPAAccounts()
124
    {
125
        $dialog = $this->getDialog();
126
        $dialog->syncDialog();
127
        $dialog->initDialog();
128
129
        $message = $this->getNewMessage(
130
            $dialog,
131
            array(new HKSPA(3)),
132
            array(AbstractMessage::OPT_PINTAN_MECH => $dialog->getSupportedPinTanMechanisms())
133
        );
134
135
        $result = $dialog->sendMessage($message);
136
        $dialog->endDialog();
137
        $sepaAccounts = new  GetSEPAAccounts($result->rawResponse);
138
139
        return $sepaAccounts->getSEPAAccountsArray();
140
    }
141
142
    /**
143
     * Gets the bank name.
144
     *
145
     * @return string
146
     */
147
    public function getBankName()
148
    {
149
        if (null == $this->bankName) {
150
            $this->getDialog()->syncDialog();
151
        }
152
153
        return $this->bankName;
154
    }
155
156
    /**
157
     * Gets statement of account.
158
     *
159
     * @param SEPAAccount $account
160
     * @param \DateTime $from
161
     * @param \DateTime $to
162
     * @return Model\StatementOfAccount\StatementOfAccount|null
163
     * @throws \Exception
164
     */
165
    public function getStatementOfAccount(SEPAAccount $account, \DateTime $from, \DateTime $to)
166
    {
167
        $responses = array();
168
169
        $this->logger->info('Start fetching statement of account results');
170
        $this->logger->info('Start date: ' . $from->format('Y-m-d'));
171
        $this->logger->info('End date  : ' . $to->format('Y-m-d'));
172
173
        $dialog = $this->getDialog();
174
        $dialog->syncDialog();
175
        $dialog->initDialog();
176
177
        $message = $this->createStateOfAccountMessage($dialog, $account, $from, $to, null);
178
        $response = $dialog->sendMessage($message);
179
        $touchdowns = $response->getTouchdowns($message);
180
        $soaResponse = new GetStatementOfAccount($response->rawResponse);
181
        $responses[] = $soaResponse->getRawMt940();
182
183
        $touchdownCounter = 1;
184
        while (isset($touchdowns[HKKAZ::NAME])) {
185
            $this->logger->info('Fetching more statement of account results (' . $touchdownCounter++ . ') ...');
186
            $message = $this->createStateOfAccountMessage(
187
                $dialog,
188
                $account,
189
                $from,
190
                $to,
191
                $touchdowns[HKKAZ::NAME]
192
            );
193
194
            $r = $dialog->sendMessage($message);
195
            $touchdowns = $r->getTouchDowns($message);
196
            $soaResponse = new GetStatementOfAccount($r->rawResponse);
197
            $responses[] = $soaResponse->getRawMt940();
198
        }
199
200
        $this->logger->info('Fetching of ' . $touchdownCounter . ' pages done.');
201
        $this->logger->debug('HKKAZ response:');
202
203
        $dialog->endDialog();
204
205
        return GetStatementOfAccount::createModelFromRawMt940(implode('', $responses));
206
    }
207
208
    /**
209
     * Helper method to create a "Statement of Account Message".
210
     *
211
     * @param Dialog $dialog
212
     * @param SEPAAccount $account
213
     * @param \DateTime $from
214
     * @param \DateTime $to
215
     * @param string|null $touchdown
216
     * @return Message
217
     * @throws \Exception
218
     */
219
    protected function createStateOfAccountMessage(
220
        Dialog $dialog,
221
        SepaAccount $account,
222
        \DateTime $from,
223
        \DateTime $to,
224
        $touchdown = null
225
    ) {
226
        // version 4, 5, 6, 7
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
227
228
        // version 5
229
        /*
230
            1 Segmentkopf                   DEG         M 1
231
            2 Kontoverbindung Auftraggeber  DEG ktv #   M 1
232
            3 Alle Konten                   DE  jn  #   M 1
233
            4 Von Datum                     DE dat  #   K 1
234
            5 Bis Datum                     DE dat  #   K 1
235
            6 Maximale Anzahl Einträge      DE num ..4  K 1 >0
236
            7 Aufsetzpunkt                  DE an ..35  K 1
237
         */
238
239
        // version 6
240
        /*
241
            1 Segmentkopf                   1 DEG           M 1
242
            2 Kontoverbindung Auftraggeber  2 DEG ktv #     M 1
243
            3 Alle Konten                   1 DE jn #       M 1
244
            4 Von Datum                     1 DE dat #      O 1
245
            5 Bis Datum                     1 DE dat #      O 1
246
            6 Maximale Anzahl Einträge      1 DE num ..4    C 1 >0
247
            7 Aufsetzpunkt                  1 DE an ..35    C 1
248
         */
249
250
        // version 7
251
        /*
252
            1 Segmentkopf                   1 DEG       M 1
253
            2 Kontoverbindung international 1 DEG kti # M 1
254
            3 Alle Konten                   1 DE jn #   M 1
255
            4 Von Datum                     1 DE dat #  O 1
256
            5 Bis Datum                     1 DE dat #  O 1
257
            6 Maximale Anzahl Einträge      1 DE num ..4 C 1 >0
258
            7 Aufsetzpunkt                  1 DE an ..35 C 1
259
         */
260
261
        switch ($dialog->getHkkazMaxVersion()) {
262
            case 4:
263
            case 5:
264
                $konto = new Deg();
265
                $konto->addDataElement($account->getAccountNumber());
266
                $konto->addDataElement($account->getSubAccount());
267
                $konto->addDataElement(static::DEFAULT_COUNTRY_CODE);
268
                $konto->addDataElement($account->getBlz());
269
                break;
270 View Code Duplication
            case 6:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
271
                $konto = new Ktv(
272
                    $account->getAccountNumber(),
273
                    $account->getSubAccount(),
274
                    new Kik(280, $account->getBlz())
275
                );
276
                break;
277 View Code Duplication
            case 7:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
278
                $konto = new Kti(
279
                    $account->getIban(),
280
                    $account->getBic(),
281
                    $account->getAccountNumber(),
282
                    $account->getSubAccount(),
283
                    new Kik(280, $account->getBlz())
284
                );
285
                break;
286
            default:
287
                throw new \Exception('Unsupported HKKAZ version: ' . $dialog->getHkkazMaxVersion());
288
        }
289
290
        $message = $this->getNewMessage(
291
            $dialog,
292
            array(
293
                new HKKAZ(
294
                    $dialog->getHkkazMaxVersion(),
295
                    3,
296
                    $konto,
297
                    HKKAZ::ALL_ACCOUNTS_N,
298
                    $from,
299
                    $to,
300
                    $touchdown
301
                )
302
            ),
303
            array(AbstractMessage::OPT_PINTAN_MECH => $dialog->getSupportedPinTanMechanisms())
304
        );
305
306
        return $message;
307
    }
308
309
    /**
310
     * Gets the saldo of given SEPAAccount.
311
     *
312
     * @param SEPAAccount $account
313
     * @return Model\Saldo|null
314
     * @throws Adapter\Exception\AdapterException
315
     * @throws Adapter\Exception\CurlException
316
     * @throws \Exception
317
     */
318
    public function getSaldo(SEPAAccount $account)
319
    {
320
        $dialog = $this->getDialog();
321
        $dialog->syncDialog();
322
        $dialog->initDialog();
323
324
        switch ((int) $dialog->getHksalMaxVersion()) {
325
            case 4:
326
            case 5:
327
                $hksalAccount = new Deg(
328
                    $account->getAccountNumber(),
0 ignored issues
show
Unused Code introduced by
The call to Deg::__construct() has too many arguments starting with $account->getAccountNumber().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
329
                    $account->getSubAccount(),
330
                    static::DEFAULT_COUNTRY_CODE, $account->getBlz()
331
                );
332
                $hksalAccount->addDataElement($account->getAccountNumber());
333
                $hksalAccount->addDataElement($account->getSubAccount());
334
                $hksalAccount->addDataElement(static::DEFAULT_COUNTRY_CODE);
335
                $hksalAccount->addDataElement($account->getBlz());
336
                break;
337 View Code Duplication
            case 6:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
338
                $hksalAccount = new Ktv(
339
                    $account->getAccountNumber(),
340
                    $account->getSubAccount(),
341
                    new Kik(280, $account->getBlz())
342
                );
343
                break;
344 View Code Duplication
            case 7:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
345
                $hksalAccount = new Kti(
346
                    $account->getIban(),
347
                    $account->getBic(),
348
                    $account->getAccountNumber(),
349
                    $account->getSubAccount(),
350
                    new Kik(280, $account->getBlz())
351
                );
352
                break;
353
            default:
354
                throw new \Exception('Unsupported HKSAL version: ' . $dialog->getHksalMaxVersion());
355
        }
356
357
        $message = new Message(
358
            $this->bankCode,
359
            $this->username,
360
            $this->pin,
361
            $dialog->getSystemId(),
362
            $dialog->getDialogId(),
363
            $dialog->getMessageNumber(),
364
            array(
365
                new HKSAL($dialog->getHksalMaxVersion(), 3, $hksalAccount, HKSAL::ALL_ACCOUNTS_N)
366
            ),
367
            array(
368
                AbstractMessage::OPT_PINTAN_MECH => $dialog->getSupportedPinTanMechanisms()
369
            )
370
        );
371
372
        $response = $dialog->sendMessage($message);
373
        $response = new GetSaldo($response->rawResponse);
374
375
        return $response->getSaldoModel();
376
    }
377
378
    /**
379
     * Helper method to retrieve a pre configured message object.
380
     * Factory for poor people :)
381
     *
382
     * @param Dialog $dialog
383
     * @param array $segments
384
     * @param array $options
385
     * @return Message
386
     */
387
    protected function getNewMessage(Dialog $dialog, array $segments, array $options)
388
    {
389
        return new Message(
390
            $this->bankCode,
391
            $this->username,
392
            $this->pin,
393
            $dialog->getSystemId(),
394
            $dialog->getDialogId(),
395
            $dialog->getMessageNumber(),
396
            $segments,
397
            $options
398
        );
399
    }
400
401
    /**
402
     * Helper method to retrieve a pre configured dialog object.
403
     * Factory for poor people :)
404
     *
405
     * @return Dialog
406
     */
407
    protected function getDialog()
408
    {
409
        return new Dialog(
410
            $this->connection,
411
            $this->bankCode,
412
            $this->username,
413
            $this->pin,
414
            $this->systemId,
415
            $this->logger
416
        );
417
    }
418
419
    /**
420
     * Needed for escaping userdata.
421
     * HBCI escape char is "?"
422
     *
423
     * @param string $string
424
     * @return string
425
     */
426 10
    protected function escapeString($string)
427
    {
428 10
        return str_replace(
429 10
            array('?', '@', ':', '+', '\''),
430 10
            array('??', '?@', '?:', '?+', '?\''),
431
            $string
432 10
        );
433
    }
434
}
435