diff --git a/.github/workflows/dependency_review.yml b/.github/workflows/dependency_review.yml new file mode 100644 index 0000000..8966511 --- /dev/null +++ b/.github/workflows/dependency_review.yml @@ -0,0 +1,20 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v3 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v1 diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml new file mode 100644 index 0000000..c83c22b --- /dev/null +++ b/.github/workflows/php.yml @@ -0,0 +1,39 @@ +name: PHP Composer + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + 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/.github/workflows/run-psalm.yml b/.github/workflows/run-psalm.yml new file mode 100644 index 0000000..691ea63 --- /dev/null +++ b/.github/workflows/run-psalm.yml @@ -0,0 +1,33 @@ +name: Psalm + +on: + push: + paths: + - '**.php' + - 'psalm.xml' + +jobs: + psalm: + name: psalm + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + extensions: libxml, mbstring, zip, pdo, intl, iconv + coverage: none + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: vendor + key: composer-${{ hashFiles('composer.lock') }} + + - name: Run composer install + run: composer install -n --prefer-dist + + - name: Run psalm + run: ./vendor/bin/psalm --output-format=github \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3055e4f..8e10539 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /nbproject /nbproject/private/ /view/analytics.html -/doc/ \ No newline at end of file +/doc/ +/vendor/ +/.idea/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index ffc9283..3b1d499 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM programistyk/php-runtimes:7.2-nginx +FROM programistyk/php-runtimes:7.4-nginx ADD . /app ADD config.nginx /etc/nginx/sites-enabled/default ADD fuelio.ini /usr/local/etc/php/conf.d/fuelio.ini \ No newline at end of file diff --git a/README.md b/README.md index ae94c1a..7f84cd5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # FuelioImport -Data converter for importingo into great Fuelio app. Currently very limited (but working) GUI is available for PHP 5 built-int webserver. +Data converter for importingo into great Fuelio app. Currently very limited (but working) GUI is available for PHP built-int webserver. FuelioImport is developed from the need to move personal fillups history from Motostat and aCar, but it is designed to offer ease of extension for new formats. @@ -29,6 +29,6 @@ After importing into Fuelio, remember to verify or set your cars fuel type! Fuel Drivvo backups are locale-dependant, so in order to support all of them, we need to be given more examples. Currently spanish, english and polish backups should be working fine. Others might missinterpret "refuelling to full" flag. ## Requirements -FuelioImport converter is built for current PHP version (PHP7), but any stable PHP version should do the job. +FuelioImport converter is built for PHP version 7.4. * For aCar backup we need SimpleXMLElement and Zip support diff --git a/apigen.neon b/apigen.neon index 5e7e99c..41fa661 100644 --- a/apigen.neon +++ b/apigen.neon @@ -1,5 +1,5 @@ source: - - lib + - src destination: doc templateTheme: bootstrap \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..4a2f7b8 --- /dev/null +++ b/composer.json @@ -0,0 +1,46 @@ +{ + "name": "programistyk/fuelio", + "description": "Fuelio Imports Converter", + "type": "project", + "homepage": "https://fuelio.programistyk.pl", + "require": { + "php": "^7.4", + "ext-zip": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-json": "*" + }, + "require-dev": { + "vimeo/psalm": "^4.23", + "phpstan/phpstan": "^1.7" + }, + "license": "MIT", + "autoload": { + "psr-4": { + "FuelioImporter\\": "src/" + } + }, + "authors": [ + { + "name": "Kamil Kamiński", + "email": "kamil@programistyk.pl", + "homepage": "https://programistyk.pl", + "role": "Developer" + }, + { + "name": "Adrian Kajda", + "role": "Developer" + } + ], + "support": { + "issues": "https://github.com/Programistyk/FuelioImport/issues", + "source": "https://github.com/Programistyk/FuelioImport" + }, + "minimum-stability": "stable", + "config": { + "platform": { + "php": "7.4" + }, + "sort-packages": true + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..3e0f584 --- /dev/null +++ b/composer.lock @@ -0,0 +1,2209 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "7d04c98e98e2c08003361cf9936ff185", + "packages": [], + "packages-dev": [ + { + "name": "amphp/amp", + "version": "v2.6.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-20T17:52:18+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, + { + "name": "composer/package-versions-deprecated", + "version": "1.11.99.5", + "source": { + "type": "git", + "url": "https://github.com/composer/package-versions-deprecated.git", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", + "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.1.0 || ^2.0", + "php": "^7 || ^8" + }, + "replace": { + "ocramius/package-versions": "1.11.99" + }, + "require-dev": { + "composer/composer": "^1.9.3 || ^2.0@dev", + "ext-zip": "^1.13", + "phpunit/phpunit": "^6.5 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "PackageVersions\\Installer", + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "PackageVersions\\": "src/PackageVersions" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", + "support": { + "issues": "https://github.com/composer/package-versions-deprecated/issues", + "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-01-17T14:14:24+00:00" + }, + { + "name": "composer/pcre", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/e300eb6c535192decd27a85bc72a9290f0d6b3bd", + "reference": "e300eb6c535192decd27a85bc72a9290f0d6b3bd", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T20:21:48+00:00" + }, + { + "name": "composer/semver", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-04-01T19:23:25+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, + { + "name": "dnoegel/php-xdg-base-dir", + "version": "v0.1.1", + "source": { + "type": "git", + "url": "https://github.com/dnoegel/php-xdg-base-dir.git", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "XdgBaseDir\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "implementation of xdg base directory specification for php", + "support": { + "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", + "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" + }, + "time": "2019-12-04T15:06:13+00:00" + }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "felixfbecker/language-server-protocol", + "version": "v1.5.2", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-language-server-protocol.git", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", + "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "*", + "squizlabs/php_codesniffer": "^3.1", + "vimeo/psalm": "^4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "LanguageServerProtocol\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "PHP classes for the Language Server Protocol", + "keywords": [ + "language", + "microsoft", + "php", + "server" + ], + "support": { + "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", + "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + }, + "time": "2022-03-02T22:36:06+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0" + }, + "time": "2020-12-01T19:48:11+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.14.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", + "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0" + }, + "time": "2022-05-31T20:59:12+00:00" + }, + { + "name": "openlss/lib-array2xml", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/nullivex/lib-array2xml.git", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "LSS": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Bryan Tong", + "email": "bryan@nullivex.com", + "homepage": "https://www.nullivex.com" + }, + { + "name": "Tony Butler", + "email": "spudz76@gmail.com", + "homepage": "https://www.nullivex.com" + } + ], + "description": "Array2XML conversion library credit to lalit.org", + "homepage": "https://www.nullivex.com", + "keywords": [ + "array", + "array conversion", + "xml", + "xml conversion" + ], + "support": { + "issues": "https://github.com/nullivex/lib-array2xml/issues", + "source": "https://github.com/nullivex/lib-array2xml/tree/master" + }, + "time": "2019-03-29T20:06:56+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.6.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "77a32518733312af16a44300404e945338981de3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/77a32518733312af16a44300404e945338981de3", + "reference": "77a32518733312af16a44300404e945338981de3", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.1" + }, + "time": "2022-03-15T21:29:03+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.7.12", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "32f10779d9cd88a9cbd972ec611a4148a3cbbc7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/32f10779d9cd88a9cbd972ec611a4148a3cbbc7e", + "reference": "32f10779d9cd88a9cbd972ec611a4148a3cbbc7e", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "support": { + "issues": "https://github.com/phpstan/phpstan/issues", + "source": "https://github.com/phpstan/phpstan/tree/1.7.12" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpstan", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2022-06-09T12:39:36+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "829d5d1bf60b2efeb0887b7436873becc71a45eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/829d5d1bf60b2efeb0887b7436873becc71a45eb", + "reference": "829d5d1bf60b2efeb0887b7436873becc71a45eb", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-18T06:17:34+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:53:40+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "433d05519ce6990bf3530fba6957499d327395c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", + "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", + "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-10T07:21:04+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c", + "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-03-13T20:07:29+00:00" + }, + { + "name": "symfony/string", + "version": "v5.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "985e6a9703ef5ce32ba617c9c7d97873bb7b2a99" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/985e6a9703ef5ce32ba617c9c7d97873bb7b2a99", + "reference": "985e6a9703ef5ce32ba617c9c7d97873bb7b2a99", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v5.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-19T10:40:37+00:00" + }, + { + "name": "vimeo/psalm", + "version": "4.23.0", + "source": { + "type": "git", + "url": "https://github.com/vimeo/psalm.git", + "reference": "f1fe6ff483bf325c803df9f510d09a03fd796f88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/f1fe6ff483bf325c803df9f510d09a03fd796f88", + "reference": "f1fe6ff483bf325c803df9f510d09a03fd796f88", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.4.2", + "amphp/byte-stream": "^1.5", + "composer/package-versions-deprecated": "^1.8.0", + "composer/semver": "^1.4 || ^2.0 || ^3.0", + "composer/xdebug-handler": "^1.1 || ^2.0 || ^3.0", + "dnoegel/php-xdg-base-dir": "^0.1.1", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.0.3", + "felixfbecker/language-server-protocol": "^1.5", + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "nikic/php-parser": "^4.13", + "openlss/lib-array2xml": "^1.0", + "php": "^7.1|^8", + "sebastian/diff": "^3.0 || ^4.0", + "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0", + "symfony/polyfill-php80": "^1.25", + "webmozart/path-util": "^2.3" + }, + "provide": { + "psalm/psalm": "self.version" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2", + "brianium/paratest": "^4.0||^6.0", + "ext-curl": "*", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpdocumentor/reflection-docblock": "^5", + "phpmyadmin/sql-parser": "5.1.0||dev-master", + "phpspec/prophecy": ">=1.9.0", + "phpunit/phpunit": "^9.0", + "psalm/plugin-phpunit": "^0.16", + "slevomat/coding-standard": "^7.0", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.3 || ^5.0 || ^6.0", + "weirdan/prophecy-shim": "^1.0 || ^2.0" + }, + "suggest": { + "ext-curl": "In order to send data to shepherd", + "ext-igbinary": "^2.0.5 is required, used to serialize caching data" + }, + "bin": [ + "psalm", + "psalm-language-server", + "psalm-plugin", + "psalm-refactor", + "psalter" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev", + "dev-3.x": "3.x-dev", + "dev-2.x": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php", + "src/spl_object_id.php" + ], + "psr-4": { + "Psalm\\": "src/Psalm/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthew Brown" + } + ], + "description": "A static analysis tool for finding errors in PHP applications", + "keywords": [ + "code", + "inspection", + "php" + ], + "support": { + "issues": "https://github.com/vimeo/psalm/issues", + "source": "https://github.com/vimeo/psalm/tree/4.23.0" + }, + "time": "2022-04-28T17:35:49+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "webmozart/path-util", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/path-util.git", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "webmozart/assert": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\PathUtil\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "support": { + "issues": "https://github.com/webmozart/path-util/issues", + "source": "https://github.com/webmozart/path-util/tree/2.3.0" + }, + "abandoned": "symfony/filesystem", + "time": "2015-12-17T08:42:14+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "^7.4", + "ext-zip": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-json": "*" + }, + "platform-dev": [], + "platform-overrides": { + "php": "7.4" + }, + "plugin-api-version": "2.3.0" +} diff --git a/lib/autoloader.php b/lib/autoloader.php deleted file mode 100644 index fab4abe..0000000 --- a/lib/autoloader.php +++ /dev/null @@ -1,16 +0,0 @@ -classes_loaded) - $this->initialize(); - - return new \ArrayIterator($this->providers); - } - - /** - * Returns converter by its name - * @param string $name Converter name - * @return \FuelioImporter\IConverter - * @throws \FuelioImporter\ProviderNotExistsException - */ - public function get($name) - { - if (!$this->classes_loaded) - $this->initialize(); - if (isset($this->providers[$name])) - return $this->providers[$name]; - throw new \FuelioImporter\ProviderNotExistsException(); - } - - /** - * Initializes classes and propagates $providers array - * - * This method reads all files in namespace FuelioImporter\Providers - * classes implementing IConverter interface - */ - public function initialize() { - $di = new DirectoryIterator(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'providers'); - foreach ($di as $spl_file) { - if ($spl_file->isFile() && !$spl_file->isDot() && $spl_file->isReadable() && stripos($spl_file->getBasename(), 'provider.class.php') !== false) { - - /* We should have a new class, verify and check if it's implementing IConverter - * Autoloader here will do the job searching for valid file - * it should not misbehave as we are using FuelioImporter namespace - */ - - $classname = 'FuelioImporter\\Providers\\' . ucfirst($spl_file->getBasename('.class.php')); - - if (!class_exists($classname, true)) - continue; - - if (in_array('FuelioImporter\\IConverter', class_implements($classname, true))) - { - $instance = new $classname(); - if (isset($this->providers[$instance->getName()])) - throw new \FuelioImporter\ProviderExistsException(); - - $this->providers[$instance->getName()] = $instance; - } - } - } - $this->classes_loaded = true; - } - -} diff --git a/lib/fuelioimporter/cost.class.php b/lib/fuelioimporter/cost.class.php deleted file mode 100644 index 488ef52..0000000 --- a/lib/fuelioimporter/cost.class.php +++ /dev/null @@ -1,137 +0,0 @@ -title = $sTitle; - } - - public function setDate($sDate) - { - $dt = new \DateTime($sDate); - $this->date = $dt->format(FuelioBackupBuilder::DATE_FORMAT); - } - - public function setOdo($iOdo) - { - $this->odo = intval($iOdo); - } - - public function setCostCategoryId($iId) - { - $this->cost_category_id = $iId; - } - - public function setNotes($sNotes) - { - $this->notes = $sNotes; - } - - public function setCost($dCost) - { - $this->cost = $dCost; - $this->setIsIncome($dCost<0); - } - - public function setFlag($flag) - { - $this->flag = $flag; - } - - public function setIdR($iId) - { - $this->idR = $iId; - } - - public function setRead($bRead) - { - $this->read = intval(boolval($bRead)); - } - - public function setReminderOdo($iOdo) - { - $this->remindOdo = $iOdo; - } - - public function setReminderDate($sDate) - { - $dt = new \DateTime($sDate); - $this->remindDate = $dt->format(FuelioBackupBuilder::DATE_FORMAT); - } - - public function setRepeatOdo($iOdo) - { - $this->repeat_odo = (int)$iOdo; - } - - public function setRepeatMonths($sMonths) - { - $this->repeat_months = (int)$sMonths; - } - - public function setIsIncome($bIsIncome) - { - $this->is_income = (int)(bool)$bIsIncome; - } - - public function setUniqueId($iUniqueId) - { - $this->unique_id = (int)$iUniqueId; - } - - public function getCostDate() - { - return $this->date; - } - - public function getData() { - $vars = get_object_vars($this); - return array_values($vars); - } -} \ No newline at end of file diff --git a/lib/fuelioimporter/costcategory.class.php b/lib/fuelioimporter/costcategory.class.php deleted file mode 100644 index e393fb4..0000000 --- a/lib/fuelioimporter/costcategory.class.php +++ /dev/null @@ -1,48 +0,0 @@ -type_id = $type_id; - $this->name = $name; - $this->priority = $priority; - $this->color = $color; - } - - public function getTypeId() - { - return $this->type_id; - } - - public function setTypeId($iId) - { - $this->type_id = $iId; - } - - public function getName() - { - return $this->name; - } - - public function getData() - { - $vars = get_object_vars($this); - return array_values($vars); - } -} \ No newline at end of file diff --git a/lib/fuelioimporter/form/field/mdlnumericfield.class.php b/lib/fuelioimporter/form/field/mdlnumericfield.class.php deleted file mode 100644 index aa7f0a0..0000000 --- a/lib/fuelioimporter/form/field/mdlnumericfield.class.php +++ /dev/null @@ -1,25 +0,0 @@ - array('class' => 'mdl-textfield__input')), $options)); - } - - public function render() - { - return sprintf('
%sInput is not a number!
', - parent::render(), - $this->options['label'] - ); - } -} \ No newline at end of file diff --git a/lib/fuelioimporter/form/iform.class.php b/lib/fuelioimporter/form/iform.class.php deleted file mode 100644 index d975325..0000000 --- a/lib/fuelioimporter/form/iform.class.php +++ /dev/null @@ -1,48 +0,0 @@ -data = $dt->format(FuelioBackupBuilder::DATE_FORMAT); - } - - public function setOdo($iOdo) { - $this->odo = (int)$iOdo; - } - - public function setFuel($dFuel) { - $this->fuel = $dFuel; - } - - public function setFullFillup($bFull) { - // force integer form of forced boolean :) - $this->full_fillup = (int)(bool)$bFull; - } - - public function setPrice($dPrice) { - $this->price = $dPrice; - } - - public function setConsumption($dConsumption) { - $this->consumption = $dConsumption; - } - - public function setGeoCoords($dLatitude, $dLongitude) { - $this->latitude = $dLatitude; - $this->longitude = $dLongitude; - - // Fuelio requires city name to display geo data on map - if (!empty($dLatitude) && empty($this->city)) - { - $this->setCity('GPS'); - } - } - - public function setCity($sCity) { - $this->city = $sCity; - } - - public function setNotes($sNotes) { - $this->notes = $sNotes; - } - - public function setMissedEntries($iMissed) { - $this->missed_entries = $iMissed; - } - - public function setTankNumber($nTankNumber) { - $this->tank_number = (int)$nTankNumber; - } - - public function setFuelType($nFuelType) { - $this->fuel_type = (int)$nFuelType; - } - - public function setVolumePrice($dVolumePrice) { - $this->volume_price = (double)$dVolumePrice; - } - - public function getData() { - $vars = get_object_vars($this); - return array_values($vars); - } - -} diff --git a/lib/fuelioimporter/fueltypes.class.php b/lib/fuelioimporter/fueltypes.class.php deleted file mode 100644 index 920d63f..0000000 --- a/lib/fuelioimporter/fueltypes.class.php +++ /dev/null @@ -1,91 +0,0 @@ -list = array(); - } - - public function addType($root, $id, $name, $active) - { - $root = (int)$root; - $element = array('name' => trim($name), 'active' => (int)(bool)$active, 'parent' => null); - if (empty($root) && !empty($name) && !empty($id) && ($root%100 === 0) ) { - $this->list[(int)$id] = $element; - return; - } - - if (!$this->validRootId($root)) { - throw new \RuntimeException('Invalid root fuel type id'); - } - - $element['parent'] = &$this->list[$root]; - } - - public function findIdByName($sName) - { - $name = trim($sName); - foreach ($this->list as $id => $element) { - if (strtolower($element['name']) === $name) { - return $id; - } - } - return -1; - } - - public function findNameById($nId) - { - if ($this->isValidId($nId)) { - return $this->list[$nId]['name']; - } - return null; - } - - public function isValidId($nId) - { - return isset($this->list[(int)$nId]); - } - - public function validRootId($nId) { - return $this->isValidId($nId) && ($nId%100 === 0); - } - - public static function getTypes() - { - $list = new FuelTypes(); - - $fh = fopen(__DIR__ . DIRECTORY_SEPARATOR . 'FuelType.csv', 'r'); - if (!$fh) { - throw new \RuntimeException('Failed opening FuelTypes.csv'); - } - $head = fgetcsv($fh); - if ($head[0] !== 'Id') { - throw new \RuntimeException('Invalid FuelTypes.csv format!'); - } - - while(!feof($fh)) { - $line = fgetcsv($fh); - $root = trim($line[1]); - $list->addType($root === '' ? null : $root, $line[0], $line[2], $line[3]); - - } - - fclose($fh); - return $list; - } -} \ No newline at end of file diff --git a/lib/fuelioimporter/icard.class.php b/lib/fuelioimporter/icard.class.php deleted file mode 100644 index 7b4366e..0000000 --- a/lib/fuelioimporter/icard.class.php +++ /dev/null @@ -1,48 +0,0 @@ -You can upload an .abp backup file from your aCar and we will convert it into Fuelio\'s CSV format. Just tap this card, or drop a file onto it.

