From cb75a4c543560f694d29c7d1eb0c1fb3639f9485 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 10 Mar 2020 21:37:53 +0100 Subject: [PATCH] Refactoring the package. * Merge with my own project * Use drupol/php-conventions * Increase test coverage * Use phpspec framework * Warning when missing tests * Fix PHPStan warnings * Fix Coding style * Optimize algorithm * Fix type declarations * Enable strict typing * Use Github CI instead of Travis --- .codecov.yml | 6 - .editorconfig | 22 +- .gitattributes | 12 +- .github/CODEOWNERS | 1 + .github/CODE_OF_CONDUCT.md | 46 ++++ .github/CONTRIBUTING.md | 29 ++ .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE.md | 13 + .github/PULL_REQUEST_TEMPLATE.md | 9 + .github/settings.yml | 49 ++++ .github/stale.yml | 10 + .github/workflows/continuous-integration.yml | 57 ++++ .gitignore | 12 +- .scrutinizer.yml | 30 ++- .travis.yml | 49 ---- LICENSE.md | 2 +- README.md | 167 +++++++----- composer.json | 37 +-- grumphp.yml.dist | 19 ++ infection.json.dist | 18 ++ phpcs.xml.dist | 185 ------------- phpspec.yml.dist | 12 + phpstan.neon | 7 + phpunit.xml.dist | 28 -- .../Tin/CountryHandler/AustriaSpec.php | 18 ++ .../Tin/CountryHandler/BelgiumSpec.php | 20 ++ .../Tin/CountryHandler/BulgariaSpec.php | 20 ++ .../Tin/CountryHandler/CroatiaSpec.php | 18 ++ .../LeKoala/Tin/CountryHandler/CyprusSpec.php | 20 ++ .../Tin/CountryHandler/CzeckRepublicSpec.php | 30 +++ .../Tin/CountryHandler/DenmarkSpec.php | 20 ++ .../Tin/CountryHandler/EstoniaSpec.php | 20 ++ .../Tin/CountryHandler/FinlandSpec.php | 21 ++ .../LeKoala/Tin/CountryHandler/FranceSpec.php | 18 ++ .../Tin/CountryHandler/GermanySpec.php | 18 ++ .../LeKoala/Tin/CountryHandler/GreeceSpec.php | 14 + .../Tin/CountryHandler/HungarySpec.php | 18 ++ .../Tin/CountryHandler/IrelandSpec.php | 29 ++ spec/LeKoala/Tin/CountryHandler/ItalySpec.php | 18 ++ .../LeKoala/Tin/CountryHandler/LatviaSpec.php | 16 ++ .../Tin/CountryHandler/LithuaniaSpec.php | 18 ++ .../Tin/CountryHandler/LuxembourgSpec.php | 18 ++ spec/LeKoala/Tin/CountryHandler/MaltaSpec.php | 16 ++ .../Tin/CountryHandler/NetherlandsSpec.php | 18 ++ .../LeKoala/Tin/CountryHandler/PolandSpec.php | 18 ++ .../Tin/CountryHandler/PortugalSpec.php | 18 ++ .../Tin/CountryHandler/RomaniaSpec.php | 16 ++ .../Tin/CountryHandler/SlovakiaSpec.php | 16 ++ .../Tin/CountryHandler/SloveniaSpec.php | 18 ++ spec/LeKoala/Tin/CountryHandler/SpainSpec.php | 18 ++ .../LeKoala/Tin/CountryHandler/SwedenSpec.php | 18 ++ .../Tin/CountryHandler/UnitedKingdomSpec.php | 16 ++ spec/LeKoala/Tin/TINSpec.php | 68 +++++ src/Algo/ATAlgorithm.php | 66 ----- src/Algo/BEAlgorithm.php | 117 -------- src/Algo/BGAlgorithm.php | 82 ------ src/Algo/CYAlgorithm.php | 87 ------ src/Algo/CZAlgorithm.php | 65 ----- src/Algo/DEAlgorithm.php | 187 ------------- src/Algo/DKAlgorithm.php | 120 --------- src/Algo/EEAlgorithm.php | 94 ------- src/Algo/ESAlgorithm.php | 102 ------- src/Algo/FIAlgorithm.php | 120 --------- src/Algo/FRAlgorithm.php | 61 ----- src/Algo/GRAlgorithm.php | 26 -- src/Algo/HRAlgorithm.php | 44 --- src/Algo/HUAlgorithm.php | 50 ---- src/Algo/IEAlgorithm.php | 81 ------ src/Algo/ITAlgorithm.php | 252 ----------------- src/Algo/LTAlgorithm.php | 137 ---------- src/Algo/LUAlgorithm.php | 123 --------- src/Algo/LVAlgorithm.php | 52 ---- src/Algo/MTAlgorithm.php | 40 --- src/Algo/NLAlgorithm.php | 57 ---- src/Algo/PLAlgorithm.php | 129 --------- src/Algo/PTAlgorithm.php | 61 ----- src/Algo/ROAlgorithm.php | 70 ----- src/Algo/SEAlgorithm.php | 193 ------------- src/Algo/SIAlgorithm.php | 65 ----- src/Algo/SKAlgorithm.php | 31 --- src/Algo/StatusCode.php | 16 -- src/Algo/TINAlgorithm.php | 26 -- src/Algo/TINAlgorithmInterface.php | 17 -- src/Algo/UKAlgorithm.php | 59 ---- src/CountryHandler/Austria.php | 54 ++++ src/CountryHandler/Belgium.php | 93 +++++++ src/CountryHandler/Bulgaria.php | 70 +++++ src/CountryHandler/CountryHandler.php | 198 ++++++++++++++ .../CountryHandlerInterface.php | 55 ++++ src/CountryHandler/Croatia.php | 42 +++ src/CountryHandler/Cyprus.php | 85 ++++++ src/CountryHandler/CzeckRepublic.php | 158 +++++++++++ src/CountryHandler/Denmark.php | 113 ++++++++ src/CountryHandler/Estonia.php | 83 ++++++ src/CountryHandler/Finland.php | 108 ++++++++ src/CountryHandler/France.php | 42 +++ src/CountryHandler/Germany.php | 217 +++++++++++++++ src/CountryHandler/Greece.php | 21 ++ src/CountryHandler/Hungary.php | 40 +++ src/CountryHandler/Ireland.php | 102 +++++++ src/CountryHandler/Italy.php | 254 ++++++++++++++++++ src/CountryHandler/Latvia.php | 43 +++ src/CountryHandler/Lithuania.php | 109 ++++++++ src/CountryHandler/Luxembourg.php | 121 +++++++++ src/CountryHandler/Malta.php | 37 +++ src/CountryHandler/Netherlands.php | 47 ++++ src/CountryHandler/Poland.php | 163 +++++++++++ src/CountryHandler/Portugal.php | 52 ++++ src/CountryHandler/Romania.php | 55 ++++ src/CountryHandler/Slovakia.php | 32 +++ src/CountryHandler/Slovenia.php | 54 ++++ src/CountryHandler/Spain.php | 116 ++++++++ src/CountryHandler/Sweden.php | 212 +++++++++++++++ src/CountryHandler/UnitedKingdom.php | 90 +++++++ src/Exception/InvalidCountryException.php | 10 - src/Exception/InvalidTINLengthException.php | 10 - src/Exception/NegativeNumberException.php | 10 - src/Exception/TINException.php | 30 ++- src/Exception/TINValidationException.php | 10 - src/TIN.php | 171 ++++++++++++ src/TINValid.php | 108 -------- src/Util/DateUtil.php | 47 ---- src/Util/NumberUtil.php | 86 ------ src/Util/StringUtil.php | 180 ------------- tests/ATAlgorithmTest.php | 24 -- tests/BEAlgorithmTest.php | 26 -- tests/BGAlgorithmTest.php | 24 -- tests/CYAlgorithmTest.php | 26 -- tests/CZAlgorithmTest.php | 119 -------- tests/DEAlgorithmTest.php | 30 --- tests/DKAlgorithmTest.php | 28 -- tests/EEAlgorithmTest.php | 28 -- tests/ESAlgorithmTest.php | 30 --- tests/FIAlgorithmTest.php | 24 -- tests/FRAlgorithmTest.php | 24 -- tests/GRAlgorithmTest.php | 22 -- tests/HRAlgorithmTest.php | 24 -- tests/HUAlgorithmTest.php | 24 -- tests/IEAlgorithmTest.php | 32 --- tests/ITAlgorithmTest.php | 24 -- tests/LTAlgorithmTest.php | 24 -- tests/LUAlgorithmTest.php | 24 -- tests/LVAlgorithmTest.php | 24 -- tests/MTAlgorithmTest.php | 24 -- tests/NLAlgorithmTest.php | 24 -- tests/PLAlgorithmTest.php | 26 -- tests/PTAlgorithmTest.php | 24 -- tests/ROAlgorithmTest.php | 24 -- tests/SEAlgorithmTest.php | 30 --- tests/SIAlgorithmTest.php | 24 -- tests/SKAlgorithmTest.php | 24 -- tests/TINValidTest.php | 20 -- tests/UKAlgorithmTest.php | 29 -- tests/src/AbstractAlgorithmSpec.php | 164 +++++++++++ 154 files changed, 4249 insertions(+), 4318 deletions(-) delete mode 100644 .codecov.yml create mode 100644 .github/CODEOWNERS create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/settings.yml create mode 100644 .github/stale.yml create mode 100644 .github/workflows/continuous-integration.yml delete mode 100644 .travis.yml create mode 100644 grumphp.yml.dist create mode 100644 infection.json.dist delete mode 100644 phpcs.xml.dist create mode 100644 phpspec.yml.dist create mode 100644 phpstan.neon delete mode 100644 phpunit.xml.dist create mode 100644 spec/LeKoala/Tin/CountryHandler/AustriaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/BelgiumSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/BulgariaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/CroatiaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/CyprusSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/CzeckRepublicSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/DenmarkSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/EstoniaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/FinlandSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/FranceSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/GermanySpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/GreeceSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/HungarySpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/IrelandSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/ItalySpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/LatviaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/LithuaniaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/LuxembourgSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/MaltaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/NetherlandsSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/PolandSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/PortugalSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/RomaniaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/SlovakiaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/SloveniaSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/SpainSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/SwedenSpec.php create mode 100644 spec/LeKoala/Tin/CountryHandler/UnitedKingdomSpec.php create mode 100644 spec/LeKoala/Tin/TINSpec.php delete mode 100644 src/Algo/ATAlgorithm.php delete mode 100644 src/Algo/BEAlgorithm.php delete mode 100644 src/Algo/BGAlgorithm.php delete mode 100644 src/Algo/CYAlgorithm.php delete mode 100644 src/Algo/CZAlgorithm.php delete mode 100644 src/Algo/DEAlgorithm.php delete mode 100644 src/Algo/DKAlgorithm.php delete mode 100644 src/Algo/EEAlgorithm.php delete mode 100644 src/Algo/ESAlgorithm.php delete mode 100644 src/Algo/FIAlgorithm.php delete mode 100644 src/Algo/FRAlgorithm.php delete mode 100644 src/Algo/GRAlgorithm.php delete mode 100644 src/Algo/HRAlgorithm.php delete mode 100644 src/Algo/HUAlgorithm.php delete mode 100644 src/Algo/IEAlgorithm.php delete mode 100644 src/Algo/ITAlgorithm.php delete mode 100644 src/Algo/LTAlgorithm.php delete mode 100644 src/Algo/LUAlgorithm.php delete mode 100644 src/Algo/LVAlgorithm.php delete mode 100644 src/Algo/MTAlgorithm.php delete mode 100644 src/Algo/NLAlgorithm.php delete mode 100644 src/Algo/PLAlgorithm.php delete mode 100644 src/Algo/PTAlgorithm.php delete mode 100644 src/Algo/ROAlgorithm.php delete mode 100644 src/Algo/SEAlgorithm.php delete mode 100644 src/Algo/SIAlgorithm.php delete mode 100644 src/Algo/SKAlgorithm.php delete mode 100644 src/Algo/StatusCode.php delete mode 100644 src/Algo/TINAlgorithm.php delete mode 100644 src/Algo/TINAlgorithmInterface.php delete mode 100644 src/Algo/UKAlgorithm.php create mode 100644 src/CountryHandler/Austria.php create mode 100644 src/CountryHandler/Belgium.php create mode 100644 src/CountryHandler/Bulgaria.php create mode 100644 src/CountryHandler/CountryHandler.php create mode 100644 src/CountryHandler/CountryHandlerInterface.php create mode 100644 src/CountryHandler/Croatia.php create mode 100644 src/CountryHandler/Cyprus.php create mode 100644 src/CountryHandler/CzeckRepublic.php create mode 100644 src/CountryHandler/Denmark.php create mode 100644 src/CountryHandler/Estonia.php create mode 100644 src/CountryHandler/Finland.php create mode 100644 src/CountryHandler/France.php create mode 100644 src/CountryHandler/Germany.php create mode 100644 src/CountryHandler/Greece.php create mode 100644 src/CountryHandler/Hungary.php create mode 100644 src/CountryHandler/Ireland.php create mode 100644 src/CountryHandler/Italy.php create mode 100644 src/CountryHandler/Latvia.php create mode 100644 src/CountryHandler/Lithuania.php create mode 100644 src/CountryHandler/Luxembourg.php create mode 100644 src/CountryHandler/Malta.php create mode 100644 src/CountryHandler/Netherlands.php create mode 100644 src/CountryHandler/Poland.php create mode 100644 src/CountryHandler/Portugal.php create mode 100644 src/CountryHandler/Romania.php create mode 100644 src/CountryHandler/Slovakia.php create mode 100644 src/CountryHandler/Slovenia.php create mode 100644 src/CountryHandler/Spain.php create mode 100644 src/CountryHandler/Sweden.php create mode 100644 src/CountryHandler/UnitedKingdom.php delete mode 100644 src/Exception/InvalidCountryException.php delete mode 100644 src/Exception/InvalidTINLengthException.php delete mode 100644 src/Exception/NegativeNumberException.php delete mode 100644 src/Exception/TINValidationException.php create mode 100644 src/TIN.php delete mode 100644 src/TINValid.php delete mode 100644 src/Util/DateUtil.php delete mode 100644 src/Util/NumberUtil.php delete mode 100644 src/Util/StringUtil.php delete mode 100644 tests/ATAlgorithmTest.php delete mode 100644 tests/BEAlgorithmTest.php delete mode 100644 tests/BGAlgorithmTest.php delete mode 100644 tests/CYAlgorithmTest.php delete mode 100644 tests/CZAlgorithmTest.php delete mode 100644 tests/DEAlgorithmTest.php delete mode 100644 tests/DKAlgorithmTest.php delete mode 100644 tests/EEAlgorithmTest.php delete mode 100644 tests/ESAlgorithmTest.php delete mode 100644 tests/FIAlgorithmTest.php delete mode 100644 tests/FRAlgorithmTest.php delete mode 100644 tests/GRAlgorithmTest.php delete mode 100644 tests/HRAlgorithmTest.php delete mode 100644 tests/HUAlgorithmTest.php delete mode 100644 tests/IEAlgorithmTest.php delete mode 100644 tests/ITAlgorithmTest.php delete mode 100644 tests/LTAlgorithmTest.php delete mode 100644 tests/LUAlgorithmTest.php delete mode 100644 tests/LVAlgorithmTest.php delete mode 100644 tests/MTAlgorithmTest.php delete mode 100644 tests/NLAlgorithmTest.php delete mode 100644 tests/PLAlgorithmTest.php delete mode 100644 tests/PTAlgorithmTest.php delete mode 100644 tests/ROAlgorithmTest.php delete mode 100644 tests/SEAlgorithmTest.php delete mode 100644 tests/SIAlgorithmTest.php delete mode 100644 tests/SKAlgorithmTest.php delete mode 100644 tests/TINValidTest.php create mode 100644 tests/src/AbstractAlgorithmSpec.php diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 0a9f473..0000000 --- a/.codecov.yml +++ /dev/null @@ -1,6 +0,0 @@ -comment: off -coverage: - status: - project: off - patch: off - diff --git a/.editorconfig b/.editorconfig index dbb25c6..173228f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,25 +1,9 @@ -# For more information about the properties used in -# this file, please see the EditorConfig documentation: -# http://editorconfig.org/ - root = true [*] -charset = utf-8 end_of_line = lf -indent_size = 4 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true - -[*.md] -trim_trailing_whitespace = false - -[*.{yml,json}] -# The indent size used in the `package.json` file cannot be changed -# https://github.com/npm/npm/pull/3180#issuecomment-16336516 -indent_size = 2 +charset = utf-8 +max_line_length = 80 indent_style = space - -[composer.json] indent_size = 4 +insert_final_newline = true diff --git a/.gitattributes b/.gitattributes index 67ad8cc..44445a9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,11 @@ +/spec export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.scrutinizer.yml export-ignore +.travis.yml export-ignore +phpunit.xml.dist export-ignore +infection.json.dist export-ignore +grumphp.yml.dist export-ignore +phpspec.yml.dist export-ignore /tests export-ignore -/phpunit.xml.dist export-ignore -/.gitignore export-ignore diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..529c034 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @drupol diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d9159b2 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at am@localheinz.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..5ebc01b --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# CONTRIBUTING + +We're using [Travis CI](https://travis-ci.com) as a continuous integration system. + +For details, see [`.travis.yml`](../.travis.yml). + +## Tests + +We're using [`grumphp/grumphp`](https://github.com/phpro/grumphp) to drive the development. + +Run + +```bash +./vendor/bin/grumphp run +``` + +to run all the tests. + +## Coding Standards + +We are using [`drupol/php-conventions`](https://github.com/drupol/php-conventions) to enforce coding standards. + +Run + +```bash +./vendor/bin/grumphp run +``` + +to automatically detect/fix coding standard violations. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..b4c89d9 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: drupol diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..3421bc3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,13 @@ +## Steps required to reproduce the problem + +1. +2. +3. + +## Expected Result + +* + +## Actual Result + +* diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..e448c72 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ +This PR + +* [x] +* [ ] +* [ ] + +Follows #. +Related to #. +Fixes #. diff --git a/.github/settings.yml b/.github/settings.yml new file mode 100644 index 0000000..7ad3c5d --- /dev/null +++ b/.github/settings.yml @@ -0,0 +1,49 @@ +# https://github.com/probot/settings + +branches: + - name: master + protection: + enforce_admins: false + required_pull_request_reviews: + dismiss_stale_reviews: true + require_code_owner_reviews: true + required_approving_review_count: 1 + required_status_checks: + contexts: + - "Grumphp" + strict: false + restrictions: null + +labels: + - name: bug + color: ee0701 + + - name: dependencies + color: 0366d6 + + - name: enhancement + color: 0e8a16 + + - name: question + color: cc317c + + - name: security + color: ee0701 + + - name: stale + color: eeeeee + +repository: + allow_merge_commit: true + allow_rebase_merge: false + allow_squash_merge: false + default_branch: master + description: "Taxpayer Identification Number (TIN) Validator" + topics: tin,taxpayer-identification-number,validator + has_downloads: true + has_issues: true + has_pages: false + has_projects: false + has_wiki: false + name: tin + private: false diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000..8eb151c --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,10 @@ +daysUntilStale: 60 + +daysUntilClose: 7 + +staleLabel: stale + +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..e09ab4a --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,57 @@ +# https://help.github.com/en/categories/automating-your-workflow-with-github-actions + +on: + - pull_request + - push + +name: "Continuous Integration" + +jobs: + run: + name: "Grumphp" + runs-on: ${{ matrix.operating-system }} + strategy: + fail-fast: false + matrix: + operating-system: [ubuntu-latest, windows-latest, macOS-latest] + php-versions: ['7.1', '7.2', '7.3', '7.4'] + + steps: + - name: Checkout + uses: actions/checkout@master + with: + fetch-depth: 1 + + - name: Install PHP + uses: shivammathur/setup-php@master + with: + php-version: ${{ matrix.php-versions }} + extensions: mbstring,xdebug,pcov + - name: Get Composer Cache Directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v1 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader + + - name: Run Grumphp + run: vendor/bin/grumphp run + env: + STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} + + - name: Send PSALM data + run: vendor/bin/psalm --shepherd --stats + continue-on-error: true + + - name: Scrutinizer + run: | + wget https://scrutinizer-ci.com/ocular.phar + php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml + continue-on-error: true diff --git a/.gitignore b/.gitignore index c49a5d8..b049cd4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ -vendor/ -composer.lock -phpunit.xml +/composer.lock +/vendor +/build +/.php_cs.cache +/example/ +/.idea/ +/test.php +/phpspec.yml +/infection.json diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 051ef9a..db99448 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -1,15 +1,21 @@ -inherit: true - build: - nodes: - analysis: - tests: - override: [php-scrutinizer-run] - -checks: - php: - code_rating: true - duplication: true + nodes: + analysis: + environment: + php: + version: 7.4 + tests: + override: + - php-scrutinizer-run filter: - paths: [src/*, tests/*] + paths: + - 'src/*' + +tools: + external_code_coverage: + timeout: 600 + php_loc: true + php_pdepend: true + php_sim: true + php_changetracking: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8c549f2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,49 +0,0 @@ -sudo: false - -language: php - -dist: trusty - -cache: - directories: - - $HOME/.composer/cache/files - -matrix: - fast_finish: true - include: - - php: 7.1 - env: - - PHPUNIT_TEST=1 - - PHPCS_TEST=1 - - PHPUNIT_COVERAGE_TEST=1 - - php: 7.2 - env: - - PHPUNIT_TEST=1 - - php: 7.3 - env: - - PHPUNIT_TEST=1 - - php: 7.4 - env: - - PHPUNIT_TEST=1 - -before_script: -# Extra $PATH - - export PATH=~/.composer/vendor/bin:$PATH - -# Init PHP - - phpenv rehash - - phpenv config-rm xdebug.ini || true - - echo 'memory_limit = 2048M' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - -# Install composer dependencies - - composer validate - - if [[ $PHPCS_TEST ]]; then composer global require squizlabs/php_codesniffer:^3 --prefer-dist --no-interaction --no-progress --no-suggest -o; fi - - composer install --prefer-source --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile - -script: - - if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi - - if [[ $PHPCS_TEST ]]; then composer run-script lint; fi - - if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi - -after_success: - - if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi diff --git a/LICENSE.md b/LICENSE.md index 4bb6d9f..ee9b35a 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ # The MIT License (MIT) -Copyright (c) 2020 Thomas Portelange +Copyright (c) 2020 Thomas Portelange, Pol Dellaiera Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the _Software_), to deal in the Software without restriction, including without limitation the diff --git a/README.md b/README.md index 782c427..f159bff 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,113 @@ +[![Latest Stable Version](https://img.shields.io/packagist/v/drupol/tin.svg?style=flat-square)](https://packagist.org/packages/drupol/tin) + [![GitHub stars](https://img.shields.io/github/stars/drupol/tin.svg?style=flat-square)](https://packagist.org/packages/drupol/tin) + [![Total Downloads](https://img.shields.io/packagist/dt/drupol/tin.svg?style=flat-square)](https://packagist.org/packages/drupol/tin) + [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/drupol/tin/Continuous%20Integration?style=flat-square)](https://github.com/drupol/tin/actions) + [![Scrutinizer code quality](https://img.shields.io/scrutinizer/quality/g/drupol/tin/refactoring.svg?style=flat-square)](https://scrutinizer-ci.com/g/drupol/tin/?branch=refactoring) + [![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/drupol/tin/refactoring.svg?style=flat-square)](https://scrutinizer-ci.com/g/drupol/tin/?branch=refactoring) + [![Mutation testing badge](https://badge.stryker-mutator.io/github.com/drupol/tin/refactoring)](https://stryker-mutator.github.io) + [![License](https://img.shields.io/packagist/l/drupol/tin.svg?style=flat-square)](https://packagist.org/packages/drupol/tin) + # Taxpayer Identification Number (TIN) Validator -[![Build Status](https://travis-ci.org/lekoala/tin.svg?branch=master)](https://travis-ci.org/lekoala/tin) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/lekoala/tin/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/lekoala/tin/?branch=master) -[![Code Coverage](https://scrutinizer-ci.com/g/lekoala/tin/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/lekoala/tin/?branch=master) -[![Build Status](https://scrutinizer-ci.com/g/lekoala/tin/badges/build.png?b=master)](https://scrutinizer-ci.com/g/lekoala/tin/build-status/master) -[![codecov.io](https://codecov.io/github/lekoala/tin/coverage.svg?branch=master)](https://codecov.io/github/lekoala/tin?branch=master) - -[![Latest Stable Version](https://poser.pugx.org/lekoala/tin/version)](https://packagist.org/packages/lekoala/tin) -[![Latest Unstable Version](https://poser.pugx.org/lekoala/tin/v/unstable)](//packagist.org/packages/lekoala/tin) -[![Total Downloads](https://poser.pugx.org/lekoala/tin/downloads)](https://packagist.org/packages/lekoala/tin) -[![License](https://poser.pugx.org/lekoala/tin/license)](https://packagist.org/packages/lekoala/tin) -[![Monthly Downloads](https://poser.pugx.org/lekoala/tin/d/monthly)](https://packagist.org/packages/lekoala/tin) -[![Daily Downloads](https://poser.pugx.org/lekoala/tin/d/daily)](https://packagist.org/packages/lekoala/tin) - -[![Dependency Status](https://www.versioneye.com/php/lekoala:tin/badge.svg)](https://www.versioneye.com/php/lekoala:tin) -[![Reference Status](https://www.versioneye.com/php/lekoala:tin/reference_badge.svg?style=flat)](https://www.versioneye.com/php/lekoala:tin/references) - -A library to validate TIN numbers for individuals. This is based on a java library, -this is why the code does not reflect best practices in php. - -Supported countries are: -- Austria (AT) -- Belgium (BE) -- Bulgaria (BG) -- Croatia (HR) -- Cyprus (CY) -- Czech Republic (CZ) - no check digit (but possible czechphp/national-identification-number-validator) -- Denmark (DK) -- Estonia (EE) -- Finland (FI) -- France (FR) -- Germany (DE) -- Greece (GR) - only size -- Hungary (HU) -- Ireland (IE) -- Italy (IT) -- Latvia (LV) - no check digit -- Lithuania (LT) -- Luxembourg (LU) -- Malta (MT) - no check digit -- Netherlands (NL) -- Poland (PL) -- Portugal (PT) -- Romania (RO) - no check digit -- Slovakia (SK) - only structure -- Slovenia (SI) -- Spain (ES) -- Sweden (SE) -- United Kingdom (UK) - only structure - -If your country is not there, feel free to open an issue with your country code and -a link to the specification. Ideally, if you can provide a PR with the algorithm and the -test that would be even better :-) +## Description + +A library to validate TIN numbers for individuals. This is based on a Java +library, this is why the code does not reflect best practices in php (yet). + +Supported countries: +* Austria (AT) +* Belgium (BE) +* Bulgaria (BG) +* Croatia (HR) +* Cyprus (CY) +* Czech Republic (CZ) +* Denmark (DK) +* Estonia (EE) +* Finland (FI) +* France (FR) +* Germany (DE) +* Greece (GR) - only size +* Hungary (HU) +* Ireland (IE) +* Italy (IT) +* Latvia (LV) - no check digit +* Lithuania (LT) +* Luxembourg (LU) +* Malta (MT) - no check digit +* Netherlands (NL) +* Poland (PL) +* Portugal (PT) +* Romania (RO) - no check digit +* Slovakia (SK) - only structure +* Slovenia (SI) +* Spain (ES) +* Sweden (SE) +* United Kingdom (UK) - only structure + +If your country is not there, feel free to open an issue with your country code, +and a link to the specification. Ideally, you can provide a pull request with +the algorithm and the tests. + +## Requirements + +* PHP >= 7.1 + +## Usage & API + +To simply check the validity of a TIN number: + +```php +isValid(); +``` -## Installation +If you want to get the reason why a number is invalid, you can use -Run +```php +check(); +} catch (TINException $e) { + // do something with the exception. +} ``` -## Usage +## Installation -To simply check the validity of a number +```composer require drupol/tin``` - $result = TINValid::checkTIN($countryCode, $number); +## Code quality, tests and benchmarks -If you want to get the reason why a number is invalid, you can use +Every time changes are introduced into the library, [Github](https://github.com/drupol/tin/actions) run the tests and the benchmarks. - try { - TINValid::validateTIN($countryCode, $number); - } - catch(TINValidationException $e) { - - } +The library has tests written with [PHPSpec](http://www.phpspec.net/). +Feel free to check them out in the `spec` directory. Run `composer phpspec` to trigger the tests. -If you want to see if a country is supported or not, you can simply use +Before each commit some inspections are executed with [GrumPHP](https://github.com/phpro/grumphp), run `./vendor/bin/grumphp run` to check manually. - $result = TINValid::isCountrySupported('be'); +[PHPInfection](https://github.com/infection/infection) is used to ensure that your code is properly tested, run `composer infection` to test your code. ## Links -[`TIN Algorithms - Public - Functional Specification`]() +* [`European Commission TIN service`](https://ec.europa.eu/taxation_customs/tin/) +* [`TIN Algorithms - Public - Functional Specification`](https://ec.europa.eu/taxation_customs/tin/specs/FS-TIN%20Algorithms-Public.docx) +* [`Taxpayer Identification Number`](https://en.wikipedia.org/wiki/Taxpayer_Identification_Number) -[`Taxpayer Identification Number`](https://en.wikipedia.org/wiki/Taxpayer_Identification_Number) +## Authors +* [Thomas Portelange](https://github.com/lekoala) +* [Pol Dellaiera](https://github.com/loophp) -## License +## Contributing -This package is licensed using the MIT License. -Please have a look at [`LICENSE.md`](LICENSE.md). +Feel free to contribute to this library by sending Github pull requests. I'm quite reactive :-) diff --git a/composer.json b/composer.json index 8dadffd..bb02ad7 100644 --- a/composer.json +++ b/composer.json @@ -1,21 +1,29 @@ { - "name": "lekoala/tin", + "name": "loophp/tin", "type": "library", "description": "Provide validation tools for Taxpayer Identification Number", - "homepage": "https://github.com/lekoala/tin", + "homepage": "https://github.com/loophp/tin", "license": "MIT", - "authors": [{ - "name": "Thomas Portelange", - "email": "thomas@lekoala.be" - }], + "authors": [ + { + "name": "Thomas Portelange", + "email": "thomas@lekoala.be" + }, + { + "name": "Pol Dellaiera", + "email": "pol.dellaiera@protonmail.com" + } + ], "require": { "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "^7.0", - "squizlabs/php_codesniffer": "^3.5" + "drupol/php-conventions": "^1.6", + "friends-of-phpspec/phpspec-code-coverage": "^4.3", + "infection/infection": "^0.13.6 || ^0.15.3", + "phpspec/phpspec": "^5.1.2 || ^6.1.1", + "phpstan/phpstan-strict-rules": "^0.12.2" }, - "prefer-stable": true, "autoload": { "psr-4": { "LeKoala\\Tin\\": "src/" @@ -23,16 +31,11 @@ }, "autoload-dev": { "psr-4": { - "LeKoala\\Tin\\Test\\": "tests/" + "tests\\LeKoala\\Tin\\": "tests/src/" } }, - "scripts": { - "lint": "phpcs src/ tests/", - "lint-clean": "phpcbf src/ tests/", - "test": "phpunit -v" - }, "support": { - "issues": "https://github.com/lekoala/tin/issues", - "source": "https://github.com/lekoala/tin" + "issues": "https://github.com/loophp/tin/issues", + "source": "https://github.com/loophp/tin" } } diff --git a/grumphp.yml.dist b/grumphp.yml.dist new file mode 100644 index 0000000..bc6c50d --- /dev/null +++ b/grumphp.yml.dist @@ -0,0 +1,19 @@ +imports: + - { resource: vendor/drupol/php-conventions/config/php71/grumphp.yml } + +parameters: + process_timeout: 3600 + extra_tasks: + phpspec: + verbose: true + metadata: + priority: 3000 + infection: + threads: 1 + test_framework: phpspec + configuration: infection.json.dist + min_msi: 50 + min_covered_msi: 50 + metadata: + priority: 2000 + diff --git a/infection.json.dist b/infection.json.dist new file mode 100644 index 0000000..3fe5fe5 --- /dev/null +++ b/infection.json.dist @@ -0,0 +1,18 @@ +{ + "timeout": 10, + "source": { + "directories": [ + "src" + ] + }, + "logs": { + "text": "build/infection.log", + "summary": "build/summary.log", + "debug": "build/debug.log", + "perMutator": "build/per-mutator.md", + "badge": { + "branch": "master" + } + }, + "testFramework":"phpspec" +} diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index 73eb205..0000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,185 +0,0 @@ - - - The PSR-2 coding standard. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - - - - - - - - - - - - - - - - - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/phpspec.yml.dist b/phpspec.yml.dist new file mode 100644 index 0000000..5dbfd76 --- /dev/null +++ b/phpspec.yml.dist @@ -0,0 +1,12 @@ +formatter.name: pretty +extensions: + FriendsOfPhpSpec\PhpSpec\CodeCoverage\CodeCoverageExtension: + format: + - html + - clover + - php + - text + output: + html: build/coverage + clover: build/logs/clover.xml + php: build/coverage.php diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..c3d30f1 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,7 @@ +parameters: + ignoreErrors: + - + message: "#Casting to array something that\\'s already array.#" + paths: + - src/CountryHandler/CountryHandler.php + - src/CountryHandler/Germany.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index c253d0e..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - tests - - - - - src/ - - tests/ - - - - - diff --git a/spec/LeKoala/Tin/CountryHandler/AustriaSpec.php b/spec/LeKoala/Tin/CountryHandler/AustriaSpec.php new file mode 100644 index 0000000..6862dc3 --- /dev/null +++ b/spec/LeKoala/Tin/CountryHandler/AustriaSpec.php @@ -0,0 +1,18 @@ +beConstructedThrough('from', ['be', '1234567890']); + + $this + ->shouldBeAnInstanceOf(TIN::class); + } + + public function it_can_be_constructed_from_a_slug() + { + $this + ->beConstructedThrough('fromSlug', ['be1234567890']); + + $this + ->shouldBeAnInstanceOf(TIN::class); + } + + public function it_can_check_if_a_tin_is_valid_or_not() + { + $this + ->beConstructedThrough('from', ['be', '1234567890']); + + $this::fromSlug('be123456789') + ->isValid() + ->shouldReturn(false); + + $this::fromSlug('be00012511119') + ->isValid() + ->shouldReturn(true); + } + + public function it_can_throw_an_exception_if_algorithm_is_not_found() + { + $this::fromSlug('foo1234') + ->shouldThrow(TINException::class) + ->during('check'); + + $this::fromSlug('ww1234') + ->shouldThrow(TINException::class) + ->during('check'); + + $this::fromSlug('ww') + ->shouldThrow(TINException::class) + ->during('check'); + + $this::fromSlug('1234') + ->shouldThrow(TINException::class) + ->during('check'); + } + + public function it_is_initializable() + { + $this->shouldHaveType(TIN::class); + } +} diff --git a/src/Algo/ATAlgorithm.php b/src/Algo/ATAlgorithm.php deleted file mode 100644 index ad9ba64..0000000 --- a/src/Algo/ATAlgorithm.php +++ /dev/null @@ -1,66 +0,0 @@ -isFollowLength($normalizedTIN)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($normalizedTIN)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($normalizedTIN)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowRules(string $tin) - { - return self::LENGTH == strlen($tin) && $this->isFollowAustriaRule($tin); - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowAustriaRule(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - try { - $sum = $c1 + $c3 + $c5 + $c7 + NumberUtil::sumDigit($c2 * 2) + NumberUtil::sumDigit($c4 * 2) + NumberUtil::sumDigit($c6 * 2) + NumberUtil::sumDigit($c8 * 2); - $check = NumberUtil::getUnit(100 - $sum); - return $c9 == $check; - } catch (NegativeNumberException $e) { - return false; - } - } -} diff --git a/src/Algo/BEAlgorithm.php b/src/Algo/BEAlgorithm.php deleted file mode 100644 index e4d3c46..0000000 --- a/src/Algo/BEAlgorithm.php +++ /dev/null @@ -1,117 +0,0 @@ -isFollowLength($str)) { - return StatusCode::INVALID_LENGTH; - } - - $this->resultDateValidation = $this->isValidDate($str); - if (!$this->isFollowPattern($str) || $this->resultDateValidation == 0) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($str)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - - /** - * @return integer - */ - public function getResultDateValidation() - { - return $this->resultDateValidation; - } - - /** - * @param integer $resultDateValidation - */ - public function setResultDateValidation($resultDateValidation) - { - $this->resultDateValidation = $resultDateValidation; - } - - public function isFollowRules(string $tin) - { - return $this->isFollowBelgiumRule1AndIsDateValid($tin) || $this->isFollowBelgiumRule2AndIsDateValid($tin); - } - - public function isFollowBelgiumRule1AndIsDateValid(string $tin) - { - $dateValid = ($this->resultDateValidation == 1 || $this->resultDateValidation == 3); - return $this->isFollowBelgiumRule1($tin) && $dateValid; - } - - public function isFollowBelgiumRule2AndIsDateValid(string $tin) - { - $dateValid = $this->resultDateValidation >= 2; - return $this->isFollowBelgiumRule2($tin) && $dateValid; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowBelgiumRule1(string $tin) - { - $divisionRemainderBy97 = intval(StringUtil::substring($tin, 0, 9)) % 97; - return 97 - $divisionRemainderBy97 == intval(StringUtil::substring($tin, 9, 11)); - } - - public function isFollowBelgiumRule2(string $tin) - { - $divisionRemainderBy97 = floatval(2 + StringUtil::substring($tin, 0, 9)) % 97; - return 97 - $divisionRemainderBy97 == intval(StringUtil::substring($tin, 9, 11)); - } - - /** - * @param string $tin - * @return integer - */ - private function isValidDate(string $tin) - { - $year = intval(StringUtil::substring($tin, 0, 2)); - $month = intval(StringUtil::substring($tin, 2, 4)); - $day = intval(StringUtil::substring($tin, 4, 6)); - - $y1 = DateUtil::validate(1900 + $year, $month, $day); - $y2 = DateUtil::validate(2000 + $year, $month, $day); - if ($day == 0 || $month == 0 || $y1 && $y2) { - return 3; - } - if ($y1) { - return 1; - } - if ($y2) { - return 2; - } - return 0; - } -} diff --git a/src/Algo/BGAlgorithm.php b/src/Algo/BGAlgorithm.php deleted file mode 100644 index aeb8b0b..0000000 --- a/src/Algo/BGAlgorithm.php +++ /dev/null @@ -1,82 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin) || !$this->isValidDate($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - return $this->isFollowBulgariaRule($tin); - } - - public function isFollowBulgariaRule(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $c10 = StringUtil::digitAt($tin, 9); - $sum = $c1 * 2 + $c2 * 4 + $c3 * 8 + $c4 * 5 + $c5 * 10 + $c6 * 9 + $c7 * 7 + $c8 * 3 + $c9 * 6; - $remainderBy11 = $sum % 11; - if ($remainderBy11 == 10) { - return $c10 == 0; - } - return $remainderBy11 == $c10; - } - - /** - * @param string $tin - * @return boolean - */ - private function isValidDate(string $tin) - { - $year = intval(StringUtil::substring($tin, 0, 2)); - $month = intval(StringUtil::substring($tin, 2, 4)); - $day = intval(StringUtil::substring($tin, 4, 6)); - if ($month >= 21 && $month <= 32) { - return DateUtil::validate(1800 + $year, $month - 20, $day); - } - if ($month >= 41 && $month <= 52) { - return DateUtil::validate(2000 + $year, $month - 40, $day); - } - return DateUtil::validate(1900 + $year, $month, $day); - } -} diff --git a/src/Algo/CYAlgorithm.php b/src/Algo/CYAlgorithm.php deleted file mode 100644 index 94a523d..0000000 --- a/src/Algo/CYAlgorithm.php +++ /dev/null @@ -1,87 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = ord($tin[8]); - $evenPositionNumbersSum = $c2 + $c4 + $c6 + $c8; - $recodedSum = $this->recodeValue($c1) + $this->recodeValue($c3) + $this->recodeValue($c5) + $this->recodeValue($c7); - $remainderBy26 = ($evenPositionNumbersSum + $recodedSum) % 26; - return $remainderBy26 + 65 == $c9; - } - - /** - * @param integer $x - * @return integer - */ - public function recodeValue($x) - { - switch ($x) { - case 0: - return 1; - case 1: - return 0; - case 2: - return 5; - case 3: - return 7; - case 4: - return 9; - case 5: - return 13; - case 6: - return 15; - case 7: - return 17; - case 8: - return 19; - case 9: - return 21; - default: - return -1; - } - } -} diff --git a/src/Algo/CZAlgorithm.php b/src/Algo/CZAlgorithm.php deleted file mode 100644 index 54f7999..0000000 --- a/src/Algo/CZAlgorithm.php +++ /dev/null @@ -1,65 +0,0 @@ -isFollowLength1($normalizedTIN) && !$this->isFollowLength2($normalizedTIN)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isValidDate($normalizedTIN)) { - return StatusCode::INVALID_PATTERN; - } - return StatusCode::VALID; - } - - public function isFollowLength1(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_1); - } - - public function isFollowLength2(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_2); - } - - /** - * @param string $tin - * @return boolean - */ - private function isValidDate(string $tin) - { - $year = intval(StringUtil::substring($tin, 0, 2)); - $month = intval(StringUtil::substring($tin, 2, 4)); - // female have +50 in their month - if ($month > 50) { - $month = $month - 50; - } - // some people have +20 in their month - if ($month > 12) { - $month = $month - 20; - } - $day = intval(StringUtil::substring($tin, 4, 6)); - - $y1 = DateUtil::validate(1900 + $year, $month, $day); - $y2 = DateUtil::validate(2000 + $year, $month, $day); - if (!$y1 || !$y2) { - return false; - } - return true; - } -} diff --git a/src/Algo/DEAlgorithm.php b/src/Algo/DEAlgorithm.php deleted file mode 100644 index cdfd2ae..0000000 --- a/src/Algo/DEAlgorithm.php +++ /dev/null @@ -1,187 +0,0 @@ -isFollowLength($normalizedTIN)) { - return StatusCode::INVALID_LENGTH; - } - if ($this->isFollowLength($normalizedTIN) && !$this->isFollowPattern($normalizedTIN)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($normalizedTIN)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return $this->isFollowLength1($tin) || $this->isFollowLength2($tin); - } - - public function isFollowLength1(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_1); - } - - public function isFollowLength2(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_2); - } - - public function isFollowPattern(string $tin) - { - return $this->isFollowPattern1($tin) || $this->isFollowPattern2($tin); - } - - public function isFollowPattern1(string $tin) - { - if (!StringUtil::isFollowPattern($tin, self::PATTERN_1)) { - return false; - } - $tab = []; - $pos = []; - for ($i = 0; $i < 10; $i++) { - $tab[$i] = StringUtil::digitAt($tin, $i); - $pos[$i] = 0; - } - for ($j = 0; $j < 10; $j++) { - $pos[$tab[$j]]++; - } - $isEncounteredTwice2 = false; - $isEncountered0 = false; - for ($k = 0; $k < 10; $k++) { - if ($pos[$k] == 2) { - if ($isEncounteredTwice2) { - return false; - } - $isEncounteredTwice2 = true; - } - if ($pos[$k] == 0) { - if ($isEncountered0) { - return false; - } - $isEncountered0 = true; - } - } - return $isEncountered0; - } - - public function isFollowPattern2(string $tin) - { - if (!StringUtil::isFollowPattern($tin, self::PATTERN_2)) { - return false; - } - $tab = []; - $pos = []; - for ($i = 0; $i < 10; $i++) { - $tab[$i] = StringUtil::digitAt($tin, $i); - $pos[$i] = 0; - } - for ($i = 0; $i < 8; $i++) { - if ($tab[$i] == $tab[$i + 1] && $tab[$i + 1] == $tab[$i + 2]) { - return false; - } - } - for ($j = 0; $j < 10; $j++) { - $pos[$tab[$j]]++; - } - $isEncounteredTwice2 = false; - $isEncounteredThrice3 = false; - for ($k = 0; $k < 10; $k++) { - if ($pos[$k] > 3) { - return false; - } - if ($pos[$k] == 3) { - if ($isEncounteredThrice3) { - return false; - } - $isEncounteredThrice3 = true; - } - if ($pos[$k] == 2) { - if ($isEncounteredTwice2) { - return false; - } - $isEncounteredTwice2 = true; - } - } - return $isEncounteredThrice3 || $isEncounteredTwice2; - } - - public function isFollowRules(string $tin) - { - return (self::LENGTH_1 == strlen($tin) && $this->isFollowRuleGermany1($tin)) || $this->isFollowRuleGermany2($tin); - } - - public function isFollowRuleGermany1(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = []; - for ($i = 0; $i < 9; $i++) { - $c2[$i] = StringUtil::digitAt($tin, $i + 1); - } - $result = ($c1 + 10) % 10; - if ($result == 0) { - $result = 10; - } - $result *= 2; - $x = $result % 11; - for ($j = 0; $j < 9; $j++) { - $x = ($x + $c2[$j]) % 10; - if ($x == 0) { - $x = 10; - } - $x *= 2; - $x %= 11; - } - $c3 = StringUtil::digitAt($tin, 10); - $total = 11 - $x; - if ($total == 10) { - return $c3 == 0; - } - return $total == $c3; - } - - public function isFollowRuleGermany2(string $tin) - { - return StringUtil::digitAt($tin, 10) == $this->calculateCheckDigit($tin); - } - - public function calculateCheckDigit(string $idnrString) - { - $ten = 10; - $eleven = 11; - $chars = str_split($idnrString); - $remainder_mod_ten = 0; - $remainder_mod_eleven = 10; - $digit = 0; - for ($length = strlen($idnrString), $counter = 0; $counter < $length - 1; $counter++) { - $digit = intval($chars[$counter]); - $remainder_mod_ten = ($digit + $remainder_mod_eleven) % 10; - if ($remainder_mod_ten == 0) { - $remainder_mod_ten = 10; - } - $remainder_mod_eleven = 2 * $remainder_mod_ten % 11; - } - $digit = 11 - $remainder_mod_eleven; - if ($digit == 10) { - $digit = 0; - } - return $digit; - } -} diff --git a/src/Algo/DKAlgorithm.php b/src/Algo/DKAlgorithm.php deleted file mode 100644 index 009dde7..0000000 --- a/src/Algo/DKAlgorithm.php +++ /dev/null @@ -1,120 +0,0 @@ -isFollowLength($whithoutHyphen)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($whithoutHyphen) || !$this->isValidDate($whithoutHyphen)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($whithoutHyphen)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - return $this->isFollowDenmarkRule($tin); - } - - /** - * @link https://cpr.dk/cpr-systemet/personnumre-uden-kontrolciffer-modulus-11-kontrol/ - * - * The CPR office has since 2007 given out social security numbers without the so called modulus 11 control. - * The social security numbers without modulus 11 are completely valid - * and are given out, as some birth years no longer have the capacity to provide them with modulus 11 control. - * - * We should not check modulus 11 control for the following birthdays: - * - * 1st of januar 1960 - * 1st of januar 1964 - * 1st of januar 1965 - * 1st of januar 1966 - * 1st of januar 1969 - * 1st of januar 1970 - * 1st of januar 1974 - * 1st of januar 1980 - * 1st of januar 1982 - * 1st of januar 1984 - * 1st of januar 1985 - * 1st of januar 1986 - * 1st of januar 1987 - * 1st of januar 1988 - * 1st of januar 1989 - * 1st of januar 1990 - * 1st of januar 1991 - * 1st of januar 1992 - * - * @param string $tin - * @return boolean - */ - public function isFollowDenmarkRule(string $tin) - { - $serialNumber = intval(StringUtil::substring($tin, 6, 10)); - $dayOfBirth = intval(StringUtil::substring($tin, 0, 2)); - $monthOfBirth = intval(StringUtil::substring($tin, 2, 4)); - $yearOfBirth = intval(StringUtil::substring($tin, 4, 6)); - if ($yearOfBirth >= 37 && $yearOfBirth <= 57 && $serialNumber >= 5000 && $serialNumber <= 8999) { - return false; - } - - $excludedYears = [60, 64, 65, 66, 69, 70, 74, 80, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92]; - if ($dayOfBirth == 1 && $monthOfBirth == 1 && in_array($yearOfBirth, $excludedYears)) { - return true; - } - - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $c10 = StringUtil::digitAt($tin, 9); - $sum = $c1 * 4 + $c2 * 3 + $c3 * 2 + $c4 * 7 + $c5 * 6 + $c6 * 5 + $c7 * 4 + $c8 * 3 + $c9 * 2; - $remainderBy11 = $sum % 11; - if ($remainderBy11 == 1) { - return false; - } - if ($remainderBy11 == 0) { - return $c10 == 0; - } - return $c10 == 11 - $remainderBy11; - } - - private function isValidDate(string $tin) - { - $day = intval(StringUtil::substring($tin, 0, 2)); - $month = intval(StringUtil::substring($tin, 2, 4)); - $year = intval(StringUtil::substring($tin, 4, 6)); - return DateUtil::validate(1900 + $year, $month, $day) || DateUtil::validate(2000 + $year, $month, $day); - } -} diff --git a/src/Algo/EEAlgorithm.php b/src/Algo/EEAlgorithm.php deleted file mode 100644 index e479894..0000000 --- a/src/Algo/EEAlgorithm.php +++ /dev/null @@ -1,94 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin) || !$this->isValidDate($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - return $this->isFollowRangeRule($tin) && $this->isFollowEstoniaRule($tin); - } - - public function isFollowRangeRule(string $tin) - { - $range = intval(StringUtil::substring($tin, 7, 10)); - return $range > 0 && $range < 711; - } - - public function isFollowEstoniaRule(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $c10 = StringUtil::digitAt($tin, 9); - $c11 = StringUtil::digitAt($tin, 10); - $sum = $c1 + $c2 * 2 + $c3 * 3 + $c4 * 4 + $c5 * 5 + $c6 * 6 + $c7 * 7 + $c8 * 8 + $c9 * 9 + $c10; - $remainderBy11 = $sum % 11; - return ($remainderBy11 < 10 && $remainderBy11 == $c11) || ($remainderBy11 == 10 && $this->isFollowEstoniaRulePart2($tin)); - } - - public function isFollowEstoniaRulePart2(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $c10 = StringUtil::digitAt($tin, 9); - $c11 = StringUtil::digitAt($tin, 10); - $sum = $c1 * 3 + $c2 * 4 + $c3 * 5 + $c4 * 6 + $c5 * 7 + $c6 * 8 + $c7 * 9 + $c8 + $c9 * 2 + $c10 * 3; - $remainderBy11 = $sum % 11; - return ($remainderBy11 < 10 && $remainderBy11 == $c11) || ($remainderBy11 == 10 && $c11 == 0); - } - - private function isValidDate(string $tin) - { - $year = intval(StringUtil::substring($tin, 1, 3)); - $month = intval(StringUtil::substring($tin, 3, 5)); - $day = intval(StringUtil::substring($tin, 5, 7)); - return DateUtil::validate(1900 + $year, $month, $day) || DateUtil::validate(2000 + $year, $month, $day); - } -} diff --git a/src/Algo/ESAlgorithm.php b/src/Algo/ESAlgorithm.php deleted file mode 100644 index 34a7087..0000000 --- a/src/Algo/ESAlgorithm.php +++ /dev/null @@ -1,102 +0,0 @@ -fillWith0($tin); - if (!$this->isFollowLength($normalizedTIN)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern1($normalizedTIN) && !$this->isFollowPattern2($normalizedTIN)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($normalizedTIN)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern1(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_1); - } - - public function isFollowPattern2(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_2); - } - - public function isFollowRules(string $tin) - { - return ($this->isFollowPattern1($tin) && $this->isFollowESRule1($tin)) || ($this->isFollowPattern2($tin) && $this->isFollowESRule2($tin)); - } - - private function isFollowESRule1(string $tin) - { - $number = intval(StringUtil::substring($tin, 0, strlen($tin) - 1)); - $checkDigit = $tin[strlen($tin) - 1]; - $remainderBy23 = $number % 23; - $sum = $remainderBy23 + 1; - return strtoupper($checkDigit) == $this->getCharFromNumber($sum); - } - - private function isFollowESRule2(string $tin) - { - $c1 = StringUtil::forDigit($this->getNumberFromChar($tin[0]), 10); - $number = intval($c1 . StringUtil::substring($tin, 1, strlen($tin) - 1)); - $checkDigit = $tin[strlen($tin) - 1]; - $remainderBy23 = $number % 23; - $sum = $remainderBy23 + 1; - - return strtoupper($checkDigit) == $this->getCharFromNumber($sum); - } - - protected function getNumberFromChar($m) - { - switch (strtoupper($m)) { - case 'K': - case 'L': - case 'M': - case 'X': - return 0; - case 'Y': - return 1; - case 'Z': - return 2; - default: - return -1; - } - } - - private function getCharFromNumber($sum) - { - return self::$tabConvertToChar[$sum - 1]; - } - - private function fillWith0(string $tin) - { - return StringUtil::fillWith0UntilLength($tin, self::LENGTH); - } -} diff --git a/src/Algo/FIAlgorithm.php b/src/Algo/FIAlgorithm.php deleted file mode 100644 index e03e120..0000000 --- a/src/Algo/FIAlgorithm.php +++ /dev/null @@ -1,120 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin) || !$this->isValidDate($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - return $this->isFollowFinlandRule($tin); - } - - public function isFollowFinlandRule(string $tin) - { - $number = intval(StringUtil::substring($tin, 0, 6) . StringUtil::substring($tin, 7, 10)); - $remainderBy31 = $number % 31; - $c11 = $tin[10]; - return $this->getMatch($remainderBy31) == $c11; - } - - public function getMatch($number) - { - if ($number < 10) { - return StringUtil::forDigit($number, 10); - } - switch ($number) { - case 10: - return 'A'; - case 11: - return 'B'; - case 12: - return 'C'; - case 13: - return 'D'; - case 14: - return 'E'; - case 15: - return 'F'; - case 16: - return 'H'; - case 17: - return 'J'; - case 18: - return 'K'; - case 19: - return 'L'; - case 20: - return 'M'; - case 21: - return 'N'; - case 22: - return 'P'; - case 23: - return 'R'; - case 24: - return 'S'; - case 25: - return 'T'; - case 26: - return 'U'; - case 27: - return 'V'; - case 28: - return 'W'; - case 29: - return 'X'; - case 30: - return 'Y'; - default: - return ' '; - } - } - - private function isValidDate(string $tin) - { - $day = intval(StringUtil::substring($tin, 0, 2)); - $month = intval(StringUtil::substring($tin, 2, 4)); - $year = intval(StringUtil::substring($tin, 4, 6)); - $c7 = StringUtil::substring($tin, 6, 7); - if ("+" == $c7) { - return DateUtil::validate(1800 + $year, $month, $day); - } - if ("-" == $c7) { - return DateUtil::validate(1900 + $year, $month, $day); - } - return "A" == $c7 && DateUtil::validate(2000 + $year, $month, $day); - } -} diff --git a/src/Algo/FRAlgorithm.php b/src/Algo/FRAlgorithm.php deleted file mode 100644 index d24f103..0000000 --- a/src/Algo/FRAlgorithm.php +++ /dev/null @@ -1,61 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_1); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_1); - } - - public function isFollowRules(string $tin) - { - return $this->isFollowFranceRule1($tin); - } - - public function isFollowFranceRule1(string $tin) - { - $number = floatval(StringUtil::substring($tin, 0, 10)); - $checkDigits = 0; - $remainderBy511 = $number % 511; - if ($remainderBy511 < 100) { - if ($remainderBy511 < 10) { - $checkDigits = intval(StringUtil::substring($tin, 12, 13)); - } else { - $checkDigits = intval(StringUtil::substring($tin, 11, 13)); - } - } else { - $checkDigits = intval(StringUtil::substring($tin, 10, 13)); - } - return $remainderBy511 == $checkDigits; - } -} diff --git a/src/Algo/GRAlgorithm.php b/src/Algo/GRAlgorithm.php deleted file mode 100644 index a15a3d4..0000000 --- a/src/Algo/GRAlgorithm.php +++ /dev/null @@ -1,26 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } -} diff --git a/src/Algo/HRAlgorithm.php b/src/Algo/HRAlgorithm.php deleted file mode 100644 index 8a92d7d..0000000 --- a/src/Algo/HRAlgorithm.php +++ /dev/null @@ -1,44 +0,0 @@ -isFollowRules($str)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowRules(string $tin) - { - $sum = 0; - $rest = 0; - $sum = StringUtil::digitAt($tin, 0) + 10; - for ($i = 1; $i < 11; $i++) { - $rest = $sum % 10; - $rest = (($rest == 0) ? 10 : $rest) * 2 % 11; - $sum = $rest + StringUtil::digitAt($tin, $i); - } - $diff = 11 - $rest; - $lastDigit = StringUtil::digitAt($tin, 10); - return ($rest == 1 && $lastDigit == 0) || $lastDigit == $diff; - } -} diff --git a/src/Algo/HUAlgorithm.php b/src/Algo/HUAlgorithm.php deleted file mode 100644 index 2618066..0000000 --- a/src/Algo/HUAlgorithm.php +++ /dev/null @@ -1,50 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - $c10 = StringUtil::digitAt($tin, 9); - $sum = 0; - for ($i = 0; $i < 9; $i++) { - $c11 = intval(StringUtil::substring($tin, $i, $i + 1)); - $sum += $c11 * ($i + 1); - } - $remainderBy11 = $sum % 11; - return $remainderBy11 == $c10; - } -} diff --git a/src/Algo/IEAlgorithm.php b/src/Algo/IEAlgorithm.php deleted file mode 100644 index eb33f92..0000000 --- a/src/Algo/IEAlgorithm.php +++ /dev/null @@ -1,81 +0,0 @@ -isFollowLength1($tin) && !$this->isFollowLength2($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (($this->isFollowLength1($tin) && !$this->isFollowPattern1($tin)) || ($this->isFollowLength2($tin) && !$this->isFollowPattern2($tin))) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength1(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_1); - } - - public function isFollowPattern1(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_1); - } - - public function isFollowLength2(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_2); - } - - public function isFollowPattern2(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_2); - } - - public function isFollowRules(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = (strlen($tin) >= 9) ? self::letterToNumber($tin[8]) : 0; - $c8 = $tin[7]; - $sum = $c9 * 9 + $c1 * 8 + $c2 * 7 + $c3 * 6 + $c4 * 5 + $c5 * 4 + $c6 * 3 + $c7 * 2; - $remainderBy23 = $sum % 23; - if ($remainderBy23 != 0) { - return StringUtil::getAlphabeticalPosition($c8) == $remainderBy23; - } - return $c8 == 'W' || $c8 == 'w'; - } - - public static function letterToNumber(string $toConv) - { - if (!$toConv) { - return 0; - } - if ($toConv == 'W' || $toConv == 'w') { - return 0; - } - return StringUtil::getAlphabeticalPosition($toConv); - } -} diff --git a/src/Algo/ITAlgorithm.php b/src/Algo/ITAlgorithm.php deleted file mode 100644 index be2cdd2..0000000 --- a/src/Algo/ITAlgorithm.php +++ /dev/null @@ -1,252 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin) || !$this->isValidDate($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - $code = StringUtil::substring($tin, 11, 12) . $this->convertCharToNumber(StringUtil::substring($tin, 12, 15)); - - $containsUpper = in_array(strtoupper($code), self::$listSet); - $containsLower = in_array(strtolower($code), self::$listSet); - $matchPattern = StringUtil::isFollowPattern($tin, self::PATTERN); - - return ($containsUpper || $containsLower) && $matchPattern; - } - - public function isFollowRules(string $tin) - { - return $this->isFollowRuleItalia($tin); - } - - public function isFollowRuleItalia(string $tin) - { - $sum = 0; - for ($i = 0; $i < 15; $i++) { - if ($i % 2 == 0) { - $sum += $this->convertOddCharacter($tin[$i]); - } else { - $sum += $this->convertEvenCharacter($tin[$i]); - } - } - $remainderBy26 = $sum % 26; - $c16 = $tin[15]; - $check = StringUtil::getAlphabeticalPosition($c16) - 1; - - return $remainderBy26 == $check; - } - - protected function convertEvenCharacter(string $c) - { - if (is_numeric($c)) { - return intval($c); - } - return StringUtil::getAlphabeticalPosition($c) - 1; - } - - protected function convertOddCharacter(string $c) - { - $normalizedChar = $c; - if (!is_numeric($normalizedChar)) { - $normalizedChar = strtoupper($normalizedChar); - } - switch ($normalizedChar) { - case '0': - case 'A': - return 1; - case '1': - case 'B': - return 0; - case '2': - case 'C': - return 5; - case '3': - case 'D': - return 7; - case '4': - case 'E': - return 9; - case '5': - case 'F': - return 13; - case '6': - case 'G': - return 15; - case '7': - case 'H': - return 17; - case '8': - case 'I': - return 19; - case '9': - case 'J': - return 21; - case 'K': - return 2; - case 'L': - return 4; - case 'M': - return 18; - case 'N': - return 20; - case 'O': - return 11; - case 'P': - return 3; - case 'Q': - return 6; - case 'R': - return 8; - case 'S': - return 12; - case 'T': - return 14; - case 'U': - return 16; - case 'V': - return 10; - case 'W': - return 22; - case 'X': - return 25; - case 'Y': - return 24; - case 'Z': - return 23; - default: - return -1; - } - } - - protected function getMonthNumber(string $m) - { - switch (strtoupper($m)) { - case 'A': - return 1; - case 'B': - return 2; - case 'C': - return 3; - case 'D': - return 4; - case 'E': - return 5; - case 'H': - return 6; - case 'L': - return 7; - case 'M': - return 8; - case 'P': - return 9; - case 'R': - return 10; - case 'S': - return 11; - case 'T': - return 12; - default: - return -1; - } - } - - private function isValidDate(string $tin) - { - $day = intval($this->convertCharToNumber(StringUtil::substring($tin, 9, 11))); - $c9 = $tin[8]; - $month = $this->getMonthNumber($c9); - $year = intval($this->convertCharToNumber(StringUtil::substring($tin, 6, 8))); - if ($day >= 1 && $day <= 31) { - return DateUtil::validate(1900 + $year, $month, $day) || DateUtil::validate(2000 + $year, $month, $day); - } - return $day >= 41 && $day <= 71 && (DateUtil::validate(1900 + $year, $month, $day - 40) || DateUtil::validate(2000 + $year, $month, $day - 40)); - } - - private function convertCharToNumber($oldStr) - { - $newStr = ''; - for ($i = 0; $i < strlen($oldStr); $i++) { - if (!is_numeric($oldStr[$i])) { - $newStr .= $this->getNumberFromChar($oldStr[$i]); - } else { - $newStr .= $oldStr[$i]; - } - } - return $newStr; - } - - protected function getNumberFromChar($m) - { - switch (strtoupper($m)) { - case 'L': - return 0; - case 'M': - return 1; - case 'N': - return 2; - case 'P': - return 3; - case 'Q': - return 4; - case 'R': - return 5; - case 'S': - return 6; - case 'T': - return 7; - case 'U': - return 8; - case 'V': - return 9; - default: - return -1; - } - } -} diff --git a/src/Algo/LTAlgorithm.php b/src/Algo/LTAlgorithm.php deleted file mode 100644 index 55378f7..0000000 --- a/src/Algo/LTAlgorithm.php +++ /dev/null @@ -1,137 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - private function checkMonth(string $tin) - { - $month = intval(StringUtil::substring($tin, 3, 5)); - return $month > 0 && $month < 13; - } - - private function checkDay(string $tin) - { - $day = intval(StringUtil::substring($tin, 5, 7)); - return $day > 0 && $day < 32; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - $sum = 0; - $c11 = intval(StringUtil::substring($tin, 10)); - for ($i = 0; $i < 10; $i++) { - $sum += $this->multiplyAccordingToWeight(intval(StringUtil::substring($tin, $i, $i + 1)), $i); - } - $remainderBy11 = $sum % 11; - if ($remainderBy11 != 10) { - return $c11 == $remainderBy11; - } - $sum2 = 0; - for ($j = 0; $j < 10; $j++) { - $sum2 += $this->multiplyAccordingToWeight2(intval(StringUtil::substring($tin, $j, $j + 1)), $j); - } - $remainderBy11 = $sum2 % 11; - if ($remainderBy11 == 10) { - return $c11 == 0; - } - return $c11 == $remainderBy11; - } - - protected function multiplyAccordingToWeight($val, $index) - { - switch ($index) { - case 0: - return $val * 1; - case 1: - return $val * 2; - case 2: - return $val * 3; - case 3: - return $val * 4; - case 4: - return $val * 5; - case 5: - return $val * 6; - case 6: - return $val * 7; - case 7: - return $val * 8; - case 8: - return $val * 9; - case 9: - return $val * 1; - default: - return -1; - } - } - - protected function multiplyAccordingToWeight2($val, $index) - { - switch ($index) { - case 0: - return $val * 3; - case 1: - return $val * 4; - case 2: - return $val * 5; - case 3: - return $val * 6; - case 4: - return $val * 7; - case 5: - return $val * 8; - case 6: - return $val * 9; - case 7: - return $val * 1; - case 8: - return $val * 2; - case 9: - return $val * 3; - default: - return -1; - } - } - - private function isValidDate(string $tin) - { - $day = intval(StringUtil::substring($tin, 5, 7)); - $month = intval(StringUtil::substring($tin, 3, 5)); - $year = intval(StringUtil::substring($tin, 1, 3)); - return DateUtil::validate(1900 + $year, $month, $day) || DateUtil::validate(2000 + $year, $month, $day); - } -} diff --git a/src/Algo/LUAlgorithm.php b/src/Algo/LUAlgorithm.php deleted file mode 100644 index 4f573ae..0000000 --- a/src/Algo/LUAlgorithm.php +++ /dev/null @@ -1,123 +0,0 @@ -isFollowLength1($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern1($tin) || !$this->isValidDate($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowRules(string $tin) - { - return $this->isFollowLuxembourgRule1($tin) && $this->isFollowLuxembourgRule2($tin); - } - - public function isFollowLength1(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_1); - } - - public function isFollowPattern1(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_1); - } - - public function isFollowLuxembourgRule1(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $c10 = StringUtil::digitAt($tin, 9); - $c11 = StringUtil::digitAt($tin, 10); - $c12 = StringUtil::digitAt($tin, 11); - try { - $sum = $c2 + $c4 + $c6 + $c8 + $c10 + $c12 - + NumberUtil::sumDigit($c1 * 2) - + NumberUtil::sumDigit($c3 * 2) - + NumberUtil::sumDigit($c5 * 2) - + NumberUtil::sumDigit($c7 * 2) - + NumberUtil::sumDigit($c9 * 2) - + NumberUtil::sumDigit($c11 * 2); - $remainderBy10 = $sum % 10; - return $remainderBy10 == 0; - } catch (NegativeNumberException $e) { - return false; - } - } - - public function isFollowLuxembourgRule2(string $tin) - { - $listNumbers = []; - for ($i = 12; $i >= 0; $i--) { - if ($i != 11) { - $listNumbers[] = StringUtil::digitAt($tin, $i); - } - } - $check = 0; - for ($j = 0; $j < count($listNumbers); $j++) { - $item = $listNumbers[$j]; - $p = self::$P[$j % 8][$item]; - $check = self::$D[$check][$p]; - } - return $check == 0; - } - - private function isValidDate(string $tin) - { - $year = intval(StringUtil::substring($tin, 0, 4)); - $month = intval(StringUtil::substring($tin, 4, 6)); - $day = intval(StringUtil::substring($tin, 6, 8)); - return DateUtil::validate($year, $month, $day); - } -} diff --git a/src/Algo/LVAlgorithm.php b/src/Algo/LVAlgorithm.php deleted file mode 100644 index 2e375b0..0000000 --- a/src/Algo/LVAlgorithm.php +++ /dev/null @@ -1,52 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isValidDate($tin)) { - return StatusCode::INVALID_PATTERN; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - /** - * @param string $tin - * @return boolean - */ - private function isValidDate(string $tin) - { - $c1c2 = StringUtil::substring($tin, 0, 2); - if ($c1c2 == '32') { - return true; - } - $day = intval($c1c2); - $month = intval(StringUtil::substring($tin, 2, 4)); - $year = intval(StringUtil::substring($tin, 4, 6)); - - $y1 = DateUtil::validate(1900 + $year, $month, $day); - $y2 = DateUtil::validate(2000 + $year, $month, $day); - if (!$y1 || !$y2) { - return false; - } - return true; - } -} diff --git a/src/Algo/MTAlgorithm.php b/src/Algo/MTAlgorithm.php deleted file mode 100644 index 0a166db..0000000 --- a/src/Algo/MTAlgorithm.php +++ /dev/null @@ -1,40 +0,0 @@ -isFollowLength($normalizedTIN)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowMaltaRule($normalizedTIN)) { - return StatusCode::INVALID_PATTERN; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowMaltaRule(string $tin) - { - $c8 = $tin[7]; - $valid = ['M', 'G', 'A', 'P', 'L', 'H', 'B', 'Z']; - if (!in_array($c8, $valid)) { - return false; - } - return true; - } -} diff --git a/src/Algo/NLAlgorithm.php b/src/Algo/NLAlgorithm.php deleted file mode 100644 index f354bf9..0000000 --- a/src/Algo/NLAlgorithm.php +++ /dev/null @@ -1,57 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $sum = $c1 * 9 + $c2 * 8 + $c3 * 7 + $c4 * 6 + $c5 * 5 + $c6 * 4 + $c7 * 3 + $c8 * 2; - $remainderBy11 = $sum % 11; - if ($remainderBy11 == 10) { - return false; - } - return $c9 == $remainderBy11; - } -} diff --git a/src/Algo/PLAlgorithm.php b/src/Algo/PLAlgorithm.php deleted file mode 100644 index 89350fb..0000000 --- a/src/Algo/PLAlgorithm.php +++ /dev/null @@ -1,129 +0,0 @@ -isFollowLength1($tin) && !$this->isFollowLength2($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPatterns($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowRules(string $tin) - { - return ($this->isFollowLength1($tin) && $this->isFollowRulePoland1($tin)) || ($this->isFollowLength2($tin) && $this->isFollowRulePoland2($tin)); - } - - public function isFollowPatterns(string $tin) - { - return $this->isFollowLength1AndPattern1($tin) || $this->isFollowLength2AndPattern2AndIsValidDate($tin); - } - - public function isFollowLength1AndPattern1(string $tin) - { - return $this->isFollowLength1($tin) && $this->isFollowPattern1($tin); - } - - public function isFollowLength2AndPattern2AndIsValidDate(string $tin) - { - return $this->isFollowLength2($tin) && $this->isFollowPattern2($tin) && $this->isValidDate($tin); - } - - public function isFollowLength1(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_1); - } - - public function isFollowPattern1(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_1); - } - - public function isFollowLength2(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_2); - } - - public function isFollowPattern2(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_2); - } - - private function isFollowRulePoland1(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $c10 = StringUtil::digitAt($tin, 9); - $sum = $c1 * 6 + $c2 * 5 + $c3 * 7 + $c4 * 2 + $c5 * 3 + $c6 * 4 + $c7 * 5 + $c8 * 6 + $c9 * 7; - $remainderBy11 = $sum % 11; - if ($remainderBy11 == 10) { - return false; - } - return $c10 == $remainderBy11; - } - - private function isFollowRulePoland2(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $c10 = StringUtil::digitAt($tin, 9); - $c11 = StringUtil::digitAt($tin, 10); - $sum = $c1 * 1 + $c2 * 3 + $c3 * 7 + $c4 * 9 + $c5 * 1 + $c6 * 3 + $c7 * 7 + $c8 * 9 + $c9 * 1 + $c10 * 3; - $lastDigit = $sum % 10; - return $c11 == 10 - $lastDigit; - } - - private function isValidDate(string $tin) - { - $year = intval(StringUtil::substring($tin, 0, 2)); - $month = intval(StringUtil::substring($tin, 2, 4)); - $day = intval(StringUtil::substring($tin, 4, 6)); - if ($month >= 1 && $month <= 12) { - return DateUtil::validate(1900 + $year, $month, $day); - } - if ($month >= 21 && $month <= 32) { - return DateUtil::validate(2000 + $year, $month - 20, $day); - } - if ($month >= 41 && $month <= 52) { - return DateUtil::validate(2100 + $year, $month - 40, $day); - } - if ($month >= 61 && $month <= 72) { - return DateUtil::validate(2200 + $year, $month - 60, $day); - } - return $month >= 81 && $month <= 92 && DateUtil::validate(1800 + $year, $month - 80, $day); - } -} diff --git a/src/Algo/PTAlgorithm.php b/src/Algo/PTAlgorithm.php deleted file mode 100644 index 1c22ee4..0000000 --- a/src/Algo/PTAlgorithm.php +++ /dev/null @@ -1,61 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $sum = $c1 * 9 + $c2 * 8 + $c3 * 7 + $c4 * 6 + $c5 * 5 + $c6 * 4 + $c7 * 3 + $c8 * 2; - $remainderBy11 = $sum % 11; - $checkDigit = 11 - $remainderBy11; - if ($checkDigit <= 9) { - return $checkDigit == $c9; - } - if ($checkDigit == 10) { - return 0 == $c9; - } - return 0 == $c9; - } -} diff --git a/src/Algo/ROAlgorithm.php b/src/Algo/ROAlgorithm.php deleted file mode 100644 index d7e703c..0000000 --- a/src/Algo/ROAlgorithm.php +++ /dev/null @@ -1,70 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin)) { - return StatusCode::INVALID_PATTERN; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return $this->isValidDate($tin) && $this->isFollowRomanianRule($tin); - } - - private function isFollowRomanianRule(string $tin) - { - $c1 = intval($tin[0]); - - if ($c1 == 0) { - return false; - } - - $county = intval(StringUtil::substring($tin, 7, 9)); - - if ($county > 47 && $county != 51 && $county != 52) { - return false; - } - - return true; - } - - /** - * @param string $tin - * @return boolean - */ - private function isValidDate(string $tin) - { - $year = intval(StringUtil::substring($tin, 1, 3)); - $month = intval(StringUtil::substring($tin, 3, 5)); - $day = intval(StringUtil::substring($tin, 5, 7)); - - $y1 = DateUtil::validate(1900 + $year, $month, $day); - $y2 = DateUtil::validate(2000 + $year, $month, $day); - if (!$y1 || !$y2) { - return false; - } - return true; - } -} diff --git a/src/Algo/SEAlgorithm.php b/src/Algo/SEAlgorithm.php deleted file mode 100644 index 1a6eb11..0000000 --- a/src/Algo/SEAlgorithm.php +++ /dev/null @@ -1,193 +0,0 @@ -isFollowLength1And2($normalizedTIN) && !$this->isFollowLength3And4($normalizedTIN)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPatterns($normalizedTIN)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($normalizedTIN)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowPatterns(string $tin) - { - return $this->isFollowPatternAndIsValidDate($tin) - || $this->isFollowPattern2AndIsValidDate2($tin) - || $this->isFollowPattern3AndIsValidDate3($tin) - || $this->isFollowPattern4AndIsValidDate4($tin); - } - - public function isFollowPatternAndIsValidDate(string $tin) - { - return $this->isFollowPattern($tin) && $this->isValidDate($tin); - } - - public function isFollowPattern2AndIsValidDate2(string $tin) - { - return $this->isFollowPattern2($tin) && $this->isValidDate2($tin); - } - - public function isFollowPattern3AndIsValidDate3(string $tin) - { - return $this->isFollowPattern3($tin) && $this->isValidDate3($tin); - } - - public function isFollowPattern4AndIsValidDate4(string $tin) - { - return $this->isFollowPattern4($tin) && $this->isValidDate4($tin); - } - - public function isFollowRules(string $tin) - { - return (intval("10") == strlen($tin) - && $this->isFollowSwedenRule1And2($tin)) - || (intval("12") == strlen($tin) - && $this->isFollowSwedenRule3And4($tin)); - } - - public function isFollowLength1And2(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_1_AND_2); - } - - public function isFollowLength3And4(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_3_AND_4); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_1); - } - - public function isFollowPattern2(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_2); - } - - public function isFollowPattern3(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_3); - } - - public function isFollowPattern4(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_4); - } - - public function isFollowSwedenRule1And2(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $c10 = StringUtil::digitAt($tin, 9); - try { - $sum = $c2 + $c4 + $c6 + $c8 - + NumberUtil::sumDigit($c1 * 2) - + NumberUtil::sumDigit($c3 * 2) - + NumberUtil::sumDigit($c5 * 2) - + NumberUtil::sumDigit($c7 * 2) - + NumberUtil::sumDigit($c9 * 2); - $check = 10 - NumberUtil::getUnit($sum); - if ($check != 10) { - return $c10 == $check; - } - return $c10 == 0; - } catch (NegativeNumberException $e) { - return false; - } - } - - public function isFollowSwedenRule3And4(string $tin) - { - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $c9 = StringUtil::digitAt($tin, 8); - $c10 = StringUtil::digitAt($tin, 9); - $c11 = StringUtil::digitAt($tin, 10); - $c12 = StringUtil::digitAt($tin, 11); - try { - $sum = $c4 + $c6 + $c8 + $c10 - + NumberUtil::sumDigit($c3 * 2) - + NumberUtil::sumDigit($c5 * 2) - + NumberUtil::sumDigit($c7 * 2) - + NumberUtil::sumDigit($c9 * 2) - + NumberUtil::sumDigit($c11 * 2); - $check = 10 - NumberUtil::getUnit($sum); - if ($check != 10) { - return $c12 == $check; - } - return $c12 == 0; - } catch (NegativeNumberException $e) { - return false; - } - } - - private function isValidDate(string $tin) - { - $year = intval(StringUtil::substring($tin, 0, 2)); - $month = intval(StringUtil::substring($tin, 2, 4)); - $day = intval(StringUtil::substring($tin, 4, 6)); - return DateUtil::validate(1900 + $year, $month, $day) || DateUtil::validate(2000 + $year, $month, $day); - } - - private function isValidDate2(string $tin) - { - $year = intval(StringUtil::substring($tin, 0, 2)); - $month = intval(StringUtil::substring($tin, 2, 4)); - $day = intval(StringUtil::substring($tin, 4, 6)); - return DateUtil::validate(1900 + $year, $month, $day - 60) || DateUtil::validate(2000 + $year, $month, $day - 60); - } - - private function isValidDate3(string $tin) - { - $year = intval(StringUtil::substring($tin, 0, 4)); - $month = intval(StringUtil::substring($tin, 4, 6)); - $day = intval(StringUtil::substring($tin, 6, 8)); - return DateUtil::validate($year, $month, $day); - } - - private function isValidDate4(string $tin) - { - $year = intval(StringUtil::substring($tin, 0, 4)); - $month = intval(StringUtil::substring($tin, 4, 6)); - $day = intval(StringUtil::substring($tin, 6, 8)); - return DateUtil::validate($year, $month, $day - 60); - } -} diff --git a/src/Algo/SIAlgorithm.php b/src/Algo/SIAlgorithm.php deleted file mode 100644 index b891f0a..0000000 --- a/src/Algo/SIAlgorithm.php +++ /dev/null @@ -1,65 +0,0 @@ -isFollowLength($tin)) { - return StatusCode::INVALID_LENGTH; - } - if (!$this->isFollowPattern($tin)) { - return StatusCode::INVALID_PATTERN; - } - if (!$this->isFollowRules($tin)) { - return StatusCode::INVALID_SYNTAX; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH); - } - - public function isFollowPattern(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN); - } - - public function isFollowRules(string $tin) - { - return $this->isFollowRangeRule($tin) && $this->isFollowSloveniaRule($tin); - } - - public function isFollowRangeRule(string $tin) - { - $intTIN = intval(StringUtil::substring($tin, 0, 7)); - return NumberUtil::isInRange($intTIN, 999999, 10000000); - } - - public function isFollowSloveniaRule(string $tin) - { - $c1 = StringUtil::digitAt($tin, 0); - $c2 = StringUtil::digitAt($tin, 1); - $c3 = StringUtil::digitAt($tin, 2); - $c4 = StringUtil::digitAt($tin, 3); - $c5 = StringUtil::digitAt($tin, 4); - $c6 = StringUtil::digitAt($tin, 5); - $c7 = StringUtil::digitAt($tin, 6); - $c8 = StringUtil::digitAt($tin, 7); - $sum = $c1 * 8 + $c2 * 7 + $c3 * 6 + $c4 * 5 + $c5 * 4 + $c6 * 3 + $c7 * 2; - $remainderBy11 = $sum % 11; - return $c8 == 11 - $remainderBy11 || (11 - $remainderBy11 == 10 && $c8 == 0); - } -} diff --git a/src/Algo/SKAlgorithm.php b/src/Algo/SKAlgorithm.php deleted file mode 100644 index 2224fcb..0000000 --- a/src/Algo/SKAlgorithm.php +++ /dev/null @@ -1,31 +0,0 @@ -isFollowLength($str)) { - return StatusCode::INVALID_LENGTH; - } - return StatusCode::VALID; - } - - public function isFollowLength(string $tin) - { - $c1c2 = StringUtil::substring($tin, 0, 2); - if ($c1c2 < 54) { - return StringUtil::isFollowLength($tin, self::LENGTH - 1); - } - return StringUtil::isFollowLength($tin, self::LENGTH); - } -} diff --git a/src/Algo/StatusCode.php b/src/Algo/StatusCode.php deleted file mode 100644 index 0e25d53..0000000 --- a/src/Algo/StatusCode.php +++ /dev/null @@ -1,16 +0,0 @@ -validate($tin); - } - - /** - * @param string $tin - * @return integer A value from StatusCode class - */ - public abstract function validate(string $tin); -} diff --git a/src/Algo/TINAlgorithmInterface.php b/src/Algo/TINAlgorithmInterface.php deleted file mode 100644 index 7f3c954..0000000 --- a/src/Algo/TINAlgorithmInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -isFollowLength1($normalizedTIN) && !$this->isFollowLength2($normalizedTIN)) { - return StatusCode::INVALID_LENGTH; - } - if (($this->isFollowLength1($normalizedTIN) && !$this->isFollowPattern1($normalizedTIN)) || ($this->isFollowLength2($normalizedTIN) && !$this->isFollowPattern2($normalizedTIN))) { - return StatusCode::INVALID_PATTERN; - } - return StatusCode::VALID; - } - - public function isFollowLength1(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_1); - } - - public function isFollowPattern1(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_1); - } - - public function isFollowLength2(string $tin) - { - return StringUtil::isFollowLength($tin, self::LENGTH_2); - } - - public function isFollowPattern2(string $tin) - { - return StringUtil::isFollowPattern($tin, self::PATTERN_2) && $this->isFollowStructureSubRule2($tin); - } - - public function isFollowStructureSubRule2(string $tin) - { - $c1c2 = strtoupper(StringUtil::substring($tin, 0, 2)); - return !("GB" == $c1c2) && !("NK" == $c1c2) && !("TN" == $c1c2) && !("ZZ" == $c1c2); - } -} diff --git a/src/CountryHandler/Austria.php b/src/CountryHandler/Austria.php new file mode 100644 index 0000000..7ed1730 --- /dev/null +++ b/src/CountryHandler/Austria.php @@ -0,0 +1,54 @@ +digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + + $sum = (int) array_sum([ + $c1, + $c3, + $c5, + $c7, + $this->digitsSum($c2 * 2), + $this->digitsSum($c4 * 2), + $this->digitsSum($c6 * 2), + $this->digitsSum($c8 * 2), + ]); + + $check = $this->getLastDigit(100 - $sum); + + return $c9 === $check; + } +} diff --git a/src/CountryHandler/Belgium.php b/src/CountryHandler/Belgium.php new file mode 100644 index 0000000..461b7ba --- /dev/null +++ b/src/CountryHandler/Belgium.php @@ -0,0 +1,93 @@ +getDateType($tin); + } + + protected function hasValidRule(string $tin): bool + { + return $this->isFollowBelgiumRule1AndIsDateValid($tin) || $this->isFollowBelgiumRule2AndIsDateValid($tin); + } + + private function getDateType(string $tin): int + { + $year = (int) (substr($tin, 0, 2)); + $month = (int) (substr($tin, 2, 2)); + $day = (int) (substr($tin, 4, 2)); + + $y1 = checkdate($month, $day, 1900 + $year); + $y2 = checkdate($month, $day, 2000 + $year); + + if (0 === $day || 0 === $month || $y1 && $y2) { + return 3; + } + + if ($y1) { + return 1; + } + + if ($y2) { + return 2; + } + + return 0; + } + + private function isFollowBelgiumRule1(string $tin): bool + { + $divisionRemainderBy97 = (int) (substr($tin, 0, 9)) % 97; + + return 97 - $divisionRemainderBy97 === (int) (substr($tin, 9, 3)); + } + + private function isFollowBelgiumRule1AndIsDateValid(string $tin): bool + { + $dateType = $this->getDateType($tin); + + return $this->isFollowBelgiumRule1($tin) && (1 === $dateType || 3 === $dateType); + } + + private function isFollowBelgiumRule2(string $tin): bool + { + $divisionRemainderBy97 = (2 + (int) substr($tin, 0, 9)) % 97; + + return 97 - $divisionRemainderBy97 === (int) (substr($tin, 9, 3)); + } + + private function isFollowBelgiumRule2AndIsDateValid(string $tin): bool + { + $dateType = $this->getDateType($tin); + + return $this->isFollowBelgiumRule2($tin) && 2 <= $dateType; + } +} diff --git a/src/CountryHandler/Bulgaria.php b/src/CountryHandler/Bulgaria.php new file mode 100644 index 0000000..0966f30 --- /dev/null +++ b/src/CountryHandler/Bulgaria.php @@ -0,0 +1,70 @@ += $month) { + return checkdate($month - 20, $day, 1800 + $year); + } + + if (41 <= $month && 52 >= $month) { + return checkdate($month - 40, $day, 2000 + $year); + } + + return checkdate($month, $day, 1900 + $year); + } + + protected function hasValidRule(string $tin): bool + { + $c1 = $this->digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + $c10 = $this->digitAt($tin, 9); + $sum = $c1 * 2 + $c2 * 4 + $c3 * 8 + $c4 * 5 + $c5 * 10 + $c6 * 9 + $c7 * 7 + $c8 * 3 + $c9 * 6; + $remainderBy11 = $sum % 11; + + if (10 === $remainderBy11) { + return 0 === $c10; + } + + return $remainderBy11 === $c10; + } +} diff --git a/src/CountryHandler/CountryHandler.php b/src/CountryHandler/CountryHandler.php new file mode 100644 index 0000000..94c8860 --- /dev/null +++ b/src/CountryHandler/CountryHandler.php @@ -0,0 +1,198 @@ +tin = $tin; + } + + /** + * @return string + */ + public function getTIN(): string + { + if (null !== $string = preg_replace('#[^[:alnum:]\-+]#u', '', $this->tin)) { + return strtoupper($string); + } + + return ''; + } + + /** + * {@inheritdoc} + */ + final public static function supports(string $country): bool + { + return strtoupper($country) === strtoupper(static::COUNTRYCODE); + } + + final public function validate(): bool + { + $tin = $this->getTIN(); + + if (!$this->hasValidLength($tin)) { + throw TINException::invalidLength(); + } + + if (!$this->hasValidPattern($tin)) { + throw TINException::invalidPattern(); + } + + if (!$this->hasValidDate($tin)) { + throw TINException::invalidDate(); + } + + if (!$this->hasValidRule($tin)) { + throw TINException::invalidSyntax(); + } + + return true; + } + + /** + * @param string $tin + * The TIN. + * + * @return CountryHandlerInterface + */ + final public function withTIN(string $tin): CountryHandlerInterface + { + $clone = clone $this; + $clone->tin = $tin; + + return $clone; + } + + /** + * Get digit at a given position. + * + * @param string $str + * @param int $index + * + * @return int + */ + protected function digitAt(string $str, int $index): int + { + return (int) $str[$index] ?? 0; + } + + /** + * @param int $int + * + * @return int + */ + protected function digitsSum(int $int): int + { + return array_reduce( + (array) str_split((string) $int), + static function (int $carry, string $digit): int { + return $carry + (int) $digit; + }, + 0 + ); + } + + /** + * Get the alphabetical position. + * + * eg: A = 1 + * + * @param string $character + * + * @return int + */ + protected function getAlphabeticalPosition(string $character): int + { + return false !== ($return = array_combine(range('a', 'z'), range(1, 26))) ? + $return[strtolower($character)] : + 0; + } + + /** + * @param int $number + * + * @return int + */ + protected function getLastDigit(int $number): int + { + $split = (array) str_split((string) $number); + + return (int) end($split); + } + + protected function hasValidDate(string $tin): bool + { + return true; + } + + /** + * Match length. + * + * @param string $tin + * + * @return bool + */ + protected function hasValidLength(string $tin): bool + { + return $this->matchLength($this->getTIN(), static::LENGTH); + } + + /** + * @param string $tin + * + * @return bool + */ + protected function hasValidPattern(string $tin): bool + { + return $this->matchPattern($this->getTIN(), static::PATTERN); + } + + protected function hasValidRule(string $tin): bool + { + return true; + } + + /** + * @param string $tin + * @param int $length + * + * @return bool + */ + protected function matchLength(string $tin, int $length): bool + { + return strlen($tin) === $length; + } + + /** + * @param string $subject + * @param string $pattern + * + * @return bool + */ + protected function matchPattern(string $subject, string $pattern): bool + { + return 1 === preg_match(sprintf('/%s/', $pattern), $subject); + } +} diff --git a/src/CountryHandler/CountryHandlerInterface.php b/src/CountryHandler/CountryHandlerInterface.php new file mode 100644 index 0000000..ec5e006 --- /dev/null +++ b/src/CountryHandler/CountryHandlerInterface.php @@ -0,0 +1,55 @@ +digitAt($tin, 0) + 10; + + for ($i = 1; 11 > $i; ++$i) { + $rest = $sum % 10; + $rest = ((0 === $rest) ? 10 : $rest) * 2 % 11; + $sum = $rest + $this->digitAt($tin, $i); + } + $diff = 11 - $rest; + $lastDigit = $this->digitAt($tin, 10); + + return (1 === $rest && 0 === $lastDigit) || $lastDigit === $diff; + } +} diff --git a/src/CountryHandler/Cyprus.php b/src/CountryHandler/Cyprus.php new file mode 100644 index 0000000..ea2ec93 --- /dev/null +++ b/src/CountryHandler/Cyprus.php @@ -0,0 +1,85 @@ +digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = ord($tin[8]); + + $evenPositionNumbersSum = $c2 + $c4 + $c6 + $c8; + + $recodedSum = array_sum([ + $this->recodeValue($c1), + $this->recodeValue($c3), + $this->recodeValue($c5), + $this->recodeValue($c7), + ]); + + $remainderBy26 = ($evenPositionNumbersSum + $recodedSum) % 26; + + return $remainderBy26 + 65 === $c9; + } + + /** + * @param int $x + * + * @return int + */ + private function recodeValue(int $x): int + { + switch ($x) { + case 1: + return 0; + case 2: + return 5; + case 3: + return 7; + case 4: + return 9; + case 5: + return 13; + case 6: + return 15; + case 7: + return 17; + case 8: + return 19; + case 9: + return 21; + } + + return 1; + } +} diff --git a/src/CountryHandler/CzeckRepublic.php b/src/CountryHandler/CzeckRepublic.php new file mode 100644 index 0000000..0f03611 --- /dev/null +++ b/src/CountryHandler/CzeckRepublic.php @@ -0,0 +1,158 @@ +\d{2})(?\d{2})(?\d{2})(?\/)?(?\d{3})(?\d{1})?$'; + + // phpcs:enable + + /** + * @var int + */ + private const MODULO = 11; + + /** + * @var int + */ + private const MONTH_AFTER_2004 = 20; + + /** + * @var int + */ + private const MONTH_FEMALE = 50; + + /** + * @param string $tin + * + * @return bool + */ + protected function hasValidDate(string $tin): bool + { + // If we reach this point, it means that it's already validated. + preg_match(sprintf('/%s/', self::PATTERN), $tin, $matches); + + $hasModulo = array_key_exists('modulo', $matches) && '' !== $matches['modulo']; + + // range of months from 1 to 12 + $allowedMonths = array_merge( + range( + 1, + 12 + ), // male + range( + 1 + self::MONTH_FEMALE, + 12 + self::MONTH_FEMALE + ) // female + ); + + // from year 2004 there can be people with +20 in their month number + // without modulo check it would work for people born between 1904 and 19{last_two_digits_of_current_year} too + if (true === $hasModulo && 4 <= $matches['year'] && date('y') >= $matches['year']) { + $allowedMonths = array_merge( + $allowedMonths, + range( + 1 + self::MONTH_AFTER_2004, + 12 + self::MONTH_AFTER_2004 + ), // male + range( + 1 + self::MONTH_FEMALE + self::MONTH_AFTER_2004, + 12 + self::MONTH_FEMALE + self::MONTH_AFTER_2004 + ) // female + ); + } + + if (!in_array((int) $matches['month'], $allowedMonths, true)) { + return false; + } + + // day is between 1 and 31 + if (1 > $matches['day'] || 31 < $matches['day']) { + return false; + } + + return true; + } + + protected function hasValidLength(string $tin): bool + { + return $this->isFollowLength1($tin) || $this->isFollowLength2($tin); + } + + protected function hasValidRule(string $tin): bool + { + // If we reach this point, it means that it's already validated. + preg_match(sprintf('/%s/', self::PATTERN), $tin, $matches); + + $hasModulo = array_key_exists('modulo', $matches) && '' !== $matches['modulo']; + + // after year 1953 everyone should have modulo + // this validation does not work for people born since year 2000 + if (53 < $matches['year'] && false === $hasModulo) { + return false; + } + + // if there is no modulo then sequence can be between 001 and 999 + if (false === $hasModulo && 1 > $matches['sequence']) { + return false; + } + + // number's modulo should be 0 + if (true === $hasModulo) { + $number = (int) $matches['year'] . $matches['month'] . $matches['day'] . $matches['sequence']; + $modulo = $number % self::MODULO; + + // from year 1954 to 1985 and sometimes even after that, modulo can be 10 which results in 0 as modulo + if (10 === $modulo) { + $modulo = 0; + } + + if (((int) $matches['modulo']) !== $modulo) { + return false; + } + } + + return true; + } + + private function isFollowLength1(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_1); + } + + private function isFollowLength2(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_2); + } +} diff --git a/src/CountryHandler/Denmark.php b/src/CountryHandler/Denmark.php new file mode 100644 index 0000000..9c63e1e --- /dev/null +++ b/src/CountryHandler/Denmark.php @@ -0,0 +1,113 @@ += $yearOfBirth && 5000 <= $serialNumber && 8999 >= $serialNumber) { + return false; + } + + $excludedYears = [60, 64, 65, 66, 69, 70, 74, 80, 82, 84, 85, 86, 87, 88, 89, 90, 91, 92]; + + if (1 === $dayOfBirth && 1 === $monthOfBirth && in_array($yearOfBirth, $excludedYears, true)) { + return true; + } + + $c1 = $this->digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + $c10 = $this->digitAt($tin, 9); + $sum = $c1 * 4 + $c2 * 3 + $c3 * 2 + $c4 * 7 + $c5 * 6 + $c6 * 5 + $c7 * 4 + $c8 * 3 + $c9 * 2; + $remainderBy11 = $sum % 11; + + if (1 === $remainderBy11) { + return false; + } + + if (0 === $remainderBy11) { + return 0 === $c10; + } + + return 11 - $remainderBy11 === $c10; + } +} diff --git a/src/CountryHandler/Estonia.php b/src/CountryHandler/Estonia.php new file mode 100644 index 0000000..82bf647 --- /dev/null +++ b/src/CountryHandler/Estonia.php @@ -0,0 +1,83 @@ + $range)) { + return false; + } + + $c1 = $this->digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + $c10 = $this->digitAt($tin, 9); + $c11 = $this->digitAt($tin, 10); + $sum = $c1 + $c2 * 2 + $c3 * 3 + $c4 * 4 + $c5 * 5 + $c6 * 6 + $c7 * 7 + $c8 * 8 + $c9 * 9 + $c10; + $remainderBy11 = $sum % 11; + + return (10 > $remainderBy11 && $remainderBy11 === $c11) || + (10 === $remainderBy11 && $this->isFollowEstoniaRulePart2($tin)); + } + + private function isFollowEstoniaRulePart2(string $tin): bool + { + $c1 = $this->digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + $c10 = $this->digitAt($tin, 9); + $c11 = $this->digitAt($tin, 10); + $sum = $c1 * 3 + $c2 * 4 + $c3 * 5 + $c4 * 6 + $c5 * 7 + $c6 * 8 + $c7 * 9 + $c8 + $c9 * 2 + $c10 * 3; + $remainderBy11 = $sum % 11; + + return (10 > $remainderBy11 && $remainderBy11 === $c11) || (10 === $remainderBy11 && 0 === $c11); + } +} diff --git a/src/CountryHandler/Finland.php b/src/CountryHandler/Finland.php new file mode 100644 index 0000000..64f5fcf --- /dev/null +++ b/src/CountryHandler/Finland.php @@ -0,0 +1,108 @@ +getMatch($remainderBy31) === $c11; + } + + private function getMatch(int $number): string + { + if (10 > $number) { + return (string) $number; + } + + switch ($number) { + case 10: + return 'A'; + case 11: + return 'B'; + case 12: + return 'C'; + case 13: + return 'D'; + case 14: + return 'E'; + case 15: + return 'F'; + case 16: + return 'H'; + case 17: + return 'J'; + case 18: + return 'K'; + case 19: + return 'L'; + case 20: + return 'M'; + case 21: + return 'N'; + case 22: + return 'P'; + case 23: + return 'R'; + case 24: + return 'S'; + case 25: + return 'T'; + case 26: + return 'U'; + case 27: + return 'V'; + case 28: + return 'W'; + case 29: + return 'X'; + case 30: + return 'Y'; + + default: + return ' '; + } + } +} diff --git a/src/CountryHandler/France.php b/src/CountryHandler/France.php new file mode 100644 index 0000000..8ea7a47 --- /dev/null +++ b/src/CountryHandler/France.php @@ -0,0 +1,42 @@ + $remainderBy511 ? + 10 > $remainderBy511 ? (int) (substr($tin, 12, 13)) : (int) (substr($tin, 11, 13)) : + (int) (substr($tin, 10, 13)); + + return $remainderBy511 === $checkDigits; + } +} diff --git a/src/CountryHandler/Germany.php b/src/CountryHandler/Germany.php new file mode 100644 index 0000000..01e4e1d --- /dev/null +++ b/src/CountryHandler/Germany.php @@ -0,0 +1,217 @@ +isFollowLength1($tin) || $this->isFollowLength2($tin); + } + + protected function hasValidPattern(string $tin): bool + { + return $this->isFollowPattern1($tin) || $this->isFollowPattern2($tin); + } + + protected function hasValidRule(string $tin): bool + { + return ($this->isFollowLength1($tin) && $this->isFollowRuleGermany1($tin)) || + ($this->isFollowLength2($tin) && $this->isFollowRuleGermany2($tin)); + } + + private function calculateCheckDigit(string $tin): int + { + $chars = (array) str_split($tin); + $remainder_mod_eleven = 10; + + for ($length = strlen($tin), $counter = 0; $length - 1 > $counter; ++$counter) { + $digit = (int) ($chars[$counter]); + $remainder_mod_ten = ($digit + $remainder_mod_eleven) % 10; + + if (0 === $remainder_mod_ten) { + $remainder_mod_ten = 10; + } + + $remainder_mod_eleven = 2 * $remainder_mod_ten % 11; + } + + $digit = 11 - $remainder_mod_eleven; + + if (10 === $digit) { + $digit = 0; + } + + return $digit; + } + + private function isFollowLength1(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_1); + } + + private function isFollowLength2(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_2); + } + + private function isFollowPattern1(string $tin): bool + { + if (!$this->matchPattern($tin, self::PATTERN_1)) { + return false; + } + + $tab = []; + $pos = []; + + for ($i = 0; 10 > $i; ++$i) { + $tab[$i] = $this->digitAt($tin, $i); + $pos[$i] = 0; + } + + for ($j = 0; 10 > $j; ++$j) { + ++$pos[$tab[$j]]; + } + $isEncounteredTwice2 = false; + $isEncountered0 = false; + + for ($k = 0; 10 > $k; ++$k) { + if (2 === $pos[$k]) { + if ($isEncounteredTwice2) { + return false; + } + $isEncounteredTwice2 = true; + } + + if (0 === $pos[$k]) { + if ($isEncountered0) { + return false; + } + $isEncountered0 = true; + } + } + + return $isEncountered0; + } + + private function isFollowPattern2(string $tin): bool + { + if (!$this->matchPattern($tin, self::PATTERN_2)) { + return false; + } + + $tab = []; + $pos = []; + + for ($i = 0; 10 > $i; ++$i) { + $tab[$i] = $this->digitAt($tin, $i); + $pos[$i] = 0; + } + + for ($i = 0; 8 > $i; ++$i) { + if ($tab[$i + 1] === $tab[$i] && $tab[$i + 1] === $tab[$i + 2]) { + return false; + } + } + + for ($j = 0; 10 > $j; ++$j) { + ++$pos[$tab[$j]]; + } + $isEncounteredTwice2 = false; + $isEncounteredThrice3 = false; + + for ($k = 0; 10 > $k; ++$k) { + if (3 < $pos[$k]) { + return false; + } + + if (3 === $pos[$k]) { + if ($isEncounteredThrice3) { + return false; + } + $isEncounteredThrice3 = true; + } + + if (2 === $pos[$k]) { + if ($isEncounteredTwice2) { + return false; + } + $isEncounteredTwice2 = true; + } + } + + return $isEncounteredThrice3 || $isEncounteredTwice2; + } + + private function isFollowRuleGermany1(string $tin): bool + { + $c1 = $this->digitAt($tin, 0); + $c2 = []; + + for ($i = 0; 9 > $i; ++$i) { + $c2[$i] = $this->digitAt($tin, $i + 1); + } + $result = ($c1 + 10) % 10; + + if (0 === $result) { + $result = 10; + } + $result *= 2; + $x = $result % 11; + + for ($j = 0; 9 > $j; ++$j) { + $x = ($x + $c2[$j]) % 10; + + if (0 === $x) { + $x = 10; + } + $x *= 2; + $x %= 11; + } + $c3 = $this->digitAt($tin, 10); + $total = 11 - $x; + + if (10 === $total) { + return 0 === $c3; + } + + return $total === $c3; + } + + private function isFollowRuleGermany2(string $tin): bool + { + return $this->digitAt($tin, 10) === $this->calculateCheckDigit($tin); + } +} diff --git a/src/CountryHandler/Greece.php b/src/CountryHandler/Greece.php new file mode 100644 index 0000000..3b4c34a --- /dev/null +++ b/src/CountryHandler/Greece.php @@ -0,0 +1,21 @@ +digitAt($tin, 9); + $sum = 0; + + for ($i = 0; 9 > $i; ++$i) { + $c11 = (int) (substr($tin, $i, 1)); + $sum += $c11 * ($i + 1); + } + $remainderBy11 = $sum % 11; + + return $remainderBy11 === $c10; + } +} diff --git a/src/CountryHandler/Ireland.php b/src/CountryHandler/Ireland.php new file mode 100644 index 0000000..efb2204 --- /dev/null +++ b/src/CountryHandler/Ireland.php @@ -0,0 +1,102 @@ +isFollowLength1($tin) || $this->isFollowLength2($tin); + } + + protected function hasValidPattern(string $tin): bool + { + if ($this->isFollowLength1($tin) && !$this->isFollowPattern1()) { + return false; + } + + return !($this->isFollowLength2($tin) && !$this->isFollowPattern2()); + } + + protected function hasValidRule(string $tin): bool + { + $c1 = $this->digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c9 = (9 <= strlen($tin)) ? $this->letterToNumber($tin[8]) : 0; + $c8 = $tin[7]; + $sum = $c9 * 9 + $c1 * 8 + $c2 * 7 + $c3 * 6 + $c4 * 5 + $c5 * 4 + $c6 * 3 + $c7 * 2; + $remainderBy23 = $sum % 23; + + if (0 !== $remainderBy23) { + return $this->getAlphabeticalPosition($c8) === $remainderBy23; + } + + return 'W' === $c8 || 'w' === $c8; + } + + private function isFollowLength1(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_1); + } + + private function isFollowLength2(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_2); + } + + private function isFollowPattern1(): bool + { + return $this->matchPattern($this->getTIN(), self::PATTERN_1); + } + + private function isFollowPattern2(): bool + { + return $this->matchPattern($this->getTIN(), self::PATTERN_2); + } + + private function letterToNumber(string $toConv): int + { + if ('W' === $toConv || 'w' === $toConv) { + return 0; + } + + return $this->getAlphabeticalPosition($toConv); + } +} diff --git a/src/CountryHandler/Italy.php b/src/CountryHandler/Italy.php new file mode 100644 index 0000000..2aa6af1 --- /dev/null +++ b/src/CountryHandler/Italy.php @@ -0,0 +1,254 @@ + + */ + private $listSet = []; + + protected function hasValidDate(string $tin): bool + { + $day = (int) ($this->convertCharToNumber(substr($tin, 9, 2))); + $c9 = $tin[8]; + $month = $this->getMonthNumber($c9); + $year = (int) ($this->convertCharToNumber(substr($tin, 6, 2))); + + if (1 <= $day && 31 >= $day) { + $d1 = checkdate($month, $day, 1900 + $year); + $d2 = checkdate($month, $day, 2000 + $year); + + return $d1 || $d2; + } + + $d1 = checkdate($month, $day - 40, 1900 + $year); + $d2 = checkdate($month, $day - 40, 2000 + $year); + + return 41 <= $day && 71 >= $day && ($d1 || $d2); + } + + protected function hasValidPattern(string $tin): bool + { + if (false !== $listSet = file(self::IT_ID_LIST_PATH, FILE_IGNORE_NEW_LINES)) { + $this->listSet = $listSet; + } + + $code = substr($tin, 11, 1) . $this->convertCharToNumber(substr($tin, 12, 3)); + + $containsUpper = in_array($code, $this->listSet, true); + $containsLower = in_array(strtolower($code), $this->listSet, true); + + return ($containsUpper || $containsLower) && parent::hasValidPattern($tin); + } + + protected function hasValidRule(string $tin): bool + { + $sum = 0; + + for ($i = 0; 15 > $i; ++$i) { + $sum += 0 === $i % 2 ? + $this->convertOddCharacter($tin[$i]) : + $this->convertEvenCharacter($tin[$i]); + } + $remainderBy26 = $sum % 26; + $c16 = $tin[15]; + $check = $this->getAlphabeticalPosition($c16) - 1; + + return $remainderBy26 === $check; + } + + private function convertCharToNumber(string $oldStr): string + { + $newStr = ''; + + for ($i = 0; strlen($oldStr) > $i; ++$i) { + $newStr .= $this->getNumberFromChar($oldStr[$i]); + } + + return $newStr; + } + + private function convertEvenCharacter(string $c): int + { + if (is_numeric($c)) { + return (int) $c; + } + + return $this->getAlphabeticalPosition($c) - 1; + } + + private function convertOddCharacter(string $c): int + { + $normalizedChar = $c; + + switch ($normalizedChar) { + case '0': + case 'A': + return 1; + case '1': + case 'B': + return 0; + case '2': + case 'C': + return 5; + case '3': + case 'D': + return 7; + case '4': + case 'E': + return 9; + case '5': + case 'F': + return 13; + case '6': + case 'G': + return 15; + case '7': + case 'H': + return 17; + case '8': + case 'I': + return 19; + case '9': + case 'J': + return 21; + case 'K': + return 2; + case 'L': + return 4; + case 'M': + return 18; + case 'N': + return 20; + case 'O': + return 11; + case 'P': + return 3; + case 'Q': + return 6; + case 'R': + return 8; + case 'S': + return 12; + case 'T': + return 14; + case 'U': + return 16; + case 'V': + return 10; + case 'W': + return 22; + case 'X': + return 25; + case 'Y': + return 24; + case 'Z': + return 23; + + default: + return -1; + } + } + + private function getMonthNumber(string $m): int + { + switch ($m) { + case 'A': + return 1; + case 'B': + return 2; + case 'C': + return 3; + case 'D': + return 4; + case 'E': + return 5; + case 'H': + return 6; + case 'L': + return 7; + case 'M': + return 8; + case 'P': + return 9; + case 'R': + return 10; + case 'S': + return 11; + case 'T': + return 12; + + default: + return -1; + } + } + + private function getNumberFromChar(string $m): int + { + if (is_numeric($m)) { + return (int) $m; + } + + switch ($m) { + case 'L': + return 0; + case 'M': + return 1; + case 'N': + return 2; + case 'P': + return 3; + case 'Q': + return 4; + case 'R': + return 5; + case 'S': + return 6; + case 'T': + return 7; + case 'U': + return 8; + case 'V': + return 9; + + default: + return -1; + } + } +} diff --git a/src/CountryHandler/Latvia.php b/src/CountryHandler/Latvia.php new file mode 100644 index 0000000..c762e28 --- /dev/null +++ b/src/CountryHandler/Latvia.php @@ -0,0 +1,43 @@ + $i; ++$i) { + $sum += $this->multiplyAccordingToWeight((int) (substr($tin, $i, 1)), $i); + } + $remainderBy11 = $sum % 11; + + if (10 !== $remainderBy11) { + return $c11 === $remainderBy11; + } + $sum2 = 0; + + for ($j = 0; 10 > $j; ++$j) { + $sum2 += $this->multiplyAccordingToWeight2((int) (substr($tin, $j, 1)), $j); + } + $remainderBy11 = $sum2 % 11; + + if (10 === $remainderBy11) { + return 0 === $c11; + } + + return $c11 === $remainderBy11; + } + + private function multiplyAccordingToWeight(int $val, int $index): int + { + switch ($index) { + case 9: + case 0: + return $val * 1; + case 1: + return $val * 2; + case 2: + return $val * 3; + case 3: + return $val * 4; + case 4: + return $val * 5; + case 5: + return $val * 6; + case 6: + return $val * 7; + case 7: + return $val * 8; + case 8: + return $val * 9; + + default: + return -1; + } + } + + private function multiplyAccordingToWeight2(int $val, int $index): int + { + switch ($index) { + case 9: + case 0: + return $val * 3; + case 1: + return $val * 4; + case 2: + return $val * 5; + case 3: + return $val * 6; + case 4: + return $val * 7; + case 5: + return $val * 8; + case 6: + return $val * 9; + case 7: + return $val * 1; + case 8: + return $val * 2; + + default: + return -1; + } + } +} diff --git a/src/CountryHandler/Luxembourg.php b/src/CountryHandler/Luxembourg.php new file mode 100644 index 0000000..8e57219 --- /dev/null +++ b/src/CountryHandler/Luxembourg.php @@ -0,0 +1,121 @@ +> + */ + private static $D = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [1, 2, 3, 4, 0, 6, 7, 8, 9, 5], + [2, 3, 4, 0, 1, 7, 8, 9, 5, 6], + [3, 4, 0, 1, 2, 8, 9, 5, 6, 7], + [4, 0, 1, 2, 3, 9, 5, 6, 7, 8], + [5, 9, 8, 7, 6, 0, 4, 3, 2, 1], + [6, 5, 9, 8, 7, 1, 0, 4, 3, 2], + [7, 6, 5, 9, 8, 2, 1, 0, 4, 3], + [8, 7, 6, 5, 9, 3, 2, 1, 0, 4], + [9, 8, 7, 6, 5, 4, 3, 2, 1, 0], + ]; + + /** + * @var array> + */ + private static $P = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + [1, 5, 7, 6, 2, 8, 3, 0, 9, 4], + [5, 8, 0, 3, 7, 9, 6, 1, 4, 2], + [8, 9, 1, 6, 0, 4, 3, 5, 2, 7], + [9, 4, 5, 3, 1, 2, 6, 8, 7, 0], + [4, 2, 8, 6, 5, 7, 3, 9, 0, 1], + [2, 7, 9, 3, 8, 0, 6, 4, 1, 5], + [7, 0, 4, 6, 9, 1, 3, 2, 5, 8], + ]; + + protected function hasValidDate(string $tin): bool + { + $year = (int) (substr($tin, 0, 4)); + $month = (int) (substr($tin, 4, 2)); + $day = (int) (substr($tin, 6, 2)); + + return checkdate($month, $day, $year); + } + + protected function hasValidRule(string $tin): bool + { + return $this->isFollowLuxembourgRule1($tin) && $this->isFollowLuxembourgRule2($tin); + } + + private function isFollowLuxembourgRule1(string $tin): bool + { + $c1 = $this->digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + $c10 = $this->digitAt($tin, 9); + $c11 = $this->digitAt($tin, 10); + $c12 = $this->digitAt($tin, 11); + + $sum = $c2 + $c4 + $c6 + $c8 + $c10 + $c12 + + $this->digitsSum($c1 * 2) + + $this->digitsSum($c3 * 2) + + $this->digitsSum($c5 * 2) + + $this->digitsSum($c7 * 2) + + $this->digitsSum($c9 * 2) + + $this->digitsSum($c11 * 2); + + $remainderBy10 = $sum % 10; + + return 0 === $remainderBy10; + } + + private function isFollowLuxembourgRule2(string $tin): bool + { + $listNumbers = []; + + for ($i = 12; 0 <= $i; --$i) { + if (11 !== $i) { + $listNumbers[] = $this->digitAt($tin, $i); + } + } + $check = 0; + $listNumbersCount = count($listNumbers); + + for ($j = 0; $listNumbersCount > $j; ++$j) { + $item = $listNumbers[$j]; + $p = self::$P[$j % 8][$item]; + $check = self::$D[$check][$p]; + } + + return 0 === $check; + } +} diff --git a/src/CountryHandler/Malta.php b/src/CountryHandler/Malta.php new file mode 100644 index 0000000..9465048 --- /dev/null +++ b/src/CountryHandler/Malta.php @@ -0,0 +1,37 @@ +digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + $sum = $c1 * 9 + $c2 * 8 + $c3 * 7 + $c4 * 6 + $c5 * 5 + $c6 * 4 + $c7 * 3 + $c8 * 2; + $remainderBy11 = $sum % 11; + + if (10 === $remainderBy11) { + return false; + } + + return $c9 === $remainderBy11; + } +} diff --git a/src/CountryHandler/Poland.php b/src/CountryHandler/Poland.php new file mode 100644 index 0000000..87e6953 --- /dev/null +++ b/src/CountryHandler/Poland.php @@ -0,0 +1,163 @@ += $month) { + return checkdate($month, $day, 1900 + $year); + } + + if (21 <= $month && 32 >= $month) { + return checkdate($month - 20, $day, 2000 + $year); + } + + if (41 <= $month && 52 >= $month) { + return checkdate($month - 40, $day, 2100 + $year); + } + + if (61 <= $month && 72 >= $month) { + return checkdate($month - 60, $day, 2200 + $year); + } + + return 81 <= $month && 92 >= $month && checkdate($month - 80, $day, 1800 + $year); + } + + protected function hasValidLength(string $tin): bool + { + return $this->isFollowLength1($tin) || $this->isFollowLength2($tin); + } + + protected function hasValidPattern(string $tin): bool + { + return $this->isFollowLength1AndPattern1($tin) || $this->isFollowLength2AndPattern2AndIsValidDate($tin); + } + + protected function hasValidRule(string $tin): bool + { + return ($this->isFollowLength1($tin) && $this->isFollowRulePoland1($tin)) || + ($this->isFollowLength2($tin) && $this->isFollowRulePoland2($tin)); + } + + private function isFollowLength1(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_1); + } + + private function isFollowLength1AndPattern1(string $tin): bool + { + return $this->isFollowLength1($tin) && $this->isFollowPattern1($tin); + } + + private function isFollowLength2(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_2); + } + + private function isFollowLength2AndPattern2AndIsValidDate(string $tin): bool + { + return $this->isFollowLength2($tin) && $this->isFollowPattern2($tin) && $this->hasValidDateWhenPattern2($tin); + } + + private function isFollowPattern1(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_1); + } + + private function isFollowPattern2(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_2); + } + + private function isFollowRulePoland1(string $tin): bool + { + $map = [ + 6, + 5, + 7, + 2, + 3, + 4, + 5, + 6, + 7, + ]; + + $sum = 0; + + foreach ($map as $key => $weight) { + $sum += $this->digitAt($tin, $key) * $weight; + } + + $remainderBy11 = $sum % 11; + + if (10 === $remainderBy11) { + return false; + } + + // @todo: Optimize that + return $this->digitAt($tin, 9) === $remainderBy11; + } + + private function isFollowRulePoland2(string $tin): bool + { + $map = [ + 1, + 3, + 7, + 9, + 1, + 3, + 7, + 9, + 1, + 3, + ]; + + $sum = 0; + + foreach ($map as $key => $weight) { + $sum += $this->digitAt($tin, $key) * $weight; + } + + $lastDigit = $sum % 10; + + return 10 - $lastDigit === $this->digitAt($tin, 10); + } +} diff --git a/src/CountryHandler/Portugal.php b/src/CountryHandler/Portugal.php new file mode 100644 index 0000000..4f1ce3a --- /dev/null +++ b/src/CountryHandler/Portugal.php @@ -0,0 +1,52 @@ +digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + $sum = $c1 * 9 + $c2 * 8 + $c3 * 7 + $c4 * 6 + $c5 * 5 + $c6 * 4 + $c7 * 3 + $c8 * 2; + $remainderBy11 = $sum % 11; + $checkDigit = 11 - $remainderBy11; + + if (9 >= $checkDigit) { + return $checkDigit === $c9; + } + + if (10 === $checkDigit) { + return 0 === $c9; + } + + return 0 === $c9; + } +} diff --git a/src/CountryHandler/Romania.php b/src/CountryHandler/Romania.php new file mode 100644 index 0000000..08ae35c --- /dev/null +++ b/src/CountryHandler/Romania.php @@ -0,0 +1,55 @@ + $c1c2) { + return $this->matchLength($tin, self::LENGTH - 1); + } + + return parent::hasValidLength($tin); + } +} diff --git a/src/CountryHandler/Slovenia.php b/src/CountryHandler/Slovenia.php new file mode 100644 index 0000000..74c71aa --- /dev/null +++ b/src/CountryHandler/Slovenia.php @@ -0,0 +1,54 @@ +isFollowRangeRule($tin) && $this->isFollowSloveniaRule($tin); + } + + private function isFollowRangeRule(string $tin): bool + { + $intTIN = (int) (substr($tin, 0, 7)); + + return 999999 < $intTIN && 10000000 > $intTIN; + } + + private function isFollowSloveniaRule(string $tin): bool + { + $c1 = $this->digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $sum = $c1 * 8 + $c2 * 7 + $c3 * 6 + $c4 * 5 + $c5 * 4 + $c6 * 3 + $c7 * 2; + $remainderBy11 = $sum % 11; + + return 11 - $remainderBy11 === $c8 || (10 === 11 - $remainderBy11 && 0 === $c8); + } +} diff --git a/src/CountryHandler/Spain.php b/src/CountryHandler/Spain.php new file mode 100644 index 0000000..ec75589 --- /dev/null +++ b/src/CountryHandler/Spain.php @@ -0,0 +1,116 @@ + + */ + private static $tabConvertToChar = [ + 'T', 'R', 'W', 'A', 'G', + 'M', 'Y', 'F', 'P', 'D', + 'X', 'B', 'N', 'J', 'Z', + 'S', 'Q', 'V', 'H', 'L', + 'C', 'K', 'E', + ]; + + public function getTIN(): string + { + return str_pad(parent::getTIN(), self::LENGTH, '0', STR_PAD_LEFT); + } + + protected function hasValidPattern(string $tin): bool + { + return $this->isFollowPattern1($tin) || $this->isFollowPattern2($tin); + } + + protected function hasValidRule(string $tin): bool + { + return ($this->isFollowPattern1($tin) && $this->isFollowRule1($tin)) || + ($this->isFollowPattern2($tin) && $this->isFollowRule2($tin)); + } + + private function getCharFromNumber(int $sum): string + { + return self::$tabConvertToChar[$sum - 1]; + } + + private function getNumberFromChar(string $m): int + { + switch ($m) { + case 'K': + case 'L': + case 'M': + case 'X': + return 0; + case 'Y': + return 1; + case 'Z': + return 2; + + default: + return -1; + } + } + + private function isFollowPattern1(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_1); + } + + private function isFollowPattern2(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_2); + } + + private function isFollowRule1(string $tin): bool + { + $number = (int) (substr($tin, 0, strlen($tin) - 1)); + $checkDigit = $tin[strlen($tin) - 1]; + $remainderBy23 = $number % 23; + $sum = $remainderBy23 + 1; + + return $this->getCharFromNumber($sum) === $checkDigit; + } + + private function isFollowRule2(string $tin): bool + { + $c1 = (string) $this->getNumberFromChar($tin[0]); + $number = (int) ($c1 . substr($tin, 1, strlen($tin))); + $checkDigit = $tin[strlen($tin) - 1]; + $remainderBy23 = $number % 23; + $sum = $remainderBy23 + 1; + + return $this->getCharFromNumber($sum) === $checkDigit; + } +} diff --git a/src/CountryHandler/Sweden.php b/src/CountryHandler/Sweden.php new file mode 100644 index 0000000..8279fc5 --- /dev/null +++ b/src/CountryHandler/Sweden.php @@ -0,0 +1,212 @@ +isFollowLength1And2($tin) || $this->isFollowLength3And4($tin); + } + + protected function hasValidPattern(string $tin): bool + { + return $this->isFollowPattern1AndIsValidDate1($tin) + || $this->isFollowPattern2AndIsValidDate2($tin) + || $this->isFollowPattern3AndIsValidDate3($tin) + || $this->isFollowPattern4AndIsValidDate4($tin); + } + + protected function hasValidRule(string $tin): bool + { + return ((int) '10' === strlen($tin) + && $this->isFollowSwedenRule1And2($tin)) + || ((int) '12' === strlen($tin) + && $this->isFollowSwedenRule3And4($tin)); + } + + private function hasValidDate1(string $tin): bool + { + $year = (int) (substr($tin, 0, 2)); + $month = (int) (substr($tin, 2, 2)); + $day = (int) (substr($tin, 4, 2)); + + return checkdate($month, $day, 1900 + $year) || checkdate($month, $day, 2000 + $year); + } + + private function hasValidDate2(string $tin): bool + { + $year = (int) (substr($tin, 0, 2)); + $month = (int) (substr($tin, 2, 2)); + $day = (int) (substr($tin, 4, 2)); + + return checkdate($month, $day - 60, 1900 + $year) || checkdate($month, $day - 60, 2000 + $year); + } + + private function hasValidDate3(string $tin): bool + { + $year = (int) (substr($tin, 0, 4)); + $month = (int) (substr($tin, 4, 2)); + $day = (int) (substr($tin, 6, 2)); + + return checkdate($month, $day, $year); + } + + private function hasValidDate4(string $tin): bool + { + $year = (int) (substr($tin, 0, 4)); + $month = (int) (substr($tin, 4, 2)); + $day = (int) (substr($tin, 6, 2)); + + return checkdate($month, $day - 60, $year); + } + + private function isFollowLength1And2(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_1_AND_2); + } + + private function isFollowLength3And4(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_3_AND_4); + } + + private function isFollowPattern1(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_1); + } + + private function isFollowPattern1AndIsValidDate1(string $tin): bool + { + return $this->isFollowPattern1($tin) && $this->hasValidDate1($tin); + } + + private function isFollowPattern2(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_2); + } + + private function isFollowPattern2AndIsValidDate2(string $tin): bool + { + return $this->isFollowPattern2($tin) && $this->hasValidDate2($tin); + } + + private function isFollowPattern3(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_3); + } + + private function isFollowPattern3AndIsValidDate3(string $tin): bool + { + return $this->isFollowPattern3($tin) && $this->hasValidDate3($tin); + } + + private function isFollowPattern4(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_4); + } + + private function isFollowPattern4AndIsValidDate4(string $tin): bool + { + return $this->isFollowPattern4($tin) && $this->hasValidDate4($tin); + } + + private function isFollowSwedenRule1And2(string $tin): bool + { + $c1 = $this->digitAt($tin, 0); + $c2 = $this->digitAt($tin, 1); + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + $c10 = $this->digitAt($tin, 9); + + $sum = $c2 + $c4 + $c6 + $c8 + + $this->digitsSum($c1 * 2) + + $this->digitsSum($c3 * 2) + + $this->digitsSum($c5 * 2) + + $this->digitsSum($c7 * 2) + + $this->digitsSum($c9 * 2); + + $check = 10 - $this->getLastDigit($sum); + + if (10 !== $check) { + return $c10 === $check; + } + + return 0 === $c10; + } + + private function isFollowSwedenRule3And4(string $tin): bool + { + $c3 = $this->digitAt($tin, 2); + $c4 = $this->digitAt($tin, 3); + $c5 = $this->digitAt($tin, 4); + $c6 = $this->digitAt($tin, 5); + $c7 = $this->digitAt($tin, 6); + $c8 = $this->digitAt($tin, 7); + $c9 = $this->digitAt($tin, 8); + $c10 = $this->digitAt($tin, 9); + $c11 = $this->digitAt($tin, 10); + $c12 = $this->digitAt($tin, 11); + + $sum = $c4 + $c6 + $c8 + $c10 + + $this->digitsSum($c3 * 2) + + $this->digitsSum($c5 * 2) + + $this->digitsSum($c7 * 2) + + $this->digitsSum($c9 * 2) + + $this->digitsSum($c11 * 2); + $check = 10 - $this->getLastDigit($sum); + + if (10 !== $check) { + return $c12 === $check; + } + + return 0 === $c12; + } +} diff --git a/src/CountryHandler/UnitedKingdom.php b/src/CountryHandler/UnitedKingdom.php new file mode 100644 index 0000000..586171a --- /dev/null +++ b/src/CountryHandler/UnitedKingdom.php @@ -0,0 +1,90 @@ +isFollowLength1($tin) || $this->isFollowLength2($tin); + } + + protected function hasValidPattern(string $tin): bool + { + if ($this->isFollowLength1($tin) && !$this->isFollowPattern1($tin)) { + return false; + } + + return !($this->isFollowLength2($tin) && !$this->isFollowPattern2($tin)); + } + + private function isFollowLength1(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_1); + } + + private function isFollowLength2(string $tin): bool + { + return $this->matchLength($tin, self::LENGTH_2); + } + + private function isFollowPattern1(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_1); + } + + private function isFollowPattern2(string $tin): bool + { + return $this->matchPattern($tin, self::PATTERN_2) && $this->isFollowStructureSubRule2($tin); + } + + private function isFollowStructureSubRule2(string $tin): bool + { + $c1c2 = substr($tin, 0, 2); + + return 'GB' !== $c1c2 && 'NK' !== $c1c2 && 'TN' !== $c1c2 && 'ZZ' !== $c1c2; + } +} diff --git a/src/Exception/InvalidCountryException.php b/src/Exception/InvalidCountryException.php deleted file mode 100644 index 3ca57ca..0000000 --- a/src/Exception/InvalidCountryException.php +++ /dev/null @@ -1,10 +0,0 @@ - + */ + private $algorithms = [ + 'AT' => Austria::class, + 'BE' => Belgium::class, + 'BG' => Bulgaria::class, + 'CY' => Cyprus::class, + 'CZ' => CzeckRepublic::class, + 'DE' => Germany::class, + 'DK' => Denmark::class, + 'EE' => Estonia::class, + 'ES' => Spain::class, + 'FI' => Finland::class, + 'FR' => France::class, + 'GR' => Greece::class, + 'HR' => Croatia::class, + 'HU' => Hungary::class, + 'IE' => Ireland::class, + 'IT' => Italy::class, + 'LT' => Lithuania::class, + 'LU' => Luxembourg::class, + 'LV' => Latvia::class, + 'MT' => Malta::class, + 'NL' => Netherlands::class, + 'PL' => Poland::class, + 'PT' => Portugal::class, + 'RO' => Romania::class, + 'SE' => Sweden::class, + 'SI' => Slovenia::class, + 'SK' => Slovakia::class, + 'UK' => UnitedKingdom::class, + ]; + + /** + * @var string + */ + private $slug; + + /** + * @throws TINException + * + * @return bool + */ + public function check(): bool + { + $parsedTin = $this->parse($this->slug); + + return $this->getAlgorithm($parsedTin['country'], $parsedTin['tin'])->validate(); + } + + /** + * @param string $countryCode + * @param string $tin + * + * @return TIN + */ + public static function from(string $countryCode, string $tin): TIN + { + return self::fromSlug($countryCode . $tin); + } + + /** + * @param string $slug + * + * @return TIN + */ + public static function fromSlug(string $slug): TIN + { + $instance = new self(); + + $instance->slug = $slug; + + return $instance; + } + + /** + * @return bool + */ + public function isValid(): bool + { + try { + $this->check(); + } catch (TINException $e) { + return false; + } + + return true; + } + + /** + * @param string $country + * @param string|null $tin + * + * @throws TINException + * + * @return CountryHandlerInterface + */ + private function getAlgorithm(string $country, ?string $tin = null): CountryHandlerInterface + { + foreach ($this->algorithms as $algorithm) { + if (true === $algorithm::supports($country)) { + return new $algorithm($tin); + } + } + + throw TINException::invalidCountry($country); + } + + /** + * @param string $slug + * + * @throws TINException + * + * @return array + */ + private function parse(string $slug): array + { + $matches = []; + $pattern = '(?[[:alpha:]]{2})(?([^[:alpha:]])([[:alnum:]\-+]+))'; + + // [alpha][alpha][not-alpha](anything) + if (0 === preg_match(sprintf('/^%s$/', $pattern), $slug, $matches)) { + throw TINException::invalidPattern(); + } + + return array_intersect_key($matches, array_flip(['country', 'tin'])); + } +} diff --git a/src/TINValid.php b/src/TINValid.php deleted file mode 100644 index 87a7979..0000000 --- a/src/TINValid.php +++ /dev/null @@ -1,108 +0,0 @@ -isValid($tin); - - if ($statusCode !== 0) { - $message = self::getMessageForStatusCode($statusCode); - throw new TINValidationException($message, $statusCode); - } - } - - /** - * Check if country is supported - * - * @param string $countryCode - * @return boolean - */ - public static function isCountrySupported(string $countryCode) - { - try { - self::getAlgoForCountry($countryCode); - return true; - } catch (InvalidCountryException $ex) { - return false; - } - } - - /** - * @param integer $statusCode - * @return string - */ - protected static function getMessageForStatusCode($statusCode) - { - switch ($statusCode) { - case 0: - return "Valid"; - case -1: - return "No Information"; - case 2: - return "No Syntax Checker"; - case 1: - return "Invalid Syntax"; - case 4: - return "Invalid Length"; - case 3: - return "Invalid Pattern"; - default: - return "Default"; - } - } - - /** - * @param string $countryCode - * @return TINAlgorithmInterface - */ - protected static function getAlgoForCountry(string $countryCode) - { - if (strlen($countryCode) != 2) { - throw new InvalidArgumentException("Country code should be 2 chars long."); - } - $class = "LeKoala\\Tin\\Algo\\" . strtoupper($countryCode) . "Algorithm"; - if (!class_exists($class)) { - throw new InvalidCountryException("Algorithm '$class' was not found."); - } - return new $class; - } -} diff --git a/src/Util/DateUtil.php b/src/Util/DateUtil.php deleted file mode 100644 index fcf90aa..0000000 --- a/src/Util/DateUtil.php +++ /dev/null @@ -1,47 +0,0 @@ -= 1 && $month <= 12 && $day >= 1 && $day <= self::getLastDayOfMonth($month, $year); - } -} diff --git a/src/Util/NumberUtil.php b/src/Util/NumberUtil.php deleted file mode 100644 index 45c29df..0000000 --- a/src/Util/NumberUtil.php +++ /dev/null @@ -1,86 +0,0 @@ - $minValue && $value < $maxValue; - } - - /** - * @param array $numbers - * @return integer - */ - public function getMinValue($numbers) - { - $minValue = $numbers[0]; - for ($i = 1; $i < count($numbers); $i++) { - if ($numbers[$i] < $minValue) { - $minValue = $numbers[$i]; - } - } - return $minValue; - } -} diff --git a/src/Util/StringUtil.php b/src/Util/StringUtil.php deleted file mode 100644 index 33e6de0..0000000 --- a/src/Util/StringUtil.php +++ /dev/null @@ -1,180 +0,0 @@ - $len - 1) { - return $s; - } - $tmp = $s[$pos]; - if ($tmp == $c) { - $prefix = ""; - if ($pos <= $len) { - $prefix = substr($s, 0, $pos); - } - $suffix = ""; - if ($pos + 1 <= $len - 1) { - $suffix = substr($s, $pos + 1); - } - return $prefix . $suffix; - } - return $s; - } - - /** - * @param string $tin - * @param integer $length - * @return string - */ - public static function fillWith0UntilLength(string $tin, int $length) - { - $normalizedTIN = $tin; - while (strlen($normalizedTIN) < $length) { - $normalizedTIN = "0" . $normalizedTIN; - } - return $normalizedTIN; - } -} diff --git a/tests/ATAlgorithmTest.php b/tests/ATAlgorithmTest.php deleted file mode 100644 index 55331b0..0000000 --- a/tests/ATAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('at', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('at', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('at', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/BEAlgorithmTest.php b/tests/BEAlgorithmTest.php deleted file mode 100644 index e743e2a..0000000 --- a/tests/BEAlgorithmTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertTrue(TINValid::checkTIN('be', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('be', self::VALID_NUMBER2)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('be', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('be', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/BGAlgorithmTest.php b/tests/BGAlgorithmTest.php deleted file mode 100644 index b189e7a..0000000 --- a/tests/BGAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('bg', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('bg', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('bg', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/CYAlgorithmTest.php b/tests/CYAlgorithmTest.php deleted file mode 100644 index e06fc93..0000000 --- a/tests/CYAlgorithmTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertTrue(TINValid::checkTIN('cy', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('cy', self::VALID_NUMBER2)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('cy', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('cy', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/CZAlgorithmTest.php b/tests/CZAlgorithmTest.php deleted file mode 100644 index 2d9269c..0000000 --- a/tests/CZAlgorithmTest.php +++ /dev/null @@ -1,119 +0,0 @@ -assertTrue(TINValid::checkTIN('cz', $value), $message); - } - - /** - * @return array - */ - public function validProvider() - { - return [ - ['000101999', 'spec1'], - ['000101999C', 'spec2'], - ['103224/0000', 'male born 2010-12-24 with +20'], - ['108224/0016', 'female born 2010-12-24 with +20'], - ['901224/0006', 'male born 1990-12-24'], - ['906224/0011', 'female born 1990-12-24'], - ['401224/001', 'male born 1940-12-24'], - ['406224/002', 'female born 1940-12-24'], - ]; - } - - public function testInvalidLength() - { - $this->assertFalse( - TINValid::checkTIN('cz', '01224/0006') - ); - } - - public function testInvalidCharacter() - { - $this->assertFalse( - TINValid::checkTIN('cz', '90A224/0006') - ); - } - - public function testInvalidMonth() - { - $this->assertFalse( - TINValid::checkTIN('cz', '901524/0006') - ); - } - - // public function testPlus20InMonthInWrongYear() - // { - // $this->assertFalse( - // TINValid::checkTIN('cz', '902124/0003') - // ); - // } - - // public function testPlus20InMonthInSeeminglyCorrectYearDifferentiatedByMissingModulo() - // { - // $this->assertFalse( - // TINValid::checkTIN('cz', '052124/001') - // ); - // } - - public function testDayShouldNotBeZero() - { - $this->assertFalse( - TINValid::checkTIN('cz', '501200/001') - ); - } - - public function testDayShouldNotBeGreaterThan31() - { - $this->assertFalse( - TINValid::checkTIN('cz', '500132/001') - ); - } - - // public function testAfterYear53ModuloIsRequired() - // { - // $this->assertFalse( - // TINValid::checkTIN('cz', '540101/001') - // ); - // } - - // public function testWithoutModuloSequenceShouldNotBeZero() - // { - // $this->assertFalse( - // TINValid::checkTIN('cz', '500101/000') - // ); - // } - - // public function testIncorrectModulo() - // { - // $this->assertFalse( - // TINValid::checkTIN('cz', '540101/0008') - // ); - // } - - // public function testIncorrectModuloIsCorrectIfItShouldBe10() - // { - // $this->assertFalse( - // TINValid::checkTIN('cz', '540101/0110') - // ); - // } -} diff --git a/tests/DEAlgorithmTest.php b/tests/DEAlgorithmTest.php deleted file mode 100644 index d96d097..0000000 --- a/tests/DEAlgorithmTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertTrue(TINValid::checkTIN('de', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('de', self::VALID_NUMBER2)); - $this->assertTrue(TINValid::checkTIN('de', self::VALID_NUMBER3)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('de', self::INVALID_NUMBER_ZERO)); - $this->assertFalse(TINValid::checkTIN('de', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('de', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/DKAlgorithmTest.php b/tests/DKAlgorithmTest.php deleted file mode 100644 index 5e77aec..0000000 --- a/tests/DKAlgorithmTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertTrue(TINValid::checkTIN('dk', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('dk', self::VALID_NUMBER2)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('dk', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('dk', self::INVALID_NUMBER_LENGTH)); - $this->assertFalse(TINValid::checkTIN('dk', self::INVALID_NUMBER_PATTERN)); - } -} diff --git a/tests/EEAlgorithmTest.php b/tests/EEAlgorithmTest.php deleted file mode 100644 index 4c5932b..0000000 --- a/tests/EEAlgorithmTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertTrue(TINValid::checkTIN('ee', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('ee', self::VALID_NUMBER2)); - $this->assertTrue(TINValid::checkTIN('ee', self::VALID_NUMBER3)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('ee', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('ee', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/ESAlgorithmTest.php b/tests/ESAlgorithmTest.php deleted file mode 100644 index 451db4a..0000000 --- a/tests/ESAlgorithmTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertTrue(TINValid::checkTIN('es', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('es', self::VALID_NUMBER2)); - $this->assertTrue(TINValid::checkTIN('es', self::VALID_NUMBER3)); - $this->assertTrue(TINValid::checkTIN('es', self::VALID_NUMBER4)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('es', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('es', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/FIAlgorithmTest.php b/tests/FIAlgorithmTest.php deleted file mode 100644 index 60e2525..0000000 --- a/tests/FIAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('fi', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('fi', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('fi', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/FRAlgorithmTest.php b/tests/FRAlgorithmTest.php deleted file mode 100644 index 061dc9a..0000000 --- a/tests/FRAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('fr', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('fr', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('fr', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/GRAlgorithmTest.php b/tests/GRAlgorithmTest.php deleted file mode 100644 index cf9c7c8..0000000 --- a/tests/GRAlgorithmTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertTrue(TINValid::checkTIN('gr', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('gr', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/HRAlgorithmTest.php b/tests/HRAlgorithmTest.php deleted file mode 100644 index bf11b64..0000000 --- a/tests/HRAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('hr', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('hr', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('hr', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/HUAlgorithmTest.php b/tests/HUAlgorithmTest.php deleted file mode 100644 index c401b43..0000000 --- a/tests/HUAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('hu', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('hu', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('hu', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/IEAlgorithmTest.php b/tests/IEAlgorithmTest.php deleted file mode 100644 index f1b5476..0000000 --- a/tests/IEAlgorithmTest.php +++ /dev/null @@ -1,32 +0,0 @@ -assertTrue(TINValid::checkTIN('ie', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('ie', self::VALID_NUMBER2)); - $this->assertTrue(TINValid::checkTIN('ie', self::VALID_NUMBER3)); - $this->assertTrue(TINValid::checkTIN('ie', self::VALID_NUMBER4)); - $this->assertTrue(TINValid::checkTIN('ie', self::VALID_NUMBER5)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('ie', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('ie', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/ITAlgorithmTest.php b/tests/ITAlgorithmTest.php deleted file mode 100644 index df4af26..0000000 --- a/tests/ITAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('it', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('it', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('it', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/LTAlgorithmTest.php b/tests/LTAlgorithmTest.php deleted file mode 100644 index 34b81d8..0000000 --- a/tests/LTAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('lt', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('lt', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('lt', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/LUAlgorithmTest.php b/tests/LUAlgorithmTest.php deleted file mode 100644 index e9a4770..0000000 --- a/tests/LUAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('lu', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('lu', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('lu', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/LVAlgorithmTest.php b/tests/LVAlgorithmTest.php deleted file mode 100644 index 9c84dd3..0000000 --- a/tests/LVAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('lv', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('lv', self::VALID_NUMBER2)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('lv', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/MTAlgorithmTest.php b/tests/MTAlgorithmTest.php deleted file mode 100644 index ec8d809..0000000 --- a/tests/MTAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('mt', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('mt', self::INVALID_NUMBER_PATTERN)); - $this->assertFalse(TINValid::checkTIN('mt', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/NLAlgorithmTest.php b/tests/NLAlgorithmTest.php deleted file mode 100644 index 7ba4de3..0000000 --- a/tests/NLAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('nl', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('nl', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('nl', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/PLAlgorithmTest.php b/tests/PLAlgorithmTest.php deleted file mode 100644 index b7d845f..0000000 --- a/tests/PLAlgorithmTest.php +++ /dev/null @@ -1,26 +0,0 @@ -assertTrue(TINValid::checkTIN('pl', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('pl', self::VALID_NUMBER2)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('pl', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('pl', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/PTAlgorithmTest.php b/tests/PTAlgorithmTest.php deleted file mode 100644 index 9e80fbd..0000000 --- a/tests/PTAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('pt', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('pt', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('pt', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/ROAlgorithmTest.php b/tests/ROAlgorithmTest.php deleted file mode 100644 index 14005c9..0000000 --- a/tests/ROAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('ro', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('ro', self::INVALID_NUMBER_LENGTH)); - $this->assertFalse(TINValid::checkTIN('ro', self::INVALID_NUMBER_DATE)); - } -} diff --git a/tests/SEAlgorithmTest.php b/tests/SEAlgorithmTest.php deleted file mode 100644 index bc69c4f..0000000 --- a/tests/SEAlgorithmTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertTrue(TINValid::checkTIN('se', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('se', self::VALID_NUMBER2)); - $this->assertTrue(TINValid::checkTIN('se', self::VALID_NUMBER3)); - $this->assertTrue(TINValid::checkTIN('se', self::VALID_NUMBER4)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('se', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('se', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/SIAlgorithmTest.php b/tests/SIAlgorithmTest.php deleted file mode 100644 index 388d668..0000000 --- a/tests/SIAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('si', self::VALID_NUMBER)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('si', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('si', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/SKAlgorithmTest.php b/tests/SKAlgorithmTest.php deleted file mode 100644 index dfda3ec..0000000 --- a/tests/SKAlgorithmTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(TINValid::checkTIN('sk', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('sk', self::VALID_NUMBER2)); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('sk', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/TINValidTest.php b/tests/TINValidTest.php deleted file mode 100644 index 9756cfc..0000000 --- a/tests/TINValidTest.php +++ /dev/null @@ -1,20 +0,0 @@ -assertTrue(TINValid::isCountrySupported('be')); - $this->assertFalse(TINValid::isCountrySupported('cn')); - } - - public function testCheckTin() - { - $this->assertIsBool(TINValid::checkTIN('be', '00012511119')); - } -} diff --git a/tests/UKAlgorithmTest.php b/tests/UKAlgorithmTest.php index 946d279..e69de29 100644 --- a/tests/UKAlgorithmTest.php +++ b/tests/UKAlgorithmTest.php @@ -1,29 +0,0 @@ -assertTrue(TINValid::checkTIN('uk', self::VALID_NUMBER)); - $this->assertTrue(TINValid::checkTIN('uk', self::VALID_NUMBER2)); - $this->markTestIncomplete( - 'Test valid number of length 8.' - ); - } - - public function testInvalidNumber() - { - $this->assertFalse(TINValid::checkTIN('uk', self::INVALID_NUMBER_CHECK)); - $this->assertFalse(TINValid::checkTIN('uk', self::INVALID_NUMBER_LENGTH)); - } -} diff --git a/tests/src/AbstractAlgorithmSpec.php b/tests/src/AbstractAlgorithmSpec.php new file mode 100644 index 0000000..87e4f4c --- /dev/null +++ b/tests/src/AbstractAlgorithmSpec.php @@ -0,0 +1,164 @@ +withTIN(mb_strtoupper($number)) + ->shouldThrow(new TINException('Invalid date.')) + ->during('validate'); + + $this + ->withTIN(mb_strtolower($number)) + ->shouldThrow(new TINException('Invalid date.')) + ->during('validate'); + } + } + + public function it_can_throw_an_exception_if_tin_has_invalid_length() + { + $numbers = (array) $this::INVALID_NUMBER_LENGTH; + + if ([] === $numbers) { + throw new SkippingException('Missing tests for length validation.'); + } + + foreach ($numbers as $number) { + $this + ->withTIN(mb_strtoupper($number)) + ->shouldThrow(new TINException('Invalid length.')) + ->during('validate'); + + $this + ->withTIN(mb_strtolower($number)) + ->shouldThrow(new TINException('Invalid length.')) + ->during('validate'); + } + } + + public function it_can_throw_an_exception_if_tin_has_invalid_number_check() + { + $numbers = (array) $this::INVALID_NUMBER_CHECK; + + if ([] === $numbers) { + throw new SkippingException('Missing tests for number check validation.'); + } + + foreach ($numbers as $number) { + $this + ->withTIN(mb_strtoupper($number)) + ->shouldThrow(new TINException('Invalid syntax.')) + ->during('validate'); + + $this + ->withTIN(mb_strtolower($number)) + ->shouldThrow(new TINException('Invalid syntax.')) + ->during('validate'); + } + } + + public function it_can_throw_an_exception_if_tin_has_invalid_pattern() + { + $numbers = (array) $this::INVALID_NUMBER_PATTERN; + + if ([] === $numbers) { + throw new SkippingException('Missing tests for pattern validation.'); + } + + foreach ($numbers as $number) { + $this + ->withTIN(mb_strtoupper($number)) + ->shouldThrow(new TINException('Invalid pattern.')) + ->during('validate'); + + $this + ->withTIN(mb_strtolower($number)) + ->shouldThrow(new TINException('Invalid pattern.')) + ->during('validate'); + } + } + + public function it_can_throw_an_exception_if_tin_has_invalid_syntax() + { + $numbers = (array) $this::INVALID_NUMBER_SYNTAX; + + if ([] === $numbers) { + throw new SkippingException('Missing tests for syntax validation.'); + } + + foreach ($numbers as $number) { + $this + ->withTIN(mb_strtoupper($number)) + ->shouldThrow(new TINException('Invalid syntax.')) + ->during('validate'); + + $this + ->withTIN(mb_strtolower($number)) + ->shouldThrow(new TINException('Invalid syntax.')) + ->during('validate'); + } + } + + public function it_can_use_withTIN_and_return_a_new_object() + { + $this + ->withTIN('BE1234567890') + ->shouldNotReturn($this); + } + + public function it_can_validate() + { + $numbers = (array) $this::VALID_NUMBER; + + if ([] === $numbers) { + throw new SkippingException('Missing tests for number validation.'); + } + + foreach ($numbers as $number) { + $this + ->withTIN(mb_strtolower($number)) + ->validate($number) + ->shouldReturn(true); + + $this + ->withTIN(mb_strtoupper($number)) + ->validate($number) + ->shouldReturn(true); + } + } + + public function it_is_initializable() + { + $this->shouldHaveType(get_class($this->getWrappedObject())); + } +}