From 73417031b182e2a29e2a47ae30bedb0b59433063 Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Sat, 10 Oct 2020 22:26:49 -0600 Subject: [PATCH 01/12] Fix createAmountFromStr() for integers --- lib/OfxParser/Utils.php | 23 ++++++++++---------- tests/OfxParser/UtilsTest.php | 40 +++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/lib/OfxParser/Utils.php b/lib/OfxParser/Utils.php index 435192b..d45273c 100644 --- a/lib/OfxParser/Utils.php +++ b/lib/OfxParser/Utils.php @@ -88,24 +88,25 @@ public static function createDateTimeFromStr($dateString, $ignoreErrors = false) */ public static function createAmountFromStr($amountString) { + // This assumes that all supported currency will have no more than + // 2 decimal places! The tell is the thousands separator, followed + // by three digits. If no thousands separator present, the only + // differentiator is number of decimal places. + // Decimal mark style (UK/US): 000.00 or 0,000.00 - if (preg_match('/^(-|\+)?([\d,]+)(\.?)([\d]{2})$/', $amountString) === 1) { - return (float)preg_replace( - ['/([,]+)/', '/\.?([\d]{2})$/'], - ['', '.$1'], - $amountString - ); + if (preg_match('/(\d,\d{3}|\.\d{1,2}$)/', $amountString) === 1) { + return (float) str_replace(',', '', $amountString); } // European style: 000,00 or 0.000,00 - if (preg_match('/^(-|\+)?([\d\.]+,?[\d]{2})$/', $amountString) === 1) { - return (float)preg_replace( - ['/([\.]+)/', '/,?([\d]{2})$/'], - ['', '.$1'], + if (preg_match('/(\d\.\d{3}|,\d{1,2}$)/', $amountString) === 1) { + return (float) str_replace( + array('.', ','), + array('', '.'), $amountString ); } - return (float)$amountString; + return (float) $amountString; } } diff --git a/tests/OfxParser/UtilsTest.php b/tests/OfxParser/UtilsTest.php index 3057d88..3a068fe 100644 --- a/tests/OfxParser/UtilsTest.php +++ b/tests/OfxParser/UtilsTest.php @@ -18,23 +18,37 @@ class UtilsTest extends TestCase public function amountConversionProvider() { return [ - '1000.00' => ['1000.00', 1000.0], - '1000,00' => ['1000,00', 1000.0], - '1,000.00' => ['1,000.00', 1000.0], - '1.000,00' => ['1.000,00', 1000.0], - '-1000.00' => ['-1000.00', -1000.0], - '-1000,00' => ['-1000,00', -1000.0], - '-1,000.00' => ['-1,000.00', -1000.0], - '-1.000,00' => ['-1.000,00', -1000.0], '1' => ['1', 1.0], '10' => ['10', 10.0], - '100' => ['100', 1.0], // @todo this is weird behaviour, should not really expect this + '100' => ['100', 100.0], + '1000.01' => ['1000.01', 1000.01], + '1000,01' => ['1000,01', 1000.01], + '1,000.01' => ['1,000.01', 1000.01], + '1.000,01' => ['1.000,01', 1000.01], + '-1' => ['-1', -1.0], + '-10' => ['-10', -10.0], + '-100' => ['-100', -100.0], + '-1000.01' => ['-1000.01', -1000.01], + '-1000,01' => ['-1000,01', -1000.01], + '-1,000.01' => ['-1,000.01', -1000.01], + '-1.000,01' => ['-1.000,01', -1000.01], '+1' => ['+1', 1.0], '+10' => ['+10', 10.0], - '+1000.00' => ['+1000.00', 1000.0], - '+1000,00' => ['+1000,00', 1000.0], - '+1,000.00' => ['+1,000.00', 1000.0], - '+1.000,00' => ['+1.000,00', 1000.0], + '+100' => ['+100', 100.0], + '+1000.01' => ['+1000.01', 1000.01], + '+1000,01' => ['+1000,01', 1000.01], + '+1,000.01' => ['+1,000.01', 1000.01], + '+1.000,01' => ['+1.000,01', 1000.01], + + // Try some bigger numbers, too. + '2,225,000' => ['2,225,000', 2225000.00], + '2,225,000.01' => ['2,225,000.01', 2225000.01], + '2.225.000' => ['2.225.000', 2225000.00], + '2.225.000,01' => ['2.225.000,01', 2225000.01], + + // And some tiny numbers. + '0.02' => ['0.02', 0.02], + '-,03' => ['-,03', -0.03], ]; } From e32defb3de85026f896f33ac07503c1069029cc3 Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Sat, 27 Mar 2021 09:16:50 -0600 Subject: [PATCH 02/12] Fixes error loading investment statements without transactions --- lib/OfxParser/Ofx/Investment.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/OfxParser/Ofx/Investment.php b/lib/OfxParser/Ofx/Investment.php index 237359a..21118a4 100644 --- a/lib/OfxParser/Ofx/Investment.php +++ b/lib/OfxParser/Ofx/Investment.php @@ -68,17 +68,19 @@ protected function buildAccount($transactionUid, SimpleXMLElement $statementResp $account->statement = new Statement(); $account->statement->currency = (string) $statementResponse->CURDEF; - $account->statement->startDate = Utils::createDateTimeFromStr( - $statementResponse->INVTRANLIST->DTSTART - ); - - $account->statement->endDate = Utils::createDateTimeFromStr( - $statementResponse->INVTRANLIST->DTEND - ); - - $account->statement->transactions = $this->buildTransactions( - $statementResponse->INVTRANLIST->children() - ); + if (@count($statementResponse->INVTRANLIST)) { + $account->statement->startDate = Utils::createDateTimeFromStr( + $statementResponse->INVTRANLIST->DTSTART + ); + + $account->statement->endDate = Utils::createDateTimeFromStr( + $statementResponse->INVTRANLIST->DTEND + ); + + $account->statement->transactions = $this->buildTransactions( + $statementResponse->INVTRANLIST->children() + ); + } return $account; } From 79e6ac90456f3bf5eb27cfbd7ea91850233ecc68 Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Sat, 27 Mar 2021 12:15:29 -0600 Subject: [PATCH 03/12] Use empty array rather than NULL for investment statements without activity --- lib/OfxParser/Ofx/Investment.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/OfxParser/Ofx/Investment.php b/lib/OfxParser/Ofx/Investment.php index 21118a4..c1c05ff 100644 --- a/lib/OfxParser/Ofx/Investment.php +++ b/lib/OfxParser/Ofx/Investment.php @@ -80,6 +80,9 @@ protected function buildAccount($transactionUid, SimpleXMLElement $statementResp $account->statement->transactions = $this->buildTransactions( $statementResponse->INVTRANLIST->children() ); + } else { + // Shouldn't this just be the default value in the Entity? + $account->statement->transactions = []; } return $account; From 3830f0cf75bbc265ff12c81b9967d6875dbaa3d8 Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Sun, 11 Apr 2021 16:59:46 -0600 Subject: [PATCH 04/12] Add support for SELLOTHER, SELLSTOCK investment nodes --- lib/OfxParser/Entities/Investment.php | 15 +++++++++++++++ .../Investment/Transaction/SellSecurity.php | 12 +++++++++++- lib/OfxParser/Ofx/Investment.php | 8 ++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/OfxParser/Entities/Investment.php b/lib/OfxParser/Entities/Investment.php index e6e3329..5a6b88e 100644 --- a/lib/OfxParser/Entities/Investment.php +++ b/lib/OfxParser/Entities/Investment.php @@ -32,5 +32,20 @@ public function loadOfx(SimpleXMLElement $node) { throw new \Exception('loadOfx method not defined in class "' . get_class() . '"'); } + + /** + * Populates instance properties from a node for the map provided. + * @param array $map array(property_name => node_name, ...) + * @param SimpleXMLElement $node + * @return $this + */ + public function loadMap($map, $node) + { + foreach ($map as $propName => $nodeName) { + if (@count($node->{$nodeName}) > 0) { + $this->{$propName} = (string) $node->{$nodeName}; + } + } + } } diff --git a/lib/OfxParser/Entities/Investment/Transaction/SellSecurity.php b/lib/OfxParser/Entities/Investment/Transaction/SellSecurity.php index 82e3f9b..82655df 100644 --- a/lib/OfxParser/Entities/Investment/Transaction/SellSecurity.php +++ b/lib/OfxParser/Entities/Investment/Transaction/SellSecurity.php @@ -27,6 +27,7 @@ * * * Optional: + * * ...many... * * Partial implementation. @@ -45,6 +46,12 @@ class SellSecurity extends Investment */ public $nodeName = 'SELLOTHER'; + /** + * How much gain is on the sale? + * @var number + */ + public $gain = null; + /** * Imports the OFX data for this node. * @param SimpleXMLElement $node @@ -55,7 +62,10 @@ public function loadOfx(SimpleXMLElement $node) // Transaction data is nested within child node $this->loadInvTran($node->INVSELL->INVTRAN) ->loadSecId($node->INVSELL->SECID) - ->loadPricing($node->INVSELL); + ->loadPricing($node->INVSELL) + ->loadMap(array( + 'gain' => 'GAIN', + ), $node->INVSELL); return $this; } diff --git a/lib/OfxParser/Ofx/Investment.php b/lib/OfxParser/Ofx/Investment.php index c1c05ff..0220792 100644 --- a/lib/OfxParser/Ofx/Investment.php +++ b/lib/OfxParser/Ofx/Investment.php @@ -14,6 +14,8 @@ use OfxParser\Entities\Investment\Transaction\Income; use OfxParser\Entities\Investment\Transaction\Reinvest; use OfxParser\Entities\Investment\Transaction\SellMutualFund; +use OfxParser\Entities\Investment\Transaction\SellSecurity; +use OfxParser\Entities\Investment\Transaction\SellStock; class Investment extends Ofx { @@ -125,6 +127,12 @@ protected function buildTransactions(SimpleXMLElement $transactions) case 'SELLMF': $item = new SellMutualFund(); break; + case 'SELLOTHER': + $item = new SellSecurity(); + break; + case 'SELLSTOCK': + $item = new SellStock(); + break; case 'DTSTART': // already processed break; From 359cd43cffc04eff4791bfc4eb9ff9d6cc04093a Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Thu, 23 Dec 2021 14:56:07 -0700 Subject: [PATCH 05/12] Update package owner, supports PHPUnit 9.5 --- README.md | 9 +++++---- composer.json | 17 ++++++++++------- phpunit.xml.dist | 21 ++++++++++----------- tests/OfxParser/Entities/InvestmentTest.php | 3 ++- tests/OfxParser/OfxTest.php | 2 +- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 880f43a..4549f9c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OFX Parser ================= -[![Build Status](https://travis-ci.org/asgrim/ofxparser.svg?branch=master)](https://travis-ci.org/asgrim/ofxparser) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/asgrim/ofxparser/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/asgrim/ofxparser/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/asgrim/ofxparser/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/asgrim/ofxparser/?branch=master) [![Latest Stable Version](https://poser.pugx.org/asgrim/ofxparser/v/stable)](https://packagist.org/packages/asgrim/ofxparser) [![License](https://poser.pugx.org/asgrim/ofxparser/license)](https://packagist.org/packages/asgrim/ofxparser) +[![Build Status](https://travis-ci.org/gitmathias/ofxparser.svg?branch=main)](https://travis-ci.org/gitmathias/ofxparser) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/gitmathias/ofxparser/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/gitmathias/ofxparser/?branch=main) [![Code Coverage](https://scrutinizer-ci.com/g/gitmathias/ofxparser/badges/coverage.png?b=main)](https://scrutinizer-ci.com/g/gitmathias/ofxparser/?branch=main) [![Latest Stable Version](https://poser.pugx.org/gitmathias/ofxparser/v/stable)](https://packagist.org/packages/gitmathias/ofxparser) [![License](https://poser.pugx.org/gitmathias/ofxparser/license)](https://packagist.org/packages/gitmathias/ofxparser) OFX Parser is a PHP library designed to parse an OFX file downloaded from a financial institution into simple PHP objects. @@ -12,7 +12,7 @@ It supports multiple Bank Accounts, the required "Sign On" response, and recogni Simply require the package using [Composer](https://getcomposer.org/): ```bash -$ composer require asgrim/ofxparser +$ composer require gitmathias/ofxparser ``` ## Usage @@ -79,5 +79,6 @@ foreach ($ofx->bankAccounts as $accountData) { ## Fork & Credits -This is a fork of [grimfor/ofxparser](https://github.com/Grimfor/ofxparser) made to be framework independent. The source repo was designed for Symfony 2 framework, so credit should be given where credit due! -Heavily refactored by [Oliver Lowe](https://github.com/loweoj) and loosely based on the ruby [ofx-parser by Andrew A. Smith](https://github.com/aasmith/ofx-parser). +Fork of archived project [asgrim/ofxparser](https://github.com/asgrim/ofxparser). + +Archive was originally forked from [grimfor/ofxparser](https://github.com/Grimfor/ofxparser) and made to be framework independent. Heavily refactored by [Oliver Lowe](https://github.com/loweoj) and loosely based on the ruby [ofx-parser by Andrew A. Smith](https://github.com/aasmith/ofx-parser). diff --git a/composer.json b/composer.json index 0104198..c075db4 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "asgrim/ofxparser", + "name": "gitmathias/ofxparser", "description": "Simple OFX file parser", "keywords": ["ofx", "open financial exchange", "finance", "parser"], "license": "MIT", @@ -17,18 +17,21 @@ { "name": "Oliver Lowe", "email": "mrtriangle@gmail.com" + }, + { + "name": "Mathias Gran", + "email": "gitmathias@users.noreply.github.com" } ], "require": { - "php": "~5.6|~7.0" + "php": ">=7.3" }, "require-dev": { - "phpunit/phpunit": "~5.5", - "squizlabs/php_codesniffer": "~2.6" + "phpunit/phpunit": "^9.5" }, - "autoload": { - "psr-0": { - "OfxParser": "lib/" + "autoload": { + "psr-4": { + "OfxParser\\": "lib/OfxParser/" } } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b93618e..0f264cd 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,5 +1,6 @@ - - ./tests - - - - - ./lib - - + xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"> + + + ./lib + + + + ./tests + - diff --git a/tests/OfxParser/Entities/InvestmentTest.php b/tests/OfxParser/Entities/InvestmentTest.php index 37a3462..a95be17 100644 --- a/tests/OfxParser/Entities/InvestmentTest.php +++ b/tests/OfxParser/Entities/InvestmentTest.php @@ -59,10 +59,11 @@ public function loadOfx(SimpleXMLElement $node) class InvestmentTest extends TestCase { /** - * @expectedException \Exception + * This should throw an exception for missing an ofx node. */ public function testLoadOfxException() { + $this->expectException(\Exception::class); $xml = new SimpleXMLElement(''); $entity = new InvestmentNoLoadOfx(); $entity->loadOfx($xml); diff --git a/tests/OfxParser/OfxTest.php b/tests/OfxParser/OfxTest.php index a73256b..5d90d31 100644 --- a/tests/OfxParser/OfxTest.php +++ b/tests/OfxParser/OfxTest.php @@ -15,7 +15,7 @@ class OfxTest extends TestCase */ protected $ofxData; - public function setUp() + protected function setUp(): void { $ofxFile = dirname(__DIR__).'/fixtures/ofxdata-xml.ofx'; From d5880dc2432b6efb7c0d9649e2e737721c31c0cd Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Thu, 23 Dec 2021 23:40:47 -0700 Subject: [PATCH 06/12] Adds github workflow for unit tests --- .github/workflows/run-composer-scripts.yml | 36 ++++++++++++++++++++++ README.md | 2 +- composer.json | 3 ++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/run-composer-scripts.yml diff --git a/.github/workflows/run-composer-scripts.yml b/.github/workflows/run-composer-scripts.yml new file mode 100644 index 0000000..83b5efa --- /dev/null +++ b/.github/workflows/run-composer-scripts.yml @@ -0,0 +1,36 @@ +name: Run Composer Scripts + +on: + push: + branches: [ $default-branch ] + pull_request: + branches: [ $default-branch ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" + # Docs: https://getcomposer.org/doc/articles/scripts.md + + - name: Run test suite + run: composer run-script test diff --git a/README.md b/README.md index 4549f9c..6f8c30a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OFX Parser ================= -[![Build Status](https://travis-ci.org/gitmathias/ofxparser.svg?branch=main)](https://travis-ci.org/gitmathias/ofxparser) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/gitmathias/ofxparser/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/gitmathias/ofxparser/?branch=main) [![Code Coverage](https://scrutinizer-ci.com/g/gitmathias/ofxparser/badges/coverage.png?b=main)](https://scrutinizer-ci.com/g/gitmathias/ofxparser/?branch=main) [![Latest Stable Version](https://poser.pugx.org/gitmathias/ofxparser/v/stable)](https://packagist.org/packages/gitmathias/ofxparser) [![License](https://poser.pugx.org/gitmathias/ofxparser/license)](https://packagist.org/packages/gitmathias/ofxparser) +![Build Status](https://github.com/gitmathias/ofxparser/actions/workflows/run-composer-scripts.yml/badge.svg) OFX Parser is a PHP library designed to parse an OFX file downloaded from a financial institution into simple PHP objects. diff --git a/composer.json b/composer.json index c075db4..3480d3e 100644 --- a/composer.json +++ b/composer.json @@ -33,5 +33,8 @@ "psr-4": { "OfxParser\\": "lib/OfxParser/" } + }, + "scripts": { + "test": "vendor/bin/phpunit" } } From b5a5ff796454b2fccc9c61047b8a8317471f01e4 Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Thu, 23 Dec 2021 23:44:07 -0700 Subject: [PATCH 07/12] Add workflow badge to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f8c30a..2b61a53 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OFX Parser ================= -![Build Status](https://github.com/gitmathias/ofxparser/actions/workflows/run-composer-scripts.yml/badge.svg) +[![Build Status](https://github.com/gitmathias/ofxparser/actions/workflows/run-composer-scripts.yml/badge.svg)](https://github.com/gitmathias/ofxparser/actions/workflows/run-composer-scripts.yml) OFX Parser is a PHP library designed to parse an OFX file downloaded from a financial institution into simple PHP objects. From 92daec27abc9028896d7dd80c10655919aa8784c Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Fri, 24 Dec 2021 00:05:37 -0700 Subject: [PATCH 08/12] Trying to trigger github build --- .../{run-composer-scripts.yml => unit-tests.yml} | 10 +++++----- .gitignore | 3 ++- .scrutinizer.yml | 10 ---------- .travis.yml | 14 -------------- README.md | 2 +- 5 files changed, 8 insertions(+), 31 deletions(-) rename .github/workflows/{run-composer-scripts.yml => unit-tests.yml} (87%) delete mode 100644 .scrutinizer.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/run-composer-scripts.yml b/.github/workflows/unit-tests.yml similarity index 87% rename from .github/workflows/run-composer-scripts.yml rename to .github/workflows/unit-tests.yml index 83b5efa..5015095 100644 --- a/.github/workflows/run-composer-scripts.yml +++ b/.github/workflows/unit-tests.yml @@ -1,10 +1,10 @@ name: Run Composer Scripts -on: - push: - branches: [ $default-branch ] - pull_request: - branches: [ $default-branch ] +on: [push] + # push: + # branches: [ $default-branch ] + # pull_request: + # branches: [ $default-branch ] jobs: build: diff --git a/.gitignore b/.gitignore index 2e3beb6..c455bfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea .DS_Store vendor -composer.lock \ No newline at end of file +composer.lock +.phpunit.*cache diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index 200fd30..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,10 +0,0 @@ -inherit: true - -before_commands: - - "composer install --no-dev --prefer-source" - -tools: - external_code_coverage: - enabled: true - timeout: 600 - runs: 1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index feb83ef..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: php - -php: - - 5.6 - - 7.0 - -before_script: - - composer update --prefer-dist $DEPENDENCIES - -script: - - vendor/bin/phpunit --coverage-text --coverage-clover=clover.xml --colors - -after_script: - - if [ $TRAVIS_PHP_VERSION = '7.0' ]; then wget https://scrutinizer-ci.com/ocular.phar; php ocular.phar code-coverage:upload --format=php-clover clover.xml; fi diff --git a/README.md b/README.md index 2b61a53..735f7c2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OFX Parser ================= -[![Build Status](https://github.com/gitmathias/ofxparser/actions/workflows/run-composer-scripts.yml/badge.svg)](https://github.com/gitmathias/ofxparser/actions/workflows/run-composer-scripts.yml) +[![Unit Tests](https://github.com/gitmathias/ofxparser/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/gitmathias/ofxparser/actions/workflows/unit-tests.yml) OFX Parser is a PHP library designed to parse an OFX file downloaded from a financial institution into simple PHP objects. From f03dbc4a1e102ffb9d4a79499a587d8aa39af0cf Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Fri, 24 Dec 2021 00:07:24 -0700 Subject: [PATCH 09/12] Rename unit test workflow --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 5015095..de09990 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,4 +1,4 @@ -name: Run Composer Scripts +name: Unit Tests on: [push] # push: From 67a70ffce699d5a1f75aba07480b759e21b5db0e Mon Sep 17 00:00:00 2001 From: gitmathias Date: Fri, 24 Dec 2021 00:40:27 -0700 Subject: [PATCH 10/12] Update package description --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3480d3e..49c3ea6 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "gitmathias/ofxparser", - "description": "Simple OFX file parser", + "description": "OFX file parser with Investments support", "keywords": ["ofx", "open financial exchange", "finance", "parser"], "license": "MIT", "authors": [ From 550b2054334f44b36c9067269ee0149f9512c57d Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Fri, 31 Dec 2021 20:48:05 -0700 Subject: [PATCH 11/12] Adds fees, gains, taxes, commission, load to INVBUY and INVSELL nodes --- lib/OfxParser/Entities/Investment.php | 23 ++-- .../Investment/Transaction/BuySecurity.php | 36 +----- .../Investment/Transaction/SellSecurity.php | 47 +------ .../Investment/Transaction/Traits/InvBuy.php | 84 ++++++++++++ .../Investment/Transaction/Traits/InvSell.php | 90 +++++++++++++ .../Investment/Transaction/Traits/InvTran.php | 28 +++- .../Investment/Transaction/Traits/Pricing.php | 19 ++- .../Investment/Transaction/Traits/SecId.php | 13 +- lib/OfxParser/Entities/LoaderTrait.php | 60 +++++++++ phpunit.xml.dist | 5 + tests/OfxParser/Entities/LoaderTraitTest.php | 120 ++++++++++++++++++ tests/OfxParser/Parsers/InvestmentTest.php | 8 +- 12 files changed, 433 insertions(+), 100 deletions(-) create mode 100644 lib/OfxParser/Entities/Investment/Transaction/Traits/InvBuy.php create mode 100644 lib/OfxParser/Entities/Investment/Transaction/Traits/InvSell.php create mode 100644 lib/OfxParser/Entities/LoaderTrait.php create mode 100644 tests/OfxParser/Entities/LoaderTraitTest.php diff --git a/lib/OfxParser/Entities/Investment.php b/lib/OfxParser/Entities/Investment.php index 5a6b88e..7c70725 100644 --- a/lib/OfxParser/Entities/Investment.php +++ b/lib/OfxParser/Entities/Investment.php @@ -4,8 +4,16 @@ use SimpleXMLElement; +use OfxParser\Entities\LoaderTrait; + abstract class Investment extends AbstractEntity implements Inspectable, OfxLoadable { + /** + * Make loadMap() available to all Invesment entities. + * @trait + */ + use LoaderTrait; + /** * Get a list of properties defined for this entity. * @@ -32,20 +40,5 @@ public function loadOfx(SimpleXMLElement $node) { throw new \Exception('loadOfx method not defined in class "' . get_class() . '"'); } - - /** - * Populates instance properties from a node for the map provided. - * @param array $map array(property_name => node_name, ...) - * @param SimpleXMLElement $node - * @return $this - */ - public function loadMap($map, $node) - { - foreach ($map as $propName => $nodeName) { - if (@count($node->{$nodeName}) > 0) { - $this->{$propName} = (string) $node->{$nodeName}; - } - } - } } diff --git a/lib/OfxParser/Entities/Investment/Transaction/BuySecurity.php b/lib/OfxParser/Entities/Investment/Transaction/BuySecurity.php index 33ebdb0..33cc4a6 100644 --- a/lib/OfxParser/Entities/Investment/Transaction/BuySecurity.php +++ b/lib/OfxParser/Entities/Investment/Transaction/BuySecurity.php @@ -3,42 +3,21 @@ namespace OfxParser\Entities\Investment\Transaction; use SimpleXMLElement; -use OfxParser\Entities\AbstractEntity; + use OfxParser\Entities\Investment; -use OfxParser\Entities\Investment\Transaction\Traits\InvTran; -use OfxParser\Entities\Investment\Transaction\Traits\SecId; -use OfxParser\Entities\Investment\Transaction\Traits\Pricing; +use OfxParser\Entities\Investment\Transaction\Traits\InvBuy; /** * OFX 203 doc: - * 13.9.2.4.3 Investment Buy/Sell Aggregates / - * - * Properties found in the aggregate. - * Used for "other securities" BUY activities and provides the - * base properties to extend for more specific activities. - * - * Required: - * aggregate - * aggregate - * - * - * - * - * - * - * Optional: - * ...many... - * - * Partial implementation. + * 13.9.2.4.4 Investment Transaction Aggregates + * is simply an */ class BuySecurity extends Investment { /** * Traits used to define properties */ - use InvTran; - use SecId; - use Pricing; + use InvBuy; /** * @var string @@ -53,10 +32,7 @@ class BuySecurity extends Investment public function loadOfx(SimpleXMLElement $node) { // Transaction data is nested within child node - $this->loadInvTran($node->INVBUY->INVTRAN) - ->loadSecId($node->INVBUY->SECID) - ->loadPricing($node->INVBUY); - + $this->loadInvBuy($node->INVBUY); return $this; } } diff --git a/lib/OfxParser/Entities/Investment/Transaction/SellSecurity.php b/lib/OfxParser/Entities/Investment/Transaction/SellSecurity.php index 82655df..72eb08b 100644 --- a/lib/OfxParser/Entities/Investment/Transaction/SellSecurity.php +++ b/lib/OfxParser/Entities/Investment/Transaction/SellSecurity.php @@ -3,55 +3,27 @@ namespace OfxParser\Entities\Investment\Transaction; use SimpleXMLElement; -use OfxParser\Entities\AbstractEntity; + use OfxParser\Entities\Investment; -use OfxParser\Entities\Investment\Transaction\Traits\InvTran; -use OfxParser\Entities\Investment\Transaction\Traits\SecId; -use OfxParser\Entities\Investment\Transaction\Traits\Pricing; +use OfxParser\Entities\Investment\Transaction\Traits\InvSell; /** * OFX 203 doc: - * 13.9.2.4.3 Investment Buy/Sell Aggregates / - * - * Properties found in the aggregate. - * Used for "other securities" SELL activities and provides the - * base properties to extend for more specific activities. - * - * Required: - * aggregate - * aggregate - * - * - * - * - * - * - * Optional: - * - * ...many... - * - * Partial implementation. + * 13.9.2.4.4 Investment Transaction Aggregates + * is simply an */ class SellSecurity extends Investment { /** * Traits used to define properties */ - use InvTran; - use SecId; - use Pricing; + use InvSell; /** * @var string */ public $nodeName = 'SELLOTHER'; - /** - * How much gain is on the sale? - * @var number - */ - public $gain = null; - /** * Imports the OFX data for this node. * @param SimpleXMLElement $node @@ -59,14 +31,7 @@ class SellSecurity extends Investment */ public function loadOfx(SimpleXMLElement $node) { - // Transaction data is nested within child node - $this->loadInvTran($node->INVSELL->INVTRAN) - ->loadSecId($node->INVSELL->SECID) - ->loadPricing($node->INVSELL) - ->loadMap(array( - 'gain' => 'GAIN', - ), $node->INVSELL); - + $this->loadInvSell($node->INVSELL); return $this; } } diff --git a/lib/OfxParser/Entities/Investment/Transaction/Traits/InvBuy.php b/lib/OfxParser/Entities/Investment/Transaction/Traits/InvBuy.php new file mode 100644 index 0000000..e08b6db --- /dev/null +++ b/lib/OfxParser/Entities/Investment/Transaction/Traits/InvBuy.php @@ -0,0 +1,84 @@ +/ + * + * Properties found in the aggregate. + * Used for "other securities" BUY activities and provides the + * base properties to extend for more specific activities. + * + * Required: + * aggregate + * aggregate + * + * + * + * + * + * + * Optional: + * + * ...many... + * + * Partial implementation. + */ +trait InvBuy +{ + /** + * Traits used to define properties + */ + use LoaderTrait; + use InvTran; + use SecId; + use Pricing; + + /** + * @var float + */ + public $commission; + + /** + * @var float + */ + public $taxes; + + /** + * @var float + */ + public $fees; + + /** + * @var float + */ + public $load; + + /** + * @param SimpleXMLElement $node + * @return $this for chaining + */ + protected function loadInvBuy(SimpleXMLElement $node) + { + $this->loadInvTran($node->INVTRAN) + ->loadSecId($node->SECID) + ->loadPricing($node) + // These are all optional fields: + ->loadMap([ + 'commission' => 'COMMISSION', + 'taxes' => 'TAXES', + 'fees' => 'FEES', + 'load' => 'LOAD', + ], $node); + + return $this; + } +} diff --git a/lib/OfxParser/Entities/Investment/Transaction/Traits/InvSell.php b/lib/OfxParser/Entities/Investment/Transaction/Traits/InvSell.php new file mode 100644 index 0000000..dfe0562 --- /dev/null +++ b/lib/OfxParser/Entities/Investment/Transaction/Traits/InvSell.php @@ -0,0 +1,90 @@ +/ + * + * Properties found in the aggregate. + * Used for "other securities" SELL activities and provides the + * base properties to extend for more specific activities. + * + * Required: + * aggregate + * aggregate + * + * + * + * + * + * + * Optional: + * + * ...many... + * + * Partial implementation. + */ +trait InvSell +{ + /** + * Traits used to define properties + */ + use LoaderTrait; + use InvTran; + use SecId; + use Pricing; + + /** + * @var float + */ + public $commission; + + /** + * @var float + */ + public $taxes; + + /** + * @var float + */ + public $fees; + + /** + * @var float + */ + public $load; + + /** + * @var float + */ + public $gain; + + /** + * @param SimpleXMLElement $node + * @return $this for chaining + */ + protected function loadInvSell(SimpleXMLElement $node) + { + $this->loadInvTran($node->INVTRAN) + ->loadSecId($node->SECID) + ->loadPricing($node) + // These are all optional fields: + ->loadMap([ + 'commission' => 'COMMISSION', + 'taxes' => 'TAXES', + 'fees' => 'FEES', + 'load' => 'LOAD', + 'gain' => 'GAIN', + ], $node); + + return $this; + } +} diff --git a/lib/OfxParser/Entities/Investment/Transaction/Traits/InvTran.php b/lib/OfxParser/Entities/Investment/Transaction/Traits/InvTran.php index c573170..10d0a90 100644 --- a/lib/OfxParser/Entities/Investment/Transaction/Traits/InvTran.php +++ b/lib/OfxParser/Entities/Investment/Transaction/Traits/InvTran.php @@ -3,6 +3,8 @@ namespace OfxParser\Entities\Investment\Transaction\Traits; use SimpleXMLElement; + +use OfxParser\Entities\LoaderTrait; use OfxParser\Utils; /** @@ -13,6 +15,8 @@ */ trait InvTran { + use LoaderTrait; + /** * This is the unique identifier in the broker's system, * NOT to be confused with the UNIQUEID node for the security. @@ -26,12 +30,25 @@ trait InvTran */ public $tradeDate; + /** + * The unique ID (FITID) of a prior transaction that + * is being reversed. + * @var string + */ + public $reversalUniqueId; + /** * Date the trade was settled * @var \DateTimeInterface */ public $settlementDate; + /** + * Server assigned transaction ID + * @var string + */ + public $serverTransactionId; + /** * Transaction memo, as provided from broker. * @var string @@ -49,12 +66,17 @@ protected function loadInvTran(SimpleXMLElement $node) // - all others optional $this->uniqueId = (string) $node->FITID; $this->tradeDate = Utils::createDateTimeFromStr($node->DTTRADE); + + // Optional, but loadMap doesn't support callbacks, atm. if (isset($node->DTSETTLE)) { $this->settlementDate = Utils::createDateTimeFromStr($node->DTSETTLE); } - if (isset($node->MEMO)) { - $this->memo = (string) $node->MEMO; - } + + $this->loadMap([ + 'serverTransactionId' => 'SRVRTID', + 'memo' => 'MEMO', + 'reversalUniqueId' => 'REVERSALFITID', + ], $node); return $this; } diff --git a/lib/OfxParser/Entities/Investment/Transaction/Traits/Pricing.php b/lib/OfxParser/Entities/Investment/Transaction/Traits/Pricing.php index d57cc96..9797ded 100644 --- a/lib/OfxParser/Entities/Investment/Transaction/Traits/Pricing.php +++ b/lib/OfxParser/Entities/Investment/Transaction/Traits/Pricing.php @@ -4,11 +4,18 @@ use SimpleXMLElement; +use OfxParser\Entities\LoaderTrait; + /** * Combo for units, price, and total */ trait Pricing { + /** + * Traits used to define properties + */ + use LoaderTrait; + /** * @var float */ @@ -44,11 +51,13 @@ trait Pricing */ protected function loadPricing(SimpleXMLElement $node) { - $this->units = (string) $node->UNITS; - $this->unitPrice = (string) $node->UNITPRICE; - $this->total = (string) $node->TOTAL; - $this->subAccountFund = (string) $node->SUBACCTFUND; - $this->subAccountSec = (string) $node->SUBACCTSEC; + $this->loadMap([ + 'units' => 'UNITS', + 'unitPrice' => 'UNITPRICE', + 'total' => 'TOTAL', + 'subAccountFund' => 'SUBACCTFUND', + 'subAccountSec' => 'SUBACCTSEC', + ], $node); return $this; } diff --git a/lib/OfxParser/Entities/Investment/Transaction/Traits/SecId.php b/lib/OfxParser/Entities/Investment/Transaction/Traits/SecId.php index 9bb3308..99780d8 100644 --- a/lib/OfxParser/Entities/Investment/Transaction/Traits/SecId.php +++ b/lib/OfxParser/Entities/Investment/Transaction/Traits/SecId.php @@ -4,12 +4,19 @@ use SimpleXMLElement; +use OfxParser\Entities\LoaderTrait; + /** * OFX 203 doc: * 13.8.1 Security Identification */ trait SecId { + /** + * Traits used to define properties + */ + use LoaderTrait; + /** * Identifier for the security being traded. * @var string @@ -30,8 +37,10 @@ protected function loadSecId(SimpleXMLElement $node) { // // - REQUIRED: , - $this->securityId = (string) $node->UNIQUEID; - $this->securityIdType = (string) $node->UNIQUEIDTYPE; + $this->loadMap([ + 'securityId' => 'UNIQUEID', + 'securityIdType' => 'UNIQUEIDTYPE', + ], $node); return $this; } diff --git a/lib/OfxParser/Entities/LoaderTrait.php b/lib/OfxParser/Entities/LoaderTrait.php new file mode 100644 index 0000000..289545a --- /dev/null +++ b/lib/OfxParser/Entities/LoaderTrait.php @@ -0,0 +1,60 @@ + node_detail, ...) + * + * If node_detail is a string, the value of that xml node will + * be assigned to the instance property in property_name. + * + * If node_detail is an array, the first value will be used + * as the xml node name and the second value will be used as + * the default value, in case the node does not exist. + * + * Example: $map = [ 'gain' => 'GAIN' ] + * Pull the value of the GAIN xml node and assign to $this->gain, + * if the GAIN xml node exists. + * + * Example: $map = [ 'fees' => ['FEES', 0]] + * Pull the value of the FEES xml node and assign to $this->fees, + * if the FEES xml node exists. If the node does not exist, assign + * zero to $this->fees. + * + * @param SimpleXMLElement $node + * @return $this + */ + public function loadMap($map, $node) + { + foreach ($map as $propName => $detail) { + $default = null; + $defaultProvided = false; + + if (is_array($detail)) { + $defaultProvided = true; + $detail = array_values($detail); + list($nodeName, $default) = $detail; + } else { + $nodeName = $detail; + } + + if (@count($node->{$nodeName}) > 0) { + $this->{$propName} = (string) $node->{$nodeName}; + } elseif ($defaultProvided) { + $this->{$propName} = $default; + } + } + + return $this; + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0f264cd..7c6c98a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,6 +16,11 @@ ./lib + ./tests diff --git a/tests/OfxParser/Entities/LoaderTraitTest.php b/tests/OfxParser/Entities/LoaderTraitTest.php new file mode 100644 index 0000000..fc1afe9 --- /dev/null +++ b/tests/OfxParser/Entities/LoaderTraitTest.php @@ -0,0 +1,120 @@ + $this->public1, + 'protected1' => $this->protected1, + 'private1' => $this->private1, + ]; + } +} + + +/** + * @covers OfxParser\Entities\LoaderTrait + */ +class LoaderTraitTest extends TestCase +{ + /** + * Let's try all the things + */ + public function testLoadMap() + { + $testXML = new SimpleXMLElement(' + + LoaderTrait + Trait + 1234 + + '); + + $tests = [ + 'I can set values for any visibility' => [ + 'input' => [ + 'map' => [ + 'public1' => 'name', + 'protected1' => 'type', + 'private1' => 'num-prop' + ], + ], + 'expected' => [ + 'properties' => [ + 'public1' => 'LoaderTrait', + 'protected1' => 'Trait', + 'private1' => '1234', + ], + ], + ], + 'I ignore a default value if the node exists' => [ + 'input' => [ + 'map' => [ + 'public1' => ['name', 'Traitzzz'] + ], + ], + 'expected' => [ + 'properties' => [ + 'public1' => 'LoaderTrait', + 'protected1' => null, + 'private1' => null, + ], + ], + ], + 'I accept the default value when the node does not exist' => [ + 'input' => [ + 'map' => [ + 'public1' => ['namezzz', 'Traitzzz'] + ], + ], + 'expected' => [ + 'properties' => [ + 'public1' => 'Traitzzz', + 'protected1' => null, + 'private1' => null, + ], + ], + ], + ]; + + foreach ($tests as $testName => $data) { + $input = $data['input']; + $expected = $data['expected']; + + $testObj = new LoaderTraitContainer(); + $actual = $testObj->loadMap($input['map'], $testXML)->getProps(); + + $this->assertEquals($actual, $expected['properties'], $testName . ' failed!'); + } + } +} diff --git a/tests/OfxParser/Parsers/InvestmentTest.php b/tests/OfxParser/Parsers/InvestmentTest.php index 54be402..04e53c7 100644 --- a/tests/OfxParser/Parsers/InvestmentTest.php +++ b/tests/OfxParser/Parsers/InvestmentTest.php @@ -60,8 +60,8 @@ public function testParseInvestmentsXML() 'settlementDate' => new \DateTime('2011-02-01'), 'securityId' => '822722622', 'securityIdType' => 'CUSIP', - 'units' => '', - 'unitPrice' => '', + 'units' => null, // Not part of INCOME definition + 'unitPrice' => null, // Not part of INCOME definition 'total' => '12.59', 'incomeType' => 'DIV', 'subAccountSec' => 'CASH', @@ -78,7 +78,7 @@ public function testParseInvestmentsXML() 'total' => '-6.97', 'incomeType' => 'DIV', 'subAccountSec' => 'CASH', - 'subAccountFund' => '', + 'subAccountFund' => null, // Not part of REINVEST definition 'actionCode' => 'REINVEST', ), '300100' => array( @@ -159,7 +159,7 @@ public function testParseInvestmentsXMLMultipleAccounts() 'total' => '-6.97', 'incomeType' => 'DIV', 'subAccountSec' => 'CASH', - 'subAccountFund' => '', + 'subAccountFund' => null, // Not a required node 'actionCode' => 'REINVEST', ), ), From 7d621bb60761eb46c5267a657df31278b71f17c4 Mon Sep 17 00:00:00 2001 From: Mathias Gran Date: Thu, 21 Mar 2024 10:28:37 -0600 Subject: [PATCH 12/12] Rework non-xml conversion to fix parse errors --- lib/OfxParser/Parser.php | 87 +++++++++++++--------------------- tests/OfxParser/ParserTest.php | 87 ++++++++++++++++++---------------- 2 files changed, 80 insertions(+), 94 deletions(-) diff --git a/lib/OfxParser/Parser.php b/lib/OfxParser/Parser.php index a5394fe..e0caf3b 100644 --- a/lib/OfxParser/Parser.php +++ b/lib/OfxParser/Parser.php @@ -60,7 +60,6 @@ public function loadFromString($ofxContent) if (stripos($ofxHeader, 'conditionallyAddNewlines($ofxSgml); $ofxXml = $this->convertSgmlToXml($ofxSgml); } @@ -72,21 +71,6 @@ public function loadFromString($ofxContent) return $ofx; } - /** - * Detect if the OFX file is on one line. If it is, add newlines automatically. - * - * @param string $ofxContent - * @return string - */ - private function conditionallyAddNewlines($ofxContent) - { - if (preg_match('/.*<\/OFX>/', $ofxContent) === 1) { - return str_replace('<', "\n<", $ofxContent); // add line breaks to allow XML to parse - } - - return $ofxContent; - } - /** * Load an XML string without PHP errors - throws exception instead * @@ -107,33 +91,6 @@ private function xmlLoadString($xmlString) return $xml; } - /** - * Detect any unclosed XML tags - if they exist, close them - * - * @param string $line - * @return string - */ - private function closeUnclosedXmlTags($line) - { - // Special case discovered where empty content tag wasn't closed - $line = trim($line); - if (preg_match('/$/', $line) === 1) { - return ''; - } - - // Matches: blah - // Does not match: - // Does not match: blah - if (preg_match( - "/<([A-Za-z0-9.]+)>([\wà-úÀ-Ú0-9\.\-\_\+\, ;:\[\]\'\&\/\\\*\(\)\+\{\|\}\!\£\$\?=@€£#%±§~`\"]+)$/", - $line, - $matches - )) { - return "<{$matches[1]}>{$matches[2]}"; - } - return $line; - } - /** * Parse the SGML Header to an Array * @@ -155,10 +112,13 @@ private function parseHeader($ofxHeader) // Only parse OFX headers and not XML headers. $ofxHeader = preg_replace('/<\?xml .*?\?>\n?/', '', $ofxHeader); $ofxHeader = preg_replace(['/"/', '/\?>/', '/<\?OFX/i'], '', $ofxHeader); + + // !', "\n<\\1>", $sgml); + + // Turn all special characters into ampersand? $sgml = preg_replace('/&(?!#?[a-z0-9]+;)/', '&', $sgml); $lines = explode("\n", $sgml); $tags = []; foreach ($lines as $i => &$line) { - $line = trim($this->closeUnclosedXmlTags($line)) . "\n"; + $line = trim($line) . "\n"; // Matches tags like or - if (!preg_match("/^<(\/?[A-Za-z0-9.]+)>$/", trim($line), $matches)) { + if (!preg_match("!^<(/?[A-Za-z0-9.]+)>(.*)$!", trim($line), $matches)) { continue; } - // If matches , looks back and replaces all tags like - // to until finds the opening tag - if ($matches[1][0] == '/') { + // If matches , looks back and closes all unmatched tags like + // VAL to VAL until finds the opening tag + if ($matches[1][0] == '/') { // If a closing tag... $tag = substr($matches[1], 1); while (($last = array_pop($tags)) && $last[1] != $tag) { - $lines[$last[0]] = "<{$last[1]}/>"; + $lines[$last[0]] = "<{$last[1]}>{$last[2]}"; } } else { - $tags[] = [$i, $matches[1]]; + $tags[] = [$i, $matches[1], $matches[2]]; + } + } + + // Clean up by closing any remaining tags + if ($tags) { + while ($last = array_pop($tags)) { + $lines[] = ""; } } - return implode("\n", array_map('trim', $lines)); + // Jam all our corrected lines into one happy line again! + // Then break out open tags for more readable/testable XML + $ret = implode('', array_map('trim', $lines)); + $ret = trim(preg_replace('!<([^/][A-Za-z0-9.]*)>!', "\n<\\1>", $ret)); + + // Finally, break out multiple close tags on the same line + while (preg_match('!(){2}!', $ret) === 1) { + $ret = preg_replace('!()()!', "\\1\n\\2", $ret); + } + + return $ret; } } diff --git a/tests/OfxParser/ParserTest.php b/tests/OfxParser/ParserTest.php index e207ed3..8545a6a 100644 --- a/tests/OfxParser/ParserTest.php +++ b/tests/OfxParser/ParserTest.php @@ -92,39 +92,10 @@ public function testXmlLoadStringLoadsValidXml() /** * @return array */ - public function closeUnclosedXmlTagsProvider() - { - return [ - ['', ''], - ['foo', 'foo'], - ['foo', 'foo'], - ['XXXXX', 'XXXXX'], - ['XXXXXXXXXXX', 'XXXXXXXXXXX'], - ['-198.98', '-198.98'], - ['-198.98', '-198.98'], - ['', ''], - ]; - } - - /** - * @dataProvider closeUnclosedXmlTagsProvider - * @param $expected - * @param $input - */ - public function testCloseUnclosedXmlTags($expected, $input) - { - $method = new \ReflectionMethod(Parser::class, 'closeUnclosedXmlTags'); - $method->setAccessible(true); - - $parser = new Parser(); - - self::assertEquals($expected, $method->invoke($parser, $input)); - } - public function convertSgmlToXmlProvider() { return [ - [<< [<< bar bat @@ -136,7 +107,21 @@ public function convertSgmlToXmlProvider() bat HERE - ], [<< [<< + bar & restaurant + bat + +HERE + , << +bar & restaurant +bat + +HERE + ], + 'everything matching from the start' => [<< XXXXX XXXXX @@ -152,17 +137,37 @@ public function convertSgmlToXmlProvider() CHECKING HERE - ],[<< - bar & restaurant - bat - + ], + 'empty memo tag outlier' => [<< HERE - , << -bar & restaurant -bat - + , << + + +HERE + ], + 'empty container' => [<< +HERE + , << +HERE + ], + 'container with value, missing closing tag' => [<<foo +HERE + , <<foo +HERE + ], + 'nested container with negative value, missing closing tag' => [<<-198.98 +HERE + , << +-198.98 + HERE ], ];