Skip to content

Commit d288957

Browse files
committed
feat: Add simple example script for ABO file parsing
- Introduced `simple_example.php` to demonstrate basic usage of the ABO Parser library. - Implemented command-line interface for parsing ABO files and displaying results. - Added option to save parsed data as JSON. docs: Create WARP.md for project guidance - Added comprehensive documentation for the `spojenet/abo-parser` project. - Included project overview, architecture, development commands, and guidelines. - Provided examples for command-line tools and library usage. test: Add PHPUnit configuration and initial test cases - Created `phpunit.xml` for PHPUnit configuration. - Added fixture files for testing various ABO formats. - Implemented integration tests for parsing different ABO file formats. - Developed unit tests for the `AboParser` class covering various scenarios. test: Implement edge cases and error handling tests - Added tests for invalid date formats, long lines, special characters, and boundary amounts. - Included tests for whitespace handling, mixed line endings, and Unicode BOM. - Ensured robustness against invalid UTF-8 sequences and file permission issues. chore: Add fixtures for basic, extended, empty, malformed, and mixed records - Created multiple fixture files to cover different ABO record scenarios for testing. - Ensured comprehensive test coverage for both valid and invalid input cases.
1 parent ea06a04 commit d288957

File tree

15 files changed

+1813
-17
lines changed

15 files changed

+1813
-17
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ composer.phar
88
/.build/
99
/nbproject/
1010
/tests/*.abo*
11-
11+
/.phpunit.cache/

Example/abo2json.php

100644100755
Lines changed: 200 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,216 @@
1+
#!/usr/bin/env php
12
<?php
3+
24
declare(strict_types=1);
35

46
/**
5-
* This file is part of the CSas Statement Tools package
7+
* ABO to JSON Converter
68
*
7-
* https://github.com/VitexSoftware/file2sharepoint
9+
* https://github.com/Spoje-NET/php-abo-parser
810
*
9-
* (c) Vítězslav Dvořák <info@vitexsoftware.cz>
11+
* (c) Spoje.Net IT s.r.o. <https://spojenet.cz>
1012
*
1113
* For the full copyright and license information, please view the LICENSE
1214
* file that was distributed with this source code.
1315
*/
1416

15-
require_once '../vendor/autoload.php';
17+
require_once __DIR__ . '/../vendor/autoload.php';
18+
19+
use SpojeNet\AboParser\AboParser;
1620

1721
\define('APP_NAME', 'abo2json');
1822