You can also import backups from aCar free version. Use Help button below to read more.

'; - } - - public function getForm() - { - return new AcarForm(); - } -} \ No newline at end of file diff --git a/lib/fuelioimporter/providers/drivvocard.class.php b/lib/fuelioimporter/providers/drivvocard.class.php deleted file mode 100644 index 53fd34a..0000000 --- a/lib/fuelioimporter/providers/drivvocard.class.php +++ /dev/null @@ -1,40 +0,0 @@ -You can drop or upload Drivvo CSV export into this card and we will convert its data to Fuelio\'s format.

Export to CSV is possible in Pro version of the app. We are able to import basic fillups, expenses and service data.

-

See help for info about units. Default units are (0 - km) and (0 - liters)'; - } - - public function getForm() - { - return new DrivvoForm(); - } -} \ No newline at end of file diff --git a/lib/fuelioimporter/providers/fuellogcard.class.php b/lib/fuelioimporter/providers/fuellogcard.class.php deleted file mode 100644 index 7a9a2be..0000000 --- a/lib/fuelioimporter/providers/fuellogcard.class.php +++ /dev/null @@ -1,39 +0,0 @@ -You can drop or upload Fuel Log\'s CSV export into this card and we will convert its data to Fuelio\'s format.

Find export option in application menu, make sure you set correct fuel type in Fuelio\'s settings.

'; - } - - public function getForm() - { - return new FuelLogForm(); - } -} \ No newline at end of file diff --git a/lib/fuelioimporter/providers/fuellogform.class.php b/lib/fuelioimporter/providers/fuellogform.class.php deleted file mode 100644 index d5e4eff..0000000 --- a/lib/fuelioimporter/providers/fuellogform.class.php +++ /dev/null @@ -1,19 +0,0 @@ - 1, 'label' => 'Export vehicle #', 'value' => 1)); - } - - public function getName() - { - return 'fuellog'; - } -} \ No newline at end of file diff --git a/lib/fuelioimporter/providers/motostatcard.class.php b/lib/fuelioimporter/providers/motostatcard.class.php deleted file mode 100644 index 03419f8..0000000 --- a/lib/fuelioimporter/providers/motostatcard.class.php +++ /dev/null @@ -1,34 +0,0 @@ -You can upload your motostat.csv file here and we will convert it into Fuelio\'s CSV format. Just tap this card or drop file onto it.

To export your car data, open Motostat, select your car and click on "Export". Make sure to select both checkboxes!'; - } - - public function getForm() - { - return null; - } -} \ No newline at end of file diff --git a/lib/fuelioimporter/vehicle.class.php b/lib/fuelioimporter/vehicle.class.php deleted file mode 100644 index 99e34a6..0000000 --- a/lib/fuelioimporter/vehicle.class.php +++ /dev/null @@ -1,170 +0,0 @@ -setName($sName); - $this->setDescription($sDescription); - $this->setDistanceUnit($iDistance_unit); - $this->setFuelUnit($iFuel_unit); - $this->setConsumptionUnit($iConsumption_unit); - } - - public function setName($sName) { - $this->name = $sName; - } - - public function setDescription($sDescription) { - $this->description = $sDescription; - } - - public function setDistanceUnit($iDistance_unit) { - $this->distance_unit = $iDistance_unit; - } - - public function setFuelUnit($iFuel_unit) { - $this->fuel_unit = $iFuel_unit; - } - - public function setConsumptionUnit($iConsumption_unit) { - $this->consumption_unit = $iConsumption_unit; - } - - public function setVIN($sVin) { - $this->vin = $sVin; - } - - public function setInsurance($sInsurance) { - $this->insurance = $sInsurance; - } - - public function setPlate($sPlate) { - $this->plate = $sPlate; - } - - public function setMake($sMake) { - $this->make = $sMake; - } - - public function setModel($sModel) { - $this->model = $sModel; - } - - public function setYear($iYear) { - $this->year = intval($iYear); - } - - public function setTankCount($nTankCount) { - $this->tank_count = (int)$nTankCount; - } - - public function setTankType($nIdx, $nType) { - $idx = (int)$nIdx; - if (($idx !== 1 && $idx !== 2) || $nType === null) { - return; //no-op, only two tanks storable - } - - $this->{'tank_'.$idx.'_type'} = (int)$nType; - } - - public function setActive($bActive) { - $this->active = (int)(bool)$bActive; - } - - public function getData() { - $vars = get_object_vars($this); - if (empty($vars['name'])) { - $vars['name'] = 'No Name'; - } - return array_values($vars); - } - -} diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..11a337a --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,6 @@ +parameters: + level: 6 + paths: + - src + - view + - web \ No newline at end of file diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..aad64c6 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/lib/fuelioimporter/ibackupentry.class.php b/src/BackupEntryInterface.php similarity index 53% rename from lib/fuelioimporter/ibackupentry.class.php rename to src/BackupEntryInterface.php index 7b7b4d8..a0d5d83 100644 --- a/lib/fuelioimporter/ibackupentry.class.php +++ b/src/BackupEntryInterface.php @@ -1,15 +1,18 @@ */ - public function getData(); -} \ No newline at end of file + public function getData(): array; +} diff --git a/src/Card/AcarCard.php b/src/Card/AcarCard.php new file mode 100644 index 0000000..5da496f --- /dev/null +++ b/src/Card/AcarCard.php @@ -0,0 +1,44 @@ +You can upload an .abp backup file from your aCar and we will convert it into Fuelio\'s CSV format. Just tap this card, or drop a file onto it.

You can also import backups from aCar free version. Use Help button below to read more.

'; + } + + public function getForm(): FormInterface + { + return new AcarForm(); + } +} diff --git a/src/Card/DrivvoCard.php b/src/Card/DrivvoCard.php new file mode 100644 index 0000000..0a3bf81 --- /dev/null +++ b/src/Card/DrivvoCard.php @@ -0,0 +1,43 @@ +You can drop or upload Drivvo CSV export into this card and we will convert its data to Fuelio\'s format.

Export to CSV is possible in Pro version of the app. We are able to import basic fillups, expenses and service data.

