From 12e379b5b52fc8ad3697813d2a8cc2e17b18df27 Mon Sep 17 00:00:00 2001 From: Jonathan Maron Date: Tue, 30 Jun 2026 05:39:51 +0200 Subject: [PATCH 1/3] Eliminate PHP 8.5 deprecation notices (keep PHP 7.3 support) ValidatorIE mapped a checksum value to a letter by incrementing the string 'A' in a loop; incrementing a non-numeric string is deprecated as of PHP 8.3 (str_increment() is the suggested replacement but only exists on 8.3+). Replace the increment with chr(ord($checkChar) + 1), which is identical for the A-V range reached here and valid on PHP 7.3. ViesTest called ReflectionMethod::setAccessible(true), which has had no effect since PHP 8.1 and is deprecated in 8.5, but is still required on PHP < 8.1 to invoke the private addOptionalArguments() via reflection. Guard each of the five calls behind PHP_VERSION_ID < 80100 so it runs only where it has an effect. No language feature newer than 7.3 is used and composer.json's "php": ">=7.3" is unchanged. --- src/Vies/Validator/ValidatorIE.php | 4 +++- tests/Vies/ViesTest.php | 20 +++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Vies/Validator/ValidatorIE.php b/src/Vies/Validator/ValidatorIE.php index 10eb54c..b1bec55 100644 --- a/src/Vies/Validator/ValidatorIE.php +++ b/src/Vies/Validator/ValidatorIE.php @@ -76,7 +76,9 @@ private function validateIENew(string $vatNumber): bool $checkChar = 'A'; for ($i = $checkVal - 1; $i > 0; $i--) { - $checkChar++; + // chr(ord(...) + 1) replaces $checkChar++ to avoid incrementing a + // non-numeric string, deprecated since PHP 8.3 (valid on PHP 7.3+). + $checkChar = chr(ord($checkChar) + 1); } return $checkChar == $checksum; diff --git a/tests/Vies/ViesTest.php b/tests/Vies/ViesTest.php index 47f2fd1..cfd3ac0 100644 --- a/tests/Vies/ViesTest.php +++ b/tests/Vies/ViesTest.php @@ -389,7 +389,9 @@ public function testCanAddOptionalArgumentsWithValue() { $viesRef = new \ReflectionClass(Vies::class); $addOptionalArguments = $viesRef->getMethod('addOptionalArguments'); - $addOptionalArguments->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $addOptionalArguments->setAccessible(true); + } $array = []; $object = new Vies(); @@ -409,7 +411,9 @@ public function testCanNotAddOptionalArgumentsWithoutValue() { $viesRef = new \ReflectionClass(Vies::class); $addOptionalArguments = $viesRef->getMethod('addOptionalArguments'); - $addOptionalArguments->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $addOptionalArguments->setAccessible(true); + } $array = []; $object = new Vies(); @@ -484,7 +488,9 @@ public function testRejectBadOptionalInformation( ) { $viesRef = new \ReflectionClass(Vies::class); $addOptionalArguments = $viesRef->getMethod('addOptionalArguments'); - $addOptionalArguments->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $addOptionalArguments->setAccessible(true); + } $array = []; $object = new Vies(); @@ -538,7 +544,9 @@ public function testAllowValidOptionalInformation( ) { $viesRef = new \ReflectionClass(Vies::class); $addOptionalArguments = $viesRef->getMethod('addOptionalArguments'); - $addOptionalArguments->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $addOptionalArguments->setAccessible(true); + } $array = []; $object = new Vies(); @@ -561,7 +569,9 @@ public function testBreakValidationOfOptionalArguments() { $viesRef = new \ReflectionClass(Vies::class); $addOptionalArguments = $viesRef->getMethod('addOptionalArguments'); - $addOptionalArguments->setAccessible(true); + if (\PHP_VERSION_ID < 80100) { + $addOptionalArguments->setAccessible(true); + } $this->expectException(\TypeError::class); $array = []; From 60937c341f476789c2087c6c135761e502b1e739 Mon Sep 17 00:00:00 2001 From: Jonathan Maron Date: Tue, 30 Jun 2026 05:39:51 +0200 Subject: [PATCH 2/3] Reject non-numeric SK VAT input before modulo (no PHP 8 warning) ValidatorSK::validate() gated only on length, first digit and third digit, then ran $vatNumber % 11 (and bcmod) on the raw string. A 10-char input that passed the gate but contained a non-digit (e.g. '222222222A') reached the modulo and triggered 'Warning: A non-numeric value encountered' on PHP 8.x, which breaks callers that escalate warnings to exceptions. Add ! ctype_digit($vatNumber) to the gate so non-numeric input returns false before any arithmetic. ctype_digit() is available on all supported PHP versions and ext-ctype is already required; valid all-digit numbers are unaffected, so there is no behavior change for valid input. Add a regression test asserting the malformed input is invalid and raises no warning. --- src/Vies/Validator/ValidatorSK.php | 1 + tests/Vies/Validator/ValidatorSKTest.php | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Vies/Validator/ValidatorSK.php b/src/Vies/Validator/ValidatorSK.php index da923dc..e490b44 100644 --- a/src/Vies/Validator/ValidatorSK.php +++ b/src/Vies/Validator/ValidatorSK.php @@ -35,6 +35,7 @@ class ValidatorSK extends ValidatorAbstract public function validate(string $vatNumber): bool { if (strlen($vatNumber) != 10 + || ! ctype_digit($vatNumber) || intval($vatNumber[0]) == 0 || ! in_array((int) $vatNumber[2], [2, 3, 4, 7, 8, 9]) ) { diff --git a/tests/Vies/Validator/ValidatorSKTest.php b/tests/Vies/Validator/ValidatorSKTest.php index 26576e6..22c1b60 100644 --- a/tests/Vies/Validator/ValidatorSKTest.php +++ b/tests/Vies/Validator/ValidatorSKTest.php @@ -4,6 +4,8 @@ namespace DragonBe\Test\Vies\Validator; +use DragonBe\Vies\Validator\ValidatorSK; + class ValidatorSKTest extends AbstractValidatorTest { /** @@ -25,4 +27,25 @@ public function vatNumberProvider() ['4060000007', false], ]; } + + /** + * @covers \DragonBe\Vies\Validator\ValidatorSK + */ + public function testValidateRaisesNoWarningOnNonNumericInput() + { + $raised = null; + set_error_handler(static function (int $errno, string $errstr) use (&$raised): bool { + $raised = $errstr; + return true; + }, E_DEPRECATED | E_WARNING); + + try { + $result = (new ValidatorSK())->validate('222222222A'); + } finally { + restore_error_handler(); + } + + self::assertFalse($result, 'Non-numeric SK input must be invalid'); + self::assertNull($raised, 'Validator must not trigger E_WARNING or E_DEPRECATED'); + } } From 65e3c0fabcae04d37e4f00dcd1fd3709a088f8fc Mon Sep 17 00:00:00 2001 From: Jonathan Maron Date: Tue, 30 Jun 2026 05:39:51 +0200 Subject: [PATCH 3/3] Modernize GitHub Actions CI workflow - Expand the test matrix to PHP 7.4-8.5, plus a non-blocking experimental 8.6 leg (continue-on-error). - Bump actions/checkout and actions/cache to v5 (Node 24): v2 is auto-failed by GitHub and v4 runs on the deprecated Node 20 runtime. - Fix the Composer cache key. It hashed composer.lock, which is gitignored and absent in CI, so the key collapsed to a constant shared across every PHP version and one vendor/ was reused for all of them (crashing quality steps). Make the key per-PHP-version and hash the tracked composer.json instead. - Add fail-fast: false so each PHP version reports independently. --- .github/workflows/php.yml | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 1b2d09a..6b682c4 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -1,42 +1,49 @@ name: PHP Composer -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] +on: [push, pull_request] jobs: build: name: ViesBuild runs-on: ${{ matrix.operating-system }} + # Let the experimental (next-PHP) leg fail without failing the workflow. + # Legs without an "experimental" value resolve to false and stay blocking. + continue-on-error: ${{ matrix.experimental || false }} strategy: + fail-fast: false matrix: operating-system: [ubuntu-latest] - php-versions: ['7.4', '8.0', '8.1'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] + include: + # Forward-looking, non-blocking signal against the next PHP. + # This plugin exists to smooth installs on newer PHP, so an + # early-warning leg against the upcoming release fits its purpose. + - operating-system: ubuntu-latest + php: '8.6' + experimental: true steps: - - name: Set default PHP version ${{ matrix.php-versions }} + - name: Set default PHP version ${{ matrix.php }} uses: shivammathur/setup-php@v2 with: - php-version: "${{ matrix.php-versions }}" + php-version: "${{ matrix.php }}" - name: Display current PHP version run: php --version - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Validate composer.json and composer.lock run: composer validate - name: Cache Composer packages id: composer-cache - uses: actions/cache@v2 + uses: actions/cache@v5 with: path: vendor - key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + key: ${{ runner.os }}-php-${{ matrix.php }}-${{ hashFiles('**/composer.json') }} restore-keys: | - ${{ runner.os }}-php- + ${{ runner.os }}-php-${{ matrix.php }}- - name: Install dependencies if: steps.composer-cache.outputs.cache-hit != 'true'