Skip to content

Fix issues found during the connection-reuse review - #12

Merged
jonathan-kosgei merged 9 commits into
masterfrom
fix-noticed-issues
Nov 3, 2026
Merged

jonathan-kosgei merged 9 commits into
masterfrom
fix-noticed-issues

Conversation

@jonathan-kosgei

@jonathan-kosgei jonathan-kosgei commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #11: fixes the items its description listed under "Noticed, not changed". The static-analysis workflow runs again on current infrastructure, the test matrix covers PHP 7.2 to 8.4 and its lowest-dependency build passes again, the pre-existing style and baseline findings are gone, Ipdata::DEFAULT_BASE_URL is public, and every request carries a User-Agent header. Nine commits, one per item. No version number changes; the CHANGELOG entry sits under an "Unreleased" heading.

Changes

  • Static analysis workflow runs again. static.yml requested the retired ubuntu-20.04 runner, so its four jobs queued for 24 hours and were cancelled on every pull request. The jobs now run on ubuntu-latest with PHP 8.4 and the latest patch releases of PHPStan 2.3, Psalm 6.20, PHP-CS-Fixer 3.95 and composer-normalize 2.56 (actions/checkout@v7, shivammathur/setup-php@v2, ramsey/composer-install@v4, which runs composer update because no lock file is committed). The repository's phpstan.neon.dist, psalm.xml and fixer rules stay in force.
  • CI matrix. ci.yml now tests PHP 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3 and 8.4 (composer.json allows >= 7.2) with shivammathur/setup-php@v2 and actions/checkout@v7, drops tools: prestissimo (a Composer 1 plugin; Composer 2 downloads in parallel on its own), stops installing Xdebug (the workflow never collects coverage and the configured xdebug.overload_var_dump setting no longer exists in Xdebug 3) and sets fail-fast: false so each PHP version reports its own result. Dependabot Bump shivammathur/setup-php from 1.7.0 to 2.37.1 in /.github/workflows #10 (setup-php 1.7.0 to 2.37.1) becomes redundant.
  • Lowest-dependency build. require-dev asks for php-http/mock-client ^1.5 instead of ^1.3. With ^1.3 the --prefer-lowest build resolves mock-client 1.3.0 together with php-http/httplug 1.1.0, whose HttpClient interface predates PSR-18, so Http\Mock\Client is not a Psr\Http\Client\ClientInterface and every test fails with a TypeError; the php-http/httplug ^2.0 dev requirement that used to pin HTTPlug 2 was removed in Add EU endpoint support and improve API client functionality #9, and the same job already failed on Dependabot Bump shivammathur/setup-php from 1.7.0 to 2.37.1 in /.github/workflows #10. mock-client 1.5.0 and newer require httplug ^2.0 themselves. Dev dependency only; the highest build keeps mock-client 1.6.1.
  • Coding style. The two pre-existing findings in src/Ipdata.php are fixed ('' !== $ip; @deprecated use bulkLookup() instead). The fixer configuration moved from .php_cs to .php-cs-fixer.dist.php with the PHP-CS-Fixer 3 API (new PhpCsFixer\Config()), same rules (@Symfony plus short array syntax); .gitignore tracks the new cache file name.
  • .php_cs vendor exclusion. Finder::exclude() takes a directory name relative to in(), so exclude(__DIR__.'/vendor') never excluded anything; the new configuration uses exclude('vendor').
  • PHPStan baseline. The two entries about $ips in bulkLookup()/buildLookup() matched nothing (both methods document @param array<string> $ips) and made every PHPStan run fail with "Ignored error pattern ... was not matched". The baseline is regenerated with PHPStan 2.3 and keeps the four entries about array return types.
  • composer.json normalized (type moves after license), so the Composer Normalize job passes.
  • Ipdata::DEFAULT_BASE_URL is public. The trailing $httpVersion argument can be passed without repeating the endpoint: new Ipdata($key, $client, $factory, Ipdata::DEFAULT_BASE_URL, '2.0'). README example and constructor documentation updated; test added.
  • User-Agent header. Every request carries User-Agent: ipdata-php/<version>. The version is the installed version of ipdata/api-client read through Composer\InstalledVersions, which ships with Composer 2 and newer, so no dependency is added; without it the header is ipdata-php. The bulk request keeps its Content-Type. Test added; README and CHANGELOG mention the header.
  • Psalm 6 adaptation (needed for the revived workflow): Psalm 6 turns findUnusedCode on by default, which reports a library's public methods as unused and adds ClassMustBeFinal for classes not marked as public API. psalm.xml sets findUnusedCode="false" explicitly (the behaviour the configuration had with Psalm 4 and 5) and the Ipdata class carries the @api tag, so it stays non-final.
  • bulkLookup() input check (Psalm 6 finding PossiblyFalseArgument): json_encode() returns false for input that cannot be encoded, for example invalid UTF-8, and the result went straight into StreamInterface::write(), failing with a TypeError. bulkLookup() now throws an \InvalidArgumentException with json_last_error_msg() before any request is created. Test added.