+

See help for info about units. Default units are (0 - km) and (0 - liters)'; + } + + public function getForm(): FormInterface + { + return new DrivvoForm(); + } +} diff --git a/src/Card/FuellogCard.php b/src/Card/FuellogCard.php new file mode 100644 index 0000000..2b4b791 --- /dev/null +++ b/src/Card/FuellogCard.php @@ -0,0 +1,42 @@ +You can drop or upload Fuel Log\'s CSV export into this card and we will convert its data to Fuelio\'s format.

Find export option in application menu, make sure you set correct fuel type in Fuelio\'s settings.

'; + } + + public function getForm(): FormInterface + { + return new FuelLogForm(); + } +} diff --git a/src/Card/MotostatCard.php b/src/Card/MotostatCard.php new file mode 100644 index 0000000..2f90668 --- /dev/null +++ b/src/Card/MotostatCard.php @@ -0,0 +1,44 @@ +You can upload your motostat.csv file here and we will convert it into Fuelio\'s CSV format. Just tap this card or drop file onto it.

To export your car data, open Motostat, select your car and click on "Export". Make sure to select both checkboxes!'; + } + + public function getForm(): ?FormInterface + { + return null; + } +} diff --git a/src/CardInterface.php b/src/CardInterface.php new file mode 100644 index 0000000..5cbe7d3 --- /dev/null +++ b/src/CardInterface.php @@ -0,0 +1,48 @@ +> Array of action menu entries + */ + public function getActions(): array; + + /** + * Returns card menu items + * @return list> Array of card menu entries + */ + public function getMenu(): array; + + /** + * Returns configuration form interface + */ + public function getForm(): ?FormInterface; +} diff --git a/src/ConverterProvider.php b/src/ConverterProvider.php new file mode 100644 index 0000000..1bea6ce --- /dev/null +++ b/src/ConverterProvider.php @@ -0,0 +1,90 @@ + + */ +class ConverterProvider implements IteratorAggregate +{ + /** @var array Internal storage of found providers */ + protected array $providers = []; + /** @var boolean $classes_loaded Flag for autodetecting available plugins */ + protected bool $classes_loaded = false; + + /** + * Interface implementation for iterating over available plugins + * @return ArrayIterator + */ + public function getIterator(): ArrayIterator + { + if (!$this->classes_loaded) { + $this->initialize(); + } + + return new ArrayIterator($this->providers); + } + + /** + * Returns converter by its name + * @param string $name Converter name + * @return ProviderInterface + * @throws ProviderNotExistsException + */ + public function get(string $name): ProviderInterface + { + if (!$this->classes_loaded) { + $this->initialize(); + } + if (isset($this->providers[$name])) { + return $this->providers[$name]; + } + throw new ProviderNotExistsException(); + } + + /** + * Initializes classes and propagates $providers array + * + * This method reads all files in namespace FuelioImporter\Providers + * classes implementing IConverter interface + */ + public function initialize(): void + { + $di = new DirectoryIterator(__DIR__ . DIRECTORY_SEPARATOR . 'Providers'); + foreach ($di as $spl_file) { + if ($spl_file->isFile() && !$spl_file->isDot() && $spl_file->isReadable() && strpos($spl_file->getBasename(), 'Provider.php') !== false) { + + /* We should have a new class, verify and check if it's implementing IConverter + * Autoloader here will do the job searching for valid file + * it should not misbehave as we are using FuelioImporter namespace + */ + + $classname = 'FuelioImporter\\Providers\\' . ucfirst($spl_file->getBasename('.php')); + + if (!class_exists($classname, true)) { + continue; + } + + if (is_subclass_of($classname, ProviderInterface::class, true)) { + $instance = new $classname(); + if (isset($this->providers[$instance->getName()])) { + throw new ProviderExistsException(); + } + + $this->providers[$instance->getName()] = $instance; + } + } + } + $this->classes_loaded = true; + } +} diff --git a/src/Cost.php b/src/Cost.php new file mode 100644 index 0000000..aa06add --- /dev/null +++ b/src/Cost.php @@ -0,0 +1,139 @@ +title = $sTitle; + } + + public function setDate(string $sDate): void + { + $dt = new \DateTime($sDate); + $this->date = $dt->format(FuelioBackupBuilder::DATE_FORMAT); + } + + public function setOdo(int $iOdo): void + { + $this->odo = $iOdo; + } + + public function setCostCategoryId(int $iId): void + { + $this->cost_category_id = $iId; + } + + public function setNotes(string $sNotes): void + { + $this->notes = $sNotes; + } + + public function setCost(float $dCost): void + { + $this->cost = $dCost; + $this->setIsIncome($dCost < 0); + } + + public function setFlag(int $flag): void + { + $this->flag = $flag; + } + + public function setIdR(int $iId): void + { + $this->idR = $iId; + } + + public function setRead(bool $bRead): void + { + $this->read = (int) $bRead; + } + + public function setReminderOdo(int $iOdo): void + { + $this->remindOdo = $iOdo; + } + + public function setReminderDate(string $sDate): void + { + $dt = new \DateTime($sDate); + $this->remindDate = $dt->format(FuelioBackupBuilder::DATE_FORMAT); + } + + public function setRepeatOdo(int $iOdo): void + { + $this->repeat_odo = $iOdo; + } + + public function setRepeatMonths(int $sMonths): void + { + $this->repeat_months = $sMonths; + } + + public function setIsIncome(bool $bIsIncome): void + { + $this->is_income = (int) $bIsIncome; + } + + public function setUniqueId(int $iUniqueId): void + { + $this->unique_id = $iUniqueId; + } + + public function getCostDate(): string + { + return $this->date; + } + + public function getData(): array + { + $vars = get_object_vars($this); + return array_values($vars); + } +} diff --git a/src/CostCategory.php b/src/CostCategory.php new file mode 100644 index 0000000..1aef768 --- /dev/null +++ b/src/CostCategory.php @@ -0,0 +1,60 @@ +type_id = $type_id; + $this->name = $name; + $this->priority = $priority; + $this->color = $color; + } + + public function getTypeId(): int + { + return $this->type_id; + } + + public function setTypeId(int $typeId): void + { + $this->type_id = $typeId; + } + + public function getName(): string + { + return $this->name; + } + + public function getPriority(): int + { + return $this->priority; + } + + public function getColor(): string + { + return $this->color; + } + + public function getData(): array + { + $vars = get_object_vars($this); + return array_values($vars); + } +} diff --git a/lib/fuelioimporter/form/baseform.class.php b/src/Form/AbstractForm.php similarity index 62% rename from lib/fuelioimporter/form/baseform.class.php rename to src/Form/AbstractForm.php index 9859ead..756fa89 100644 --- a/lib/fuelioimporter/form/baseform.class.php +++ b/src/Form/AbstractForm.php @@ -1,37 +1,40 @@ Submitted data */ - protected $data; + protected array $data; /** * @var \Throwable[] Array of validation errors */ - protected $errors; + protected array $errors; /** * @var bool Determines if form data was submitted */ - protected $is_submitted; + protected bool $is_submitted; // - public function offsetExists($offset) + public function offsetExists($offset): bool { if (!is_string($offset)) { throw new \InvalidArgumentException('Offset name must be of string type.'); @@ -39,18 +42,18 @@ public function offsetExists($offset) return array_key_exists($offset, $this->fields); } - public function offsetGet($offset) + public function offsetGet($offset): ?FormFieldInterface { return $this->offsetExists($offset) ? $this->fields[$offset] : null; } - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { if ($offset !== null && !is_string($offset)) { throw new \InvalidArgumentException('Offset name must be of string type.'); } - if (!$value instanceof IFormField) { + if (!$value instanceof FormFieldInterface) { throw new \InvalidArgumentException('Only IFormField instances are allowed.'); } @@ -62,15 +65,15 @@ public function offsetSet($offset, $value) $value->setForm($this); } - public function offsetUnset($offset) + public function offsetUnset($offset): void { unset($this->fields[$offset]); } - public function getData() + public function getData(): ?iterable { - $out = array(); - foreach($this->fields as $name => $field) { + $out = []; + foreach ($this->fields as $name => $field) { $data = null; if ($field->isValid()) { $data = $field->getValue(); @@ -80,31 +83,31 @@ public function getData() return $out; } - abstract public function getName(); + abstract public function getName(): string; - public function isSubmitted() + public function isSubmitted(): bool { return $this->is_submitted; } - public function isValid() + public function isValid(): bool { return $this->isSubmitted() && empty($this->errors); } - public function process($post_data) + public function process($post_data): void { $this->is_submitted = array_key_exists($this->getName(), $post_data); if (!$this->is_submitted) { return; } - foreach ($post_data[$this->getName()] as $name=>$value) { - if (!$this->offsetExists($name)) { + foreach ($post_data[$this->getName()] as $name => $value) { + $field = $this[$name] ?? null; + if (!$field) { throw new \InvalidArgumentException('Unexpected form field.'); } - $field = $this->offsetGet($name); try { $field->setValue($value); if (!$field->isValid()) { @@ -118,14 +121,15 @@ public function process($post_data) } } - public function getIterator() + /** @return \ArrayIterator */ + public function getIterator(): \ArrayIterator { return new \ArrayIterator($this->fields); } - public function getErrors() + public function getErrors(): array { return $this->errors; } // -} \ No newline at end of file +} diff --git a/lib/fuelioimporter/providers/acarform.class.php b/src/Form/AcarForm.php similarity index 65% rename from lib/fuelioimporter/providers/acarform.class.php rename to src/Form/AcarForm.php index fd446c2..9d3a45b 100644 --- a/lib/fuelioimporter/providers/acarform.class.php +++ b/src/Form/AcarForm.php @@ -1,19 +1,20 @@ 1, 'label' => 'Export vehicle #', 'value' => 1)); } - public function getName() + public function getName(): string { return 'acar'; } -} \ No newline at end of file +} diff --git a/lib/fuelioimporter/providers/drivvoform.class.php b/src/Form/DrivvoForm.php similarity index 67% rename from lib/fuelioimporter/providers/drivvoform.class.php rename to src/Form/DrivvoForm.php index 0dfe69c..568426a 100644 --- a/lib/fuelioimporter/providers/drivvoform.class.php +++ b/src/Form/DrivvoForm.php @@ -1,21 +1,22 @@ 0, 'label' => 'Distance Unit #', 'value' => 0)); $this[] = new MDLNumericField('fuel_unit', array('min' => 0, 'label' => 'Fuel Unit #', 'value' => 0)); - } - public function getName() + public function getName(): string { return 'drivvo'; } -} \ No newline at end of file +} diff --git a/src/Form/Field/MDLNumericField.php b/src/Form/Field/MDLNumericField.php new file mode 100644 index 0000000..5899409 --- /dev/null +++ b/src/Form/Field/MDLNumericField.php @@ -0,0 +1,38 @@ + [ + 'class' => 'mdl-textfield__input' + ] + ], + $options + ) + ); + } + + public function render(): string + { + return sprintf( + '

%sInput is not a number!
', + parent::render(), + $this->options['label'] + ); + } +} diff --git a/lib/fuelioimporter/form/field/numericfield.class.php b/src/Form/Field/NumericField.php similarity index 65% rename from lib/fuelioimporter/form/field/numericfield.class.php rename to src/Form/Field/NumericField.php index 99ddc3c..9f32a03 100644 --- a/lib/fuelioimporter/form/field/numericfield.class.php +++ b/src/Form/Field/NumericField.php @@ -1,11 +1,12 @@ label => field label *
  • attributes => html attributes
  • */ -class NumericField implements IFormField +class NumericField implements FormFieldInterface { /** - * @var array Internal field options + * @var array Internal field options */ - protected $options; + protected array $options; /** * @var mixed Raw field value as processed by form @@ -32,25 +33,26 @@ class NumericField implements IFormField /** * @var int|null normalized integer */ - protected $value = null; + protected ?int $value = null; /** * @var string Field name */ - protected $name; + protected string $name; /** - * @var IForm Parent form + * @var ?FormInterface Parent form */ - protected $form; + protected ?FormInterface $form = null; - public function __construct($name, $options = array()) + /** @param array $options */ + public function __construct(string $name, array $options = []) { - $defaults = array('min' => 1, 'label' => $name, 'attributes' => array()); + $defaults = array('min' => 1, 'label' => $name, 'attributes' => []); $this->options = array_merge($defaults, $options); - if ($name === null || empty((string)$name)) { + if (empty($name)) { throw new \InvalidArgumentException('Field needs a name!'); } $this->name = $name; @@ -63,7 +65,7 @@ public function __construct($name, $options = array()) } // - public function getName() + public function getName(): string { return $this->name; } @@ -73,9 +75,9 @@ public function getValue() return $this->value; } - public function isValid() + public function isValid(): bool { - if (!empty($this->value) && !is_numeric($this->value)) { + if (!empty($this->value)) { $val = ''; if (function_exists('mb_strimwidth')) { $val = mb_strimwidth($val, 0, 20, '…'); @@ -92,21 +94,23 @@ public function isValid() return true; } - public function setForm(IForm $form) + public function setForm(FormInterface $form): void { $this->form = $form; } - public function setValue($value) + /** @param float|int|string $value */ + public function setValue($value): void { $this->raw_value = $value; $this->value = $this->normalizeValue($value); } - public function render() + public function render(): string { - return sprintf('', + return sprintf( + '', addcslashes($this->form ? sprintf('%s[%s]', $this->form->getName(), $this->getName()) : $this->getName(), '"'), addcslashes((string)$this->getValue(), '"'), $this->getRenderingAttributes($this->options['attributes']) @@ -114,15 +118,19 @@ public function render() } // - protected function normalizeValue($value) { - return intval($value, 10); + /** @param string|int|float $value */ + protected function normalizeValue($value): int + { + return (int) $value; } - protected function getRenderingAttributes($attributes) { + /** @param iterable $attributes */ + protected function getRenderingAttributes(iterable $attributes): string + { $vals = array(); - foreach ($attributes as $name=>$value) { - $vals[] = sprintf('%s="%s"', str_replace(' ', '_', $name), addcslashes($value,'"')); + foreach ($attributes as $name => $value) { + $vals[] = sprintf('%s="%s"', str_replace(' ', '_', $name), addcslashes((string) $value, '"')); } return implode(' ', $vals); } -} \ No newline at end of file +} diff --git a/lib/fuelioimporter/form/iformfield.class.php b/src/Form/FormFieldInterface.php similarity index 67% rename from lib/fuelioimporter/form/iformfield.class.php rename to src/Form/FormFieldInterface.php index 177849d..5bf4876 100644 --- a/lib/fuelioimporter/form/iformfield.class.php +++ b/src/Form/FormFieldInterface.php @@ -1,5 +1,7 @@ + * @extends \IteratorAggregate + */ +interface FormInterface extends \ArrayAccess, \IteratorAggregate +{ + /** + * Processes $_POST data for form + * @param array $post_data PHP's $_POST + */ + public function process(array $post_data): void; + + /** + * Returns form identification string for name= + */ + public function getName(): string; + + /** + * Checks processed data for validation errors + */ + public function isValid(): bool; + + /** + * Returns if processed data had form fields attached + */ + public function isSubmitted(): bool; + + /** + * Returns processed data + * @return null|array + */ + public function getData(): ?iterable; + + /** + * Returns collected validation errors + * @return \Throwable[] + */ + public function getErrors(): array; +} diff --git a/lib/fuelioimporter/form/formvalidatorexception.class.php b/src/Form/FormValidatorException.php similarity index 58% rename from lib/fuelioimporter/form/formvalidatorexception.class.php rename to src/Form/FormValidatorException.php index 428a9c6..25f99dc 100644 --- a/lib/fuelioimporter/form/formvalidatorexception.class.php +++ b/src/Form/FormValidatorException.php @@ -1,9 +1,13 @@ 1, 'label' => 'Export vehicle #', 'value' => 1]); + } + + public function getName(): string + { + return 'fuellog'; + } +} diff --git a/src/FuelLogEntry.php b/src/FuelLogEntry.php new file mode 100644 index 0000000..07b1dee --- /dev/null +++ b/src/FuelLogEntry.php @@ -0,0 +1,121 @@ +data = $dt->format(FuelioBackupBuilder::DATE_FORMAT); + } + + public function setOdo(int $iOdo): void + { + $this->odo = $iOdo; + } + + public function setFuel(float $dFuel): void + { + $this->fuel = $dFuel; + } + + public function setFullFillup(bool $bFull): void + { + // force integer form of forced boolean :) + $this->full_fillup = (int) $bFull; + } + + public function setPrice(float $dPrice): void + { + $this->price = $dPrice; + } + + public function setConsumption(float $dConsumption): void + { + $this->consumption = $dConsumption; + } + + public function setGeoCoords(float $dLatitude, float $dLongitude): void + { + $this->latitude = $dLatitude; + $this->longitude = $dLongitude; + + // Fuelio requires city name to display geo data on map + if (!empty($dLatitude) && empty($this->city)) { + $this->setCity('GPS'); + } + } + + public function setCity(string $sCity): void + { + $this->city = $sCity; + } + + public function setNotes(string $sNotes): void + { + $this->notes = $sNotes; + } + + public function setMissedEntries(int $iMissed): void + { + $this->missed_entries = $iMissed; + } + + public function setTankNumber(int $nTankNumber): void + { + $this->tank_number = $nTankNumber; + } + + public function setFuelType(int $nFuelType): void + { + $this->fuel_type = $nFuelType; + } + + public function setVolumePrice(float $dVolumePrice): void + { + $this->volume_price = $dVolumePrice; + } + + public function getData(): array + { + $vars = get_object_vars($this); + return array_values($vars); + } +} diff --git a/lib/fuelioimporter/FuelType.csv b/src/FuelType.csv similarity index 100% rename from lib/fuelioimporter/FuelType.csv rename to src/FuelType.csv diff --git a/src/FuelTypes.php b/src/FuelTypes.php new file mode 100644 index 0000000..d12ae41 --- /dev/null +++ b/src/FuelTypes.php @@ -0,0 +1,95 @@ + + */ + protected array $list = []; + + public function addType(?int $root, int $id, string $name, bool $active): void + { + $element = ['name' => trim($name), 'active' => (int) $active, 'parent' => null]; + if ($root === null && !empty($name) && !empty($id) && ($root%100 === 0)) { + $this->list[$id] = $element; + return; + } + + if (!$root || !$this->validRootId($root)) { + throw new \RuntimeException('Invalid root fuel type id'); + } + + /** @psalm-suppress PossiblyNullArrayOffset */ + $element['parent'] = &$this->list[$root]; + } + + public function findIdByName(string $sName): int + { + $name = trim($sName); + foreach ($this->list as $id => $element) { + if (strtolower($element['name']) === $name) { + return $id; + } + } + return -1; + } + + public function findNameById(int $nId): ?string + { + if ($this->isValidId($nId)) { + return $this->list[$nId]['name']; + } + return null; + } + + public function isValidId(int $nId): bool + { + return isset($this->list[$nId]); + } + + public function validRootId(int $nId): bool + { + return $this->isValidId($nId) && ($nId%100 === 0); + } + + public static function getTypes(): FuelTypes + { + $list = new self(); + + $fh = fopen(__DIR__ . DIRECTORY_SEPARATOR . 'FuelType.csv', 'r'); + if (!$fh) { + throw new \RuntimeException('Failed opening FuelTypes.csv'); + } + $head = fgetcsv($fh); + if ($head[0] !== 'Id') { + throw new \RuntimeException('Invalid FuelTypes.csv format!'); + } + + while (!feof($fh)) { + $line = fgetcsv($fh); + $root = trim($line[1]); + $list->addType($root === '' ? null : (int) $root, (int) $line[0], $line[2], (bool) $line[3]); + } + + fclose($fh); + return $list; + } +} diff --git a/lib/fuelioimporter/fueliobackupbuilder.class.php b/src/FuelioBackupBuilder.php similarity index 73% rename from lib/fuelioimporter/fueliobackupbuilder.class.php rename to src/FuelioBackupBuilder.php index 22279c9..76b13fc 100644 --- a/lib/fuelioimporter/fueliobackupbuilder.class.php +++ b/src/FuelioBackupBuilder.php @@ -1,92 +1,92 @@ fwrite("## Vehicle,,,,,,,,,,,,,,\n"); $this->fputcsv(array('Name','Description','DistUnit','FuelUnit','ConsumptionUnit','ImportCSVDateFormat', 'VIN', 'Insurance', 'Plate', 'Make', 'Model', 'Year', 'TankCount', 'Tank1Type', 'Tank2Type', 'Active')); } - + /** * Writes Vehicle data - * @param \FuelioImporter\Vehicle $vehicle */ - public function writeVehicle(Vehicle $vehicle) + public function writeVehicle(Vehicle $vehicle): void { $this->fputcsv($vehicle->getData()); } - + /** * Writes fuel log header */ - public function writeFuelLogHeader() + public function writeFuelLogHeader(): void { $this->fwrite("## Log,,,,,,,,,,,,,\n"); $this->fputcsv(array('Data','Odo(km)','Fuel(litres)','Full','Price(optional)','l/100km(optional)','latitude(optional)','longitude(optional)','City(optional)','Notes(optional)','Missed', 'TankNumber', 'FuelType', 'VolumePrice')); } - + /** * Writes fuel log entry - * @param \FuelioImporter\FuelLogEntry $entry */ - public function writeFuelLog(FuelLogEntry $entry) + public function writeFuelLog(FuelLogEntry $entry): void { $this->fputcsv($entry->getData()); } - + /** * Writes cost categories header starting optional costs backup */ - public function writeCostCategoriesHeader() + public function writeCostCategoriesHeader(): void { $this->fwrite("## CostCategories,,,,,,,,,,,\n"); $this->fputcsv(array('CostTypeID', 'Name', 'priority')); } - + /** * Writes costs header into stream */ - public function writeCoststHeader() + public function writeCoststHeader(): void { $this->fwrite("## Costs,,,,,,,,,,,,,,,\n"); $this->fputcsv(array('CostTitle', 'Date', 'Odo', 'CostTypeID', 'Notes', 'Cost', 'flag', 'idR', 'read', 'RemindOdo', 'RemindDate', 'isTemplate', 'RepeatOdo', 'RepeatMonths', 'isIncome', 'UniqueId')); } - + /** * Writes cost category into stream - * @param \FuelioImporter\CostCategory $category */ - public function writeCostCategory(CostCategory $category) + public function writeCostCategory(CostCategory $category): void { $this->fputcsv($category->getData()); } - + /** * Writes cost into stream - * @param \FuelioImporter\Cost $cost */ - public function writeCost(Cost $cost) + public function writeCost(Cost $cost): void { $this->fputcsv($cost->getData()); } diff --git a/src/InvalidFileFormatException.php b/src/InvalidFileFormatException.php new file mode 100644 index 0000000..45bc9b7 --- /dev/null +++ b/src/InvalidFileFormatException.php @@ -0,0 +1,18 @@ + + */ + public function getErrors(): array; + + /** + * Array of warnings + * @return array + */ + public function getWarnings(): array; + + /** + * Method that processes given file returning SplTempFileObject + * @param null|array $form_data + */ + public function processFile(SplFileObject $in, ?iterable $form_data): FuelioBackupBuilder; + + /** + * Method returns a CardInterface for visual representation + */ + public function getCard(): CardInterface; + + /** + * Optional stylesheet to include on page + */ + public function getStylesheetLocation(): ?string; + + /** + * Sets car name + * + * @param string $name Car name + */ + public function setCarName(string $name): void; +} diff --git a/src/ProviderNotExistsException.php b/src/ProviderNotExistsException.php new file mode 100644 index 0000000..f8522a6 --- /dev/null +++ b/src/ProviderNotExistsException.php @@ -0,0 +1,18 @@ +|false> List of files in zip archive */ + protected array $archive_files = []; + /** @var array metadata.inf file contents as array */ + protected array $metadata = []; + /** @var array preferences.xml file contents as array */ + protected array $preferences = []; + /** @var array List of expenses from expenses.xml (as CostCategory instances) */ + protected array $expenses = []; + /** @var array List of services from services.xml (as CostCategory instances) */ + protected array $services = []; // Vehicle number provided by form - protected $selected_vehicle = 1; - // Fuel types declared in aCar backup - protected $acar_fuels = array(); - /** @var FuelTypes|null */ - protected $fuel_types; + protected int $selected_vehicle = 1; + /** @var array Fuel types declared in aCar backup */ + protected array $acar_fuels = []; + protected ?FuelTypes $fuel_types = null; // @see IConverter - public function getName() { + public function getName(): string + { return 'acar'; } // @see IConverter - public function getTitle() { + public function getTitle(): string + { return 'aCar ABP'; } // @see IConverter - public function getOutputFileName() + public function getOutputFileName(): string { return 'aCar-car-' . $this->selected_vehicle; } // @see IConverter - public function getStylesheetLocation() { + public function getStylesheetLocation(): ?string + { return null; } // @see IConverter - public function setCarName($name) { + public function setCarName($name): void + { if (!empty($name)) { $this->car_name = $name; } } // @see IConverter - public function processFile(SplFileObject $stream, $form_data) { + public function processFile(SplFileObject $in, ?iterable $form_data): FuelioBackupBuilder + { + if (!$form_data) { + throw new FormValidatorException('Missing form data, you need to choose vehicle #'); + } // We need to verify that we've got valid archive - $in = new ZipArchive(); + $zip = new ZipArchive(); $out = new FuelioBackupBuilder(); /** @todo: Provide more detailed Zip-related error handling */ - if ($in->open($stream->getPathname()) !== true) - throw new \FuelioImporter\InvalidFileFormatException(); // For basics + if ($zip->open($in->getPathname()) !== true) { + throw new InvalidFileFormatException(); // For basics + } // If no metadata.inf, throw error // list contents $i = 0; - while (($stat = $in->statIndex($i++)) !== false) { + while (($stat = $zip->statIndex($i++)) !== false) { $normalized_name = strtolower($stat['name']); $this->archive_files[$normalized_name] = $stat; } - $this->validateInputFile($in, $out); + $this->validateInputFile($zip, $out); - $this->readPreferences($in, $out); + $this->readPreferences($zip, $out); - $this->processFuelTypes($in); + $this->processFuelTypes($zip); // Read vehicle data - $this->selected_vehicle = intval($form_data['vehicle_id'], 10); - $data = $this->getVehicle($this->selected_vehicle, $in); + $this->selected_vehicle = (int) $form_data['vehicle_id']; + $data = $this->getVehicle($this->selected_vehicle, $zip); // Process vehicle header - $this->processVehicle($data, $in, $out); + $this->processVehicle($data, $zip, $out); // Process fuellings - $this->processFuellings($data, $in, $out); + $this->processFuellings($data, $zip, $out); // Process Costs (expenses and services) - $this->processCosts($data, $in, $out); + $this->processCosts($data, $zip, $out); - $in->close(); + $zip->close(); $out->rewind(); return $out; } // @see IConverter - public function getErrors() { - return array(); + public function getErrors(): array + { + return []; } // @see IConverter - public function getWarnings() { - return array(); + public function getWarnings(): array + { + return []; } // @see IConverter - public function getCard() { + public function getCard(): CardInterface + { return new AcarCard(); } @@ -128,17 +143,21 @@ public function getCard() { * @param ZipArchive $in Input archive * @param FuelioBackupBuilder $out Output file */ - protected function readPreferences(ZipArchive $in, FuelioBackupBuilder $out) { + protected function readPreferences(ZipArchive $in, FuelioBackupBuilder $out): void + { // Read all preferences and store them as array $xml = new \SimpleXMLElement(stream_get_contents($in->getStream('preferences.xml'))); foreach ($xml->preference as $node) { $atts = $node->attributes(); - $key = (string) $atts['name']; - $type = (string) $atts['type']; + if (!$atts) { + continue; + } + $key = (string) ($atts['name'] ?? ''); + $type = (string) ($atts['type'] ?? ''); $value = (string) $node; - if ($type == 'java.lang.Boolean') { - $value = ($value == 'true'); + if ($type === 'java.lang.Boolean') { + $value = ($value === 'true'); } $this->preferences[$key] = $value; @@ -148,18 +167,20 @@ protected function readPreferences(ZipArchive $in, FuelioBackupBuilder $out) { /** * Reads metadata.inf into $metadata array * @param ZipArchive $in Input archive - * @throws \FuelioImporter\InvalidFileFormatException When no metadata.inf in archive + * @throws InvalidFileFormatException When no metadata.inf in archive */ - protected function readMetadata(ZipArchive $in) + protected function readMetadata(ZipArchive $in): void { $metastream = $in->getStream('metadata.inf'); - if (false === $metastream) - throw new \FuelioImporter\InvalidFileFormatException(); + if (false === $metastream) { + throw new InvalidFileFormatException(); + } while (!feof($metastream)) { $entry = explode('=', fgets($metastream), 2); - if (count($entry) == 2) + if (count($entry) == 2) { $this->metadata[trim($entry[0])] = trim($entry[1]); + } } } @@ -167,17 +188,20 @@ protected function readMetadata(ZipArchive $in) * Throws error if provided file is not Full Backup of aCar data * @param ZipArchive $in Input archive * @param FuelioBackupBuilder $out Output file - * @throws \FuelioImporter\InvalidFileFormatException + * @throws InvalidFileFormatException */ - protected function validateInputFile(ZipArchive $in, FuelioBackupBuilder $out) { - if (!isset($this->archive_files['metadata.inf'])) + protected function validateInputFile(ZipArchive $in, FuelioBackupBuilder $out): void + { + if (!isset($this->archive_files['metadata.inf'])) { throw new \FuelioImporter\InvalidFileFormatException(); + } $this->readMetadata($in); // At this moment we support only full backups - if (@$this->metadata['acar.backup.type'] != 'Full-Backup') + if (@$this->metadata['acar.backup.type'] !== 'Full-Backup') { throw new \FuelioImporter\InvalidFileFormatException('At this moment we support only Full Backups!'); + } } /** @@ -187,7 +211,8 @@ protected function validateInputFile(ZipArchive $in, FuelioBackupBuilder $out) { * @param FuelioBackupBuilder $out Output file * @throws InvalidUnitException */ - public function processVehicle(SimpleXMLElement $data, ZipArchive $in, FuelioBackupBuilder $out) { + public function processVehicle(SimpleXMLElement $data, ZipArchive $in, FuelioBackupBuilder $out): void + { $out->writeVehicleHeader(); $vehicle = new Vehicle($this->car_name, ''); @@ -213,14 +238,14 @@ public function processVehicle(SimpleXMLElement $data, ZipArchive $in, FuelioBac * Reads fuel unit from aCar preferences or vehicle node * @param SimpleXmlElement $vehicleNode Vehicle node for additional data * @return integer Vehicle constant - * @throws \FuelioImporter\InvalidUnitException On unsupported unit + * @throws InvalidUnitException On unsupported unit */ - protected function getFuelUnit(SimpleXMLElement $vehicleNode) { + protected function getFuelUnit(SimpleXMLElement $vehicleNode): int + { // New aCar version stores volume unit per vehicle, not globally if (array_key_exists('acar.volume-unit', $this->preferences)) { $volume_unit = $this->preferences['acar.volume-unit']; - } - else { + } else { $volume_unit = (string)$vehicleNode->{'volume-unit'}; } switch ($volume_unit) { @@ -234,21 +259,21 @@ protected function getFuelUnit(SimpleXMLElement $vehicleNode) { case 'gallon': // TODO: Can anybody confirm this? case 'gal (UK)': return Vehicle::GALLONS_UK; default: - throw new \FuelioImporter\InvalidUnitException(); + throw new InvalidUnitException(); } } /** * Reads distance unit from aCar preferences or vehicle node * @return integer Vehicle constant - * @throws \FuelioImporter\InvalidUnitException + * @throws InvalidUnitException */ - protected function getDistanceUnit(SimpleXMLElement $vehicleNode) { + protected function getDistanceUnit(SimpleXMLElement $vehicleNode): int + { // New aCar version stores distance unit per vehicle, not globally if (array_key_exists('acar.distance-unit', $this->preferences)) { $distance_unit = $this->preferences['acar.distance-unit']; - } - else { + } else { $distance_unit = (string)$vehicleNode->{'distance-unit'}; } switch ($distance_unit) { @@ -260,16 +285,17 @@ protected function getDistanceUnit(SimpleXMLElement $vehicleNode) { case 'kilometer': return Vehicle::KILOMETERS; default: - throw new \FuelioImporter\InvalidUnitException(); + throw new InvalidUnitException(); } } /** * Reads consumption unit from aCars preferences * @return integer Vehicle constant - * @throws \FuelioImporter\InvalidUnitException + * @throws InvalidUnitException */ - protected function getConsumptionUnit() { + protected function getConsumptionUnit(): int + { // @TODO: check the format behind other options: // mpg (us), mpg (imperial), gal/100mi (us), gal/100mi (imperial), km/L, km/gal (us), km/gal (imperial). mi/L switch ($this->preferences['acar.fuel-efficiency-unit']) { @@ -283,7 +309,7 @@ protected function getConsumptionUnit() { case 'km/L': return Vehicle::KM_PER_L; default: - throw new \FuelioImporter\InvalidUnitException(); + throw new InvalidUnitException(); } } @@ -295,7 +321,8 @@ protected function getConsumptionUnit() { * @throws InvalidFileFormatException * @throws FormValidatorException */ - protected function getVehicle($iVehicle, ZipArchive $in) { + protected function getVehicle(int $iVehicle, ZipArchive $in): SimpleXMLElement + { $stream = $in->getStream('vehicles.xml'); $xml = new \SimpleXMLElement(stream_get_contents($stream)); if (!$xml) { @@ -316,9 +343,9 @@ protected function getVehicle($iVehicle, ZipArchive $in) { * @param string $date * @return string ATOM-formatted DateTime string */ - protected function readDate($date) { - $dt = DateTime::createFromFormat('m/d/Y - H:i', (string) $date); - return $dt->format(DateTime::ATOM); + protected function readDate(string $date): string + { + return DateTime::createFromFormat('m/d/Y - H:i', $date)->format(\DateTimeInterface::ATOM); } /** @@ -327,28 +354,33 @@ protected function readDate($date) { * @param ZipArchive $in Input archive * @param FuelioBackupBuilder $out Output file */ - protected function processFuellings(SimpleXMLElement $data, ZipArchive $in, FuelioBackupBuilder $out) { + protected function processFuellings(SimpleXMLElement $data, ZipArchive $in, FuelioBackupBuilder $out): void + { $out->writeFuelLogHeader(); foreach ($data->{'fillup-records'}->{'fillup-record'} as $record) { $entry = new FuelLogEntry(); - $entry->setDate($this->readDate($record->date)); - $entry->setFuel((string) $record->volume); - $entry->setPrice((string) $record->{'total-cost'}); - $entry->setOdo((string) $record->{'odometer-reading'}); + $entry->setDate($this->readDate((string)$record->date)); + $entry->setFuel((float) $record->volume); + $entry->setPrice((float) $record->{'total-cost'}); + $entry->setOdo((int) $record->{'odometer-reading'}); // According to Adrian Kajda consumption is calculated by app itself // and we should not store it // $consumption = (string) $record->{'fuel-efficiency'}; // $entry->setConsumption($this->calculateConsumption($consumption, $this->getConsumptionUnit())); - $entry->setGeoCoords((string) $record->latitude, (string) $record->longitude); - $entry->setFullFillup((string) $record->partial != 'true'); - $entry->setMissedEntries((string) $record->{'previous-missed-fillups'} == 'true'); + $entry->setGeoCoords((float) $record->latitude, (float) $record->longitude); + $entry->setFullFillup((string) $record->partial !== 'true'); + $entry->setMissedEntries((int) ($record->{'previous-missed-fillups'} === 'true')); $notes = (string) $record->{'fuel-brand'} . ' ' . (string) $record->{'fueling-station-address'} . ' ' . (string) $record->notes; $entry->setNotes(trim($notes)); $entry->setMissedEntries(0); - $entry->setFuelType($this->getFuelType((string)$record->{'fuel-type-id'})); + $fuelType = $this->getFuelType((int)$record->{'fuel-type-id'}); + if (!$fuelType) { + throw new InvalidUnitException('Cannot determine fuel unit'); + } + $entry->setFuelType($fuelType); $out->writeFuelLog($entry); } } @@ -358,7 +390,7 @@ protected function processFuellings(SimpleXMLElement $data, ZipArchive $in, Fuel * @param float $consumption Fuel consumption * @param int $iFormat Consumption unit * @return float Fuel consumption at L/100km - * @throws \FuelioImporter\InvalidUnitException + * @throws InvalidUnitException */ /*protected function calculateConsumption($consumption, $iFormat) { $dConsumption = floatval(str_replace(',', '.', $consumption)); @@ -390,24 +422,23 @@ protected function processFuellings(SimpleXMLElement $data, ZipArchive $in, Fuel * @param ZipArchive $in Input archive * @throws InvalidFileFormatException */ - protected function readServicesAsCategories(ZipArchive $in) { + protected function readServicesAsCategories(ZipArchive $in): void + { // Depending on aCar version, this data are in services.xml or event-subtypes.xml if ($in->statName('event-subtypes.xml') !== false) { $this->readNewServiceDefinition(new \SimpleXMLElement(stream_get_contents($in->getStream('event-subtypes.xml')))); - } - - else if ($in->statName('services.xml') !== false) { + } elseif ($in->statName('services.xml') !== false) { $this->readOldServiceDefinition(new \SimpleXMLElement(stream_get_contents($in->getStream('services.xml')))); } - else throw new InvalidFileFormatException(); + throw new InvalidFileFormatException(); } /** * Reads old-style service definitions * @param SimpleXMLElement $node */ - private function readOldServiceDefinition(SimpleXMLElement $node) + private function readOldServiceDefinition(SimpleXMLElement $node): void { foreach ($node->service as $service) { $atts = $service->attributes(); @@ -421,7 +452,7 @@ private function readOldServiceDefinition(SimpleXMLElement $node) * Reads new service definitions * @param SimpleXMLElement $node */ - private function readNewServiceDefinition(SimpleXMLElement $node) + private function readNewServiceDefinition(SimpleXMLElement $node): void { foreach ($node->{'event-subtype'} as $subtype) { $atts = $subtype->attributes(); @@ -443,21 +474,22 @@ private function readNewServiceDefinition(SimpleXMLElement $node) * @param ZipArchive $in Input archive * @throws InvalidFileFormatException */ - protected function readExpensesAsCategories(ZipArchive $in) { + protected function readExpensesAsCategories(ZipArchive $in): void + { // Depending on aCar version, this data are in expenses.xml or event-subtypes.xml if ($in->statName('event-subtypes.xml') !== false) { $this->readNewExpensesAsCategories(new \SimpleXMLElement(stream_get_contents($in->getStream('event-subtypes.xml')))); - } - else if ($in->statName('expenses.xml') !== false) { + } elseif ($in->statName('expenses.xml') !== false) { $this->readOldExpensesAsCategories(new \SimpleXMLElement(stream_get_contents($in->getStream('expenses.xml')))); - } else throw new InvalidFileFormatException(); + } + throw new InvalidFileFormatException(); } /** * Reads new expense definitions * @param SimpleXMLElement $node */ - private function readNewExpensesAsCategories(SimpleXMLElement $node) + private function readNewExpensesAsCategories(SimpleXMLElement $node): void { foreach ($node->{'event-subtype'} as $subtype) { $atts = $subtype->attributes(); @@ -478,7 +510,7 @@ private function readNewExpensesAsCategories(SimpleXMLElement $node) * Reads old-style expense definitions * @param SimpleXMLElement $node */ - private function readOldExpensesAsCategories(SimpleXMLElement $node) + private function readOldExpensesAsCategories(SimpleXMLElement $node): void { foreach ($node->expense as $expense) { $atts = $expense->attributes(); @@ -493,7 +525,8 @@ private function readOldExpensesAsCategories(SimpleXMLElement $node) * @param ZipArchive $in Input archive * @param FuelioBackupBuilder $out Output file */ - protected function processCostCategories(ZipArchive $in, FuelioBackupBuilder $out) { + protected function processCostCategories(ZipArchive $in, FuelioBackupBuilder $out): void + { $this->readExpensesAsCategories($in); $this->readServicesAsCategories($in); @@ -514,11 +547,12 @@ protected function processCostCategories(ZipArchive $in, FuelioBackupBuilder $ou * @param SimpleXMLElement $expense * @param FuelioBackupBuilder $out */ - protected function processExpense(SimpleXMLElement $expense, FuelioBackupBuilder $out) { + protected function processExpense(SimpleXMLElement $expense, FuelioBackupBuilder $out): void + { $cost = new Cost(); - $cost->setDate($this->readDate($expense->date)); - $cost->setCost((string) $expense->{'total-cost'}); - $cost->setOdo((string) $expense->{'odometer-reading'}); + $cost->setDate($this->readDate((string) $expense->date)); + $cost->setCost((float) $expense->{'total-cost'}); + $cost->setOdo((int) $expense->{'odometer-reading'}); // Set category if ($expense->expenses && $expense->expenses->expense[0]) { $atts = $expense->expenses->expense[0]->attributes(); @@ -547,7 +581,7 @@ protected function processExpense(SimpleXMLElement $expense, FuelioBackupBuilder $title = trim($title[0]); if (strpos($title, ',') !== false) { - $title = substr($title, 0, strpos($title, ',')); + $title = substr($title, 0, strpos($title, ',') ?: null); } $cost->setNotes(trim($notes)); @@ -560,11 +594,12 @@ protected function processExpense(SimpleXMLElement $expense, FuelioBackupBuilder * @param SimpleXMLElement $service * @param FuelioBackupBuilder $out */ - protected function processService(SimpleXMLElement $service, FuelioBackupBuilder $out) { + protected function processService(SimpleXMLElement $service, FuelioBackupBuilder $out): void + { $cost = new Cost(); - $cost->setDate($this->readDate($service->date)); - $cost->setCost((string) $service->{'total-cost'}); - $cost->setOdo((string) $service->{'odometer-reading'}); + $cost->setDate($this->readDate((string) $service->date)); + $cost->setCost((float) $service->{'total-cost'}); + $cost->setOdo((int) $service->{'odometer-reading'}); // Set category if ($service->services && $service->services->service[0]) { $atts = $service->services->service[0]->attributes(); @@ -593,7 +628,7 @@ protected function processService(SimpleXMLElement $service, FuelioBackupBuilder $title = trim($title[0]); if (strpos($title, ',') !== false) { - $title = substr($title, 0, strpos($title, ',')); + $title = substr($title, 0, strpos($title, ',') ?: null); } $cost->setNotes(trim($notes)); @@ -607,7 +642,8 @@ protected function processService(SimpleXMLElement $service, FuelioBackupBuilder * @param ZipArchive $in Input archive * @param FuelioBackupBuilder $out Output file */ - protected function processCosts(SimpleXMLElement $data, ZipArchive $in, FuelioBackupBuilder $out) { + protected function processCosts(SimpleXMLElement $data, ZipArchive $in, FuelioBackupBuilder $out): void + { $out->writeCostCategoriesHeader(); $this->processCostCategories($in, $out); @@ -632,14 +668,15 @@ protected function processCosts(SimpleXMLElement $data, ZipArchive $in, FuelioBa $type = (string)$event_record->type; if ($type === 'expense') { $this->processExpense($event_record, $out); - } else if ($type === 'service') { + } elseif ($type === 'service') { $this->processService($event_record, $out); } } } } - protected function processFuelTypes(ZipArchive $in) { + protected function processFuelTypes(ZipArchive $in): void + { if ($in->statName('fuel-types.xml') !== false) { $this->readFuelTypes(new \SimpleXMLElement(stream_get_contents($in->getStream('fuel-types.xml')))); } else { @@ -647,8 +684,9 @@ protected function processFuelTypes(ZipArchive $in) { } } - protected function readFuelTypes(SimpleXMLElement $root_node) { - $this->acar_fuels = array(); + protected function readFuelTypes(SimpleXMLElement $root_node): void + { + $this->acar_fuels = []; foreach ($root_node->{'fuel-type'} as $node) { $atts = $node->attributes(); $id = (int)(string)$atts['id']; @@ -663,8 +701,8 @@ protected function readFuelTypes(SimpleXMLElement $root_node) { $this->fuel_types = FuelTypes::getTypes(); } - protected function getFuelType($iAcarFuelType) { - + protected function getFuelType(int $iAcarFuelType): ?int + { if (!$this->fuel_types) { return null; } @@ -678,7 +716,7 @@ protected function getFuelType($iAcarFuelType) { $name = $this->acar_fuels[$iAcarFuelType]['name']; switch ($name) { - case 'Autogas/LPG' : $name = 'LPG/GPL'; break; + case 'Autogas/LPG': $name = 'LPG/GPL'; break; case 'CNG - Methane': $name = 'CNG'; break; case 'GPL': $name = 'GPL/LPG'; break; } @@ -686,9 +724,9 @@ protected function getFuelType($iAcarFuelType) { $fuelio_type = $this->fuel_types->findIdByName($name); if ($fuelio_type === -1) { // Not found, lets assign generic category - switch($this->acar_fuels[$iAcarFuelType]['category']) { - case 'gasoline' : return FuelTypes::FUEL_ROOT_GASOLINE; - case 'diesel' : return FuelTypes::FUEL_ROOT_DIESEL; + switch ($this->acar_fuels[$iAcarFuelType]['category']) { + case 'gasoline': return FuelTypes::FUEL_ROOT_GASOLINE; + case 'diesel': return FuelTypes::FUEL_ROOT_DIESEL; case 'bioalcohol': return FuelTypes::FUEL_ROOT_ETHANOL; case 'gas': return FuelTypes::FUEL_ROOT_LPG; } @@ -697,7 +735,7 @@ protected function getFuelType($iAcarFuelType) { return $fuelio_type !== -1 ? $fuelio_type : null; } - protected function determineVehicleFuelType(SimpleXMLElement $vehicle) + protected function determineVehicleFuelType(SimpleXMLElement $vehicle): ?int { foreach ($vehicle->{'fillup-records'}->{'fillup-record'} as $fillup) { $sFuelType = (string)$fillup->{'fuel-type-id'}; @@ -710,5 +748,4 @@ protected function determineVehicleFuelType(SimpleXMLElement $vehicle) } return null; } - } diff --git a/lib/fuelioimporter/providers/drivvoprovider.class.php b/src/Providers/DrivvoProvider.php similarity index 67% rename from lib/fuelioimporter/providers/drivvoprovider.class.php rename to src/Providers/DrivvoProvider.php index be6bc1a..c4b31f5 100644 --- a/lib/fuelioimporter/providers/drivvoprovider.class.php +++ b/src/Providers/DrivvoProvider.php @@ -1,113 +1,117 @@ list of warnings */ + protected array $warnings = []; - const FUELLING_HEADERS = [ + private const FUELLING_HEADERS = [ '##Refuelling', '#Reabastecimiento' ]; - const SERVICE_HEADERS = [ + private const SERVICE_HEADERS = [ '##Service', '#Servicio' ]; - const EXPENSE_HEADERS = [ + private const EXPENSE_HEADERS = [ '##Expense', ]; - const TRUTHY = [ + private const TRUTHY = [ 'Si', 'Yes', 'Tak', 'Ja' ]; - const FALSY = [ - 'No', - 'Nie', - 'Nein' - ]; +// private const FALSY = [ +// 'No', +// 'Nie', +// 'Nein' +// ]; - public function getName() + public function getName(): string { return 'drivvo'; } - public function getTitle() + public function getTitle(): string { return 'Drivvo'; } - public function getOutputFileName() + public function getOutputFileName(): string { return $this->output_filename ?: $this->getTitle(); } - public function getStylesheetLocation() + public function getStylesheetLocation(): ?string { return null; } - public function setCarName($name) + public function setCarName($name): void { if (!empty($name)) { $this->output_filename = $name; } } - public function getCard() + public function getCard(): CardInterface { return new DrivvoCard(); } - public function getErrors() + public function getErrors(): array { - return array(); + return []; } - public function getWarnings() + public function getWarnings(): array { return $this->warnings; } - public function processFile(SplFileObject $in, $form_data) + public function processFile(SplFileObject $in, ?iterable $form_data): FuelioBackupBuilder { if ($in->isDir() || ($in->isFile() && !$in->isReadable())) { throw new InvalidFileFormatException('File is not readable'); } + if (!$form_data) { + throw new FormValidatorException('No form data received'); + } + if (!ini_get("auto_detect_line_endings")) { ini_set("auto_detect_line_endings", '1'); } - $this->dist_unit = $form_data['dist_unit']; - $this->fuel_unit = $form_data['fuel_unit']; + $this->dist_unit = (int) $form_data['dist_unit']; + $this->fuel_unit = (int) $form_data['fuel_unit']; // Configure reader $in->setFlags(SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE); @@ -149,7 +153,7 @@ public function processFile(SplFileObject $in, $form_data) * @param SplFileObject $in * @param FuelioBackupBuilder $out */ - protected function processVehicles(SplFileObject $in, FuelioBackupBuilder $out) + protected function processVehicles(SplFileObject $in, FuelioBackupBuilder $out): void { // Write out selected vehicle $out->writeVehicleHeader(); @@ -158,7 +162,7 @@ protected function processVehicles(SplFileObject $in, FuelioBackupBuilder $out) $vname = "Drivvo Car"; $this->output_filename .= $vname; $description="Imported"; - + $vehicle = new Vehicle( $vname, $description, // Use Notes as description @@ -169,7 +173,7 @@ protected function processVehicles(SplFileObject $in, FuelioBackupBuilder $out) $out->writeVehicle($vehicle); } - protected function processFillups(SplFileObject $in, FuelioBackupBuilder $out) + protected function processFillups(SplFileObject $in, FuelioBackupBuilder $out): void { $this->rewindToHeader($in, self::FUELLING_HEADERS); if ($in->eof()) { @@ -187,10 +191,10 @@ protected function processFillups(SplFileObject $in, FuelioBackupBuilder $out) $data = $in->fgetcsv(); if ($data && $data[0] !== '' && $data[0] > 0) { $entry = new FuelLogEntry(); - $entry->setDate($this->normalizeDate($data[1])); - $entry->setOdo((double)$data[0]); - $entry->setFuel((double)$data[5]); - $entry->setVolumePrice((double)$data[3]); + $entry->setDate($this->normalizeDate((string)$data[1])); + $entry->setOdo((int)$data[0]); + $entry->setFuel((float)$data[5]); + $entry->setVolumePrice((float)$data[3]); //Full fillup //In drivvo this is translated phrase - yes/no, si, no etc... @@ -198,18 +202,18 @@ protected function processFillups(SplFileObject $in, FuelioBackupBuilder $out) $fullfillup = $data[6]; $ifull = (int)in_array($fullfillup, self::TRUTHY, true); - $entry->setFullFillup($ifull); + $entry->setFullFillup((bool)$ifull); - $entry->setPrice($data[4]); - $entry->setNotes($data[18]); + $entry->setPrice((float)$data[4]); + $entry->setNotes((string)$data[18]); $out->writeFuelLog($entry); } - - } while (!$in->eof() && strpos($data[0], '#', 0) !== 0); + } while (!$in->eof() && $data && strpos($data[0] ?? '', '#', 0) !== 0); } - protected function processExpense(SplFileObject $in, FuelioBackupBuilder $out) { + protected function processExpense(SplFileObject $in, FuelioBackupBuilder $out): void + { // make,model,title,date,mileage,costs,note,recurrence $this->rewindToHeader($in, self::EXPENSE_HEADERS); if ($in->eof()) { @@ -218,27 +222,28 @@ protected function processExpense(SplFileObject $in, FuelioBackupBuilder $out) { $header = $in->fgetcsv(); - if (count($header) < 7) { + if (!$header || count($header) < 7) { $this->warnings[] = 'Skipping expenses as the header is not recognized.'; return; } do { $data = $in->fgetcsv(); - if ($data !== false && $data[0]!=='' && $data[0] > 0) { + if ($data && $data[0] !== '' && $data[0] > 0) { $cost = new Cost(); - $cost->setOdo($data[0]); - $cost->setDate($this->normalizeDate($data[1])); - $cost->setCost((double)$data[2]); + $cost->setOdo((int)$data[0]); + $cost->setDate($this->normalizeDate((string)$data[1])); + $cost->setCost((float)$data[2]); $cost->setCostCategoryId(2); - $cost->setTitle(trim($data[3])); - $cost->setNotes(trim($data[6])); + $cost->setTitle(trim($data[3] ?? '')); + $cost->setNotes(trim($data[6] ?? '')); $out->writeCost($cost); } - } while (!$in->eof() && strpos($data[0], '#', 0) !== 0); + } while (!$in->eof() && strpos($data[0] ?? '', '#', 0) !== 0); } - protected function processService(SplFileObject $in, FuelioBackupBuilder $out) { + protected function processService(SplFileObject $in, FuelioBackupBuilder $out): void + { // make,model,title,date,mileage,costs,note,recurrence $this->rewindToHeader($in, self::SERVICE_HEADERS); @@ -256,16 +261,15 @@ protected function processService(SplFileObject $in, FuelioBackupBuilder $out) { $data = $in->fgetcsv(); if ($data && $data[0] !== '' && $data[0] > 0 && count($header) === 6) { $cost = new Cost(); - $cost->setOdo($data[0]); - $cost->setDate($this->normalizeDate($data[1])); - $cost->setCost((double)$data[2]); + $cost->setOdo((int)$data[0]); + $cost->setDate($this->normalizeDate((string)$data[1])); + $cost->setCost((float)$data[2]); $cost->setCostCategoryId(1); - $cost->setTitle(trim($data[3])); - $cost->setNotes(trim($data[5])); + $cost->setTitle(trim($data[3] ?? '')); + $cost->setNotes(trim($data[5] ?? '')); $out->writeCost($cost); } - } while (!$in->eof() && strpos($data[0], '#', 0) !== 0); - + } while (!$in->eof() && strpos($data[0] ?? '', '#', 0) !== 0); } /** @@ -275,7 +279,7 @@ protected function processService(SplFileObject $in, FuelioBackupBuilder $out) { * * Currently it only detects dd/mm/YYYY format and turns it into YYYY-MM-DD */ - protected function normalizeDate($date): string + protected function normalizeDate(string $date): string { // Let's assume date could be written as X/Y/ZZZZ // Let's assume it's written with '/' as separator @@ -287,11 +291,12 @@ protected function normalizeDate($date): string return $date; //no-op } - protected function rewindToHeader(SplFileObject $file, array $headers) + /** @param array $headers */ + protected function rewindToHeader(SplFileObject $file, array $headers): void { $file->rewind(); do { $line = $file->fgetcsv(); - } while (!$file->eof() && !in_array($line[0], $headers, true)); + } while (!$file->eof() && !in_array($line[0] ?? '', $headers, true)); } -} \ No newline at end of file +} diff --git a/lib/fuelioimporter/providers/fuellogprovider.class.php b/src/Providers/FuellogProvider.php similarity index 72% rename from lib/fuelioimporter/providers/fuellogprovider.class.php rename to src/Providers/FuellogProvider.php index 26b8fc5..28212ce 100644 --- a/lib/fuelioimporter/providers/fuellogprovider.class.php +++ b/src/Providers/FuellogProvider.php @@ -1,71 +1,76 @@ > */ + protected array $vehicles = []; /** @var string Vehicle key used to import data */ - protected $vehicle_key = null; - /** @var int Vehicle index provided by user */ - protected $selected_vehicle = null; + protected string $vehicle_key = ''; + /** @var ?int Vehicle index provided by user */ + protected ?int $selected_vehicle = null; /** @var string|null Output filename */ - protected $output_filename = null; + protected ?string $output_filename = ''; /** @var string Delimiter used in CSV parser */ - private $delimiter = ','; + private string $delimiter = ','; - public function getName() + public function getName(): string { return 'fuellog'; } - public function getTitle() + public function getTitle(): string { return 'Fuel Log'; } - public function getOutputFileName() + public function getOutputFileName(): string { return $this->output_filename ?: $this->getTitle(); } - public function getStylesheetLocation() + public function getStylesheetLocation(): ?string { return null; } - public function setCarName($name) + public function setCarName($name): void { if (!empty($name)) { $this->output_filename = $name; } } - public function getCard() + public function getCard(): CardInterface { return new FuellogCard(); } - public function getErrors() + public function getErrors(): array { - return array(); + return []; } - public function getWarnings() + public function getWarnings(): array { - return array(); + return []; } - public function processFile(\SplFileObject $in, $form_data) + public function processFile(\SplFileObject $in, ?iterable $form_data): FuelioBackupBuilder { if ($in->isDir() || ($in->isFile() && !$in->isReadable())) { throw new InvalidFileFormatException(); @@ -88,7 +93,7 @@ public function processFile(\SplFileObject $in, $form_data) } // Import vehicles - $this->selected_vehicle = (int)$form_data['vehicle_id'] - 1; + $this->selected_vehicle = (int)($form_data['vehicle_id'] ?? 0) - 1; if ($this->selected_vehicle < 0) { $this->selected_vehicle = null; // Autoselect } @@ -103,15 +108,15 @@ public function processFile(\SplFileObject $in, $form_data) return $out; } - protected function detectDelimiter(\SplFileObject $in) + protected function detectDelimiter(\SplFileObject $in): void { $pos = $in->ftell(); $line = $in->fgetcsv($this->delimiter); - if ($line[0] !== 'make') { + if ($line && $line[0] !== 'make') { $this->delimiter = ';'; $in->fseek($pos); $line = $in->fgetcsv($this->delimiter); - if ($line[0] !== 'make') { + if ($line && $line[0] !== 'make') { throw new InvalidFileFormatException(); } } @@ -125,17 +130,17 @@ protected function detectDelimiter(\SplFileObject $in) * @throws InvalidFileFormatException * @throws InvalidUnitException */ - protected function processVehicles(\SplFileObject $in, FuelioBackupBuilder $out) + protected function processVehicles(\SplFileObject $in, FuelioBackupBuilder $out): void { // "make","model","note","distance","volume","consumption" $this->detectDelimiter($in); $header = $in->fgetcsv($this->delimiter); - if ($header[0] !== 'make' || count($header) < 6) { + if (!$header || $header[0] !== 'make' || count($header) < 6) { throw new InvalidFileFormatException(); } do { - if (!($line = $in->fgetcsv($this->delimiter)) || strpos($line[0], '#', 0) === 0) { + if (!($line = $in->fgetcsv($this->delimiter)) || strpos($line[0] ?? '', '#', 0) === 0) { break; } $key = $line[0] . '.' . $line[1]; @@ -146,7 +151,6 @@ protected function processVehicles(\SplFileObject $in, FuelioBackupBuilder $out) } $this->vehicles[$key] = $line; - } while (!$in->eof() && strpos($line[0], '#', 0) !== 0); // If user provided a valid index, select that vehicle @@ -172,23 +176,23 @@ protected function processVehicles(\SplFileObject $in, FuelioBackupBuilder $out) // Prepare Vehicle $data = $this->vehicles[$this->vehicle_key]; - $vname = trim($data[0]) . ' ' . trim($data[1]); // Build proper name: Make + Model; - $this->output_filename .= $vname; + $vname = trim((string)$data[0]) . ' ' . trim((string)$data[1]); // Build proper name: Make + Model; + $this->output_filename = $vname; $vehicle = new Vehicle( $vname, - $data[2], // Use Notes as description - $this->getDistanceUnit($data[3]), - $this->getVolumeUnit($data[4]), - $this->getConsumptionUnit($data[5]) + (string)$data[2], // Use Notes as description + $this->getDistanceUnit((string)$data[3]), + $this->getVolumeUnit((string)$data[4]), + $this->getConsumptionUnit((string)$data[5]) ); $out->writeVehicle($vehicle); } - protected function processFillups(\SplFileObject $in, FuelioBackupBuilder $out) + protected function processFillups(\SplFileObject $in, FuelioBackupBuilder $out): void { // "make","model","date","mileage","fuel","price","partial","note" $header = $in->fgetcsv($this->delimiter); - if ($header[0] !== 'make' || count($header) !== 8) { + if (!$header || $header[0] !== 'make' || count($header) !== 8) { throw new InvalidFileFormatException(); } @@ -201,31 +205,32 @@ protected function processFillups(\SplFileObject $in, FuelioBackupBuilder $out) } // Skip data for car not selected - $data_key = $data[0].'.'.$data[1]; + $data_key = (string)$data[0] . '.' . (string)$data[1]; if ($data_key !== $this->vehicle_key) { continue; } - + $entry = new FuelLogEntry(); - $entry->setDate($this->normalizeDate($data[2])); - $entry->setOdo((double)$data[3]); - $entry->setFuel((double)$data[4]); - $entry->setVolumePrice((double)$data[5]); + $entry->setDate($this->normalizeDate($data[2] ?? '')); + $entry->setOdo((int)$data[3]); + $entry->setFuel((float)$data[4]); + $entry->setVolumePrice((float)$data[5]); $entry->setFullFillup($data[6] !== '1'); - $entry->setNotes($data[7]); + $entry->setNotes((string)$data[7]); $out->writeFuelLog($entry); - } while (!$in->eof() && strpos($data[0], '#', 0) !== 0); + } while (!$in->eof() && strpos($data[0] ?? '', '#', 0) !== 0); } - protected function processCosts(\SplFileObject $in, FuelioBackupBuilder $out) { + protected function processCosts(\SplFileObject $in, FuelioBackupBuilder $out): void + { // make,model,title,date,mileage,costs,note,recurrence if ($in->eof()) { return; // Turns out costs are optional in file, so skip if we are at its end } $header = $in->fgetcsv($this->delimiter); - if ($header[0] !== 'make' || count($header) !== 8) { + if (!$header || $header[0] !== 'make' || count($header) !== 8) { throw new InvalidFileFormatException(); } @@ -243,24 +248,22 @@ protected function processCosts(\SplFileObject $in, FuelioBackupBuilder $out) { } // Skip data for car not selected - $data_key = $data[0].'.'.$data[1]; + $data_key = (string)$data[0] . '.' . (string)$data[1]; if ($data_key !== $this->vehicle_key) { continue; } $cost = new Cost(); - $cost->setCost((double)$data[5]); + $cost->setCost((float)$data[5]); $cost->setCostCategoryId(FuelioBackupBuilder::SAFE_CATEGORY_ID); - $cost->setDate($this->normalizeDate($data[3])); - $cost->setTitle(trim($data[2])); - $cost->setNotes(trim($data[6])); - $cost->setOdo($data[4]); - $cost->setReminderDate($this->convertCostReminder($this->normalizeDate($data[3]), $data[7])); - $cost->setRepeatMonths($this->convertRepeatMonths($data[7])); + $cost->setDate($this->normalizeDate($data[3] ?? '')); + $cost->setTitle(trim($data[2] ?? '')); + $cost->setNotes(trim($data[6] ?? '')); + $cost->setOdo((int)$data[4]); + $cost->setReminderDate($this->convertCostReminder($this->normalizeDate($data[3] ?? ''), $data[7] ?? '')); + $cost->setRepeatMonths($this->convertRepeatMonths($data[7] ?? '')); $out->writeCost($cost); - - } while (!$in->eof() && strpos($data[0], '#', 0) !== 0); - + } while (!$in->eof() && strpos($data[0] ?? '', '#', 0) !== 0); } /** @@ -270,46 +273,42 @@ protected function processCosts(\SplFileObject $in, FuelioBackupBuilder $out) { * * Currently it only detects dd/mm/YYYY format and turns it into YYYY-MM-DD */ - protected function normalizeDate($date) + protected function normalizeDate(string $date): string { // Let's assume date could be written as X/Y/ZZZZ - if (strlen($date) >= 8) { - // Let's assume it's written with '/' as separator - // and it's actually D/M/YYYY, as we have no way of detecting M/D/YYYY when day part is < 13 - if ($date[1] === '/' || $date[2] === '/') { - $parts = explode('/', $date, 3); - return $parts[2] . '-' . $parts[1] . '-' . $parts[0]; // YYYY-MM-DD - } + // Let's assume it's written with '/' as separator + // and it's actually D/M/YYYY, as we have no way of detecting M/D/YYYY when day part is < 13 + if (strlen($date) >= 8 && ($date[1] === '/' || $date[2] === '/')) { + $parts = explode('/', $date, 3); + return $parts[2] . '-' . $parts[1] . '-' . $parts[0]; // YYYY-MM-DD } + return $date; //no-op } - /** * Returns distance unit extracted from log - * @param $raw string * @return int Vehicle const * @throws InvalidUnitException */ - protected function getDistanceUnit($raw) + protected function getDistanceUnit(string $raw): int { /* Based on FuelLog's explanations.txt */ switch ((int)$raw) { - case 1 : return Vehicle::KILOMETERS; - case 2 : return Vehicle::MILES; - case 3 : throw new InvalidUnitException('Hours as distance units are not supported.'); - default : throw new InvalidUnitException('Unsupported distance unit: ' . substr($raw, 1, 10)); + case 1: return Vehicle::KILOMETERS; + case 2: return Vehicle::MILES; + case 3: throw new InvalidUnitException('Hours as distance units are not supported.'); + default: throw new InvalidUnitException('Unsupported distance unit: ' . substr($raw, 1, 10)); } } /** * Returns volume unit extracted from log - * @param $raw string * @return int Vehicle const * @throws InvalidUnitException */ - protected function getVolumeUnit($raw) + protected function getVolumeUnit(string $raw): int { /* Based on FuelLog's explanations.txt */ @@ -326,11 +325,10 @@ protected function getVolumeUnit($raw) /** * Returns consumption unit extracted from log - * @param $raw string * @return int Vehicle const * @throws InvalidUnitException */ - protected function getConsumptionUnit($raw) + protected function getConsumptionUnit(string $raw): int { /* Based on FuelLog's explanations.txt */ @@ -390,12 +388,11 @@ protected function getConsumptionUnit($raw) /** * Returns cost date moved according to recurrence type - * @param $sDate string current cost date - * @param $raw_recurrence string - * @return null|string New reminders date + * @param string $sDate current cost date + * @return string New reminders date * @throws InvalidUnitException */ - protected function convertCostReminder($sDate, $raw_recurrence) + protected function convertCostReminder(string $sDate, string $raw_recurrence): string { /* Based on FuelLog's explanations.txt */ @@ -404,7 +401,7 @@ protected function convertCostReminder($sDate, $raw_recurrence) } $new_date = new \DateTime($sDate); - switch((int)$raw_recurrence) { + switch ((int)$raw_recurrence) { case 0 : return Cost::EMPTY_DATE; // One-time case 1 : $step = 'P1D'; break; // Daily cost case 2 : $step = 'P1W'; break; // Weekly cost @@ -426,9 +423,9 @@ protected function convertCostReminder($sDate, $raw_recurrence) * @param $raw_recurrence string * @return int Number of recurrence months */ - protected function convertRepeatMonths($raw_recurrence) + protected function convertRepeatMonths(string $raw_recurrence): int { - switch((int)$raw_recurrence) { + switch ((int)$raw_recurrence) { case 3 : return 1; case 4 : return 2; case 5 : return 12; @@ -438,4 +435,4 @@ protected function convertRepeatMonths($raw_recurrence) default: return 0; // By default there is no monthly repetition } } -} \ No newline at end of file +} diff --git a/lib/fuelioimporter/providers/motostatprovider.class.php b/src/Providers/MotostatProvider.php similarity index 67% rename from lib/fuelioimporter/providers/motostatprovider.class.php rename to src/Providers/MotostatProvider.php index d1bdfd9..97d1391 100644 --- a/lib/fuelioimporter/providers/motostatprovider.class.php +++ b/src/Providers/MotostatProvider.php @@ -1,27 +1,34 @@ > */ + protected array $costs_data = []; + /** @var array */ + protected array $categories = []; + /** @var list */ + protected array $costs = []; + + public function __construct() + { // keys are normalized categories names (see findCategory) - $this->categories = array( + $this->categories = [ 'purchase_price' => new CostCategory(FuelioBackupBuilder::SAFE_CATEGORY_ID + 1, 'Purchase price'), 'tech_inspection' => new CostCategory(FuelioBackupBuilder::SAFE_CATEGORY_ID + 2, 'Tech inspection'), 'miscellaneous' => new CostCategory(FuelioBackupBuilder::SAFE_CATEGORY_ID + 3, 'Miscellaneous'), @@ -33,45 +40,53 @@ public function __construct() { 'inspection' => new CostCategory(FuelioBackupBuilder::SAFE_CATEGORY_ID + 8, 'inspection'), 'oil_change' => new CostCategory(FuelioBackupBuilder::SAFE_CATEGORY_ID + 9, 'Oil change'), 'insurance' => new CostCategory(31, 'Insurance') - ); + ]; } - public function getName() { + public function getName(): string + { return 'motostat'; } - public function getTitle() { + public function getTitle(): string + { return 'Motostat'; } - public function getOutputFileName() + public function getOutputFileName(): string { return $this->getTitle(); } - public function getCard() { + public function getCard(): CardInterface + { return new MotostatCard(); } - public function setCarName($name) { + public function setCarName($name): void + { if (!empty($name)) { $this->car_name = $name; } } - public function getErrors() { - return array(); + public function getErrors(): array + { + return []; } - public function getWarnings() { - return array(); + public function getWarnings(): array + { + return []; } - public function getStylesheetLocation() { + public function getStylesheetLocation(): ?string + { return null; } - public function processFile(\SplFileObject $in, $form_data) { + public function processFile(\SplFileObject $in, ?iterable $form_data): FuelioBackupBuilder + { if ($in->isDir() || ($in->isFile() && !$in->isReadable())) { throw new InvalidFileFormatException(); } @@ -83,14 +98,14 @@ public function processFile(\SplFileObject $in, $form_data) { // Verify file header $head = $in->fgetcsv(';'); - if (count($head) !== 23) { + if (!$head || count($head) !== 23) { throw new InvalidFileFormatException(); } // First column might have BOM - $hasBOM = substr($head[0], 0, 3) === pack('CCC', 239, 187, 191); + $hasBOM = strpos($head[0] ?? '', pack('CCC', 239, 187, 191)) === 0; if ($hasBOM) { - $head[0] = substr($head[0], 3); + $head[0] = substr($head[0] ?? '', 3); } if ($head[0] !== 'cost_id') { @@ -104,7 +119,7 @@ public function processFile(\SplFileObject $in, $form_data) { $this->processFuelings($in, $out); // process cost data and categories - $this->processCostData($in, $out); + $this->processCostData($out); // empty memory $this->cleanup(); @@ -112,7 +127,8 @@ public function processFile(\SplFileObject $in, $form_data) { } //cost_id;fueling_id;cost_type;date;fuel_id;gas_station_id;odometer;trip_odometer;quantity;cost;notes;fueling_type;tires;driving_style;route_motorway;route_country;route_city;bc_consumption;bc_avg_speed;ac;currency;fuel_name;gas_station_name - protected function processFuelings(SplFileObject $in, FuelioBackupBuilder $out) { + protected function processFuelings(SplFileObject $in, FuelioBackupBuilder $out): void + { $out->writeFuelLogHeader(); while (!$in->eof() && (($log = $in->fgetcsv(';')) !== false)) { // log only fillups, costs have dedicated category @@ -120,17 +136,18 @@ protected function processFuelings(SplFileObject $in, FuelioBackupBuilder $out) $this->costs_data[] = $log; continue; } - if (empty($log[1])) + if (empty($log[1])) { continue; // no fueling_id + } $entry = new FuelLogEntry(); - $entry->setDate($log[3]); - $entry->setOdo($log[6]); - $entry->setFuel($log[8]); + $entry->setDate((string)$log[3]); + $entry->setOdo((int)$log[6]); + $entry->setFuel((float)$log[8]); $entry->setFullFillup($log[11] === 'full'); - $entry->setPrice($log[9]); - $entry->setConsumption($log[18]); - $entry->setNotes($log[21]); + $entry->setPrice((float)$log[9]); + $entry->setConsumption((float)$log[18]); + $entry->setNotes((string)$log[21]); $out->writeFuelLog($entry); } @@ -139,16 +156,17 @@ protected function processFuelings(SplFileObject $in, FuelioBackupBuilder $out) /** * Initializes costs and cost_categories arrays */ - protected function processCostData(SplFileObject $in, FuelioBackupBuilder $out) { + protected function processCostData(FuelioBackupBuilder $out): void + { foreach ($this->costs_data as $line) { - $category = $this->findCategory($line[2]); + $category = $this->findCategory((string)$line[2]); $cost = new Cost(); $cost->setTitle($category->getName()); - $cost->setDate($line[3]); - $cost->setOdo($line[6]); + $cost->setDate((string)$line[3]); + $cost->setOdo((int)$line[6]); $cost->setCostCategoryId($category->getTypeId()); - $cost->setNotes($line[10]); - $cost->setCost($line[9]); + $cost->setNotes((string)$line[10]); + $cost->setCost((float)$line[9]); $this->costs[] = $cost; } @@ -169,9 +187,9 @@ protected function processCostData(SplFileObject $in, FuelioBackupBuilder $out) /** * Returns CostCategory instance * @param string $category_name Category name - * @return CostCategory */ - protected function findCategory($category_name) { + protected function findCategory(string $category_name): CostCategory + { // Normalize name $normalized_category_name = strtolower(trim($category_name)); @@ -182,13 +200,13 @@ protected function findCategory($category_name) { // Add new category $category = new CostCategory(count($this->categories) + 1, $category_name); - $this->categories[] = $category; + $this->categories[$normalized_category_name] = $category; return $category; } - protected function cleanup() { - $this->costs = array(); - $this->categories = array(); + protected function cleanup(): void + { + $this->costs = []; + $this->categories = []; } - } diff --git a/lib/fuelioimporter/uploaderror.class.php b/src/UploadError.php similarity index 70% rename from lib/fuelioimporter/uploaderror.class.php rename to src/UploadError.php index 3c9bdc8..fbe40bb 100644 --- a/lib/fuelioimporter/uploaderror.class.php +++ b/src/UploadError.php @@ -1,21 +1,24 @@ $file_array */ + public function __construct(array $file_array, int $code = 0, ?Throwable $previous = null) { - $message = 'File upload error'; switch ($file_array['error']) { case UPLOAD_ERR_CANT_WRITE: $message = 'Cannot store file on server: Write error'; break; case UPLOAD_ERR_FORM_SIZE: case UPLOAD_ERR_INI_SIZE: $message = 'Exceeded file size limit.'; break; case UPLOAD_ERR_NO_FILE: $message = 'No file sent'; break; case UPLOAD_ERR_PARTIAL: + default: $message = 'File upload error'; break; } parent::__construct($message, $code, $previous); } -} \ No newline at end of file +} diff --git a/src/Vehicle.php b/src/Vehicle.php new file mode 100644 index 0000000..76d704c --- /dev/null +++ b/src/Vehicle.php @@ -0,0 +1,200 @@ +setName($sName); + $this->setDescription($sDescription); + $this->setDistanceUnit($iDistance_unit); + $this->setFuelUnit($iFuel_unit); + $this->setConsumptionUnit($iConsumption_unit); + $this->setActive($bActive); + $this->setVIN(''); + $this->setInsurance(''); + $this->setPlate(''); + $this->setMake(''); + $this->setModel(''); + $this->setYear(''); + $this->setTankCount(0); + } + + public function setName(string $sName): void + { + $this->name = $sName; + } + + public function setDescription(string $sDescription): void + { + $this->description = $sDescription; + } + + public function setDistanceUnit(int $iDistance_unit): void + { + $this->distance_unit = $iDistance_unit; + } + + public function setFuelUnit(int $iFuel_unit): void + { + $this->fuel_unit = $iFuel_unit; + } + + public function setConsumptionUnit(int $iConsumption_unit): void + { + $this->consumption_unit = $iConsumption_unit; + } + + public function setVIN(string $sVin): void + { + $this->vin = $sVin; + } + + public function setInsurance(string $sInsurance): void + { + $this->insurance = $sInsurance; + } + + public function setPlate(string $sPlate): void + { + $this->plate = $sPlate; + } + + public function setMake(string $sMake): void + { + $this->make = $sMake; + } + + public function setModel(string $sModel): void + { + $this->model = $sModel; + } + + public function setYear(string $iYear): void + { + $this->year = $iYear; + } + + public function setTankCount(int $nTankCount): void + { + $this->tank_count = $nTankCount; + } + + public function setTankType(int $nIdx, ?int $nType): void + { + if (($nIdx !== 1 && $nIdx !== 2) || $nType === null) { + return; //no-op, only two tanks storable + } + + $this->{'tank_'.$nIdx.'_type'} = $nType; + } + + public function setActive(bool $bActive): void + { + $this->active = (int) $bActive; + } + + public function getData(): array + { + $vars = get_object_vars($this); + if (empty($vars['name'])) { + $vars['name'] = 'No Name'; + } + return array_values($vars); + } +} diff --git a/view/_append_analytics.php b/view/_append_analytics.php new file mode 100644 index 0000000..8dec4c3 --- /dev/null +++ b/view/_append_analytics.php @@ -0,0 +1,5 @@ + - +