From c7db8e577e268d947945ad00240e7c8b6b8bb87b Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 09:46:01 +0000 Subject: [PATCH 01/10] add ReturnTypeWillChange annotation to FortifiWebhookPayload --- src/Payloads/FortifiWebhookPayload.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Payloads/FortifiWebhookPayload.php b/src/Payloads/FortifiWebhookPayload.php index 140a365..c1c2972 100644 --- a/src/Payloads/FortifiWebhookPayload.php +++ b/src/Payloads/FortifiWebhookPayload.php @@ -2,6 +2,7 @@ namespace Fortifi\Webhooks\Payloads; use Packaged\Helpers\Objects; +use ReturnTypeWillChange; abstract class FortifiWebhookPayload implements \JsonSerializable { @@ -29,6 +30,7 @@ final public function __construct($event) * @return mixed data which can be serialized by json_encode, * which is a value of any type other than a resource. */ + #[ReturnTypeWillChange] public function jsonSerialize() { return [ From e01279ca8432d28333e7ef72d0605b61c0054b64 Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 09:58:46 +0000 Subject: [PATCH 02/10] update phpunit --- composer.json | 2 +- phpunit.xml | 20 +++++++++++++++ phpunit.xml.dist | 27 -------------------- tests/Payloads/FortifiWebhookPayloadTest.php | 3 ++- tests/Payloads/Test/TestWHPTest.php | 3 ++- 5 files changed, 25 insertions(+), 30 deletions(-) create mode 100755 phpunit.xml delete mode 100755 phpunit.xml.dist diff --git a/composer.json b/composer.json index 6ac775d..8b9f87e 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "packaged/helpers": "~1.0||~2.0" }, "require-dev": { - "phpunit/phpunit": "~4.5" + "phpunit/phpunit": "~9" }, "autoload": { "psr-4": { diff --git a/phpunit.xml b/phpunit.xml new file mode 100755 index 0000000..326f4cf --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,20 @@ + + + + + + ./tests + + + + + src + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100755 index 55fd283..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - ./tests - - - - - src - - - vendor - tests - - - diff --git a/tests/Payloads/FortifiWebhookPayloadTest.php b/tests/Payloads/FortifiWebhookPayloadTest.php index ad1d75c..a8038dd 100644 --- a/tests/Payloads/FortifiWebhookPayloadTest.php +++ b/tests/Payloads/FortifiWebhookPayloadTest.php @@ -2,8 +2,9 @@ namespace Fortifi\Tests\Webhooks\Payloads; use Fortifi\Webhooks\Payloads\FortifiWebhookPayload; +use PHPUnit\Framework\TestCase; -class FortifiWebhookPayloadTest extends \PHPUnit_Framework_TestCase +class FortifiWebhookPayloadTest extends TestCase { public function testWebookPayload() { diff --git a/tests/Payloads/Test/TestWHPTest.php b/tests/Payloads/Test/TestWHPTest.php index f81c442..51e97ac 100644 --- a/tests/Payloads/Test/TestWHPTest.php +++ b/tests/Payloads/Test/TestWHPTest.php @@ -2,8 +2,9 @@ namespace Fortifi\Tests\Webhooks\Payloads\Test; use Fortifi\Webhooks\Payloads\Test\TestWHP; +use PHPUnit\Framework\TestCase; -class TestWHPTest extends \PHPUnit_Framework_TestCase +class TestWHPTest extends TestCase { public function testTransport() { From cbc5a28eae12556fa9c12943d27ad8cab46d864e Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 10:08:27 +0000 Subject: [PATCH 03/10] add tests workflow --- .github/workflows/tests.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..cbc29f4 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,33 @@ +name: Tests + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + + name: PHP ${{ matrix.php }} + + steps: + - uses: actions/checkout@v6 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run tests + run: vendor/bin/phpunit --configuration phpunit.xml From 497feedb2322126577b0cf684162d4c6595e1db4 Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 10:29:29 +0000 Subject: [PATCH 04/10] additional deprecation handling --- phpunit.xml | 3 ++ src/Payloads/FortifiWebhookPayload.php | 3 +- src/Webhooks.php | 2 +- tests/Payloads/FortifiWebhookPayloadTest.php | 32 ++++++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 326f4cf..8958c0a 100755 --- a/phpunit.xml +++ b/phpunit.xml @@ -6,6 +6,9 @@ colors="true" processIsolation="false" stopOnFailure="true" + convertErrorsToExceptions="true" + convertNoticesToExceptions="true" + convertWarningsToExceptions="true" bootstrap="vendor/autoload.php"> diff --git a/src/Payloads/FortifiWebhookPayload.php b/src/Payloads/FortifiWebhookPayload.php index c1c2972..f1e1601 100644 --- a/src/Payloads/FortifiWebhookPayload.php +++ b/src/Payloads/FortifiWebhookPayload.php @@ -2,7 +2,6 @@ namespace Fortifi\Webhooks\Payloads; use Packaged\Helpers\Objects; -use ReturnTypeWillChange; abstract class FortifiWebhookPayload implements \JsonSerializable { @@ -30,7 +29,7 @@ final public function __construct($event) * @return mixed data which can be serialized by json_encode, * which is a value of any type other than a resource. */ - #[ReturnTypeWillChange] + #[\ReturnTypeWillChange] public function jsonSerialize() { return [ diff --git a/src/Webhooks.php b/src/Webhooks.php index ff99e23..86202f4 100644 --- a/src/Webhooks.php +++ b/src/Webhooks.php @@ -62,7 +62,7 @@ public static function getPayload($json) $decoded = json_decode($json); $events = static::all(); $event = Objects::property($decoded, 'event'); - if(isset($events[$event])) + if($event !== null && isset($events[$event])) { return call_user_func([$events[$event], 'hydrateFromJson'], $json); } diff --git a/tests/Payloads/FortifiWebhookPayloadTest.php b/tests/Payloads/FortifiWebhookPayloadTest.php index a8038dd..0ba71ae 100644 --- a/tests/Payloads/FortifiWebhookPayloadTest.php +++ b/tests/Payloads/FortifiWebhookPayloadTest.php @@ -2,6 +2,7 @@ namespace Fortifi\Tests\Webhooks\Payloads; use Fortifi\Webhooks\Payloads\FortifiWebhookPayload; +use Fortifi\Webhooks\Payloads\Test\TestWHP; use PHPUnit\Framework\TestCase; class FortifiWebhookPayloadTest extends TestCase @@ -26,4 +27,35 @@ public function testWebookPayload() $this->assertEquals(456, $object->getRequestId()); } } + + /** + * Test that jsonSerialize works without deprecation in PHP 8+ + * Requires #[\ReturnTypeWillChange] attribute on jsonSerialize() + */ + public function testJsonSerializeCompatibility() + { + $deprecation = null; + set_error_handler( + function($errno, $errstr) use (&$deprecation) { + if(strpos($errstr, 'jsonSerialize') !== false) + { + $deprecation = $errstr; + } + return true; + }, + E_DEPRECATED + ); + + try + { + $payload = new TestWHP('test.event'); + json_encode($payload); + } + finally + { + restore_error_handler(); + } + + $this->assertNull($deprecation, $deprecation ?? ''); + } } From 54811b3adc41abb8c102558ee308779ce7f86a07 Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 10:29:38 +0000 Subject: [PATCH 05/10] add missing tests --- tests/WebhooksTest.php | 103 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 tests/WebhooksTest.php diff --git a/tests/WebhooksTest.php b/tests/WebhooksTest.php new file mode 100644 index 0000000..e337635 --- /dev/null +++ b/tests/WebhooksTest.php @@ -0,0 +1,103 @@ +assertIsArray($all); + $this->assertNotEmpty($all); + $this->assertArrayHasKey(CustomerWHE::CREATED, $all); + $this->assertEquals(CustomerCreatedWHP::class, $all[CustomerWHE::CREATED]); + } + + public function testGetPayloadWithValidEvent() + { + $json = json_encode([ + 'event' => CustomerWHE::CREATED, + 'sig' => 'test-sig', + 'uuid' => 'test-uuid', + 'rqid' => 'test-rqid', + 'data' => ['customerFid' => 'cust-123'], + ]); + + $payload = Webhooks::getPayload($json); + + $this->assertInstanceOf(CustomerCreatedWHP::class, $payload); + $this->assertEquals(CustomerWHE::CREATED, $payload->getEventType()); + $this->assertEquals('test-uuid', $payload->getPayloadId()); + $this->assertEquals('test-rqid', $payload->getRequestId()); + } + + public function testGetPayloadWithUnknownEvent() + { + $json = json_encode([ + 'event' => 'unknown.event', + 'sig' => 'test-sig', + 'uuid' => 'test-uuid', + 'rqid' => 'test-rqid', + 'data' => [], + ]); + + $payload = Webhooks::getPayload($json); + + $this->assertNull($payload); + } + + public function testGetPayloadWithMissingEvent() + { + $json = json_encode([ + 'sig' => 'test-sig', + 'uuid' => 'test-uuid', + 'data' => [], + ]); + + $payload = Webhooks::getPayload($json); + + $this->assertNull($payload); + } + + public function testAllWithDisplayNames() + { + $displayNames = Webhooks::allWithDisplayNames(); + + $this->assertIsArray($displayNames); + $this->assertNotEmpty($displayNames); + $this->assertCount(count(Webhooks::all()), $displayNames); + + foreach($displayNames as $name) + { + $this->assertIsString($name); + $this->assertNotEmpty($name); + } + } + + public function testGetDisplayName() + { + $this->assertEquals('Customer Created', Webhooks::getDisplayName('customer.created')); + $this->assertEquals('Customer Email Unsubscribed', Webhooks::getDisplayName('customer.email.unsubscribed')); + $this->assertEquals('Invoice Add Payment', Webhooks::getDisplayName('invoice.add.payment')); + } + + public function testGetDisplayNamePreservesCase() + { + $this->assertEquals('Test Event', Webhooks::getDisplayName('test.event')); + } + + public function testAllEventsMapToValidClasses() + { + foreach(Webhooks::all() as $event => $class) + { + $this->assertTrue(class_exists($class), "Class $class for event $event does not exist"); + } + } +} From d28f987daf322d3f5c3bc74a2120d19f3eccf37b Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 10:45:11 +0000 Subject: [PATCH 06/10] include 8.5 --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cbc29f4..bde70d8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] name: PHP ${{ matrix.php }} @@ -27,7 +27,7 @@ jobs: coverage: none - name: Install dependencies - run: composer install --prefer-dist --no-progress + run: composer install --no-interaction --prefer-dist --no-progress - name: Run tests run: vendor/bin/phpunit --configuration phpunit.xml From 99db29d279fa1a0383859ff98be26f5214511dfc Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 10:54:25 +0000 Subject: [PATCH 07/10] add coverage check --- .github/workflows/tests.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index bde70d8..29d618a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,10 +1,10 @@ -name: Tests +name: PHPUnit on: push: - branches: [master] + branches: [ master ] pull_request: - branches: [master] + branches: [ master ] jobs: test: @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] + php: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ] name: PHP ${{ matrix.php }} @@ -29,5 +29,10 @@ jobs: - name: Install dependencies run: composer install --no-interaction --prefer-dist --no-progress - - name: Run tests - run: vendor/bin/phpunit --configuration phpunit.xml + - name: Run PHPUnit + run: | + vendor/bin/phpunit --coverage-text --coverage-filter src 2>&1 | tee coverage.txt + if ! grep -q "Lines:\s*100.00%" coverage.txt; then + echo "Coverage is not 100%" + exit 1 + fi From 3e1e22627376ff507ab6ff653d5bf5519994a119 Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 10:55:13 +0000 Subject: [PATCH 08/10] add missing coverage driver --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 29d618a..cacd23e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,7 +24,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - coverage: none + coverage: xdebug - name: Install dependencies run: composer install --no-interaction --prefer-dist --no-progress From 4813bb360a07559276b1ba09e674670049e07855 Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 11:00:39 +0000 Subject: [PATCH 09/10] update coverage check --- .github/workflows/tests.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cacd23e..29ce537 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,9 +30,13 @@ jobs: run: composer install --no-interaction --prefer-dist --no-progress - name: Run PHPUnit + run: vendor/bin/phpunit --coverage-text=coverage.txt --coverage-filter src + + - name: Check coverage run: | - vendor/bin/phpunit --coverage-text --coverage-filter src 2>&1 | tee coverage.txt - if ! grep -q "Lines:\s*100.00%" coverage.txt; then - echo "Coverage is not 100%" + cat coverage.txt + COVERAGE=$(awk '/Summary:/{found=1} found && /Lines:/{match($0, /([0-9]+\.[0-9]+)%/, a); print a[1]; exit}' coverage.txt) + if [ "$COVERAGE" != "100.00" ]; then + echo "Coverage is $COVERAGE%, not 100%" exit 1 fi From 4adc736401392b0fdc2c20ba3abb194ccc2eeb16 Mon Sep 17 00:00:00 2001 From: Tom Kay Date: Thu, 5 Feb 2026 11:54:44 +0000 Subject: [PATCH 10/10] update threshold --- .github/workflows/tests.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 29ce537..cd3d475 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,13 +30,15 @@ jobs: run: composer install --no-interaction --prefer-dist --no-progress - name: Run PHPUnit - run: vendor/bin/phpunit --coverage-text=coverage.txt --coverage-filter src + run: vendor/bin/phpunit --coverage-text=coverage.txt - name: Check coverage run: | cat coverage.txt - COVERAGE=$(awk '/Summary:/{found=1} found && /Lines:/{match($0, /([0-9]+\.[0-9]+)%/, a); print a[1]; exit}' coverage.txt) - if [ "$COVERAGE" != "100.00" ]; then - echo "Coverage is $COVERAGE%, not 100%" + COVERAGE=$(grep -A3 'Summary:' coverage.txt | grep 'Lines:' | grep -oP '\d+\.\d+(?=%)') + THRESHOLD=100 + if (( $(echo "$COVERAGE < $THRESHOLD" | bc -l) )); then + echo "Coverage is $COVERAGE%, below ${THRESHOLD}% threshold" exit 1 fi + echo "Coverage is $COVERAGE% (threshold: ${THRESHOLD}%)"