geocoder-php/geoips-provider
Geocoder provider for the geoips.com service, letting you resolve IP addresses into geographic coordinates and location data through the Geocoder PHP library's provider interface. A lightweight way to add GeoIPs-backed IP geolocation lookups to your PHP application.
This package is abandoned as of 4.2.0. The upstream GeoIPs service has shut down its APIs, so the GeoIPs-backed driver no longer returns data. Treat 4.2.0 as a legacy pin, not a package to adopt.
If you are inheriting a project that already depends on it:
composer show --direct | grep -i geoip
composer why <vendor>/<package>
config/geoip.php (or equivalent) for the configured service/driver. Anything pointing at GeoIPs must be replaced or removed before the next deploy."<vendor>/<package>": "4.2.0" or the last version that worked) so a future composer update does not silently change behaviour on an unmaintained dependency.grep -rn "GeoIP\|geoip\|getLocation\|country_code" app/ resources/ routes/ tests/
The first real use case is almost always "look up a visitor's country for currency/locale/redirect/compliance decisions." If that is the only use case, the cheapest migration is often not a like-for-like driver swap but moving the lookup behind your own interface and filling it with a maintained provider (MaxMind GeoLite2 via geoip2/geoip2, a CDN/edge header such as Cloudflare's CF-IPCountry, or a paid API). Do that before adding any new features on top of the abandoned package.
Wrap it before you replace it. Introduce an application-owned contract now, even if the implementation still delegates to this package. This is the single highest-leverage change, because it makes the eventual removal a one-file diff:
// app/Contracts/GeoResolver.php
interface GeoResolver
{
public function countryFor(string $ip): ?string;
}
// app/Services/LegacyGeoIpResolver.php
final class LegacyGeoIpResolver implements GeoResolver
{
public function countryFor(string $ip): ?string
{
return cache()->remember("geo:{$ip}", now()->addDay(), function () use ($ip) {
return GeoIP::getLocation($ip)?->country_code ?? null;
});
}
}
Bind the interface in a service provider, inject GeoResolver everywhere, and never let the facade leak into controllers, Blade views, or models.
Resolve at the edge of the request, store the result. Geo lookups are I/O. Resolve once during registration/checkout/first visit and persist the country on the user or order record. Subsequent reads should hit your database, not the resolver.
Use a fallback chain, not a single provider. A resilient resolver tries, in order: trusted edge header → local MaxMind database → cached last-known value → configured default. An abandoned driver belongs at the end of that chain, if anywhere, and only behind a short timeout.
Queue anything non-blocking. If you are enriching existing records with country data, do it in a job with retries and a dead-letter path. A shutdown upstream API turns a synchronous backfill into a fleet of hung requests.
Cache aggressively and key by IP + provider. Geo data changes slowly; API availability does not. A 24-hour cache both cuts latency and blunts the impact of the dead GeoIPs endpoint, since cached rows keep serving after the provider fails.
Test against a fake, not the network. Bind a stub GeoResolver returning fixed countries in your test suite. Never let feature tests reach a third-party Geo endpoint — with an abandoned provider, those tests are now permanently red or silently asserting on null.
Migration path, concretely: add the interface and binding (one PR, no behaviour change) → add a second implementation backed by MaxMind or edge headers → switch the binding via config → delete the legacy implementation and the Composer dependency. Each step is independently deployable.
null results — not stale-but-plausible data. Any code path that assumes a non-null location will now throw or, worse, silently write empty country codes. Audit for ->country_code on a possibly-null result and coerce explicitly.null is a valid answer. Private/reserved IPs (10.0.0.0/8, 127.0.0.1, ::1), IPv6 addresses, and local development traffic will not resolve. Design for a nullable country and a documented default, and make sure local and CI environments never depend on a live lookup.iso_code, country_code, country, and countryCode are not interchangeable across drivers and versions. Normalise to a single two-letter ISO 3166-1 alpha-2 string at the boundary; everything downstream should speak only that.php artisan config:cache after editing config/geoip.php, or your change has no effect in production. This is the most common "I fixed it locally" failure with these packages.composer audit for transitive advisories. Add a scheduled reminder to revisit the dependency; an unmaintained package tends to break on the next framework major, at the worst possible time.composer outdated will otherwise re-litigate this from scratch.How can I help you explore Laravel packages today?