Installation Add the package via Composer:
composer require maxromanovsky/php-maxmind-geoip
First Use Case: Basic Lookup
Load a MaxMind GeoIP database (e.g., GeoLite2-City.mmdb) and perform a lookup:
use MaxMind\Db\Reader;
$reader = new Reader('/path/to/GeoLite2-City.mmdb');
$record = $reader->get($ipAddress); // e.g., '192.0.2.1'
get($ip): Fetch record for an IP.getAll($ip): Return all metadata (including deprecated fields).getIncludes(): List included datasets (for composite databases).Where to Look First
examples/ in the repo for use cases (e.g., CLI scripts, web requests).tests/ for edge-case handling (e.g., invalid IPs, missing files).Web Request Integration Attach to middleware or service providers to auto-enrich requests:
// app/Http/Middleware/GeoIpMiddleware.php
public function handle($request, Closure $next) {
$ip = $request->ip();
$geoData = app(GeoIpService::class)->lookup($ip);
$request->merge(['geo' => $geoData]);
return $next($request);
}
Caching Strategies Cache responses for performance (e.g., 24-hour TTL for static IPs):
$cacheKey = "geoip:{$ip}";
$geoData = Cache::remember($cacheKey, now()->addHours(24), function() use ($reader, $ip) {
return $reader->get($ip);
});
Composite Databases
Use getIncludes() to merge multiple datasets (e.g., City + ISP):
$reader = new Reader('/path/to/composite.mmdb');
$includes = $reader->getIncludes();
foreach ($includes as $include) {
$data[$include->getName()] = $reader->get($ip, $include->getName());
}
Bulk Lookups Process batches (e.g., for analytics):
$ips = ['192.0.2.1', '198.51.100.2'];
$results = collect($ips)->map(fn($ip) => $reader->get($ip));
Reader instance for dependency injection:
$this->app->singleton(Reader::class, function ($app) {
return new Reader(storage_path('app/GeoLite2-City.mmdb'));
});
ValidatesWhenResolved to ensure the database file exists:
public function resolveRouteBinding($ip, $reader) {
if (!file_exists($reader->getPath())) {
abort(500, 'GeoIP database missing');
}
}
config/geoip.php):
$path = config('geoip.database_path');
$reader = new Reader($path);
Database File Permissions
Reader throws MaxMind\Exception\InvalidDatabaseException if the file is unreadable.www-data) has read access:
chmod 644 /path/to/database.mmdb
chown www-data:www-data /path/to/database.mmdb
IPv6 Handling
Reader::withFeatures(Reader::FEATURE_IPV6) if needed:
$reader = Reader::open($path, Reader::MODE_MEMORY, Reader::FEATURE_IPV6);
Memory Usage
GeoLite2-City.mmdb) consume ~50MB+ RAM when loaded.Reader::MODE_FILE for disk-based lookups:
$reader = Reader::open($path, Reader::MODE_FILE);
Deprecated Fields
get() returns only "current" fields; use getAll() for legacy fields (e.g., country_code3).Time Zone Data
time_zone) may be incomplete or outdated.mminfo tool to check the database:
mminfo /path/to/database.mmdb
try {
$record = $reader->get($ip);
} catch (\MaxMind\Exception\InvalidIpAddressException $e) {
Log::warning("Invalid IP: {$ip}");
}
Custom Metadata
Extend the Reader to add application-specific logic:
class ExtendedReader extends Reader {
public function getCountryName($ip) {
$record = $this->get($ip);
return $record['country']['names']['en'] ?? 'Unknown';
}
}
Database Updates Automate updates via a Laravel command:
// app/Console/Commands/UpdateGeoIpDatabase.php
public function handle() {
$this->downloadFromMaxMind();
$this->replaceDatabaseFile();
}
Fallback Logic Implement fallback databases (e.g., local cache if MaxMind API fails):
try {
return $reader->get($ip);
} catch (\Exception $e) {
return $this->fallbackReader->get($ip);
}
Testing
Mock the Reader in unit tests:
$mockReader = Mockery::mock(Reader::class);
$mockReader->shouldReceive('get')
->with('192.0.2.1')
->andReturn(['country' => 'US']);
How can I help you explore Laravel packages today?