Adding a New Bank (4.x)
Older Version Documentation
You are viewing documentation for version 4.x. For the latest version with immutable architecture, see 5.x documentation or read the 5.0 Upgrade Guide.
This guide explains how to add support for a new bank to version 4.x of the library.
Overview
Adding a new bank involves:
- Creating a parser class that implements
ParserInterface - Writing regex patterns to extract transaction data
- Creating comprehensive tests
- Documenting the parser
Step 1: Create Parser Class
Create a new parser in src/Parser/YourBank/YourBankMailParser.php:
<?php
declare(strict_types=1);
namespace Tomaj\BankMailsParser\Parser\YourBank;
use Tomaj\BankMailsParser\MailContent;
use Tomaj\BankMailsParser\Parser\ParserInterface;
class YourBankMailParser implements ParserInterface
{
public function parse(string $content): ?MailContent
{
// Extract required transaction field
$pattern = '/Transaction amount: ([\d.]+) ([A-Z]{3})/';
$res = preg_match($pattern, $content, $result);
if ($res !== 1) {
return null; // Email format not recognized
}
// Create MailContent
$mailContent = new MailContent();
// Set required fields
$mailContent->setAmount((float) $result[1]);
$mailContent->setCurrency($result[2]);
// Extract and set optional fields
$accountNumber = $this->extractAccountNumber($content);
if ($accountNumber !== null) {
$mailContent->setAccountNumber($accountNumber);
}
$vs = $this->extractVariableSymbol($content);
if ($vs !== null) {
$mailContent->setVs($vs);
}
$transactionDate = $this->extractTransactionDate($content);
if ($transactionDate !== null) {
$mailContent->setTransactionDate($transactionDate);
}
return $mailContent;
}
private function extractAccountNumber(string $content): ?string
{
$pattern = '/Account: ([A-Z0-9]+)/';
$res = preg_match($pattern, $content, $result);
return $res === 1 ? $result[1] : null;
}
private function extractVariableSymbol(string $content): ?string
{
$pattern = '/Reference: (\d+)/';
$res = preg_match($pattern, $content, $result);
return $res === 1 ? $result[1] : null;
}
private function extractTransactionDate(string $content): int|false|null
{
$pattern = '/Date: ([\d.]+)/';
$res = preg_match($pattern, $content, $result);
if ($res === 1) {
return strtotime($result[1]);
}
return null;
}
}Step 2: Implement ParserInterface
The ParserInterface defines one required method:
public function parse(string $content): ?MailContentReturn values:
MailContentobject if parsing succeedsnullif email format is not recognized or required fields are missing
Step 3: Multi-Transaction Support (Optional)
If the bank sends multiple transactions in one email, implement parseMulti():
public function parseMulti(string $content): array
{
// Split by transaction separator
$transactions = array_slice(explode("Transaction Date:", $content), 1);
$mailContents = [];
foreach ($transactions as $transaction) {
$mailContent = $this->parse("Transaction Date:" . $transaction);
if ($mailContent !== null) {
$mailContents[] = $mailContent;
}
}
return $mailContents;
}Step 4: Write Tests
Create tests/Parser/YourBank/YourBankMailParserTest.php:
<?php
declare(strict_types=1);
namespace Tomaj\BankMailsParser\Tests\Parser\YourBank;
use PHPUnit\Framework\TestCase;
use Tomaj\BankMailsParser\Parser\YourBank\YourBankMailParser;
class YourBankMailParserTest extends TestCase
{
public function testParsesBasicTransaction()
{
$email = 'Dear Customer,
Transaction amount: 150.00 EUR
Account: SK9999999999999999999999
Reference: 1234567890
Date: 15.01.2024
Best regards,
Your Bank';
$parser = new YourBankMailParser();
$mailContent = $parser->parse($email);
$this->assertNotNull($mailContent);
$this->assertEquals(150.00, $mailContent->getAmount());
$this->assertEquals('EUR', $mailContent->getCurrency());
$this->assertEquals('SK9999999999999999999999', $mailContent->getAccountNumber());
$this->assertEquals('1234567890', $mailContent->getVs());
}
public function testReturnsNullForInvalidFormat()
{
$email = 'This is not a valid bank email';
$parser = new YourBankMailParser();
$mailContent = $parser->parse($email);
$this->assertNull($mailContent);
}
}Step 5: Test Coverage
Ensure your tests cover:
- ✅ Successful parsing with all fields
- ✅ Successful parsing with minimal required fields
- ✅ Invalid email format (returns
null) - ✅ Edge cases (special characters, whitespace, etc.)
- ✅ Multi-transaction parsing (if supported)
- ✅ Date format variations
- ✅ Amount format variations
Run tests:
composer testStep 6: Static Analysis
Ensure PHPStan passes:
composer phpstanFix any type errors or add proper PHPStan annotations.
Step 7: Code Style
Check code style with PHP_CodeSniffer (phpcs):
composer phpcsFix automatically with phpcbf:
composer phpcbfStep 8: Documentation
Add documentation for your parser:
- Create
docs/guide/yourbank.mdwith usage examples - Add entry to sidebar in
docs/.vitepress/config.mts - Update main README.md if needed
Best Practices
Regex Patterns
- Use named capturing groups when helpful
- Make patterns as specific as possible
- Test with actual bank emails
- Handle whitespace variations
Type Safety
- Use strict types (
declare(strict_types=1)) - Use strict comparisons (
===instead of==) - Check
preg_match()return value with=== 1 - Handle
nullvalues properly
Error Handling
- Return
nullfor unrecognized formats - Don't throw exceptions for parsing failures
- Validate required fields before returning
MailContent
Testing
- Use real email samples (anonymized)
- Test edge cases thoroughly
- Aim for high coverage (≥95%)
- Include mutation testing consideration
Example: Complete Parser
See existing parsers for complete examples:
- Simple format: VubMailParser
- Multi-transaction: CsobMailParser
- Complex format: TatraBankaMailParser
Submitting Your Parser
- Fork the repository
- Create a feature branch
- Implement parser and tests
- Ensure all quality checks pass
- Submit a pull request
See Contributing Guide for details.
Next Steps
- Check API Reference for
MailContentgetters/setters - Review existing parsers for patterns and best practices
- Read Contributing Guide
- Upgrade to 5.x