Not changed

  • No Accept header: the API answers with JSON for every request, so the header would not change the response; the client already validates the response Content-Type.
  • The two moderate Dependabot alerts on the default branch: the alerts page is visible to repository administrators only, so the flagged packages could not be identified from here. composer.lock is not committed; apart from the mock-client lower bound above, the require-dev constraints are unchanged.
  • The tool versions in static.yml are pinned to minor versions (phpstan:2.3, psalm:6.20, php-cs-fixer:3.95, composer-normalize:2.56); setup-php installs the latest patch release of each, so a new minor release of an analyzer never changes a pull request's result unexpectedly.

Verification

Local run with PHP 8.3.6 and PHPUnit 8.5.56, repeated on the final tree before each push:

  • vendor/bin/phpunit: OK (30 tests, 109 assertions) (27 tests on master).
  • phpstan analyse --no-progress (PHPStan 2.3.5, phpstan.neon.dist, level 6 with the baseline): [OK] No errors (master: two "Ignored error pattern ... was not matched" errors).
  • psalm --no-progress --no-cache (psalm.xml, errorLevel 2): Psalm 6.20.7 No errors found! (master with the same version: ClassMustBeFinal, three PossiblyUnusedMethod, one PossiblyFalseArgument). Psalm 6 requires PHP 8.3.16 or newer, so locally its version gate was bypassed on PHP 8.3.6; the workflow runs it on PHP 8.4. Psalm 5.26.1 also reports No errors found!.
  • php-cs-fixer fix --dry-run (PHP-CS-Fixer 3.95.27, .php-cs-fixer.dist.php): Found 0 of 2 files that can be fixed (master: two findings in src/Ipdata.php).
  • composer-normalize --dry-run (composer-normalize 2.56.0): ./composer.json is already normalized. (master: not normalized). composer validate: ./composer.json is valid.
  • composer update --prefer-lowest --prefer-stable --no-install with platform.php set to 7.2.34: with php-http/mock-client ^1.3 the lowest set is mock-client 1.3.0, httplug 1.1.0 and client-common 1.9.0, and Http\Mock\Client is not a PSR-18 client; with ^1.5 it is mock-client 1.5.0, httplug 2.0.0, client-common 2.0.0, nyholm/psr7 1.2.0, psr/http-client 1.0.0 and PHPUnit 8.5.0, and the mock client implements Psr\Http\Client\ClientInterface.
  • Each of the nine commits passes vendor/bin/phpunit and the fixer on its own.
  • CI on the final head (757cc07): CI run #25 https://github.com/ipdata/php/actions/runs/41643063629 succeeded with all nine jobs green ("Build lowest version" on PHP 7.2 with PHPUnit 8.5.52 and "Build" on PHP 7.2, 7.3, 7.4, 8.0, 8.1, 8.2, 8.3 and 8.4; the lowest and the PHP 8.4 logs show OK (30 tests, 109 assertions)); Static analysis run #21 https://github.com/ipdata/php/actions/runs/41643063628 succeeded (PHPStan, Psalm, PHP-CS-Fixer, Composer Normalize). On the previous head (dea7adc) the four static-analysis jobs and the eight matrix builds already passed; only "Build lowest version" failed with the TypeError described above, which the last commit fixes.

🤖 Generated with Claude Code

PHP-CS-Fixer 3 reads .php-cs-fixer.dist.php and builds the configuration
with "new PhpCsFixer\Config()"; the removed Config::create() and the old
.php_cs file name made the configuration unusable with current releases.
The finder excluded __DIR__.'/vendor', but Finder::exclude() takes a
directory name relative to in(), so vendor/ was never excluded; it is
now excluded as 'vendor'. The rules (@symfony plus short array syntax)
are unchanged. The cache file name in .gitignore follows the rename.

