diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a4386c..3afd5b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,16 +7,16 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [ 8.2, 8.3 ] + php: [ 8.2, 8.3, 8.4 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Cache Composer dependencies uses: actions/cache@v3 with: path: /tmp/composer-cache - key: ${{ runner.os }}-${{ hashFiles('**/composer.lock') }} + key: ${{ runner.os }}-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }} - name: Composer install uses: php-actions/composer@v6 @@ -24,12 +24,12 @@ jobs: php_version: ${{ matrix.php }} - name: Archive build - run: mkdir /tmp/github-actions/ && tar -cvf /tmp/github-actions/build.tar ./ + run: mkdir /tmp/github-actions/ && tar --exclude=".git" -cvf /tmp/github-actions/build.tar ./ - name: Upload build archive for test runners - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: build-artifact + name: build-artifact-${{ matrix.php }} path: /tmp/github-actions phpunit: @@ -37,15 +37,15 @@ jobs: needs: [ composer ] strategy: matrix: - php: [ 8.2, 8.3 ] + php: [ 8.2, 8.3, 8.4 ] outputs: coverage: ${{ steps.store-coverage.outputs.coverage_text }} steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: build-artifact + name: build-artifact-${{ matrix.php }} path: /tmp/github-actions - name: Extract build archive @@ -56,15 +56,16 @@ jobs: env: XDEBUG_MODE: cover with: + version: 10 php_version: ${{ matrix.php }} php_extensions: xdebug coverage_text: _coverage/coverage.txt coverage_clover: _coverage/clover.xml - name: Store coverage data - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: code-coverage + name: code-coverage-${{ matrix.php }}-${{ github.run_number }} path: _coverage coverage: @@ -72,30 +73,32 @@ jobs: needs: [ phpunit ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: code-coverage + name: code-coverage-${{ matrix.php }}-${{ github.run_number }} path: _coverage - name: Output coverage run: cat "_coverage/coverage.txt" - name: Upload to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} phpstan: runs-on: ubuntu-latest needs: [ composer ] strategy: matrix: - php: [ 8.2, 8.3 ] + php: [ 8.2, 8.3, 8.4 ] steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: build-artifact + name: build-artifact-${{ matrix.php }} path: /tmp/github-actions - name: Extract build archive @@ -112,12 +115,12 @@ jobs: needs: [ composer ] strategy: matrix: - php: [ 8.2, 8.3 ] + php: [ 8.2, 8.3, 8.4 ] steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: build-artifact + name: build-artifact-${{ matrix.php }} path: /tmp/github-actions - name: Extract build archive @@ -136,12 +139,12 @@ jobs: needs: [ composer ] strategy: matrix: - php: [ 8.2, 8.3 ] + php: [ 8.2, 8.3, 8.4 ] steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: build-artifact + name: build-artifact-${{ matrix.php }} path: /tmp/github-actions - name: Extract build archive @@ -162,7 +165,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - gh api "/repos/${{ github.repository }}/actions/artifacts?name=build-artifact" | jq ".artifacts[] | select(.name == \"build-artifact\") | .id" > artifact-id-list.txt + gh api "/repos/${{ github.repository }}/actions/artifacts" | jq ".artifacts[] | select(.name | startswith(\"build-artifact\")) | .id" > artifact-id-list.txt while read id do echo -n "Deleting artifact ID $id ... " diff --git a/composer.lock b/composer.lock index c3e48f8..9a02392 100644 --- a/composer.lock +++ b/composer.lock @@ -827,16 +827,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.0", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -875,7 +875,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -883,20 +883,20 @@ "type": "tidelift" } ], - "time": "2025-02-12T12:17:51+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.4.0", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -915,7 +915,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -939,9 +939,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2024-12-30T11:07:19+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "pdepend/pdepend", @@ -1209,16 +1209,11 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.24", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "338b92068f58d9f8035b76aed6cf2b9e5624c025" - }, + "version": "1.12.32", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/338b92068f58d9f8035b76aed6cf2b9e5624c025", - "reference": "338b92068f58d9f8035b76aed6cf2b9e5624c025", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2770dcdf5078d0b0d53f94317e06affe88419aa8", + "reference": "2770dcdf5078d0b0d53f94317e06affe88419aa8", "shasum": "" }, "require": { @@ -1263,7 +1258,7 @@ "type": "github" } ], - "time": "2025-04-16T13:01:53+00:00" + "time": "2025-09-30T10:16:31+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1588,16 +1583,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.45", + "version": "10.5.58", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "bd68a781d8e30348bc297449f5234b3458267ae8" + "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8", - "reference": "bd68a781d8e30348bc297449f5234b3458267ae8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e24fb46da450d8e6a5788670513c1af1424f16ca", + "reference": "e24fb46da450d8e6a5788670513c1af1424f16ca", "shasum": "" }, "require": { @@ -1607,7 +1602,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", @@ -1618,13 +1613,13 @@ "phpunit/php-timer": "^6.0.0", "sebastian/cli-parser": "^2.0.1", "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.3", + "sebastian/comparator": "^5.0.4", "sebastian/diff": "^5.1.1", "sebastian/environment": "^6.1.0", - "sebastian/exporter": "^5.1.2", + "sebastian/exporter": "^5.1.4", "sebastian/global-state": "^6.0.2", "sebastian/object-enumerator": "^5.0.0", - "sebastian/recursion-context": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", "sebastian/type": "^4.0.0", "sebastian/version": "^4.0.1" }, @@ -1669,7 +1664,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.58" }, "funding": [ { @@ -1680,12 +1675,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2025-02-06T16:08:12+00:00" + "time": "2025-09-28T12:04:46+00:00" }, { "name": "psr/container", @@ -1960,16 +1963,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.3", + "version": "5.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", "shasum": "" }, "require": { @@ -2025,15 +2028,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2024-10-18T14:56:07+00:00" + "time": "2025-09-07T05:25:07+00:00" }, { "name": "sebastian/complexity", @@ -2226,16 +2241,16 @@ }, { "name": "sebastian/exporter", - "version": "5.1.2", + "version": "5.1.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "reference": "0735b90f4da94969541dac1da743446e276defa6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", "shasum": "" }, "require": { @@ -2244,7 +2259,7 @@ "sebastian/recursion-context": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -2292,15 +2307,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2025-09-24T06:09:11+00:00" }, { "name": "sebastian/global-state", @@ -2536,23 +2563,23 @@ }, { "name": "sebastian/recursion-context", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -2587,15 +2614,28 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2025-08-10T07:50:56+00:00" }, { "name": "sebastian/type", @@ -2708,16 +2748,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.12.2", + "version": "3.13.4", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa" + "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa", - "reference": "6d4cf6032d4b718f168c90a96e36c7d0eaacb2aa", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ad545ea9c1b7d270ce0fc9cbfb884161cd706119", + "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119", "shasum": "" }, "require": { @@ -2788,20 +2828,20 @@ "type": "thanks_dev" } ], - "time": "2025-04-13T04:10:18+00:00" + "time": "2025-09-05T05:47:09+00:00" }, { "name": "symfony/config", - "version": "v7.2.3", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "7716594aaae91d9141be080240172a92ecca4d44" + "reference": "8a09223170046d2cfda3d2e11af01df2c641e961" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/7716594aaae91d9141be080240172a92ecca4d44", - "reference": "7716594aaae91d9141be080240172a92ecca4d44", + "url": "https://api.github.com/repos/symfony/config/zipball/8a09223170046d2cfda3d2e11af01df2c641e961", + "reference": "8a09223170046d2cfda3d2e11af01df2c641e961", "shasum": "" }, "require": { @@ -2847,7 +2887,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.2.3" + "source": "https://github.com/symfony/config/tree/v7.3.4" }, "funding": [ { @@ -2858,25 +2898,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-01-22T12:07:01+00:00" + "time": "2025-09-22T12:46:16+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.2.5", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "58ab71379f14a741755717cece2868bf41ed45d8" + "reference": "82119812ab0bf3425c1234d413efd1b19bb92ae4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/58ab71379f14a741755717cece2868bf41ed45d8", - "reference": "58ab71379f14a741755717cece2868bf41ed45d8", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/82119812ab0bf3425c1234d413efd1b19bb92ae4", + "reference": "82119812ab0bf3425c1234d413efd1b19bb92ae4", "shasum": "" }, "require": { @@ -2927,7 +2971,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.2.5" + "source": "https://github.com/symfony/dependency-injection/tree/v7.3.4" }, "funding": [ { @@ -2938,25 +2982,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-03-13T12:21:46+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { @@ -2969,7 +3017,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -2994,7 +3042,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -3010,20 +3058,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/filesystem", - "version": "v7.2.0", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", "shasum": "" }, "require": { @@ -3060,7 +3108,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.2.0" + "source": "https://github.com/symfony/filesystem/tree/v7.3.2" }, "funding": [ { @@ -3071,16 +3119,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-25T15:15:23+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -3139,7 +3191,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -3150,6 +3202,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -3159,19 +3215,20 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -3219,7 +3276,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -3230,25 +3287,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { @@ -3266,7 +3327,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -3302,7 +3363,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -3318,24 +3379,25 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-04-25T09:37:31+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.2.5", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "c37b301818bd7288715d40de634f05781b686ace" + "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/c37b301818bd7288715d40de634f05781b686ace", - "reference": "c37b301818bd7288715d40de634f05781b686ace", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", + "reference": "0f020b544a30a7fe8ba972e53ee48a74c0bc87f4", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" }, "require-dev": { "symfony/property-access": "^6.4|^7.0", @@ -3378,7 +3440,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.2.5" + "source": "https://github.com/symfony/var-exporter/tree/v7.3.4" }, "funding": [ { @@ -3389,12 +3451,16 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2025-03-13T12:21:46+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "theseer/tokenizer", diff --git a/example/01-get-customer-by-id.php b/example/01-get-customer-by-id.php index a01a0fe..11995a7 100644 --- a/example/01-get-customer-by-id.php +++ b/example/01-get-customer-by-id.php @@ -35,5 +35,5 @@ echo "Last name: $customer->lastName\n"; } catch(CustomerNotFoundException) { - echo "No customer was found with the email address $customerId\n"; + echo "No customer was found with the ID $customerId\n"; } diff --git a/phpcs.xml b/phpcs.xml index a80be70..23146e3 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -24,7 +24,6 @@ - diff --git a/src/Client.php b/src/Client.php index 18781bb..7321ffa 100644 --- a/src/Client.php +++ b/src/Client.php @@ -3,10 +3,14 @@ use Gt\Fetch\Http; use Gt\Http\Response; +use Gt\Json\JsonDecodeException; use Gt\Json\JsonObject; use Gt\Json\JsonPrimitive\JsonArrayPrimitive; use Gt\Json\JsonPrimitive\JsonNullPrimitive; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ readonly class Client { const USER_AGENT = "github.com/BrightFlair/SpektrixAPI"; const BASE_URI = "https://system.spektrix.com/{client}/api"; @@ -28,6 +32,15 @@ public function createCustomer( ?string $lastName = null, ?string $mobile = null, ):Customer { +// This isn't typically the way URL-encoded data will be passed, if + or - +// characters are present, Spektrix's servers will fail. + $email = rawurlencode($email); + $email = str_replace("+", "%2b", $email); + $firstName = rawurlencode($firstName ?? ""); + $firstName = str_replace("'", "%27", $firstName); + $lastName = rawurlencode($lastName ?? ""); + $lastName = str_replace("'", "%27", $lastName); + $endpoint = Endpoint::createCustomer; $authenticatedRequest = new AuthenticatedRequest( $this->secretKey, @@ -35,8 +48,8 @@ public function createCustomer( $this->client, [ "email" => $email, - "firstName" => $firstName ?? "", - "lastName" => $lastName ?? "", + "firstName" => $firstName, + "lastName" => $lastName, "mobile" => $mobile ?? "", "birthDate" => ("D, d M Y H:i:s T"), "friendlyId" => uniqid(), @@ -256,14 +269,20 @@ private function json(AuthenticatedRequest $authenticatedRequest):?JsonObject { return null; } - throw new SpektrixAPIException("HTTP $response->status"); + $text = $response->awaitText(); + throw new SpektrixAPIException("HTTP $response->status - $text"); } if($response->status === 204) { return new JsonNullPrimitive(); } - return $response->awaitJson(); + try { + return $response->awaitJson(); + } + catch(JsonDecodeException $exception) { + throw new SpektrixAPIException($exception->getMessage()); + } } } diff --git a/test/phpunit/ClientTest.php b/test/phpunit/ClientTest.php index 8d66357..676471d 100644 --- a/test/phpunit/ClientTest.php +++ b/test/phpunit/ClientTest.php @@ -8,6 +8,7 @@ use BrightFlair\SpektrixAPI\TagNotFoundException; use Gt\Fetch\Http; use Gt\Http\Response; +use Gt\Http\Stream; use Gt\Json\JsonObject; use Gt\Json\JsonObjectBuilder; use Gt\Json\JsonPrimitive\JsonArrayPrimitive; @@ -302,6 +303,24 @@ public function testRemoveTagFromCustomer():void { $sut->removeTagFromCustomer($tagId, $customerId); } + public function testInvalidJson():void { + $bodyStream = self::createMock(Stream::class); + $bodyStream->method("getContents") + ->willReturn("Wait a minute, this ain't JSON!"); + + $response = (new Response(200))->withBody($bodyStream); + + $fetchClient = self::createMock(Http::class); + $fetchClient->expects(self::once()) + ->method("awaitFetch") + ->willReturn($response); + + $sut = new Client(self::TEST_USERNAME, self::TEST_CLIENT, self::TEST_SECRET_KEY, $fetchClient); + self::expectException(SpektrixAPIException::class); + self::expectExceptionMessage("Error decoding JSON: Syntax error"); + $sut->getCustomer(id: "ABC-123"); + } + private function getFetchClient( string $uri, int $status,