A simple, framework-agnostic PHP utility for Cloudflare geolocation detection and client information extraction. Provides helpers to access geolocation, language, and client information (OS, browser, device, resolution) from HTTP headers.
- ๐ Detects Cloudflare geolocation headers (country, IP, etc.)
- ๐ Helper methods to access geolocation, language, and client info (OS, browser, device, resolution)
- ๐ Configurable country-to-language mapping (supports multiple official languages per country)
- ๐ค Language negotiation: matches browser and available site languages for multi-language countries
- ๐ช Configurable language cookie name
- โ๏ธ Configurable fields for returned visitor info
- ๐ ๏ธ Local development simulation - Fake Cloudflare headers for testing without production setup
- ๐ญ Auto-detection of local environments (localhost, local IPs, missing Cloudflare headers)
- ๐งช Fully tested with Pest (100% coverage)
- โ PSR-12 compliant, static analysis and style checks
- ๐ Simple utility class - no framework dependencies or complex setup required
For comprehensive documentation, examples, and advanced usage patterns, visit our Complete Wiki Documentation:
- Quick Start Guide - Get running in minutes
- Framework Integration - Laravel, Symfony, CodeIgniter guides
- CloudFlare Setup - Production configuration
- Local Development - Testing without CloudFlare
- Multi-language Websites - International applications
- Production Deployment - Best practices & monitoring
- API Development - RESTful APIs with geolocation
- Configuration Reference - Complete options guide
- Error Handling - Robust error management
- Caching Strategies - Performance optimization
- Analytics Integration - Geographic tracking
- Troubleshooting - Common issues & solutions
This package is intentionally designed as a simple utility library rather than a complex framework integration. Here's why:
- Single responsibility: Extract and process geolocation data from HTTP headers
- Pure functions: No side effects, no global state, predictable behavior
- Framework-agnostic: Works with any PHP application or framework
- No service providers needed: Just instantiate the class when you need it
- No configuration files: Pass configuration directly to the constructor
- No middleware complexity: Use it exactly where and when you need it
- Developer control: You decide how and when to use geolocation data
- Zero runtime dependencies: Only requires PHP 8.3+
- Small footprint: Single class, focused functionality
- Fast installation: No complex dependency trees
- Version compatibility: No framework version constraints
This approach makes the package more reliable, easier to understand, and simpler to maintain - following the Unix philosophy of "do one thing and do it well."
composer require rumenx/php-geolocation
When developing locally where Cloudflare is not available, you can simulate its functionality:
use Rumenx\Geolocation\Geolocation;
// Create a simulated instance for a specific country
$geo = Geolocation::simulate('DE', [
'DE' => ['de'],
'CA' => ['en', 'fr']
]);
echo $geo->getCountryCode(); // 'DE'
echo $geo->getIp(); // Simulated IP like '192.168.4.123'
use Rumenx\Geolocation\GeolocationSimulator;
// Generate fake Cloudflare headers
$headers = GeolocationSimulator::fakeCloudflareHeaders('JP', [
'user_agent' => 'Custom User Agent',
'server_name' => 'dev.example.com'
]);
// Create instance with simulated server data
$geo = new Geolocation($headers, ['JP' => ['ja', 'en']]);
$geo = new Geolocation();
if ($geo->isLocalDevelopment()) {
// Automatically detected: localhost, local IPs, or missing Cloudflare headers
echo "Running in local development mode";
}
// Get list of built-in countries
$countries = GeolocationSimulator::getAvailableCountries();
// ['US', 'CA', 'GB', 'DE', 'FR', 'JP', 'AU', 'BR']
// Get random country for testing
$randomCountry = GeolocationSimulator::randomCountry();
For Laravel and Symfony, check the /examples
directory for middleware and event listeners that automatically inject simulated Cloudflare headers in development environments.
use Rumenx\Geolocation\Geolocation;
// Simple usage with defaults
$geo = new Geolocation();
$country = $geo->getCountryCode();
$ip = $geo->getIp();
$info = $geo->getGeoInfo();
// Advanced usage with custom configuration
$countryToLanguage = [
'CA' => ['en', 'fr'], // Canada: English (default), French
'DE' => ['de'], // Germany: German
'CH' => ['de', 'fr', 'it'], // Switzerland: German, French, Italian
// Add more countries as needed...
];
$geo = new Geolocation(
$_SERVER, // HTTP server array (optional, defaults to $_SERVER)
$countryToLanguage, // Country-to-language mapping (optional)
'my_lang_cookie' // Custom cookie name (optional, defaults to 'lang')
);
// Get best language for visitor based on country and browser preferences
$availableSiteLanguages = ['en', 'fr', 'de', 'es'];
$lang = $geo->getLanguageForCountry(null, $availableSiteLanguages);
// Language selection logic:
// 1. If browser preferred language matches a country language and is available, use it
// 2. Else, check all browser languages for a match with available languages
// 3. Else, use the first country language as fallback
// 4. Returns null if no match found
// Check if language should be set (based on cookie)
if ($geo->shouldSetLanguage()) {
// Set language in your application
setcookie($geo->languageCookieName, $lang);
}
// Get specific information
$country = $geo->getCountryCode(); // 'US', 'CA', 'DE', etc.
$ip = $geo->getIp(); // '192.168.1.1'
$browser = $geo->getBrowser(); // ['name' => 'Chrome', 'version' => '91.0']
$os = $geo->getOs(); // 'Windows 10', 'macOS', 'Linux', etc.
$device = $geo->getDeviceType(); // 'desktop', 'mobile', 'tablet'
$resolution = $geo->getResolution(); // ['width' => 1920, 'height' => 1080]
// Get all information at once
$info = $geo->getGeoInfo();
// Returns: [
// 'country_code' => 'US',
// 'ip' => '192.168.1.1',
// 'preferred_language' => 'en-US',
// 'all_languages' => ['en-US', 'en', 'fr'],
// 'user_agent' => 'Mozilla/5.0...',
// 'browser' => ['name' => 'Chrome', 'version' => '91.0'],
// 'os' => 'Windows 10',
// 'device_type' => 'desktop',
// 'resolution' => ['width' => 1920, 'height' => 1080]
// ]
// Get only specific fields
$specificInfo = $geo->getGeoInfo(['country_code', 'ip', 'browser']);
// In a controller
class HomeController extends Controller
{
public function index(Request $request)
{
$geo = new Geolocation(
$request->server->all(),
config('app.country_to_language', [])
);
$country = $geo->getCountryCode();
$lang = $geo->getLanguageForCountry(null, ['en', 'fr', 'de']);
if ($geo->shouldSetLanguage() && $lang) {
app()->setLocale($lang);
}
return view('home', [
'geo' => $geo->getGeoInfo(['country_code', 'ip']),
'language' => $lang
]);
}
}
// In a middleware (optional)
class GeolocationMiddleware
{
public function handle($request, Closure $next)
{
$geo = new Geolocation($request->server->all());
$lang = $geo->getLanguageForCountry();
if ($lang && $geo->shouldSetLanguage()) {
app()->setLocale($lang);
}
return $next($request);
}
}
// In a controller
class HomeController extends AbstractController
{
public function index(Request $request): Response
{
$geo = new Geolocation(
$request->server->all(),
$this->getParameter('country_to_language')
);
$country = $geo->getCountryCode();
$lang = $geo->getLanguageForCountry(null, ['en', 'fr', 'de']);
if ($geo->shouldSetLanguage() && $lang) {
$request->setLocale($lang);
}
return $this->render('home.html.twig', [
'geo' => $geo->getGeoInfo(),
'language' => $lang
]);
}
}
// In an event listener (optional)
class GeolocationListener
{
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
$geo = new Geolocation($request->server->all());
$lang = $geo->getLanguageForCountry();
if ($lang && $geo->shouldSetLanguage()) {
$request->setLocale($lang);
}
}
}
// In any PHP application
session_start();
$geo = new Geolocation($_SERVER, [
'US' => ['en'],
'CA' => ['en', 'fr'],
'DE' => ['de'],
'FR' => ['fr']
]);
$country = $geo->getCountryCode();
$lang = $geo->getLanguageForCountry(null, ['en', 'fr', 'de']);
// Set language preference
if ($geo->shouldSetLanguage() && $lang) {
$_SESSION['language'] = $lang;
setcookie('lang', $lang, time() + (86400 * 30)); // 30 days
}
// Use the information
echo "Welcome visitor from: " . ($country ?? 'Unknown');
echo "Preferred language: " . ($lang ?? 'Default');
The package is simple and requires minimal configuration. All settings are passed directly to the constructor:
$geo = new Geolocation($server, $countryToLanguage, $languageCookieName);
$server
(array, optional): HTTP server array, defaults to$_SERVER
$countryToLanguage
(array, optional): Country code to language mapping$languageCookieName
(string, optional): Language cookie name, defaults to'lang'
Map country codes (ISO 3166-1 alpha-2) to language codes or arrays. The first language is the default for the country:
$countryToLanguage = [
'US' => ['en'], // United States: English only
'CA' => ['en', 'fr'], // Canada: English (default), French
'CH' => ['de', 'fr', 'it', 'rm'], // Switzerland: German (default), French, Italian, Romansh
'BE' => ['nl', 'fr', 'de'], // Belgium: Dutch (default), French, German
'IN' => ['hi', 'en'], // India: Hindi (default), English
'ZA' => ['en', 'af', 'zu'], // South Africa: English (default), Afrikaans, Zulu
// Add more countries as needed...
];
// Use defaults for everything
$geo = new Geolocation();
$geo = new Geolocation($_SERVER, [
'DE' => ['de'],
'FR' => ['fr'],
'ES' => ['es']
]);
$geo = new Geolocation($_SERVER, [], 'user_language');
$geo = new Geolocation(
$_SERVER, // or $request->server->all() in frameworks
[
'US' => ['en'],
'CA' => ['en', 'fr'],
'MX' => ['es'],
'DE' => ['de'],
'AT' => ['de'],
'CH' => ['de', 'fr', 'it'],
'FR' => ['fr'],
'BE' => ['nl', 'fr'],
'IT' => ['it'],
'ES' => ['es'],
'BR' => ['pt'],
'PT' => ['pt'],
'RU' => ['ru'],
'CN' => ['zh'],
'JP' => ['ja'],
'KR' => ['ko']
],
'preferred_language'
);
No configuration files, service providers, or complex setup needed!
The examples/
directory contains practical demonstrations of the package capabilities:
demo.php
- Interactive demo showing simulation in local development with multiple countries
LaravelDevelopmentMiddleware.php
- Laravel middleware for automatic header injection in developmentSymfonyDevelopmentListener.php
- Symfony event listener for request-level simulation
content-localization.php
- Redirect visitors to country-specific domainsapi-endpoint.php
- REST API with geolocation-based responses (currency, features, etc.)multi-language.php
- Automatic language detection with fallbacks for multi-language sites
# Basic simulation demo
php examples/demo.php
# Content localization
php examples/content-localization.php
# API endpoint simulation
php examples/api-endpoint.php
# Multi-language detection
php examples/multi-language.php
All examples automatically detect local development and use simulation, so they work perfectly without Cloudflare setup! ๐
We welcome contributions! Please see our Contributing Guide for details on how to contribute to this project.
This project adheres to a Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to the project maintainers.
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
composer test
- Run static analysis:
composer analyze
- Check code style:
composer style
- Submit a pull request
- go-geolocation - Go adaptation of this package with similar functionality for the Go ecosystem
- Python version - Python adaptation planned for future release
- WordPress plugin - WordPress integration plugin planned
- Drupal module - Drupal integration module planned
If you discover a security vulnerability, please see our Security Policy for information on how to report it responsibly.
All notable changes to this project are documented in the Changelog.
- ๐ Documentation
- ๐ Issue Tracker
- ๐ฌ Discussions
- ๐ Sponsor this project