parseAccountInfo($content); // Parse transactions $transactions = $this->parseTransactions($content); // Formatar como dados tabulares $headers = ['DTPOSTED', 'TRNTYPE', 'TRNAMT', 'FITID', 'NAME', 'MEMO']; $data = []; foreach ($transactions as $txn) { $data[] = [ $txn['date'] ?? '', $txn['type'] ?? '', $txn['amount'] ?? '', $txn['fitid'] ?? '', $txn['name'] ?? '', $txn['memo'] ?? '', ]; } return [ 'headers' => $headers, 'data' => $data, 'total_rows' => count($data), 'account_info' => $accountInfo, 'raw_transactions' => $transactions, ]; } /** * Get headers (OFX has fixed structure) */ public function getHeaders(string $filePath, array $options = []): array { return ['DTPOSTED', 'TRNTYPE', 'TRNAMT', 'FITID', 'NAME', 'MEMO']; } /** * Get preview data */ public function getPreview(string $filePath, int $rows = 10, array $options = []): array { $parsed = $this->parse($filePath, $options); $preview = []; $count = 0; foreach ($parsed['data'] as $row) { if ($count >= $rows) { break; } $preview[] = [ 'row_index' => $count, 'data' => $row, ]; $count++; } return [ 'preview' => $preview, 'total_rows' => $parsed['total_rows'], 'columns_count' => count($parsed['headers']), 'headers' => $parsed['headers'], 'account_info' => $parsed['account_info'] ?? null, ]; } /** * Parse account information from OFX */ protected function parseAccountInfo(string $content): array { $info = []; // Bank ID if (preg_match('/([^<\n]+)/i', $content, $matches)) { $info['bank_id'] = trim($matches[1]); } // Account ID if (preg_match('/([^<\n]+)/i', $content, $matches)) { $info['account_id'] = trim($matches[1]); } // Account Type if (preg_match('/([^<\n]+)/i', $content, $matches)) { $info['account_type'] = trim($matches[1]); } // Currency if (preg_match('/([^<\n]+)/i', $content, $matches)) { $info['currency'] = trim($matches[1]); } // Balance if (preg_match('/([^<\n]+)/i', $content, $matches)) { $info['balance'] = floatval(trim($matches[1])); } // Balance Date if (preg_match('/([^<\n]+)/i', $content, $matches)) { $info['balance_date'] = $this->parseOfxDate(trim($matches[1])); } return $info; } /** * Parse transactions from OFX */ protected function parseTransactions(string $content): array { $transactions = []; // Find all STMTTRN blocks preg_match_all('/(.*?)<\/STMTTRN>/is', $content, $matches); // Também tentar sem tag de fechamento (OFX SGML) if (empty($matches[1])) { // Split by STMTTRN tags $parts = preg_split('//i', $content); array_shift($parts); // Remover parte antes do primeiro STMTTRN foreach ($parts as $part) { // Encontrar fim da transação $endPos = stripos($part, ''); if ($endPos !== false) { $part = substr($part, 0, $endPos); } else { // Tentar encontrar próximo STMTTRN ou fim de lista $nextPos = stripos($part, ''); if ($nextPos !== false) { $part = substr($part, 0, $nextPos); } $endListPos = stripos($part, ''); if ($endListPos !== false) { $part = substr($part, 0, $endListPos); } } $txn = $this->parseTransaction($part); if (!empty($txn['amount'])) { $transactions[] = $txn; } } } else { foreach ($matches[1] as $block) { $txn = $this->parseTransaction($block); if (!empty($txn['amount'])) { $transactions[] = $txn; } } } return $transactions; } /** * Parse a single transaction block */ protected function parseTransaction(string $block): array { $txn = []; // Type (CREDIT, DEBIT, etc.) if (preg_match('/([^<\n]+)/i', $block, $matches)) { $txn['type'] = trim($matches[1]); } // Date Posted if (preg_match('/([^<\n]+)/i', $block, $matches)) { $txn['date'] = $this->parseOfxDate(trim($matches[1])); } // Amount if (preg_match('/([^<\n]+)/i', $block, $matches)) { $txn['amount'] = floatval(str_replace(',', '.', trim($matches[1]))); } // FIT ID (unique identifier) if (preg_match('/([^<\n]+)/i', $block, $matches)) { $txn['fitid'] = trim($matches[1]); } // Name/Payee if (preg_match('/([^<\n]+)/i', $block, $matches)) { $txn['name'] = trim($matches[1]); } // Memo if (preg_match('/([^<\n]+)/i', $block, $matches)) { $txn['memo'] = trim($matches[1]); } // Check Number if (preg_match('/([^<\n]+)/i', $block, $matches)) { $txn['check_num'] = trim($matches[1]); } return $txn; } /** * Parse OFX date format (YYYYMMDDHHMMSS) */ protected function parseOfxDate(string $date): string { // Remove timezone info $date = preg_replace('/\[.*\]/', '', $date); $date = trim($date); if (strlen($date) >= 8) { $year = substr($date, 0, 4); $month = substr($date, 4, 2); $day = substr($date, 6, 2); return "$day/$month/$year"; } return $date; } /** * Check if parser supports the extension */ public static function supports(string $extension): bool { return in_array(strtolower($extension), self::$supportedExtensions); } }