19-
if ($argc === 1) {
20-
echo $argv[0]." <source/files/path/*.*> <Sharepoint/dest/folder/path/> [/path/to/config/.env] \n";
23+
/**
24+
* Display usage information
25+
*/
26+
function showUsage(): void
27+
{
28+
echo "Usage: " . basename($_SERVER['argv'][0]) . " [OPTIONS]\n";
29+
echo "\nOptions:\n";
30+
echo " --abofile <file> Path to ABO file to parse\n";
31+
echo " --jsonfile <file> Output JSON file (optional, defaults to stdout)\n";
32+
echo " --help, -h Show this help message\n";
33+
echo "\nExamples:\n";
34+
echo " " . basename($_SERVER['argv'][0]) . " --abofile data.abo\n";
35+
echo " " . basename($_SERVER['argv'][0]) . " --abofile data.abo --jsonfile output.json\n";
36+
echo " cat data.abo | " . basename($_SERVER['argv'][0]) . "\n";
37+
echo "\nIf no --abofile is provided, the script reads from stdin.\n";
38+
}
39+
40+
/**
41+
* Parse command line arguments
42+
*
43+
* @param array<string> $argv Command line arguments
44+
* @return array<string, mixed> Parsed arguments
45+
*/
46+
function parseArguments(array $argv): array
47+
{
48+
$options = [
49+
'abofile' => null,
50+
'jsonfile' => null,
51+
'help' => false
52+
];
53+
54+
$i = 1;
55+
while ($i < count($argv)) {
56+
switch ($argv[$i]) {
57+
case '--abofile':
58+
if (!isset($argv[$i + 1])) {
59+
fprintf(STDERR, "Error: --abofile requires a file path\n");
60+
exit(1);
61+
}
62+
$options['abofile'] = $argv[$i + 1];
63+
$i += 2;
64+
break;
65+
66+
case '--jsonfile':
67+
if (!isset($argv[$i + 1])) {
68+
fprintf(STDERR, "Error: --jsonfile requires a file path\n");
69+
exit(1);
70+
}
71+
$options['jsonfile'] = $argv[$i + 1];
72+
$i += 2;
73+
break;
74+
75+
case '--help':
76+
case '-h':
77+
$options['help'] = true;
78+
$i++;
79+
break;
80+
81+
default:
82+
fprintf(STDERR, "Error: Unknown option '%s'\n", $argv[$i]);
83+
showUsage();
84+
exit(1);
85+
}
86+
}
87+
88+
return $options;
89+
}
90+
91+
/**
92+
* Read input data from file or stdin
93+
*
94+
* @param string|null $filePath File path or null for stdin
95+
* @return string Input data
96+
*/
97+
function readInput(?string $filePath): string
98+
{
99+
if ($filePath !== null) {
100+
if (!file_exists($filePath)) {
101+
fprintf(STDERR, "Error: File '%s' does not exist\n", $filePath);
102+
exit(1);
103+
}
104+
105+
$content = file_get_contents($filePath);
106+
if ($content === false) {
107+
fprintf(STDERR, "Error: Unable to read file '%s'\n", $filePath);
108+
exit(1);
109+
}
110+
111+
return $content;
112+
}
113+
114+
// Read from stdin
115+
$content = '';
116+
while (!feof(STDIN)) {
117+
$chunk = fread(STDIN, 8192);
118+
if ($chunk === false) {
119+
fprintf(STDERR, "Error: Unable to read from stdin\n");
120+
exit(1);
121+
}
122+
$content .= $chunk;
123+
}
124+
125+
return $content;
126+
}
127+
128+
/**
129+
* Write output to file or stdout
130+
*
131+
* @param string $data Data to write
132+
* @param string|null $filePath Output file path or null for stdout
133+
*/
134+
function writeOutput(string $data, ?string $filePath): void
135+
{
136+
if ($filePath !== null) {
137+
$result = file_put_contents($filePath, $data);
138+
if ($result === false) {
139+
fprintf(STDERR, "Error: Unable to write to file '%s'\n", $filePath);
140+
exit(1);
141+
}
142+
fprintf(STDERR, "Output written to: %s\n", $filePath);
143+
} else {
144+
echo $data;
145+
}
146+
}
147+
148+
/**
149+
* Clean data for JSON encoding by ensuring all strings are valid UTF-8
150+
*
151+
* @param mixed $data Data to clean
152+
* @return mixed Cleaned data
153+
*/
154+
function cleanDataForJson($data)
155+
{
156+
if (is_array($data)) {
157+
return array_map('cleanDataForJson', $data);
158+
} elseif (is_string($data)) {
159+
// Remove or replace invalid UTF-8 sequences
160+
$cleaned = mb_convert_encoding($data, 'UTF-8', 'UTF-8');
161+
// Remove non-printable characters except common whitespace
162+
return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $cleaned);
163+
}
164+
return $data;
165+
}
166+
167+
// Main execution
168+
try {
169+
$options = parseArguments($_SERVER['argv']);
170+
171+
if ($options['help']) {
172+
showUsage();
173+
exit(0);
174+
}
175+
176+
// Read input
177+
$input = readInput($options['abofile']);
178+
179+
if (empty(trim($input))) {
180+
fprintf(STDERR, "Error: No input data provided\n");
181+
exit(1);
182+
}
183+
184+
// Convert to UTF-8 if needed (force conversion from common encodings)
185+
// ABO files are often in Windows-1250 or ISO-8859-2 encoding
186+
$input = mb_convert_encoding($input, 'UTF-8', 'auto');
187+
188+
// Parse ABO data
189+
$parser = new AboParser();
190+
$parsedData = $parser->parse($input);
191+
192+
// Clean data for JSON encoding
193+
$cleanedData = cleanDataForJson($parsedData);
194+
195+
// Convert to JSON with pretty printing
196+
$jsonData = json_encode($cleanedData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
197+
198+
if ($jsonData === false) {
199+
$jsonError = json_last_error_msg();
200+
fprintf(STDERR, "Error: Unable to encode data to JSON: %s\n", $jsonError);
201+
// Debug: show some sample data
202+
fprintf(STDERR, "Debug: Sample parsed data structure:\n");
203+
fprintf(STDERR, "Statements count: %d\n", count($parsedData['statements']));
204+
fprintf(STDERR, "Transactions count: %d\n", count($parsedData['transactions']));
205+
fprintf(STDERR, "Raw records count: %d\n", count($parsedData['raw_records']));
206+
exit(1);
207+
}
208+
209+
// Write output
210+
writeOutput($jsonData . "\n", $options['jsonfile']);
211+
212+
} catch (\Exception $e) {
213+
fprintf(STDERR, "Error: %s\n", $e->getMessage());
214+
exit(1);
21215
}
22216

Example/simple_example.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
declare(strict_types=1);
5+
6+
/**
7+
* Simple ABO Parser Example
8+
*
9+
* This example demonstrates basic usage of the ABO Parser library
10+
*
11+
* https://github.com/Spoje-NET/php-abo-parser
12+
*
13+
* (c) Spoje.Net IT s.r.o. <https://spojenet.cz>
14+
*/
15+
16+
require_once __DIR__ . '/../vendor/autoload.php';
17+
18+
use SpojeNet\AboParser\AboParser;
19+
20+
// Check if file path is provided
21+
if ($argc < 2) {
22+
echo "Usage: php simple_example.php <abo-file>\n";
23+
echo "Example: php simple_example.php ../tests/27_CZ8408000000000002122722_06029396af87517e_CZK_2025-08-21.abo-standard\n";
24+
exit(1);
25+
}
26+
27+
$filePath = $argv[1];
28+
29+
try {
30+
// Create parser instance
31+
$parser = new AboParser();
32+
33+
// Parse the ABO file
34+
echo "Parsing ABO file: {$filePath}\n";
35+
$data = $parser->parseFile($filePath);
36+
37+
// Display summary
38+
echo "\n=== PARSING RESULTS ===\n";
39+
echo "Format Version: " . ($data['format_version'] ?? 'unknown') . "\n";
40+
echo "Account Statements: " . count($data['statements']) . "\n";
41+
echo "Transactions: " . count($data['transactions']) . "\n";
42+
echo "Raw Records: " . count($data['raw_records']) . "\n";
43+
44+
// Display account statements
45+
if (!empty($data['statements'])) {
46+
echo "\n=== ACCOUNT STATEMENTS ===\n";
47+
foreach ($data['statements'] as $i => $statement) {
48+
echo "Statement #" . ($i + 1) . ":\n";
49+
echo " Account Number: " . $statement['account_number'] . "\n";
50+
echo " Account Name: " . $statement['account_name'] . "\n";
51+
echo " Statement Number: " . $statement['statement_number'] . "\n";
52+
echo " Accounting Date: " . $statement['accounting_date'] . "\n";
53+
echo " Old Balance: " . $statement['old_balance'] . " " . $statement['old_balance_sign'] . "\n";
54+
echo " New Balance: " . $statement['new_balance'] . " " . $statement['new_balance_sign'] . "\n";
55+
echo " Debit Turnover: " . $statement['debit_turnover'] . " " . $statement['debit_turnover_sign'] . "\n";
56+
echo " Credit Turnover: " . $statement['credit_turnover'] . " " . $statement['credit_turnover_sign'] . "\n";
57+
echo "\n";
58+
}
59+
}
60+
61+
// Display transactions
62+
if (!empty($data['transactions'])) {
63+
echo "=== TRANSACTIONS ===\n";
64+
foreach ($data['transactions'] as $i => $transaction) {
65+
echo "Transaction #" . ($i + 1) . ":\n";
66+
echo " Amount: " . $transaction['amount'] . "\n";
67+
echo " Counter Account: " . $transaction['counter_account'] . "\n";
68+
echo " Document Number: " . $transaction['document_number'] . "\n";
69+
echo " Variable Symbol: " . $transaction['variable_symbol'] . "\n";
70+
echo " Constant Symbol: " . $transaction['constant_symbol'] . "\n";
71+
echo " Specific Symbol: " . $transaction['specific_symbol'] . "\n";
72+
echo " Valuation Date: " . $transaction['valuation_date'] . "\n";
73+
echo " Accounting Code: " . $transaction['accounting_code'] . "\n";
74+
echo " Data Type: " . $transaction['data_type'] . "\n";
75+
76+
if (!empty($transaction['additional_info'])) {
77+
echo " Additional Info: " . $transaction['additional_info'] . "\n";
78+
}
79+
echo "\n";
80+
}
81+
}
82+
83+
// Optionally save to JSON file
84+
if ($argc > 2) {
85+
$jsonFile = $argv[2];
86+
$jsonData = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
87+
file_put_contents($jsonFile, $jsonData);
88+
echo "JSON data saved to: {$jsonFile}\n";
89+
}
90+
91+
echo "\nParsing completed successfully!\n";
92+
93+
} catch (Exception $e) {
94+
echo "Error: " . $e->getMessage() . "\n";
95+
exit(1);
96+
}

0 commit comments

Comments
 (0)