gryfoss / http-cf-geolocation-service
Wrapper around GeoIP service and CloudFlare functionality which grants ISO code of the detected country during masking of IPs.
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/gryfoss/http-cf-geolocation-service
Requires
- php: ^8.2
- geoip2/geoip2: ^3.2
- symfony/http-foundation: ^7.3
Requires (Dev)
- phpunit/phpunit: ^11.0
README
A high-performance PHP service for detecting user geolocation using Cloudflare headers with MaxMind GeoIP database fallback.
π― Purpose
CFGeolocationService is designed to provide fast and accurate geolocation detection by leveraging Cloudflare's edge network capabilities while maintaining reliability through GeoIP database fallbacks. This dual-strategy approach ensures optimal performance and accuracy for web applications behind Cloudflare's proxy network.
Key Benefits
- π Performance: Prioritizes Cloudflare headers for instant geolocation (no database lookup required)
- π‘οΈ Reliability: Falls back to MaxMind GeoIP2 database when Cloudflare headers are unavailable
- π Accuracy: Uses Cloudflare's global edge network data combined with MaxMind's proven database
- β‘ Speed: Minimal overhead with intelligent caching strategy
- π Security: Validates all inputs and handles edge cases gracefully
ποΈ How It Works
1. Check CF-IPCountry header β Valid ISO country code? β Return country
β
2. Check CF-Connecting-IP header β Valid IP address? β Use for GeoIP lookup
β
3. Fall back to Symfony getClientIp() β Perform GeoIP database lookup β Return country
Dual Detection Strategy
-
Primary: Cloudflare Headers (instant results)
CF-IPCountry: ISO 3166-1 alpha-2 country codeCF-Connecting-IP: Real client IP address
-
Fallback: GeoIP Database Lookup
- MaxMind GeoLite2/GeoIP2 Country database
- Handles cases where CF headers are missing or invalid
π Installation
Via Composer
composer require gryfoss/http-cf-geolocation-service
Requirements
- PHP: 8.1 or higher
- Extensions: mbstring, filter
- Dependencies:
symfony/http-foundation^7.3geoip2/geoip2^3.2
π Usage
Basic Usage
<?php use GryfOSS\Geolocation\CFGeolocationService; use Symfony\Component\HttpFoundation\Request; // Initialize with GeoIP database path $service = new CFGeolocationService('/path/to/GeoLite2-Country.mmdb'); // Get current request $request = Request::createFromGlobals(); // Detect client IP address $clientIp = $service->getIp($request); echo "Client IP: " . $clientIp; // e.g., "203.0.113.1" // Detect country code $countryCode = $service->getCountryCode($request); echo "Country: " . $countryCode; // e.g., "US"
With Cloudflare Headers
When your application is behind Cloudflare, the service automatically uses CF headers:
// Cloudflare provides these headers: // CF-Connecting-IP: 203.0.113.1 // CF-IPCountry: US $countryCode = $service->getCountryCode($request); // Returns "US" instantly (no database lookup needed) $clientIp = $service->getIp($request); // Returns "203.0.113.1" (real client IP, not Cloudflare's)
Without Cloudflare Headers
The service gracefully falls back to GeoIP database lookup:
// No CF headers available $countryCode = $service->getCountryCode($request); // Performs GeoIP database lookup on client IP // Returns country code based on IP geolocation
Error Handling
use GryfOSS\Geolocation\CFGeolocationService; try { $service = new CFGeolocationService('/invalid/path/database.mmdb'); } catch (\InvalidArgumentException $e) { echo "Database not found: " . $e->getMessage(); } try { $countryCode = $service->getCountryCode($request); } catch (\Exception $e) { echo "Geolocation failed: " . $e->getMessage(); }
Complete Examples
See examples.php for comprehensive usage examples including:
- Basic geolocation detection
- Cloudflare header simulation
- GeoIP database fallback scenarios
- Error handling demonstrations
- Testing with various IP addresses
# Run examples (requires database download first)
./pull-free-db.sh
php examples.php
π§ͺ Testing
Download GeoIP Database
β οΈ Important: Due to MaxMind's licensing restrictions, the GeoIP database cannot be bundled with this package. You must download it separately.
# Download free GeoLite2 database ./pull-free-db.sh # Or manually download from: # https://dev.maxmind.com/geoip/geoip2/geolite2/
Run Tests
# Install development dependencies composer install # Download test database ./pull-free-db.sh # Run complete test suite ./vendor/bin/phpunit # Run tests with coverage XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-text # Run tests with readable output ./vendor/bin/phpunit --testdox # Run usage examples php examples.php
Test Coverage
The project maintains 100% code coverage:
- β Lines: 100%
- β Methods: 100%
- β Classes: 100%
# Verify coverage locally XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-html coverage-report # Open coverage-report/index.html in your browser
Testing Strategy
- Unit Tests: 32 unit tests covering all methods and edge cases
- Integration Tests: 6 integration tests with real GeoIP database
- Total: 38 tests with 47 assertions
- Data Providers: Multiple test scenarios for various input combinations
- Exception Testing: Error handling and edge case validation
- Real Database: Tests use actual GeoLite2-Country.mmdb for authenticity
ποΈ GeoIP Database Setup
Database Licensing Notice
β οΈ Important Licensing Information
This package does not include the MaxMind GeoIP database due to licensing restrictions:
- GeoLite2 databases are available for free but require separate download
- Commercial GeoIP2 databases require a MaxMind license
- Distribution restrictions prevent bundling databases with open-source packages
Download Options
Option 1: Free GeoLite2 Database
# Use the included script ./pull-free-db.sh # Manual download wget https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb
Option 2: Official MaxMind Account
- Create account at MaxMind
- Generate license key
- Download GeoLite2 or purchase GeoIP2 databases
- Follow MaxMind's terms of service
Option 3: Automated Updates
# Set up cron job for monthly updates 0 0 1 * * /path/to/your/project/pull-free-db.sh
Database Paths
// Common database locations $service = new CFGeolocationService('/usr/local/share/GeoIP/GeoLite2-Country.mmdb'); $service = new CFGeolocationService('/var/lib/GeoIP/GeoLite2-Country.mmdb'); $service = new CFGeolocationService('./data/GeoLite2-Country.mmdb');
π§ Configuration
Environment Variables
# Set database path via environment export GEOIP_DATABASE_PATH="/path/to/GeoLite2-Country.mmdb"
// Use in your application $databasePath = $_ENV['GEOIP_DATABASE_PATH'] ?? '/default/path/GeoLite2-Country.mmdb'; $service = new CFGeolocationService($databasePath);
Framework Integration
Symfony
# config/services.yaml services: GryfOSS\Geolocation\CFGeolocationService: arguments: $databasePath: '%env(GEOIP_DATABASE_PATH)%'
Laravel
// config/app.php or service provider $this->app->singleton(CFGeolocationService::class, function ($app) { return new CFGeolocationService(env('GEOIP_DATABASE_PATH')); });
π Quality Assurance
GitHub Actions CI/CD
- Multi-PHP Testing: 8.2, 8.3, 8.4
- Automated Database Download: Fresh GeoLite2 database for each test run
- 100% Coverage Enforcement: Builds fail if coverage drops below 100%
- Cross-Platform: Ubuntu latest with comprehensive test matrix
Code Quality Standards
- PSR-4 Autoloading: Namespace compliance
- PHPDoc Documentation: Comprehensive method and class documentation
- Type Declarations: Strict typing for all parameters and returns
- Error Handling: Graceful exception handling for all failure modes
- Input Validation: Thorough validation of all external inputs
π€ Contributing
We welcome contributions from the community! Here's how you can help:
Reporting Issues
- Bug Reports: Use the GitHub Issues to report bugs
- Feature Requests: Propose new features or improvements
- Security Issues: Report security vulnerabilities privately
Pull Requests
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes with tests
- Ensure 100% test coverage:
XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-text - Follow coding standards: PSR-12 compliance
- Submit a pull request with detailed description
Development Guidelines
- Write tests first: TDD approach preferred
- Maintain 100% coverage: All new code must be fully tested
- Follow existing patterns: Consistent with current codebase
- Document thoroughly: PHPDoc for all public methods
- Validate inputs: Handle edge cases gracefully
Code of Conduct
Please follow our code of conduct in all interactions:
- Be respectful and inclusive
- Focus on constructive feedback
- Help others learn and grow
- Maintain professionalism
π License
This project is licensed under the MIT License - see the LICENSE file for details.
π₯ Authors
- IDCT Bartosz PachoΕek - GitHub - Initial work
π Acknowledgments
- MaxMind for providing GeoIP2 and GeoLite2 databases
- Cloudflare for their excellent geolocation headers
- Symfony for the robust HTTP foundation component
- P3TERX for maintaining the GeoLite2 mirror repository
- PHP Community for continuous improvements and feedback
π Additional Resources
Made with β€οΈ by the GryfOSS team