Issues (2)

src/Ofx.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Beccha\OfxParser;
6
7
use Beccha\OfxParser\Entity\BankAccount;
8
use Beccha\OfxParser\Entity\Institution;
9
use Beccha\OfxParser\Entity\Payee;
10
use Beccha\OfxParser\Entity\SignOn;
11
use Beccha\OfxParser\Entity\Statement;
12
use Beccha\OfxParser\Entity\Status;
13
use Beccha\OfxParser\Entity\Transaction;
14
use Beccha\OfxParser\Exception\UnRecognisedDateFormat;
15
use DateTime;
16
use Exception;
17
use SimpleXMLElement;
18
19
/**
20
 * The OFX object
21
 *
22
 * Heavily refactored from Guillaume Bailleul's grimfor/ofxparser
23
 *
24
 * Second refactor by Oliver Lowe to unify the API across all
25
 * OFX data-types.
26
 *
27
 * Based on Andrew A Smith's Ruby ofx-parser
28
 *
29
 * @author Guillaume BAILLEUL <[email protected]>
30
 * @author James Titcumb <[email protected]>
31
 * @author Oliver Lowe <[email protected]>
32
 */
33
class Ofx
34
{
35
    private SignOn $signOn;
36
    /**
37
     * @var array|BankAccount[]
38
     */
39
    private array $bankAccounts;
40
41
    /**
42
     * @param SimpleXMLElement $xml
43
     * @throws Exception
44
     */
45
    public function __construct(SimpleXMLElement $xml)
46
    {
47
        $this->signOn = $this->buildSignOn($xml->SIGNONMSGSRSV1->SONRS);
48
        $this->bankAccounts = $this->buildBankAccounts($xml);
49
    }
50
51
    public function getSignOn(): SignOn
52
    {
53
        return $this->signOn;
54
    }
55
56
    /**
57
     * @return array|BankAccount[]
58
     */
59
    public function getBankAccounts(): array
60
    {
61
        return $this->bankAccounts;
62
    }
63
64
    /**
65
     * @param SimpleXMLElement $xml
66
     * @return SignOn
67
     * @throws Exception
68
     */
69
    private function buildSignOn(SimpleXMLElement $xml): SignOn
70
    {
71
        $institute = new Institution(
72
            (string)$xml->FI->FID,
73
            (string)$xml->FI->ORG
74
        );
75
76
        return new SignOn(
77
            $this->buildStatus($xml->STATUS),
78
            $this->createDateTimeFromStr((string)$xml->DTSERVER),
79
            (string)$xml->LANGUAGE,
80
            $institute
81
        );
82
    }
83
84
    /**
85
     * @param SimpleXMLElement $xml
86
     * @return array<BankAccount>
87
     * @throws Exception
88
     */
89
    private function buildBankAccounts(SimpleXMLElement $xml): array
90
    {
91
        // Loop through the bank accounts
92
        $bankAccounts = [];
93
        foreach ($xml->BANKMSGSRSV1->STMTTRNRS as $accountStatement) {
94
            $bankAccounts[] = $this->buildBankAccount($accountStatement);
0 ignored issues
show
It seems like $accountStatement can also be of type null; however, parameter $xml of Beccha\OfxParser\Ofx::buildBankAccount() does only seem to accept SimpleXMLElement, maybe add an additional type check? ( Ignorable by Annotation )

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

94
            $bankAccounts[] = $this->buildBankAccount(/** @scrutinizer ignore-type */ $accountStatement);
Loading history...
95
        }
96
        return $bankAccounts;
97
    }
98
99
    /**
100
     * @param SimpleXMLElement $xml
101
     * @return BankAccount
102
     * @throws Exception
103
     */
104
    private function buildBankAccount(SimpleXMLElement $xml): BankAccount
105
    {
106
        return new BankAccount(
107
            (string)$xml->STMTRS->BANKACCTFROM->BRANCHID,
108
            (string)$xml->STMTRS->BANKACCTFROM->ACCTID,
109
            (string)$xml->STMTRS->BANKACCTFROM->ACCTTYPE,
110
            (float)($xml->STMTRS->LEDGERBAL->BALAMT),
111
            $this->createDateTimeFromStr(
112
                (string)$xml->STMTRS->LEDGERBAL->DTASOF
113
            ),
114
            (string)$xml->STMTRS->BANKACCTFROM->BANKID,
115
            $this->buildStatement($xml),
116
            (string)$xml->TRNUID
117
        );
118
    }
119
120
    /**
121
     * @throws Exception
122
     */
123
    private function buildStatement(SimpleXMLElement $xml): Statement
124
    {
125
        return new Statement(
126
            (string)$xml->STMTRS->CURDEF,
127
            $this->buildTransactions($xml->STMTRS->BANKTRANLIST->STMTTRN),
128
            $this->createDateTimeFromStr((string)$xml->STMTRS->BANKTRANLIST->DTSTART),
129
            $this->createDateTimeFromStr((string)$xml->STMTRS->BANKTRANLIST->DTEND)
130
        );
131
    }
132
133
    /**
134
     * @return array<Transaction>
135
     * @throws Exception
136
     */
137
    private function buildTransactions(SimpleXMLElement $transactions): array
138
    {
139
        $transactionEntities = [];
140
        foreach ($transactions as $t) {
141
            $payee = $this->buildPayee($t->PAYEE);
142
143
            $transaction = new Transaction(
144
                (string)$t->TRNTYPE,
145
                ($this->createDateTimeFromStr((string)$t->DTPOSTED)),
146
                (float)$t->TRNAMT,
147
                (string)$t->FITID,
148
                (string)$t->NAME,
149
                (string)$t->MEMO,
150
                (string)$t->SIC,
151
                (string)$t->CHECKNUM,
152
                $payee
153
            );
154
155
            $transactionEntities[] = $transaction;
156
        }
157
158
        return $transactionEntities;
159
    }
160
161
    private function buildStatus(SimpleXMLElement $xml): Status
162
    {
163
        return new Status(
164
            (string)$xml->CODE,
165
            (string)$xml->SEVERITY,
166
            (string)$xml->MESSAGE
167
        );
168
    }
169
170
    private function buildPayee(SimpleXMLElement $xml): Payee
171
    {
172
        return new Payee(
173
            (string)$xml->NAME,
174
            (string)$xml->ADDR1,
175
            (string)$xml->ADDR2,
176
            (string)$xml->ADDR3,
177
            (string)$xml->CITY,
178
            (string)$xml->STATE,
179
            (string)$xml->POSTALCODE,
180
            (string)$xml->COUNTRY,
181
            (string)$xml->PHONE
182
        );
183
    }
184
185
    /**
186
     * Create a DateTime object from a valid OFX date format
187
     *
188
     * Supports:
189
     * YYYYMMDDHHMMSS.XXX[gmt offset:tz name]
190
     * YYYYMMDDHHMMSS.XXX
191
     * YYYYMMDDHHMMSS
192
     * YYYYMMDD
193
     * YYYY-MM-DD
194
     * @throws Exception
195
     */
196
    private function createDateTimeFromStr(string $dateString): DateTime
197
    {
198
        $regex = "/"
199
            . "(\d{4})-?(\d{2})-?(\d{2})?" // YYYYMMDD   YYYY-MM-DD          1,2,3
200
            . "(?:(\d{2})(\d{2})(\d{2}))?" // HHMMSS   - optional  4,5,6
201
            . "(?:\.(\d{3}))?" // .XXX     - optional  7
202
            . "(?:\[(-?\d+):(\w{3}]))?" // [-n:TZ]  - optional  8,9
203
            . "/";
204
205
        if (preg_match($regex, $dateString, $matches)) {
206
            $year = (int)$matches[1];
207
            $month = (int)$matches[2];
208
            $day = (int)$matches[3];
209
            $hour = isset($matches[4]) ? (int)$matches[4] : 0;
210
            $min = isset($matches[5]) ? (int)$matches[5] : 0;
211
            $sec = isset($matches[6]) ? (int)$matches[6] : 0;
212
213
            $format = $year . '-' . $month . '-' . $day . ' ' . $hour . ':' . $min . ':' . $sec;
214
215
            return new DateTime($format);
216
        }
217
        throw new UnRecognisedDateFormat($dateString);
218
    }
219
}
220