Fix the two findings the fixer reports in src/Ipdata.php: yoda_style on
the empty-IP comparison and phpdoc_annotation_without_dot on the
@deprecated annotation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The trailing $httpVersion constructor argument comes after $baseUrl, so
callers on PHP 7 had to repeat the literal 'https://api.ipdata.co' to
reach it. With the constant public they pass Ipdata::DEFAULT_BASE_URL
instead, the same way Ipdata::EU_BASE_URL is used. The README example
and the constructor documentation use it; a test covers the constant's
value and visibility and its use together with $httpVersion.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Every request now carries "User-Agent: ipdata-php/<version>", where the
version is the installed version of ipdata/api-client as recorded by
Composer in vendor/composer/installed.php and read through
Composer\InstalledVersions, which ships with Composer 2 and newer, so no
dependency is added. When that class is not available, or the package
is not installed through Composer, the header is "ipdata-php". The
value is computed once in the constructor and set in createRequest(),
so GET and bulk POST requests carry it; the bulk request keeps its
Content-Type header.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
json_encode() returns false when the IP addresses cannot be encoded, for
example for invalid UTF-8, and the result was passed straight to
StreamInterface::write(), which fails with a TypeError under
strict_types. bulkLookup() now throws an InvalidArgumentException that
carries json_last_error_msg() before any request is created. Psalm 6
reports the unchecked result (PossiblyFalseArgument) at the
repository's errorLevel 2.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The baseline ignored "parameter $ips with no value type specified" for
bulkLookup() and buildLookup(), but both methods document
@PARAM array<string> $ips, so PHPStan never reports these errors and
fails the run with "Ignored error pattern ... was not matched". The
baseline is regenerated with PHPStan 2.3 (level 6, phpstan.neon.dist)
and keeps the four entries about the array return types.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
composer-normalize orders the top-level keys in schema order, which puts
"type" after "license". Nothing else changes; the Composer Normalize job
passes again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Psalm 6 enables findUnusedCode by default, which reports the public
methods of a library as unused, and it reports ClassMustBeFinal for
classes that are neither extended nor marked as public API. psalm.xml
now sets findUnusedCode="false" explicitly, the behaviour the
configuration had with Psalm 4 and 5, and the Ipdata class carries the
@api tag: it is instantiated and may be extended by users, so it must
not become final.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Static analysis workflow requested the retired ubuntu-20.04 runner,
so its jobs queued for 24 hours and were cancelled on every pull
request, and the pinned PHPStan 0.12, Psalm 4 and PHP-CS-Fixer 2
releases do not run on current PHP. The jobs now run on ubuntu-latest
with PHP 8.4 and the latest patch releases of PHPStan 2.3, Psalm 6.20,
PHP-CS-Fixer 3.95 and composer-normalize 2.56, using actions/checkout
v7, shivammathur/setup-php v2 and ramsey/composer-install v4 (without
a lock file it runs "composer update").

The CI workflow tested PHP 7.2 to 8.0 with setup-php 1.7.0 and checkout
v2 and requested prestissimo, a Composer 1 plugin that no longer
installs. The matrix now covers PHP 7.2 to 8.4 (composer.json allows
">= 7.2") with the current actions, drops prestissimo (Composer 2
downloads in parallel on its own) and installs no coverage driver: the
workflow never collects coverage, and the Xdebug 2 setting
xdebug.overload_var_dump it configured no longer exists. fail-fast is
off so every PHP version reports its own result.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
With "php-http/mock-client": "^1.3", the lowest-dependency build resolves
mock-client 1.3.0 together with php-http/httplug 1.1.0, whose HttpClient
interface predates PSR-18, so Http\Mock\Client is not a
Psr\Http\Client\ClientInterface and every test fails with a TypeError.
The "php-http/httplug": "^2.0" entry that used to pin HTTPlug 2 was
removed as unused in #9. mock-client 1.5.0 and newer require httplug
^2.0 themselves, so raising the lower bound restores a PSR-18 mock
client in the lowest build; the highest build keeps mock-client 1.6.1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@jonathan-kosgei
jonathan-kosgei merged commit 2fe30ec into master Nov 3, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants