From ad3eb1b3265abedf022ae8885de53cadd249ffaa Mon Sep 17 00:00:00 2001 From: Alpha Olomi Date: Wed, 15 Nov 2023 10:28:32 +0300 Subject: [PATCH] Refactor gitignore and gitattributes, update version and add documentation --- .gitattributes | 2 +- .github/FUNDING.yml | 1 + .github/workflows/run-phpunit-tests.yml | 37 + .gitignore | 14 +- CHANGELOG.md | 21 + README.md | 61 +- composer.json | 15 +- composer.lock | 1634 ----------------------- phpunit.xml.dist | 8 +- src/HashGenerator.php | 7 +- src/Mrz.php | 2 + src/MrzParser.php | 1049 +++++++++++++++ src/StringChecks.php | 6 +- tests/MrzParserTest.php | 42 + tests/MrzTest.php | 3 +- 15 files changed, 1243 insertions(+), 1659 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/run-phpunit-tests.yml create mode 100644 CHANGELOG.md delete mode 100644 composer.lock create mode 100644 src/MrzParser.php create mode 100644 tests/MrzParserTest.php diff --git a/.gitattributes b/.gitattributes index 0319a0c..6a3ba65 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,4 +12,4 @@ /.php-cs-fixer.dist.php export-ignore /art export-ignore /docs export-ignore - / UPGRADING.md export -ignore +/ UPGRADING.md export-ignore diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6515bec --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: alphaolomi diff --git a/.github/workflows/run-phpunit-tests.yml b/.github/workflows/run-phpunit-tests.yml new file mode 100644 index 0000000..6529be6 --- /dev/null +++ b/.github/workflows/run-phpunit-tests.yml @@ -0,0 +1,37 @@ +name: Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest, windows-latest] + php: [8.2, 8.1] + stability: [prefer-lowest, prefer-stable] + + name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: none + + - name: Setup problem matchers + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Install dependencies + run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction + + - name: Execute tests + run: vendor/bin/phpunit diff --git a/.gitignore b/.gitignore index cac762f..adc6804 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,12 @@ -/vendor/ -/.idea/ +.idea +.php_cs +.php_cs.cache +.phpunit.result.cache +build +composer.lock +coverage +docs +phpunit.xml +psalm.xml +vendor +.php-cs-fixer.cache diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..78ac50f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,21 @@ +# Changelog + +All notable changes to `alphalomi/mrz` will be documented in this file. + +### Unreleased + +- Update README.md +- Add `MrzParser` class +- Update +- Skip Accent Dialectic characters tests +- Add Testing GH Action +- Add FUNDING.yml + +### 1.0.1 - 2023-11-13 + +- Fix use of deprecated `utf8_decode` function + + +### 1.0.0 - 2023-11-13 + +- initial release diff --git a/README.md b/README.md index 827ab9b..b23a5ca 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# MRZ for PHP +# [MRZ]() for PHP Machine Readable Zone for PHP, originally based on [mrz](https://github.com/MrEko/mrz) @@ -12,23 +12,76 @@ composer require alphaolomi/mrz ## Usage +### Writing + ``` php +use Alphaolomi\Mrz\Mrz; + $mrz = new Mrz("I", "Tanzania", "D23148958907", date("dmy",strtotime("1999-10-14")), "M", date("dmy",strtotime("2030-12-31")), "TZA", "OLOMI", "ALPHA"); echo $mrz->TD1CodeGenerator(); ``` +### Reading + +``` php +use Alphaolomi\Mrz\MrzParser; + +$mrzParser = new MrzParser(); + +$mrzOcrString = 'PTUNKKONI<parse($mrzOcrString); + +print(json_encode($mrzData, JSON_PRETTY_PRINT)); +``` + +#### Reading Formats Supported + +1. TD1 + + ``` + I=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.17.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "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.17.1" - }, - "time": "2023-08-13T19:53:39+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "10.1.7", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "355324ca4980b8916c18b9db29f3ef484078f26e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e", - "reference": "355324ca4980b8916c18b9db29f3ef484078f26e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.1" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "10.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-10-04T15:34:17+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "4.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-31T06:24:48+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:56:09+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-31T14:07:24+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:57:52+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "10.4.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", - "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "10.4-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.2" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2023-10-26T07:21:45+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae", - "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:58:15+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:58:43+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:59:15+00:00" - }, - { - "name": "sebastian/comparator", - "version": "5.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-14T13:18:12+00:00" - }, - { - "name": "sebastian/complexity", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.10", - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-28T11:50:59+00:00" - }, - { - "name": "sebastian/diff", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.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", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-01T07:48:21+00:00" - }, - { - "name": "sebastian/environment", - "version": "6.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-04-11T05:39:26+00:00" - }, - { - "name": "sebastian/exporter", - "version": "5.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc", - "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-24T13:22:09+00:00" - }, - { - "name": "sebastian/global-state", - "version": "6.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4", - "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-07-19T07:19:23+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.10", - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-31T09:25:50+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:08:32+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:06:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:05:40+00:00" - }, - { - "name": "sebastian/type", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:10:45+00:00" - }, - { - "name": "sebastian/version", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-07T11:34:05+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2021-07-28T10:34:58+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": "^8.0" - }, - "platform-dev": [], - "plugin-api-version": "2.3.0" -} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d15fdb4..e417c0f 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -5,16 +5,16 @@ tests - + + ./src diff --git a/src/HashGenerator.php b/src/HashGenerator.php index 5c9bef2..09a340a 100644 --- a/src/HashGenerator.php +++ b/src/HashGenerator.php @@ -6,12 +6,13 @@ * Trait HashGenerator * * @package Alphaolomi\Mrz + * @version 1.0.0 */ trait HashGenerator { /** * Generates a hash for the document number. - * + * * Returns a hash string if the document number is to be hashed, * otherwise returns a placeholder character. * @@ -24,7 +25,7 @@ public function document_number_hash(): string /** * Generates a hash for the birth date. - * + * * @return string The hash string for the birth date. */ public function birth_date_hash(): string @@ -34,7 +35,7 @@ public function birth_date_hash(): string /** * Generates a hash for the expiry date. - * + * * @return string The hash string for the expiry date. */ public function expiry_date_hash(): string diff --git a/src/Mrz.php b/src/Mrz.php index d9d66b6..452d28b 100644 --- a/src/Mrz.php +++ b/src/Mrz.php @@ -8,6 +8,8 @@ * Class Mrz * * @package Alphaolomi\Mrz + * @version 1.0.0 + * @see https://en.wikipedia.org/wiki/Machine-readable_passport */ class Mrz { diff --git a/src/MrzParser.php b/src/MrzParser.php new file mode 100644 index 0000000..2149d38 --- /dev/null +++ b/src/MrzParser.php @@ -0,0 +1,1049 @@ +countries = + array( + "AFG" => "Afghanistan", + "ALB" => "Albania", + "DZA" => "Algeria", + "ASM" => "American Samoa", + "AND" => "Andorra", + "AGO" => "Angola", + "AIA" => "Anguilla", + "ATA" => "Antarctica", + "ATG" => "Antigua and Barbuda", + "ARG" => "Argentina", + "ARM" => "Armenia", + "ABW" => "Aruba", + "AUS" => "Australia", + "AUT" => "Austria", + "AZE" => "Azerbaijan", + "BHS" => "Bahamas (the)", + "BHR" => "Bahrain", + "BGD" => "Bangladesh", + "BRB" => "Barbados", + "BLR" => "Belarus", + "BEL" => "Belgium", + "BLZ" => "Belize", + "BEN" => "Benin", + "BMU" => "Bermuda", + "BTN" => "Bhutan", + "BOL" => "Bolivia (Plurinational State of)", + "BES" => "Bonaire, Sint Eustatius and Saba", + "BIH" => "Bosnia and Herzegovina", + "BWA" => "Botswana", + "BVT" => "Bouvet Island", + "BRA" => "Brazil", + "IOT" => "British Indian Ocean Territory (the)", + "BRN" => "Brunei Darussalam", + "BGR" => "Bulgaria", + "BFA" => "Burkina Faso", + "BDI" => "Burundi", + "CPV" => "Cabo Verde", + "KHM" => "Cambodia", + "CMR" => "Cameroon", + "CAN" => "Canada", + "CYM" => "Cayman Islands (the)", + "CAF" => "Central African Republic (the)", + "TCD" => "Chad", + "CHL" => "Chile", + "CHN" => "China", + "CXR" => "Christmas Island", + "CCK" => "Cocos (Keeling) Islands (the)", + "COL" => "Colombia", + "COM" => "Comoros (the)", + "COD" => "Congo (the Democratic Republic of the)", + "COG" => "Congo (the)", + "COK" => "Cook Islands (the)", + "CRI" => "Costa Rica", + "HRV" => "Croatia", + "CUB" => "Cuba", + "CUW" => "Curaçao", + "CYP" => "Cyprus", + "CZE" => "Czechia", + "CIV" => "Côte d'Ivoire", + "DNK" => "Denmark", + "DJI" => "Djibouti", + "DMA" => "Dominica", + "DOM" => "Dominican Republic (the)", + "ECU" => "Ecuador", + "EGY" => "Egypt", + "SLV" => "El Salvador", + "GNQ" => "Equatorial Guinea", + "ERI" => "Eritrea", + "EST" => "Estonia", + "SWZ" => "Eswatini", + "ETH" => "Ethiopia", + "FLK" => "Falkland Islands (the) [Malvinas]", + "FRO" => "Faroe Islands (the)", + "FJI" => "Fiji", + "FIN" => "Finland", + "FRA" => "France", + "GUF" => "French Guiana", + "PYF" => "French Polynesia", + "ATF" => "French Southern Territories (the)", + "GAB" => "Gabon", + "GMB" => "Gambia (the)", + "GEO" => "Georgia", + "DEU" => "Germany", + "GHA" => "Ghana", + "GIB" => "Gibraltar", + "GRC" => "Greece", + "GRL" => "Greenland", + "GRD" => "Grenada", + "GLP" => "Guadeloupe", + "GUM" => "Guam", + "GTM" => "Guatemala", + "GGY" => "Guernsey", + "GIN" => "Guinea", + "GNB" => "Guinea-Bissau", + "GUY" => "Guyana", + "HTI" => "Haiti", + "HMD" => "Heard Island and McDonald Islands", + "VAT" => "Holy See (the)", + "HND" => "Honduras", + "HKG" => "Hong Kong", + "HUN" => "Hungary", + "ISL" => "Iceland", + "IND" => "India", + "IDN" => "Indonesia", + "IRN" => "Iran (Islamic Republic of)", + "IRQ" => "Iraq", + "IRL" => "Ireland", + "IMN" => "Isle of Man", + "ISR" => "Israel", + "ITA" => "Italy", + "JAM" => "Jamaica", + "JPN" => "Japan", + "JEY" => "Jersey", + "JOR" => "Jordan", + "KAZ" => "Kazakhstan", + "KEN" => "Kenya", + "KIR" => "Kiribati", + "PRK" => "Korea (the Democratic People's Republic of)", + "KOR" => "Korea (the Republic of)", + "KWT" => "Kuwait", + "KGZ" => "Kyrgyzstan", + "LAO" => "Lao People's Democratic Republic (the)", + "LVA" => "Latvia", + "LBN" => "Lebanon", + "LSO" => "Lesotho", + "LBR" => "Liberia", + "LBY" => "Libya", + "LIE" => "Liechtenstein", + "LTU" => "Lithuania", + "LUX" => "Luxembourg", + "MAC" => "Macao", + "MDG" => "Madagascar", + "MWI" => "Malawi", + "MYS" => "Malaysia", + "MDV" => "Maldives", + "MLI" => "Mali", + "MLT" => "Malta", + "MHL" => "Marshall Islands (the)", + "MTQ" => "Martinique", + "MRT" => "Mauritania", + "MUS" => "Mauritius", + "MYT" => "Mayotte", + "MEX" => "Mexico", + "FSM" => "Micronesia (Federated States of)", + "MDA" => "Moldova (the Republic of)", + "MCO" => "Monaco", + "MNG" => "Mongolia", + "MNE" => "Montenegro", + "MSR" => "Montserrat", + "MAR" => "Morocco", + "MOZ" => "Mozambique", + "MMR" => "Myanmar", + "NAM" => "Namibia", + "NRU" => "Nauru", + "NPL" => "Nepal", + "NLD" => "Netherlands (the)", + "NCL" => "New Caledonia", + "NZL" => "New Zealand", + "NIC" => "Nicaragua", + "NER" => "Niger (the)", + "NGA" => "Nigeria", + "NIU" => "Niue", + "NFK" => "Norfolk Island", + "MKD" => "North Macedonia", + "MNP" => "Northern Mariana Islands (the)", + "NOR" => "Norway", + "OMN" => "Oman", + "PAK" => "Pakistan", + "PLW" => "Palau", + "PSE" => "Palestine, State of", + "PAN" => "Panama", + "PNG" => "Papua New Guinea", + "PRY" => "Paraguay", + "PER" => "Peru", + "PHL" => "Philippines (the)", + "PCN" => "Pitcairn", + "POL" => "Poland", + "PRT" => "Portugal", + "PRI" => "Puerto Rico", + "QAT" => "Qatar", + "ROU" => "Romania", + "RUS" => "Russian Federation (the)", + "RWA" => "Rwanda", + "REU" => "Réunion", + "BLM" => "Saint Barthélemy", + "SHN" => "Saint Helena, Ascension and Tristan da Cunha", + "KNA" => "Saint Kitts and Nevis", + "LCA" => "Saint Lucia", + "MAF" => "Saint Martin (French part)", + "SPM" => "Saint Pierre and Miquelon", + "VCT" => "Saint Vincent and the Grenadines", + "WSM" => "Samoa", + "SMR" => "San Marino", + "STP" => "Sao Tome and Principe", + "SAU" => "Saudi Arabia", + "SEN" => "Senegal", + "SRB" => "Serbia", + "SYC" => "Seychelles", + "SLE" => "Sierra Leone", + "SGP" => "Singapore", + "SXM" => "Sint Maarten (Dutch part)", + "SVK" => "Slovakia", + "SVN" => "Slovenia", + "SLB" => "Solomon Islands", + "SOM" => "Somalia", + "ZAF" => "South Africa", + "SGS" => "South Georgia and the South Sandwich Islands", + "SSD" => "South Sudan", + "ESP" => "Spain", + "LKA" => "Sri Lanka", + "SDN" => "Sudan (the)", + "SUR" => "Suriname", + "SJM" => "Svalbard and Jan Mayen", + "SWE" => "Sweden", + "CHE" => "Switzerland", + "SYR" => "Syrian Arab Republic (the)", + "TWN" => "Taiwan (Province of China)", + "TJK" => "Tajikistan", + "TZA" => "Tanzania, the United Republic of", + "THA" => "Thailand", + "TLS" => "Timor-Leste", + "TGO" => "Togo", + "TKL" => "Tokelau", + "TON" => "Tonga", + "TTO" => "Trinidad and Tobago", + "TUN" => "Tunisia", + "TUR" => "Turkey", + "TKM" => "Turkmenistan", + "TCA" => "Turks and Caicos Islands (the)", + "TUV" => "Tuvalu", + "UGA" => "Uganda", + "UKR" => "Ukraine", + "ARE" => "United Arab Emirates (the)", + "GBR" => "United Kingdom of Great Britain and Northern Ireland (the)", + "UMI" => "United States Minor Outlying Islands (the)", + "USA" => "United States of America (the)", + "URY" => "Uruguay", + "UZB" => "Uzbekistan", + "VUT" => "Vanuatu", + "VEN" => "Venezuela (Bolivarian Republic of)", + "VNM" => "Viet Nam", + "VGB" => "Virgin Islands (British)", + "VIR" => "Virgin Islands (U.S.)", + "WLF" => "Wallis and Futuna", + "ESH" => "Western Sahara*", + "YEM" => "Yemen", + "ZMB" => "Zambia", + "ZWE" => "Zimbabwe", + "ALA" => "Åland Islands" + ); + + $this->non_ISO_3166 = + array( + "XBA" => "African Development Bank", + "XIM" => "African Export–Import Bank", + "XCC" => "Caribbean Community", + /* Caribbean Community or one of its emissaries */ + "XCO" => "Common Market for Eastern and Southern Africa", + "XEC" => "Economic Community of West African States", + "EUE" => "European Union", + "D" => "Germany", + "XPO" => "International Criminal Police Organization (Interpol)", + "IMO" => "International Maritime Organisation", + "RKS" => "Kosovo", + "XOM" => "Sovereign Military Order of Malta", + "WSA" => "World Service Authority World Passport" + ); + + $this->united_nations = + array( + "UNK" => "United Nations Interim Administration Mission in Kosovo (UNMIK)", + /* Travel document issued by the United Nations Interim Administration Mission in Kosovo (UNMIK) for Resident of Kosovo */ + "UNO" => "United Nations Organization Official", + "UNA" => "United Nations Organization Specialized Agency Official", + "XAA" => "Stateless (per Article 1 of 1954 convention)", + /* Stateless person, as per the 1954 Convention Relating to the Status of Stateless Persons */ + "XXB" => "Refugee (per Article 1 of 1951 convention, amended by 1967 protocol)", + /* Refugee, as per the 1951 Convention Relating to the Status of Refugees */ + "XXC" => "Refugee (non-convention)", + "XXX" => "Unspecified Nationality / Unknown", + "UTO" => "Utopian" + ); + + $this->british_nationals = + array( + "GBR" => "United Kingdom of Great Britain and Northern Ireland Citizen", + /* British National (Proper) */ + "GBD" => "United Kingdom of Great Britain and Northern Ireland Dependent Territories Citizen", + /* British Overseas Territories Citizen (BOTC) */ + "GBN" => "United Kingdom of Great Britain and Northern Ireland National (Overseas)", + /* British National (Overseas) */ + "GBO" => "United Kingdom of Great Britain and Northern Ireland Oversees Citizen", + /* British Overseas Citizen */ + "GBP" => "United Kingdom of Great Britain and Northern Ireland Protected Person", + /* British Protected Person */ + "GBS" => "United Kingdom of Great Britain and Northern Ireland Subject", + /* British Subject */ + ); + + $this->countries = array_merge( + $this->countries, + $this->non_ISO_3166, + $this->united_nations, + $this->british_nationals + ); + + // # Check Digit Weight + $this->checkDigitValues = + array( + "<" => "0", + "A" => "10", "B" => "11", "C" => "12", "D" => "13", "E" => "14", + "F" => "15", "G" => "16", "H" => "17", "I" => "18", "J" => "19", + "K" => "20", "L" => "21", "M" => "22", "N" => "23", "O" => "24", + "P" => "25", "Q" => "26", "R" => "27", "S" => "28", "T" => "29", + "U" => "30", "V" => "31", "W" => "32", "X" => "33", "Y" => "34", + "Z" => "35" + ); + } + + private function returnCountryName($str) + { + return (array_key_exists($str, $this->countries)) ? $this->countries[$str] : "Unknown Country"; + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + private function returnCheckDigitValues($str) + { + return $this->checkDigitValues[$str]; + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + private function stripPadding($str) + { + + if (!$str || $str == null) { + + return $str; + } else { + + $return = trim(preg_replace('/stripPadding($names[0]); + $name['firstName'] = $this->stripPadding($names[1]); + return $name; + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + /** + * Get the full date value for the shortened date specified and also take + * into account the 19xx/20xx centennial into account when calculating the + * correct year value to return. + * + * t = 1 > Date of Birth + * t = 2 > Expiry Date + */ + + private function getFullDate($str, $t) + { + + $d = date('YY') + 11; // Documents are valid for 10 years and 9 months max after being issued. + $centennial = substr($d, 2, 2); + + $year = ($t == 1) ? + ((substr($str, 0, 2) > $centennial) ? '19' . substr($str, 0, 2) : '20' . substr($str, 0, 2)) + : ((substr($str, 0, 2) < $centennial) ? '19' . substr($str, 0, 2) : '20' . substr($str, 0, 2)); + + $date = substr($str, 4, 2) . '/' . substr($str, 2, 2) . '/' . $year; + + return $date; + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + /** + * Get the gender/sex of the person using the sex/gender character specified. + * + * @param str The gender/sex character + * @returns {{abbr: *, full: *}} - The abbreviation (character) and the full gender/sex text + * @private + */ + private function getSex($str) + { + + if ($str == 'M') { + $sex['abbr'] = 'M'; + $sex['full'] = 'Male'; + } else if ($str == 'F') { + $sex['abbr'] = 'F'; + $sex['full'] = 'Female'; + } else { + $sex['abbr'] = 'X'; + $sex['full'] = 'Unspecified'; + } + + return $sex; + } + + /** + * Get the country/territory name using the ISO code of the nationality value. + * + * @param str The ISO code of the Country/Territory + * @returns {{abbr: *, full: *}} - The abbreviation (ISO) and the full country/territory name + * @private + */ + private function getCountry($str) + { + + try { + + $region['abbr'] = $str; + $region['full'] = $this->returnCountryName($str); + return $region; + } catch (Exception $err) { + + return ('Invalid region'); + } + } + + /** + * Performs the verification of the string to match the check digit. + * This is used to check the validity of the value of the string to check for any counter-fit issues. + * + * @param str The value to perform the validation on + * @param digit The value of the check digit which is to be compared to the result of the algorithm + * @returns {boolean} Whether the computed result of the value specified matches the check digit value + * @private + */ + private function checkDigitVerify($str, $digit) + { + + $numbers = array(); + $weighting = array(7, 3, 1); + + for ($i = 0; $i < strlen($str); $i++) { + + if (preg_match('/[A-Za-z<]/', $str[$i], $match)) { + array_push($numbers, $this->returnCheckDigitValues($str[$i])); + } else { + array_push($numbers, (int)$str[$i]); + } + } + + $curWeight = 0; + $total = 0; + + for ($j = 0; $j < count($numbers); $j++) { + $total += $numbers[$j] * $weighting[$curWeight]; + $curWeight++; + if ($curWeight == 3) { + $curWeight = 0; + } + } + + return $total % 10 == $digit; + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + /** + * Parser of Passport MRZ strings. + * + * Size 3 Machine Readable Travel Documents (TD3) + * Specified in Part 4 of ICAO Doc 9303 + * https://www.icao.int/publications/pages/publication.aspx?docnum=9303 + * + * The machine readable zone on a passport has + * 2 lines, each consisting of 44 characters. Below a reference to the format: + * 01 - 02: Document code + * 03 - 05: Issuing state or organization + * 06 - 44: Names + * 45 - 53: Document number + * 54 - 54: Check digit + * 55 - 57: Nationality + * 58 - 63: Date of birth + * 64 - 64: Check digit + * 65 - 65: Sex + * 66 - 71: Date of expiry + * 72 - 72: Check digit + * 73 - 86: Personal number + * 87 - 87: Check digit + * 88 - 88: Check digit + */ + + private function parseMRZPassport($mrz) + { + + try { + + $documentCode = substr($mrz, 0, 1); + + $issuerOrg = $this->getCountry($this->stripPadding(substr($mrz, 2, 3))); + + $names = $this->getNames(substr($mrz, 5, 39)); + + $documentNumberRaw = substr($mrz, 44, 9); + $documentNumber = $this->stripPadding($documentNumberRaw); + $checkDigit1 = substr($mrz, 53, 1); + $checkDigitVerify1 = $this->checkDigitVerify($documentNumberRaw, $checkDigit1); + + $nationality = $this->getCountry($this->stripPadding(substr($mrz, 54, 3))); + + $dobRaw = substr($mrz, 57, 6); + $dob = $this->getFullDate($this->stripPadding($dobRaw), 1); + $checkDigit2 = substr($mrz, 63, 1); + $checkDigitVerify2 = $this->checkDigitVerify($dobRaw, $checkDigit2); + + $sex = $this->getSex($this->stripPadding(substr($mrz, 64, 1))); + $expiryRaw = substr($mrz, 65, 6); + $expiry = $this->getFullDate($this->stripPadding($expiryRaw), 2); + + $checkDigit3 = $this->stripPadding(substr($mrz, 71, 1)); + $checkDigitVerify3 = $this->checkDigitVerify($expiryRaw, $checkDigit3); + + $personalNumberRaw = substr($mrz, 72, 14); + $personalNumber = $this->stripPadding($personalNumberRaw); + $checkDigit4 = substr($mrz, 86, 1); + $checkDigitVerify4 = $this->checkDigitVerify($personalNumberRaw, $checkDigit4); + + $finalCheckDigitRaw = $documentNumberRaw . $checkDigit1 . + $dobRaw . $checkDigit2 . + $expiryRaw . $checkDigit3 . + $personalNumberRaw . $checkDigit4; + $checkDigit5 = substr($mrz, 87, 1); + $checkDigitVerify5 = $this->checkDigitVerify($finalCheckDigitRaw, $checkDigit5); + + $passport['documentCode'] = substr($mrz, 0, 1); + $passport['documentType'] = ($documentCode == 'P') ? 'PASSPORT' : 'UNKNOWN'; + $passport['issuerOrg'] = $issuerOrg; + $passport['names'] = $names; + $passport['documentNumber'] = $documentNumber; + $passport['nationality'] = $nationality; + $passport['dob'] = $dob; + $passport['sex'] = $sex; + $passport['expiry'] = $expiry; + $passport['personalNumber'] = $personalNumber; + + $passport['checkDigit']['documentNumber']['checkDigit1'] = $checkDigit1; + $passport['checkDigit']['documentNumber']['checkDigitVerify1'] = $checkDigitVerify1; + + $passport['checkDigit']['dob']['checkDigit2'] = $checkDigit2; + $passport['checkDigit']['dob']['checkDigitVerify2'] = $checkDigitVerify2; + + $passport['checkDigit']['expiry']['checkDigit3'] = $checkDigit3; + $passport['checkDigit']['expiry']['checkDigitVerify3'] = $checkDigitVerify3; + + $passport['checkDigit']['personalNumber']['checkDigit4'] = $checkDigit4; + $passport['checkDigit']['personalNumber']['checkDigitVerify4'] = $checkDigitVerify4; + + $passport['checkDigit']['finalCheck']['checkDigit5'] = $checkDigit5; + $passport['checkDigit']['finalCheck']['checkDigitVerify5'] = $checkDigitVerify5; + + $passport['is_valid'] = $checkDigitVerify1 && $checkDigitVerify2 && $checkDigitVerify3 && $checkDigitVerify4 && $checkDigitVerify5; + + return $passport; + } catch (Exception $e) { + $error['error'] = 'Details parsing failed. ' . $e; + return $error; + } + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + /** + * Parser of ID-1 MRZ strings. + * + * Size 1 Machine Readable Travel Documents (TD1) + * Specified in Part 5 of ICAO Doc 9303 + * https://www.icao.int/publications/pages/publication.aspx?docnum=9303 + * + * The machine readable zone on a identity card has + * 3 lines, each consisting of 30 characters. Below a reference to the format: + * 01 - 01: Document code + * 02 - 02: Document type + * 03 - 05: Issuing state or organization + * 06 - 14: Document number + * 15 - 15: Check digit + * 16 - 30: Optional data + * 31 - 36: Date of birth + * 37 - 37: Check digit + * 38 - 38: Sex + * 39 - 44: Date of expiry + * 45 - 45: Check digit + * 46 - 48: Nationality + * 49 - 59: Optional data 2 + * 60 - 60: Composite check digit + * 61 - 90: Name + * + */ + + private function parseMrzID1($mrz) + { + + try { + + $documentCode1 = substr($mrz, 0, 1); + $documentCode2 = substr($mrz, 1, 1); + + $issuerOrg = $this->getCountry($this->stripPadding(substr($mrz, 2, 3))); + + $documentNumberRaw = substr($mrz, 5, 9); + $documentNumber = $this->stripPadding($documentNumberRaw); + $checkDigit1 = substr($mrz, 14, 1); + $checkDigitVerify1 = $this->checkDigitVerify($documentNumberRaw, $checkDigit1); + + $optionalData = $this->stripPadding(substr($mrz, 15, 15)); + + $dobRaw = substr($mrz, 30, 6); + $dob = $this->getFullDate($this->stripPadding($dobRaw), 1); + $checkDigit2 = substr($mrz, 36, 1); + $checkDigitVerify2 = $this->checkDigitVerify($dobRaw, $checkDigit2); + + $sex = $this->getSex($this->stripPadding(substr($mrz, 37, 1))); + + $expiryRaw = substr($mrz, 38, 6); + $expiry = $this->getFullDate($this->stripPadding($expiryRaw), 2); + + $checkDigit3 = $this->stripPadding(substr($mrz, 44, 1)); + $checkDigitVerify3 = $this->checkDigitVerify($expiryRaw, $checkDigit3); + + $nationality = $this->getCountry($this->stripPadding(substr($mrz, 45, 3))); + + $optionalData2 = $this->stripPadding(substr($mrz, 48, 11)); + + $finalCheckDigitRaw = $documentNumberRaw . $checkDigit1 . $dobRaw . $checkDigit2 . $expiryRaw . $checkDigit3 . $optionalData2; + $checkDigit4 = substr($mrz, 59, 1); + $checkDigitVerify4 = $this->checkDigitVerify($finalCheckDigitRaw, $checkDigit4); + + $names = $this->getNames(substr($mrz, 60, 30)); + + $id['documentCode'] = substr($mrz, 0, 1); + $id['documentType'] = ($documentCode1 == 'I') ? 'ID-1' : 'UNKNOWN'; + $id['documentType'] .= ($documentCode2 == 'R') ? ' Residence Card' : ''; + $id['issuerOrg'] = $issuerOrg; + $id['names'] = $names; + $id['documentNumber'] = $documentNumber; + $id['optionalData'] = $optionalData; + $id['optionalData2'] = $optionalData2; + $id['nationality'] = $nationality; + $id['dob'] = $dob; + $id['sex'] = $sex; + $id['expiry'] = $expiry; + + $id['checkDigit']['documentNumber']['checkDigit1'] = $checkDigit1; + $id['checkDigit']['documentNumber']['checkDigitVerify1'] = $checkDigitVerify1; + + $id['checkDigit']['dob']['checkDigit2'] = $checkDigit2; + $id['checkDigit']['dob']['checkDigitVerify2'] = $checkDigitVerify2; + + $id['checkDigit']['expiry']['checkDigit3'] = $checkDigit3; + $id['checkDigit']['expiry']['checkDigitVerify3'] = $checkDigitVerify3; + + $id['checkDigit']['finalCheck']['checkDigit4'] = $checkDigit4; + $id['checkDigit']['finalCheck']['checkDigitVerify4'] = $checkDigitVerify4; + + $id['is_valid'] = $checkDigitVerify1 && $checkDigitVerify2 && $checkDigitVerify3 && $checkDigitVerify4; + + return $id; + } catch (Exception $e) { + $error['error'] = 'Details parsing failed. ' . $e; + return $error; + } + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + /** + * Parser of ID-2 (TD2) Travel Document MRZ strings. + * + * Size 2 Machine Readable Travel Documents (TD2) + * Specified in Part6 of ICAO Doc 9303 + * https://www.icao.int/publications/pages/publication.aspx?docnum=9303 + * + * TD2 MRZ is almost the same as Visa MRV-B, only difference being + * that TD2 has a final composite checksum + * + * The machine readable zone on a visa has + * 2 lines, each consisting of 36 characters. Below a reference to the format: + * 01 - 02: Document code + * 03 - 05: Issuing state or organization + * 06 - 36: Names + * 37 - 45: Document Number + * 46 - 46: Check digit + * 47 - 49: Nationality + * 50 - 55: Date of birth + * 56 - 56: Check digit + * 57 - 57: Sex + * 58 - 63: Date of expiry + * 64 - 64: Check digit + * 65 - 71: Optional data + * 72 - 72: Composite check digit + */ + + private function parseMrzID2($mrz) + { + + try { + + $documentCode1 = substr($mrz, 0, 1); + $documentCode2 = substr($mrz, 1, 1); + + $issuerOrg = $this->getCountry($this->stripPadding(substr($mrz, 2, 3))); + + $names = $this->getNames(substr($mrz, 5, 31)); + + $documentNumberRaw = substr($mrz, 36, 9); + $documentNumber = $this->stripPadding($documentNumberRaw); + $checkDigit1 = substr($mrz, 45, 1); + $checkDigitVerify1 = $this->checkDigitVerify($documentNumberRaw, $checkDigit1); + + $nationalityRaw = substr($mrz, 46, 3); + $nationality = $this->getCountry($this->stripPadding($nationalityRaw)); + + $dobRaw = substr($mrz, 49, 6); + $dob = $this->getFullDate($this->stripPadding($dobRaw), 1); + $checkDigit2 = substr($mrz, 55, 1); + $checkDigitVerify2 = $this->checkDigitVerify($dobRaw, $checkDigit2); + + $sex = $this->getSex($this->stripPadding(substr($mrz, 56, 1))); + + $expiryRaw = substr($mrz, 57, 6); + $expiry = $this->getFullDate($this->stripPadding($expiryRaw), 2); + + $checkDigit3 = $this->stripPadding(substr($mrz, 63, 1)); + $checkDigitVerify3 = $this->checkDigitVerify($expiryRaw, $checkDigit3); + + $optionalData = $this->stripPadding(substr($mrz, 64, 7)); + + $finalCheckDigitRaw = $documentNumberRaw . $checkDigit1 . $dobRaw . $checkDigit2 . $expiryRaw . $checkDigit3 . $optionalData; + $checkDigit4 = substr($mrz, 71, 1); + $checkDigitVerify4 = $this->checkDigitVerify($finalCheckDigitRaw, $checkDigit4); + + $id['documentCode'] = substr($mrz, 0, 1); + $id['documentType'] = ($documentCode1 == 'I') ? 'ID-2' : 'UNKNOWN'; + $id['documentType'] .= ($documentCode2 == 'R') ? ' Residence Card' : ''; + $id['issuerOrg'] = $issuerOrg; + $id['names'] = $names; + + $id['documentNumber'] = $documentNumber; + $id['checkDigit']['documentNumber']['checkDigit1'] = $checkDigit1; + $id['checkDigit']['documentNumber']['checkDigitVerify1'] = $checkDigitVerify1; + + $id['nationality'] = $nationality; + + $id['dob'] = $dob; + $id['checkDigit']['passport']['checkDigit2'] = $checkDigit2; + $id['checkDigit']['passport']['checkDigitVerify2'] = $checkDigitVerify2; + + $id['sex'] = $sex; + + $id['expiry'] = $expiry; + $id['checkDigit']['expiry']['checkDigit3'] = $checkDigit3; + $id['checkDigit']['expiry']['checkDigitVerify3'] = $checkDigitVerify3; + + $id['optionalData'] = $optionalData; + + $id['checkDigit']['finalCheck']['checkDigit4'] = $checkDigit4; + $id['checkDigit']['finalCheck']['checkDigitVerify4'] = $checkDigitVerify4; + + $id['is_valid'] = $checkDigitVerify1 && $checkDigitVerify2 && $checkDigitVerify3 && $checkDigitVerify4; + + return $id; + } catch (Exception $e) { + $error['error'] = 'Details parsing failed. ' . $e; + return $error; + } + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + /** + * Parser for French ID cards. + * + * They have 2 rows of 36 characters, which makes them similar to the ID-2 and MRV-B formats, + * but the position of the fields is very different + * https://www.dcode.fr/french-id-card + * + */ + private function parseMrzFrenchID($mrz) + { + try { + $documentCode1 = substr($mrz, 0, 1); + $documentCode2 = substr($mrz, 1, 1); + + $issuerOrg = $this->getCountry($this->stripPadding(substr($mrz, 2, 3))); + + $documentNumberRaw = substr($mrz, 36, 12); + $documentNumber = $this->stripPadding($documentNumberRaw); + $checkDigit1 = substr($mrz, 48, 1); + $checkDigitVerify1 = $this->checkDigitVerify($documentNumberRaw, $checkDigit1); + + $optionalData = $this->stripPadding(substr($mrz, 30, 6)); + + $dobRaw = substr($mrz, 63, 6); + $dob = $this->getFullDate($this->stripPadding($dobRaw), 1); + $checkDigit2 = substr($mrz, 69, 1); + $checkDigitVerify2 = $this->checkDigitVerify($dobRaw, $checkDigit2); + + $sex = $this->getSex($this->stripPadding(substr($mrz, 70, 1))); + + /* + // data do not exist + + $expiryRaw = substr($mrz, 38, 6); + $expiry = $this->getFullDate( $this->stripPadding( $expiryRaw ), 2 ); + */ + + $checkDigit3 = null; //data missing + //$checkDigitVerify3 = $this->checkDigitVerify( $expiryRaw, $checkDigit3 ); + $checkDigitVerify3 = 1; //data missing, set to 1 + + $nationality = $this->getCountry($this->stripPadding(substr($mrz, 2, 3))); + + //$optionalData2 = $this->stripPadding( substr($mrz, 48, 11) ); + + $finalCheckDigitRaw = substr($mrz, 0, 71); + $checkDigit4 = $this->stripPadding(substr($mrz, 71, 1)); + $checkDigitVerify4 = $this->checkDigitVerify($finalCheckDigitRaw, $checkDigit4); + + $id['documentCode'] = substr($mrz, 0, 1); + $id['documentType'] = ($documentCode1 == 'I') ? 'ID-1' : 'UNKNOWN'; + $id['documentType'] .= ($documentCode2 == 'R') ? ' Residence Card' : ''; + $id['issuerOrg'] = $issuerOrg; + $id['names']['lastName'] = $this->stripPadding(substr($mrz, 5, 25)); + $id['names']['firstName'] = $this->stripPadding(substr($mrz, 49, 14)); + $id['documentNumber'] = $documentNumber; + $id['optionalData'] = $optionalData; + //$id['optionalData2'] = $optionalData2; + $id['nationality'] = $nationality; + $id['dob'] = $dob; + $id['sex'] = $sex; + //$id['expiry'] = $expiry; // data do not exist + + $id['checkDigit']['documentNumber']['checkDigit1'] = $checkDigit1; + $id['checkDigit']['documentNumber']['checkDigitVerify1'] = $checkDigitVerify1; + + $id['checkDigit']['dob']['checkDigit2'] = $checkDigit2; + $id['checkDigit']['dob']['checkDigitVerify2'] = $checkDigitVerify2; + + $id['checkDigit']['expiry']['checkDigit3'] = $checkDigit3; + $id['checkDigit']['expiry']['checkDigitVerify3'] = $checkDigitVerify3; + + $id['checkDigit']['finalCheck']['checkDigit4'] = $checkDigit4; + $id['checkDigit']['finalCheck']['checkDigitVerify4'] = $checkDigitVerify4; + + $id['is_valid'] = $checkDigitVerify1 && $checkDigitVerify2 && $checkDigitVerify3 && $checkDigitVerify4; + + return $id; + } catch (Exception $e) { + $error['error'] = 'Details parsing failed. ' . $e; + return $error; + } + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + /** + * Parser of MRV-B Visa MRZ strings. + * + * Specified in Part 7 of ICAO Doc 9303 + * https://www.icao.int/publications/pages/publication.aspx?docnum=9303 + * + * MRV-B Visa MRZ is almost identical to ID-2, the only difference being + * it does not have a final composite check digit + * + * The machine readable zone on a visa has + * 2 lines, each consisting of 36 characters. Below a reference to the format: + * 01 - 02: Document code + * 03 - 05: Issuing state or organization + * 06 - 36: Names + * 37 - 45: Document Number + * 46 - 46: Check digit + * 47 - 49: Nationality + * 50 - 55: Date of birth + * 56 - 56: Check digit + * 57 - 57: Sex + * 58 - 63: Date of expiry + * 64 - 64: Check digit + * 65 - 72: Optional data + * + */ + + private function parseMRZVisa($mrz) + { + + try { + + $documentCode1 = substr($mrz, 0, 1); + $documentCode2 = substr($mrz, 1, 1); + + $issuerOrg = $this->getCountry($this->stripPadding(substr($mrz, 2, 3))); + + $names = $this->getNames(substr($mrz, 5, 31)); + + $documentNumberRaw = substr($mrz, 36, 9); + $documentNumber = $this->stripPadding($documentNumberRaw); + $checkDigit1 = substr($mrz, 45, 1); + $checkDigitVerify1 = $this->checkDigitVerify($documentNumberRaw, $checkDigit1); + + $nationality = $this->getCountry($this->stripPadding(substr($mrz, 46, 3))); + + $dobRaw = substr($mrz, 49, 6); + $dob = $this->getFullDate($this->stripPadding($dobRaw), 1); + $checkDigit2 = substr($mrz, 55, 1); + $checkDigitVerify2 = $this->checkDigitVerify($dobRaw, $checkDigit2); + + $sex = $this->getSex($this->stripPadding(substr($mrz, 56, 1))); + + $expiryRaw = substr($mrz, 57, 6); + $expiry = $this->getFullDate($this->stripPadding($expiryRaw), 2); + + $checkDigit3 = $this->stripPadding(substr($mrz, 63, 1)); + $checkDigitVerify3 = $this->checkDigitVerify($expiryRaw, $checkDigit3); + + $optionalData = $this->stripPadding(substr($mrz, 64, 8)); + + $id['documentCode'] = substr($mrz, 0, 1); + $id['documentType'] = ($documentCode1 == 'V') ? 'ID-2 Visa' : 'UNKNOWN'; + $id['VisaType'] = $documentCode2; + $id['issuerOrg'] = $issuerOrg; + $id['names'] = $names; + + $id['documentNumber'] = $documentNumber; + $id['checkDigit']['documentNumber']['checkDigit1'] = $checkDigit1; + $id['checkDigit']['documentNumber']['checkDigitVerify1'] = $checkDigitVerify1; + + $id['nationality'] = $nationality; + + $id['dob'] = $dob; + $id['checkDigit']['passport']['checkDigit2'] = $checkDigit2; + $id['checkDigit']['passport']['checkDigitVerify2'] = $checkDigitVerify2; + + $id['sex'] = $sex; + + $id['expiry'] = $expiry; + $id['checkDigit']['expiry']['checkDigit3'] = $checkDigit3; + $id['checkDigit']['expiry']['checkDigitVerify3'] = $checkDigitVerify3; + + $id['optionalData'] = $optionalData; + + $id['is_valid'] = $checkDigitVerify1 && $checkDigitVerify2 && $checkDigitVerify3; + + return $id; + } catch (Exception $e) { + $error['error'] = 'Details parsing failed. ' . $e; + return $error; + } + } + + # = = = = = = = = = = = = = = = = = = = = = = = = # + # = = = = = = = = = = = = = = = = = = = = = = = = # + + public function parseMRZ($mrz) + { + + $mrz = str_replace(array("\n\r", "\n", "\r"), "", $mrz); + $len = strlen($mrz); + + if ($mrz == null || ($len != 88 && $len != 90 && $len != 72)) { + $error['error'] = 'Invalid MRZ length for ICAO document.'; + return $error; + } + + $documentCode = $this->stripPadding(substr($mrz, 0, 1)); + + switch ($documentCode) { + + case 'P': + return $this->parseMrzPassport($mrz); + + case 'A': + case 'C': + case 'I': + if ($len == 90) { + return $this->parseMrzID1($mrz); + } else if ($len == 72) { + if (substr($mrz, 2, 3) == 'FRA') { + // special handling for french + return $this->parseMrzFrenchID($mrz); + } else { + // normal ID-2 + return $this->parseMrzID2($mrz); + } + } + break; + + case 'V': + return $this->parseMRZVisa($mrz); + } + + $error['error'] = 'Unknown document.'; + return $error; + } +} + +# = = = = = = = = = = = = = = = = = = = = = = = = # +# = = = = = = = = = = = = = = = = = = = = = = = = # diff --git a/src/StringChecks.php b/src/StringChecks.php index d94f5a4..02d2d5f 100644 --- a/src/StringChecks.php +++ b/src/StringChecks.php @@ -6,9 +6,11 @@ /** * Trait StringChecks - * - * @author Evandro Kondrat + * * @package Alphaolomi\Mrz + * @version 1.0.0 + * + * @author Evandro Kondrat */ trait StringChecks { diff --git a/tests/MrzParserTest.php b/tests/MrzParserTest.php new file mode 100644 index 0000000..7741c14 --- /dev/null +++ b/tests/MrzParserTest.php @@ -0,0 +1,42 @@ +assertInstanceOf(MrzParser::class, $mrzParser); + } + + public function testMrzParserParsesValidMrzTd1StringCorrectly(): void + { + $mrzParser = new MrzParser(); + $parsedData = $mrzParser->parseMRZ(self::VALID_TD1_MRZ_STRING); + $this->assertIsArray($parsedData); + } + + + public function testMrzParserParsesValidMrzTd3StringCorrectly(): void + { + $mrzParser = new MrzParser(); + $parsedData = $mrzParser->parseMRZ(self::VALID_TD3_MRZ_STRING); + $this->assertIsArray($parsedData); + } +} diff --git a/tests/MrzTest.php b/tests/MrzTest.php index 6bc7857..876e1d3 100644 --- a/tests/MrzTest.php +++ b/tests/MrzTest.php @@ -8,10 +8,11 @@ final class MrzTest extends TestCase { /** - * + * skipped */ public function testTd1Case01(): void { + $this->markTestSkipped('ESPAÑOLA is Out of scope'); $mrz = new Mrz( "ID", "ESP",