Writing QIF files
Build the transactions
Section titled “Build the transactions”new Transaction() takes the account type, a Types case. Every setter returns the
transaction, so they chain:
use Silarhi\Qif\Enums\Types;use Silarhi\Qif\Transaction;
$transaction = (new Transaction(Types::Cash)) ->setDate(new DateTimeImmutable('2019-12-31')) ->setDescription('INV666') ->setAmount(18.99) ->setCategory('Sales') ->addSplit('Sales', 18) ->addSplit('Tax', 0.99, 'VAT') ->markAsReconciled();| Method | Sets |
|---|---|
setDate(DateTimeImmutable $date) |
The date (D) |
setAmount(float|int|string $float) |
The amount (T): see String amounts |
setDescription(?string $description) |
The payee (P) |
setCategory(?string $category) |
The category (L), e.g. Housing:Rent |
addSplit(string $category, float|int|string $amount, ?string $memo = null) |
Adds a split: category (S), amount ($) and memo (E) |
removeSplit(string $category) |
Removes every split of the category |
setStatus(Status $status) |
The cleared status (C) |
markAsReconciled() |
setStatus(Status::Reconciled) |
markAsCleared() |
setStatus(Status::Cleared) |
markAsNotCleared() |
setStatus(Status::NotCleared), the default |
setMemo(?string $memo) |
The memo (M) |
setAddress(?string $address) |
The address (A), its lines separated by "\n" |
Splits are a list: several splits can share a category, and addSplit() adds one at the end each time.
String amounts
Section titled “String amounts”setAmount() and addSplit() take a float, an int or a string. A string is read like the amounts of a QIF file,
its decimal separator detected: '1,234.50' and '1.234,50' are both 1234.5, '12,50' is 12.5. A string that is
not an amount ('12 EUR') or is ambiguous ('1,234': 1234 or 1.234) throws an InvalidArgumentException. See
Amounts.
Write them
Section titled “Write them”Writer::addTransaction() collects the transactions. Casting the writer to a string (echo $writer,
(string) $writer) returns the QIF content, and saveToFile() writes it to disk, replacing the file if it exists:
<?php
use Silarhi\Qif\Enums\Types;use Silarhi\Qif\Transaction;use Silarhi\Qif\Writer;
$writer = new Writer();
$writer->addTransaction( (new Transaction(Types::Bank)) ->setDate(new DateTimeImmutable('2026-01-15')) ->setDescription('Example Hardware Store') ->setAddress("1 Market Square\nSpringfield") ->setMemo('Drill and bits') ->setAmount(-118.99) ->setCategory('Home') ->addSplit('Home:Tools', -100) ->addSplit('Tax', -18.99, 'VAT') ->markAsCleared());
$writer->addTransaction( (new Transaction(Types::Bank)) ->setDate(new DateTimeImmutable('2026-01-31')) ->setDescription('Acme Corp') ->setAmount('2,400.00') ->setCategory('Income:Salary'));
echo $writer;!Type:BankD15/01/2026T-118.99LHomeSHome:Tools$-100STax$-18.99EVATCcPExample Hardware StoreA1 Market SquareASpringfieldMDrill and bits^!Type:BankD31/01/2026T2400LIncome:SalaryCPAcme Corp^$writer->saveToFile('/path/to/export.qif'); // or write it to diskgetTransactions() returns the transactions added so far. saveToFile() throws a RuntimeException when the
directory is not writable or the file cannot be opened: see Error handling.
Date format
Section titled “Date format”The writer formats dates as d/m/Y by default, the way the parser reads them by default.
Pass DateFormat::MonthFirst to the constructor to write m/d/Y, for the US versions of Quicken: a file read with
DateFormat::MonthFirst is then written back with its dates in the same order.
<?php
use Silarhi\Qif\Enums\DateFormat;use Silarhi\Qif\Parser;use Silarhi\Qif\Writer;
// A file from a US version of Quicken$content = "!Type:Bank\nD01/31/2026\nT-42.50\nPDemo Grocery\n^\n";
$writer = new Writer(DateFormat::MonthFirst);foreach ((new Parser($content, dateFormat: DateFormat::MonthFirst))->parse() as $transaction) { $writer->addTransaction($transaction);}
echo $writer;!Type:BankD01/31/2026T-42.5CPDemo Grocery^Make sure the application importing the files expects the order you write.
Output format
Section titled “Output format”The writer outputs the type, date (d/m/Y, or m/d/Y: see Date format), amount, category, splits,
cleared status, payee, address and memo of each transaction, in this order, then ^:
- every transaction starts with its own
!Type:line, empty when the type isnull; nullfields are left out, empty strings are written (a barePline);- each split is written as its
Sline, its$line, then itsEline when it has a memo; - the
Cline is always written, empty forStatus::NotCleared,cforClearedandXforReconciled; - the address is written as one
Aline per line ("\n","\r\n"or"\r"); - amounts use PHP’s float to string conversion:
2400,-84.3, no thousands separator; - lines end with
PHP_EOL, and there is no line break after the last^.
Each transaction is a Stringable too: (string) $transaction returns its own record, dates day first, and
$transaction->toQif(DateFormat::MonthFirst) returns it with the dates month first.
Round trip
Section titled “Round trip”The writer outputs every field the parser reads, so a parsed transaction can be written back as is and parses back to
the same values. Pass the writer the DateFormat the file was parsed with to keep its date order. Only the formatting
changes: the amounts lose their thousands separators, the * cleared status becomes c, each record gets its own
!Type: line, the lines follow the writer’s order, and the !Account list of export.qif is not written. An empty
string you set yourself (setDescription('')) is written as a bare P line, which the parser reads back as null:
!AccountNCheckingTBank^!Type:BankD03/01/2026T-1,250.00CXPExample Property RentalsA12 Sample StreetASpringfieldMJanuary rentLHousing:Rent^D05/01/2026T2,400.00C*PAcme CorpMSalaryLIncome:Salary^D07/01/2026T-84.30PDemo GroceryLFoodSFood:Groceries$-64.30EWeekly groceriesSHousehold$-20.00^<?php
use Silarhi\Qif\Parser;use Silarhi\Qif\Writer;
$parser = Parser::parseFile('export.qif');
$writer = new Writer();foreach ($parser->getTransactions() as $transaction) { $writer->addTransaction($transaction);}
echo $writer;!Type:BankD03/01/2026T-1250LHousing:RentCXPExample Property RentalsA12 Sample StreetASpringfieldMJanuary rent^!Type:BankD05/01/2026T2400LIncome:SalaryCcPAcme CorpMSalary^!Type:BankD07/01/2026T-84.3LFoodSFood:Groceries$-64.3EWeekly groceriesSHousehold$-20CPDemo Grocery^