Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# 6.5
# 6.5 (unreleased)

## Upgrade note

**6.5 rejects files that 6.4 accepted.** The record order of CFONB 120 and 240 files and the dates are now checked, in
strict and non-strict mode alike: a file whose records are out of order, or that holds a zero-filled or out-of-range
mandatory date, now throws a `ParseException` where 6.4 returned wrong data silently. `$strict = false` does not restore
the 6.4 behaviour. Before upgrading, parse a sample of the files you receive with 6.5 and catch `ParseException`: the
[troubleshooting guide](https://cfonb-parser.silarhi.dev/guides/troubleshooting/) lists the new messages.

## Changed

Expand All @@ -20,6 +28,36 @@ Files that parsed with 6.4 can now throw a `ParseException`: every case below us

- Support `\r` (classic Mac OS) line endings

# 6.4

## Changed

- 6.4.2: CFONB 120 and 240 parsing is about 20% faster, with 12–17% less peak memory, and gives the same results

6.4.0 and 6.4.1 hold no change to the library itself: same code as 6.3, development tooling only.

# 6.3

## Changed

- [BC] Every class is now `final`: the `Banking` objects (`Statement`, `Balance`, `Operation`, `OperationDetail`,
`Transfer`, `Header`, `Transaction`, `Total`), `CfonbCodes` and the readers, which are also `readonly`
(`final readonly class Cfonb120Reader`, `Cfonb240Reader` and `CfonbReader`). Wrap them instead of extending them

# 6.2

## Fixed

- 6.2.1: CFONB 120: `Operation::getBankOperationReference()` accepts any character, not only letters, digits and
spaces: a reference holding e.g. `-` or `/` no longer makes the `04` line fail to parse

# 6.1

## Added

- CFONB 120: `Operation::getBankOperationReference()`, the bank operation reference (positions 105 to 120 of the `04`
line) that identifies an operation, `null` when blank

# 6.0

Drop support for php 8.1
Expand Down
9 changes: 6 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ yarn install # Git hooks: lint-staged formats the staged files on commit

## Quality checks

The CI runs them all, on every supported PHP version. Please make sure your changes pass them before submitting a pull
request:
Please make sure your changes pass them before submitting a pull request. The CI runs PHPUnit on every supported PHP
version, with the lowest and the latest dependencies, and the other checks once (PHPStan, Psalm, PHP CS Fixer,
`composer validate` and `composer normalize`, the documentation examples). Rector is not run by the CI: run it locally.

```bash
# Run tests
Expand Down Expand Up @@ -47,7 +48,9 @@ php docs/scripts/render-examples.php --check
All at once:

```bash
vendor/bin/phpunit && vendor/bin/phpstan analyse && vendor/bin/psalm && vendor/bin/php-cs-fixer fix --dry-run --diff
vendor/bin/phpunit && vendor/bin/phpstan analyse && vendor/bin/psalm && vendor/bin/php-cs-fixer fix --dry-run --diff \
&& vendor/bin/rector process --dry-run && composer validate --strict && composer normalize --dry-run \
&& php docs/scripts/render-examples.php --check
```

## Documentation
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@
- **CFONB 120 statements** — `Statement` objects with opening/closing balances (`01`/`07`), operations (`04`) and their details (`05`).
- **CFONB 240 transfers** — `Transfer` objects with a header (`31`), transactions (`34`) and a total (`39`).
- **Signed amounts** — CFONB 120 decimals and sign characters are decoded: credits are positive, debits are negative.
- **Typed value objects** — getters return floats, `DateTimeImmutable` dates and nullable strings, not raw arrays.
- **Tolerant input** — handles `\n`, `\r\n` and `\r` line endings, and files with no line breaks at all.
- **Strict by default** — malformed lines and records out of order throw a `ParseException` instead of producing silent garbage.
- **Typed value objects** — getters return floats, dates (`DateTimeImmutable` for CFONB 120, typed `DateTimeInterface` for CFONB 240) and nullable strings, not raw arrays.
- **Tolerant input** — handles `\n`, `\r\n` and `\r` (since 6.5) line endings, and files with no line breaks at all.
- **Strict parsing** — malformed lines and, since 6.5, records out of order and zero-filled dates throw a `ParseException` instead of producing silent garbage, whatever the mode: non-strict mode only accepts blank CFONB 120 operation references.

## Requirements

Expand Down
5 changes: 2 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
{
"name": "silarhi/cfonb-parser",
"description": "A PHP Parser for CFONB transactions with zero dependencies",
"description": "A PHP parser for CFONB 120 and 240 bank files (statements and transfers), with zero dependencies",
"license": "MIT",
"type": "library",
"keywords": [
"cfonb",
"parser",
"etebac",
"swift"
"etebac"
],
"authors": [
{
Expand Down
12 changes: 11 additions & 1 deletion docs/src/content/docs/guides/cfonb-120.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,17 @@ The reader goes through the lines in order:
- a detail line (`05`) is added to the last operation read.
- a new balance line (`07`) closes the open statement, which is then added to the list: the next operation line needs a
new `01` line.
- a blank line is ignored.
- an empty line is ignored; a line of spaces or tabs throws a `ParseException`.

Any other order throws a `ParseException`, in strict and non-strict mode alike: a `07` line, an operation or a detail
line with no open statement, a `01` line while a statement is still open, a statement still open at the end of the file
(a truncated file).

:::note[Since 6.5]
6.4 did not check this order: it paired the balance lines by their position in the file, so records out of order
produced wrong statements silently instead of throwing.
:::

<Example name="cfonb120-sequence" />

So every statement returned has both balances: `hasOldBalance()` and `hasNewBalance()` are always `true` on them, and
Expand Down Expand Up @@ -140,6 +145,11 @@ Every CFONB 120 date is mandatory, the value date of an operation included. Some
as zeros: `000000` fails with `Unable to parse date "000000": the date is zero-filled (empty), but this field is
mandatory`, in strict and non-strict mode alike.

:::note[Since 6.5]
6.4 read a zero-filled date as 30 November 1999, and a date that does not exist as a date of the next month, instead of
throwing.
:::

## Strict mode (default) vs non-strict mode

In strict mode, any line that cannot be parsed throws a `ParseException`. Non-strict mode only relaxes one rule: a blank
Expand Down
9 changes: 7 additions & 2 deletions docs/src/content/docs/guides/cfonb-240.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ All the fields, with their positions in the line: [CFONB 240 objects](/reference
- a transaction line (`34`) is added to the current transfer.
- a total line (`39`) is set on the current transfer, which is then added to the list and closed: the next transaction
or total line needs a new header.
- a blank line is ignored.
- an empty line is ignored; a line of spaces or tabs throws a `ParseException`.

So every transfer returned has a header and a total: `getHeader()` and `getTotal()` never throw on them. Any other order
throws a `ParseException`: a transaction or a total line outside a transfer (before the first header, or between a
Expand All @@ -95,6 +95,11 @@ Unable to start a transfer with the "31" header record of sequence number 1: the
Unable to close the transfer of the "31" header record of sequence number 1: missing "39" total record at the end of the file
```

:::note[Since 6.5]
6.4 did not check this order: such records were attached to the wrong transfer, or dropped, instead of throwing. The
date checks below are new in 6.5 too.
:::

## Amounts

Transaction and total amounts are 12 digits in cents, read as floats with 2 decimals: `000000171200` is `1712.00`.
Expand All @@ -103,7 +108,7 @@ sum of the transactions.

## Dates and strict mode

Dates are `DateTimeImmutable` objects at midnight, read from `DDMMYY`, like the
Dates are typed `DateTimeInterface` and are `DateTimeImmutable` objects at midnight, read from `DDMMYY`, like the
[CFONB 120 dates](/guides/cfonb-120/#dates): a date that does not exist, such as `320420`, throws a `ParseException`.
`getInitialTransactionSettlementDate()` is `null` when blank or zero-filled (`000000`); the other dates are mandatory, and
`000000` there throws a `ParseException`.
Expand Down
15 changes: 12 additions & 3 deletions docs/src/content/docs/guides/troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,17 @@ import Example from '../../../components/Example.astro'

The readers throw a `ParseException` on the first line they cannot parse. The message tells what failed:

:::caution[Upgrading to 6.5]
6.5, not released yet, rejects files that 6.4 accepted: records out of order and zero-filled or out-of-range dates
throw since 6.5, in strict and non-strict mode alike, where 6.4 returned wrong data silently. The messages about the
order of the records (`missing "01" old balance`, `has no "07" new balance`, `missing "31" header`, `has no "39" total`)
and about dates are new in 6.5. Parse a sample of the files you receive before upgrading: see the
[changelog](https://github.com/silarhi/cfonb-parser/blob/6.x/CHANGELOG.md).
:::

| Message | Cause |
| -------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `Unable to find a parser for the line : "…"` | The line is not 120 (CFONB 120) or 240 (CFONB 240) characters long, or its record code is unknown: CFONB 120 supports `01`, `04`, `05` and `07`, CFONB 240 `31`, `34` and `39` |
| `Unable to find a parser for the line :`<br />`"…"` | The line is not 120 (CFONB 120) or 240 (CFONB 240) characters long, or its record code is unknown: CFONB 120 supports `01`, `04`, `05` and `07`, CFONB 240 `31`, `34` and `39` |
| `Regex does not match the line "…"` | A field holds unexpected characters: letters in a numeric field, a date or an amount, an invalid sign character |
| `The value of key "…" is not a string` | A required field is blank, e.g. the currency of a balance, or the reference of an operation in strict mode |
| `The value of key "…" is not numeric` | A numeric field is blank, e.g. the number of decimals of an amount |
Expand Down Expand Up @@ -72,8 +80,9 @@ To reject a file before parsing it, e.g. to tell an uploader it is not a CFONB 1

## Line endings and trailing spaces

`\n`, `\r\n` and `\r` (classic Mac OS) line endings are read alike, as are files with no line breaks at all, split
every 120 (or 240) characters. Blank lines are ignored.
`\n`, `\r\n` and `\r` (classic Mac OS, since 6.5) line endings are read alike, as are files with no line breaks at
all, split every 120 (or 240) characters. Empty lines are ignored, but not a line of spaces or tabs: it throws
`Unable to find a parser for the line`.

<Example name="troubleshooting-line-endings" />

Expand Down
7 changes: 4 additions & 3 deletions docs/src/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ composer require silarhi/cfonb-parser
negative.
</Card>
<Card title="Typed value objects" icon="setting">
Getters return floats, `DateTimeImmutable` dates and nullable strings, not raw arrays.
Getters return floats, dates (`DateTimeImmutable`, typed `DateTimeInterface` for CFONB 240) and nullable
strings, not raw arrays.
</Card>
<Card title="Tolerant input, strict parsing" icon="warning">
`\n`, `\r\n` and `\r` line endings and files without line breaks are read alike; a malformed line or a record
out of order throws a `ParseException` instead of producing silent garbage.
`\n`, `\r\n` and `\r` (since 6.5) line endings and files without line breaks are read alike; a malformed line,
or since 6.5 a record out of order, throws a `ParseException` instead of producing silent garbage.
</Card>
</CardGrid>
5 changes: 3 additions & 2 deletions docs/src/content/docs/reference/readers.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ foreach ($reader->parseCfonb240('My Content') as $transfer) {
## Input

- `$content` is the contents of the file, not its path: read it with `file_get_contents()` or from an upload.
- Lines are separated by `\n`, `\r\n` or `\r`; a file without line breaks is split every `LINE_LENGTH` characters. Blank
lines, and line breaks at the start and the end, are ignored.
- Lines are separated by `\n`, `\r\n` or `\r` (`\r` since 6.5); a file without line breaks is split every `LINE_LENGTH`
characters. Empty lines, and line breaks at the start and the end, are ignored; a line of spaces or tabs is not, and
throws a `ParseException`.
- The characters are kept as they are: no encoding conversion, only the whitespace around each field is trimmed.
- An empty string gives an empty list.

Expand Down
5 changes: 5 additions & 0 deletions src/Banking/Element.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

namespace Silarhi\Cfonb\Banking;

/**
* Base type of the parsed records.
*
* @internal
*/
abstract class Element
{
}
5 changes: 5 additions & 0 deletions src/Banking/Noop.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@

namespace Silarhi\Cfonb\Banking;

/**
* Result of an empty line, skipped by the readers.
*
* @internal
*/
final class Noop extends Element
{
}
1 change: 1 addition & 0 deletions src/Contracts/ParserInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use Silarhi\Cfonb\Banking\Element;

/** @internal */
interface ParserInterface
{
/**
Expand Down
Loading