Skip to content

Pure PHP Integration

Rumen Damyanov edited this page Jul 31, 2025 · 1 revision

Pure PHP Integration

Framework-agnostic usage patterns for integrating php-geolocation into any PHP application, including legacy systems, custom applications, and microservices.

Table of Contents

Basic Integration

Simple Integration

<?php
require_once 'vendor/autoload.php';

use Rumenx\Geolocation\Geolocation;

// Basic setup - works anywhere in your PHP application
$geo = new Geolocation();

echo "Welcome visitor from " . $geo->getCountryCode() . "!";
echo "Detected language: " . $geo->getLanguage();

Complete Basic Example

<?php
require_once 'vendor/autoload.php';

use Rumenx\Geolocation\Geolocation;

class SimpleGeolocationApp
{
    private Geolocation $geo;
    private array $config;

    public function __construct(array $config = []) {
        $this->config = array_merge([
            'default_language' => 'en',
            'available_languages' => ['en', 'fr', 'de', 'es'],
            'cookie_name' => 'user_language',
            'cookie_lifetime' => 30 * 24 * 60 * 60 // 30 days
        ], $config);

        $this->geo = new Geolocation(
            $_SERVER,
            $this->getCountryMapping(),
            $this->config['cookie_name']
        );
    }

    public function getCountryCode(): string {
        return $this->geo->getCountryCode();
    }

    public function getLanguage(): string {
        $language = $this->geo->getLanguage();

        // Ensure it's in our available languages
        if (!in_array($language, $this->config['available_languages'])) {
            return $this->config['default_language'];
        }

        return $language;
    }

    public function setLanguage(string $language): bool {
        if (!in_array($language, $this->config['available_languages'])) {
            return false;
        }

        return setcookie(
            $this->config['cookie_name'],
            $language,
            time() + $this->config['cookie_lifetime'],
            '/',
            null,
            isset($_SERVER['HTTPS']),
            true
        );
    }

    public function getWelcomeMessage(): string {
        $messages = [
            'en' => 'Welcome!',
            'fr' => 'Bienvenue!',
            'de' => 'Willkommen!',
            'es' => '¡Bienvenido!'
        ];

        $language = $this->getLanguage();
        return $messages[$language] ?? $messages['en'];
    }

    public function getClientInfo(): array {
        return [
            'country' => $this->getCountryCode(),
            'language' => $this->getLanguage(),
            'city' => $this->geo->getCity(),
            'region' => $this->geo->getRegion(),
            'browser' => $this->getBrowserInfo(),
            'device' => $this->getDeviceType()
        ];
    }

    private function getBrowserInfo(): array {
        $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';

        $browsers = [
            'Chrome' => '/Chrome\/([0-9.]+)/',
            'Firefox' => '/Firefox\/([0-9.]+)/',
            'Safari' => '/Version\/([0-9.]+).*Safari/',
            'Edge' => '/Edg\/([0-9.]+)/',
            'Opera' => '/OPR\/([0-9.]+)/'
        ];

        foreach ($browsers as $browser => $pattern) {
            if (preg_match($pattern, $userAgent, $matches)) {
                return [
                    'name' => $browser,
                    'version' => $matches[1]
                ];
            }
        }

        return ['name' => 'Unknown', 'version' => ''];
    }

    private function getDeviceType(): string {
        $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';

        if (preg_match('/Mobile|Android|iPhone|iPad/', $userAgent)) {
            return 'mobile';
        } elseif (preg_match('/Tablet|iPad/', $userAgent)) {
            return 'tablet';
        }

        return 'desktop';
    }

    private function getCountryMapping(): array {
        return [
            'US' => ['en'], 'CA' => ['en', 'fr'], 'GB' => ['en'],
            'DE' => ['de'], 'AT' => ['de'], 'CH' => ['de', 'fr'],
            'FR' => ['fr'], 'BE' => ['fr', 'nl'], 'ES' => ['es'],
            'IT' => ['it'], 'NL' => ['nl'], 'PT' => ['pt'], 'BR' => ['pt']
        ];
    }
}

// Usage
$app = new SimpleGeolocationApp([
    'available_languages' => ['en', 'fr', 'de', 'es'],
    'default_language' => 'en'
]);

// Handle language switching
if (isset($_GET['lang'])) {
    if ($app->setLanguage($_GET['lang'])) {
        header('Location: ' . $_SERVER['PHP_SELF']);
        exit;
    }
}

echo $app->getWelcomeMessage();
echo " (Country: " . $app->getCountryCode() . ")";

Adding to Existing Applications

Drop-in Integration

<?php
// drop-in-geolocation.php - Include this in any existing application

if (!class_exists('DropInGeolocation')) {

class DropInGeolocation
{
    private static ?self $instance = null;
    private Geolocation $geo;
    private array $cache = [];

    private function __construct() {
        if (!class_exists('Rumenx\Geolocation\Geolocation')) {
            throw new RuntimeException('php-geolocation package not found. Install via composer.');
        }

        $this->geo = new Geolocation();
    }

    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    // Quick access methods
    public static function country(): string {
        return self::getInstance()->getCountryCode();
    }

    public static function language(): string {
        return self::getInstance()->getLanguage();
    }

    public static function city(): string {
        return self::getInstance()->getCity();
    }

    public static function isEurope(): bool {
        $euCountries = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'];
        return in_array(self::country(), $euCountries);
    }

    public static function isGDPRRequired(): bool {
        return self::isEurope();
    }

    public static function getCurrencySymbol(): string {
        $currencies = [
            'US' => '$', 'CA' => 'C$', 'GB' => '£', 'EU' => '',
            'JP' => '¥', 'CN' => '¥', 'IN' => '', 'BR' => 'R$',
            'AU' => 'A$', 'NZ' => 'NZ$', 'ZA' => 'R', 'MX' => '$',
            'RU' => '', 'KR' => '', 'TH' => '฿', 'SG' => 'S$'
        ];

        $country = self::country();

        // EU countries use Euro
        if (self::isEurope() && !in_array($country, ['GB', 'SE', 'DK', 'NO', 'CH', 'PL', 'CZ', 'HU', 'RO', 'BG', 'HR'])) {
            return '';
        }

        return $currencies[$country] ?? '$';
    }

    public static function getTimezone(): string {
        $timezones = [
            'US' => 'America/New_York',
            'CA' => 'America/Toronto',
            'GB' => 'Europe/London',
            'DE' => 'Europe/Berlin',
            'FR' => 'Europe/Paris',
            'ES' => 'Europe/Madrid',
            'IT' => 'Europe/Rome',
            'JP' => 'Asia/Tokyo',
            'CN' => 'Asia/Shanghai',
            'AU' => 'Australia/Sydney',
            'BR' => 'America/Sao_Paulo',
            'IN' => 'Asia/Kolkata',
            'RU' => 'Europe/Moscow'
        ];

        return $timezones[self::country()] ?? 'UTC';
    }

    // Delegate to main geolocation instance
    public function getCountryCode(): string {
        return $this->cache['country'] ??= $this->geo->getCountryCode();
    }

    public function getLanguage(): string {
        return $this->cache['language'] ??= $this->geo->getLanguage();
    }

    public function getCity(): string {
        return $this->cache['city'] ??= $this->geo->getCity();
    }

    public function getRegion(): string {
        return $this->cache['region'] ??= $this->geo->getRegion();
    }
}

}

// Auto-initialize if composer autoloader is present
if (class_exists('Rumenx\Geolocation\Geolocation')) {
    DropInGeolocation::getInstance();
}
?>

Usage in existing applications:

<?php
// In any existing PHP file, just include the drop-in file
require_once 'drop-in-geolocation.php';

// Now you can use it anywhere
echo "Welcome visitor from " . DropInGeolocation::country();
echo "Your currency: " . DropInGeolocation::getCurrencySymbol();

if (DropInGeolocation::isGDPRRequired()) {
    echo "GDPR compliance required for your location.";
}

// Use in conditionals
$showEuropeSpecificContent = DropInGeolocation::isEurope();
$defaultTimezone = DropInGeolocation::getTimezone();

Integration with Existing Config Systems

<?php
// For applications with existing configuration systems

class GeolocationConfigAdapter
{
    private array $appConfig;

    public function __construct(array $existingConfig) {
        $this->appConfig = $existingConfig;
    }

    public function createGeolocation(): Geolocation {
        // Map your existing config to geolocation config
        $countryMapping = $this->mapLanguageConfig();
        $cookieName = $this->appConfig['session']['cookie_name'] ?? 'app_language';

        return new Geolocation($_SERVER, $countryMapping, $cookieName);
    }

    private function mapLanguageConfig(): array {
        // Example: convert your app's language config to geolocation format
        $supportedLanguages = $this->appConfig['languages']['supported'] ?? ['en'];

        $mapping = [];
        foreach ($this->getCountryList() as $country) {
            $mapping[$country] = $supportedLanguages;
        }

        return $mapping;
    }

    private function getCountryList(): array {
        return ['US', 'CA', 'GB', 'DE', 'FR', 'ES', 'IT', 'NL', 'BR', 'JP'];
    }
}

// Usage with your existing config
$yourAppConfig = [
    'languages' => [
        'supported' => ['en', 'fr', 'de'],
        'default' => 'en'
    ],
    'session' => [
        'cookie_name' => 'app_lang'
    ]
];

$adapter = new GeolocationConfigAdapter($yourAppConfig);
$geo = $adapter->createGeolocation();

Custom Middleware Patterns

Simple Middleware Class

<?php
class GeolocationMiddleware
{
    private Geolocation $geo;
    private array $config;

    public function __construct(array $config = []) {
        $this->config = array_merge([
            'redirect_unsupported' => false,
            'supported_countries' => [],
            'default_redirect' => '/unsupported-region',
            'language_redirect' => true
        ], $config);

        $this->geo = new Geolocation();
    }

    public function handle(callable $next): void {
        // Check country restrictions
        if (!empty($this->config['supported_countries'])) {
            $country = $this->geo->getCountryCode();
            if (!in_array($country, $this->config['supported_countries'])) {
                if ($this->config['redirect_unsupported']) {
                    header('Location: ' . $this->config['default_redirect']);
                    exit;
                }
            }
        }

        // Handle language redirects
        if ($this->config['language_redirect']) {
            $this->handleLanguageRedirect();
        }

        // Set global variables for the application
        $GLOBALS['geo_country'] = $this->geo->getCountryCode();
        $GLOBALS['geo_language'] = $this->geo->getLanguage();
        $GLOBALS['geo_city'] = $this->geo->getCity();

        // Continue to next middleware or application
        $next();
    }

    private function handleLanguageRedirect(): void {
        $currentPath = $_SERVER['REQUEST_URI'] ?? '/';
        $detectedLang = $this->geo->getLanguage();

        // Check if URL already has language prefix
        if (!preg_match('/^\/[a-z]{2}\//', $currentPath)) {
            // Redirect to language-prefixed URL
            $newPath = '/' . $detectedLang . $currentPath;
            header('Location: ' . $newPath);
            exit;
        }
    }

    public function getGeolocation(): Geolocation {
        return $this->geo;
    }
}

// Middleware chain example
class MiddlewareChain
{
    private array $middlewares = [];

    public function add(callable $middleware): self {
        $this->middlewares[] = $middleware;
        return $this;
    }

    public function handle(callable $final): void {
        $this->executeChain(0, $final);
    }

    private function executeChain(int $index, callable $final): void {
        if ($index >= count($this->middlewares)) {
            $final();
            return;
        }

        $middleware = $this->middlewares[$index];
        $next = function() use ($index, $final) {
            $this->executeChain($index + 1, $final);
        };

        $middleware($next);
    }
}

// Usage
$middleware = new GeolocationMiddleware([
    'supported_countries' => ['US', 'CA', 'GB', 'DE', 'FR'],
    'redirect_unsupported' => true,
    'language_redirect' => true
]);

$chain = new MiddlewareChain();
$chain->add([$middleware, 'handle']);
$chain->add(function($next) {
    // Additional middleware
    $next();
});

$chain->handle(function() {
    // Your application logic
    echo "Application running with country: " . ($GLOBALS['geo_country'] ?? 'Unknown');
});

Router Integration

<?php
class GeolocationRouter
{
    private array $routes = [];
    private Geolocation $geo;

    public function __construct() {
        $this->geo = new Geolocation();
    }

    public function addRoute(string $path, callable $handler, array $countries = []): self {
        $this->routes[] = [
            'path' => $path,
            'handler' => $handler,
            'countries' => $countries,
            'pattern' => $this->pathToPattern($path)
        ];
        return $this;
    }

    public function addCountryRoute(string $country, string $path, callable $handler): self {
        return $this->addRoute($path, $handler, [$country]);
    }

    public function addLanguageRoute(string $language, string $path, callable $handler): self {
        $this->routes[] = [
            'path' => $path,
            'handler' => $handler,
            'language' => $language,
            'pattern' => $this->pathToPattern($path)
        ];
        return $this;
    }

    public function route(string $requestPath = null): void {
        $requestPath = $requestPath ?: ($_SERVER['REQUEST_URI'] ?? '/');
        $requestPath = parse_url($requestPath, PHP_URL_PATH);

        $country = $this->geo->getCountryCode();
        $language = $this->geo->getLanguage();

        foreach ($this->routes as $route) {
            // Check if path matches
            if (!preg_match($route['pattern'], $requestPath, $matches)) {
                continue;
            }

            // Check country restrictions
            if (!empty($route['countries']) && !in_array($country, $route['countries'])) {
                continue;
            }

            // Check language restrictions
            if (isset($route['language']) && $route['language'] !== $language) {
                continue;
            }

            // Execute handler with matched parameters
            $params = array_slice($matches, 1);
            call_user_func_array($route['handler'], $params);
            return;
        }

        // No route found
        $this->handle404();
    }

    private function pathToPattern(string $path): string {
        // Convert /user/{id} to /user/([^/]+)
        $pattern = preg_replace('/\{([^}]+)\}/', '([^/]+)', $path);
        return '#^' . $pattern . '$#';
    }

    private function handle404(): void {
        http_response_code(404);
        echo "404 - Page Not Found";
    }
}

// Usage
$router = new GeolocationRouter();

// General routes
$router->addRoute('/', function() {
    echo "Welcome to our global site!";
});

// Country-specific routes
$router->addCountryRoute('US', '/pricing', function() {
    echo "US Pricing: $9.99/month";
});

$router->addCountryRoute('GB', '/pricing', function() {
    echo "UK Pricing: £7.99/month";
});

$router->addCountryRoute('DE', '/pricing', function() {
    echo "German Pricing: €8.99/month";
});

// Language-specific routes
$router->addLanguageRoute('fr', '/contact', function() {
    echo "Contactez-nous!";
});

$router->addLanguageRoute('de', '/contact', function() {
    echo "Kontaktieren Sie uns!";
});

// Route with parameters
$router->addRoute('/user/{id}', function($id) {
    echo "User profile for ID: $id";
});

// Execute routing
$router->route();

Session Management

Session-Aware Geolocation

<?php
class SessionGeolocation
{
    private Geolocation $geo;
    private string $sessionKey;

    public function __construct(string $sessionKey = 'geolocation_data') {
        $this->sessionKey = $sessionKey;
        $this->ensureSessionStarted();
        $this->geo = new Geolocation();
        $this->initializeSession();
    }

    private function ensureSessionStarted(): void {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
    }

    private function initializeSession(): void {
        if (!isset($_SESSION[$this->sessionKey])) {
            $_SESSION[$this->sessionKey] = [
                'country' => $this->geo->getCountryCode(),
                'language' => $this->geo->getLanguage(),
                'city' => $this->geo->getCity(),
                'region' => $this->geo->getRegion(),
                'detected_at' => time(),
                'user_override' => false
            ];
        }
    }

    public function getCountryCode(): string {
        return $_SESSION[$this->sessionKey]['country'];
    }

    public function getLanguage(): string {
        return $_SESSION[$this->sessionKey]['language'];
    }

    public function getCity(): string {
        return $_SESSION[$this->sessionKey]['city'];
    }

    public function setUserLanguage(string $language): void {
        $_SESSION[$this->sessionKey]['language'] = $language;
        $_SESSION[$this->sessionKey]['user_override'] = true;
        $_SESSION[$this->sessionKey]['override_at'] = time();
    }

    public function setUserCountry(string $country): void {
        $_SESSION[$this->sessionKey]['country'] = $country;
        $_SESSION[$this->sessionKey]['user_override'] = true;
        $_SESSION[$this->sessionKey]['override_at'] = time();
    }

    public function isUserOverride(): bool {
        return $_SESSION[$this->sessionKey]['user_override'] ?? false;
    }

    public function resetToDetected(): void {
        $this->initializeSession();
        $_SESSION[$this->sessionKey]['user_override'] = false;
    }

    public function getSessionData(): array {
        return $_SESSION[$this->sessionKey];
    }

    public function refreshDetection(): void {
        if (!$this->isUserOverride()) {
            // Only refresh if user hasn't manually set preferences
            $_SESSION[$this->sessionKey]['country'] = $this->geo->getCountryCode();
            $_SESSION[$this->sessionKey]['language'] = $this->geo->getLanguage();
            $_SESSION[$this->sessionKey]['city'] = $this->geo->getCity();
            $_SESSION[$this->sessionKey]['region'] = $this->geo->getRegion();
            $_SESSION[$this->sessionKey]['detected_at'] = time();
        }
    }
}

// Enhanced session with preferences
class UserPreferencesGeolocation extends SessionGeolocation
{
    private string $prefsKey;

    public function __construct(string $sessionKey = 'geolocation_data', string $prefsKey = 'user_preferences') {
        $this->prefsKey = $prefsKey;
        parent::__construct($sessionKey);
        $this->initializePreferences();
    }

    private function initializePreferences(): void {
        if (!isset($_SESSION[$this->prefsKey])) {
            $_SESSION[$this->prefsKey] = [
                'preferred_language' => null,
                'preferred_currency' => null,
                'timezone' => null,
                'date_format' => null,
                'number_format' => null
            ];
        }
    }

    public function setPreferredLanguage(string $language): void {
        $_SESSION[$this->prefsKey]['preferred_language'] = $language;
        $this->setUserLanguage($language);
    }

    public function getPreferredLanguage(): ?string {
        return $_SESSION[$this->prefsKey]['preferred_language'];
    }

    public function setPreferredCurrency(string $currency): void {
        $_SESSION[$this->prefsKey]['preferred_currency'] = $currency;
    }

    public function getPreferredCurrency(): string {
        $preferred = $_SESSION[$this->prefsKey]['preferred_currency'];
        if ($preferred) {
            return $preferred;
        }

        // Auto-detect based on country
        $currencyMap = [
            'US' => 'USD', 'CA' => 'CAD', 'GB' => 'GBP',
            'EU' => 'EUR', 'JP' => 'JPY', 'AU' => 'AUD',
            'BR' => 'BRL', 'IN' => 'INR', 'CN' => 'CNY'
        ];

        return $currencyMap[$this->getCountryCode()] ?? 'USD';
    }

    public function getFormattedPrice(float $amount): string {
        $currency = $this->getPreferredCurrency();
        $country = $this->getCountryCode();

        $formats = [
            'USD' => '$%.2f',
            'EUR' => '€%.2f',
            'GBP' => '£%.2f',
            'JPY' => '¥%.0f',
            'CAD' => 'C$%.2f'
        ];

        $format = $formats[$currency] ?? '$%.2f';
        return sprintf($format, $amount);
    }

    public function getAllPreferences(): array {
        return array_merge($_SESSION[$this->prefsKey], [
            'detected_country' => $this->getCountryCode(),
            'detected_language' => $this->getLanguage(),
            'detected_city' => $this->getCity(),
            'is_override' => $this->isUserOverride()
        ]);
    }
}

// Usage
$geoSession = new UserPreferencesGeolocation();

// Handle user preference changes
if (isset($_POST['language'])) {
    $geoSession->setPreferredLanguage($_POST['language']);
}

if (isset($_POST['currency'])) {
    $geoSession->setPreferredCurrency($_POST['currency']);
}

// Use in templates
$welcomeMessage = $geoSession->getLanguage() === 'fr' ? 'Bienvenue' : 'Welcome';
$price = $geoSession->getFormattedPrice(9.99);

echo "$welcomeMessage! Price: $price";

Template Integration

Template Helper Functions

<?php
// template-helpers.php - Include this for template integration

class GeolocationTemplateHelpers
{
    private static ?Geolocation $geo = null;

    private static function getGeo(): Geolocation {
        if (self::$geo === null) {
            self::$geo = new Geolocation();
        }
        return self::$geo;
    }

    // Country helpers
    public static function country(): string {
        return self::getGeo()->getCountryCode();
    }

    public static function countryName(): string {
        $names = [
            'US' => 'United States', 'CA' => 'Canada', 'GB' => 'United Kingdom',
            'DE' => 'Germany', 'FR' => 'France', 'ES' => 'Spain', 'IT' => 'Italy',
            'NL' => 'Netherlands', 'BR' => 'Brazil', 'JP' => 'Japan', 'AU' => 'Australia'
        ];

        return $names[self::country()] ?? self::country();
    }

    public static function isCountry(string $country): bool {
        return self::country() === strtoupper($country);
    }

    public static function inCountries(array $countries): bool {
        return in_array(self::country(), array_map('strtoupper', $countries));
    }

    // Language helpers
    public static function language(): string {
        return self::getGeo()->getLanguage();
    }

    public static function isLanguage(string $language): bool {
        return self::language() === strtolower($language);
    }

    public static function languageName(): string {
        $names = [
            'en' => 'English', 'fr' => 'Français', 'de' => 'Deutsch',
            'es' => 'Español', 'it' => 'Italiano', 'pt' => 'Português',
            'nl' => 'Nederlands', 'ru' => 'Русский', 'ja' => '日本語',
            'zh' => '中文', 'ar' => 'العربية'
        ];

        return $names[self::language()] ?? ucfirst(self::language());
    }

    // Location helpers
    public static function city(): string {
        return self::getGeo()->getCity();
    }

    public static function region(): string {
        return self::getGeo()->getRegion();
    }

    public static function locationString(): string {
        $parts = array_filter([
            self::city(),
            self::region(),
            self::countryName()
        ]);

        return implode(', ', $parts);
    }

    // Utility helpers
    public static function currency(): string {
        $currencies = [
            'US' => '$', 'CA' => 'C$', 'GB' => '£', 'EU' => '',
            'JP' => '¥', 'CN' => '¥', 'IN' => '', 'BR' => 'R$',
            'AU' => 'A$', 'RU' => '', 'KR' => ''
        ];

        $country = self::country();

        // EU countries (simplified list)
        if (in_array($country, ['DE', 'FR', 'ES', 'IT', 'NL', 'BE', 'AT', 'IE', 'PT', 'FI', 'GR'])) {
            return '';
        }

        return $currencies[$country] ?? '$';
    }

    public static function formatPrice(float $amount): string {
        return self::currency() . number_format($amount, 2);
    }

    public static function isEurope(): bool {
        $euCountries = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'];
        return in_array(self::country(), $euCountries);
    }

    public static function needsGDPR(): bool {
        return self::isEurope();
    }

    public static function timezone(): string {
        $timezones = [
            'US' => 'America/New_York', 'CA' => 'America/Toronto',
            'GB' => 'Europe/London', 'DE' => 'Europe/Berlin',
            'FR' => 'Europe/Paris', 'ES' => 'Europe/Madrid',
            'IT' => 'Europe/Rome', 'JP' => 'Asia/Tokyo',
            'CN' => 'Asia/Shanghai', 'AU' => 'Australia/Sydney',
            'BR' => 'America/Sao_Paulo', 'IN' => 'Asia/Kolkata',
            'RU' => 'Europe/Moscow'
        ];

        return $timezones[self::country()] ?? 'UTC';
    }

    public static function localTime(string $format = 'Y-m-d H:i:s'): string {
        $timezone = new DateTimeZone(self::timezone());
        $date = new DateTime('now', $timezone);
        return $date->format($format);
    }

    // Content helpers
    public static function welcomeMessage(): string {
        $messages = [
            'en' => 'Welcome',
            'fr' => 'Bienvenue',
            'de' => 'Willkommen',
            'es' => 'Bienvenido',
            'it' => 'Benvenuto',
            'pt' => 'Bem-vindo',
            'nl' => 'Welkom',
            'ru' => 'Добро пожаловать',
            'ja' => 'いらっしゃいませ',
            'zh' => '欢迎'
        ];

        return $messages[self::language()] ?? $messages['en'];
    }

    public static function flagEmoji(): string {
        $flags = [
            'US' => '🇺🇸', 'CA' => '🇨🇦', 'GB' => '🇬🇧', 'DE' => '🇩🇪',
            'FR' => '🇫🇷', 'ES' => '🇪🇸', 'IT' => '🇮🇹', 'NL' => '🇳🇱',
            'BR' => '🇧🇷', 'JP' => '🇯🇵', 'AU' => '🇦🇺', 'IN' => '🇮🇳',
            'CN' => '🇨🇳', 'RU' => '🇷🇺', 'KR' => '🇰🇷', 'MX' => '🇲🇽'
        ];

        return $flags[self::country()] ?? '🌍';
    }
}

// Make functions available globally (optional)
if (!function_exists('geo_country')) {
    function geo_country(): string {
        return GeolocationTemplateHelpers::country();
    }

    function geo_language(): string {
        return GeolocationTemplateHelpers::language();
    }

    function geo_currency(): string {
        return GeolocationTemplateHelpers::currency();
    }

    function geo_welcome(): string {
        return GeolocationTemplateHelpers::welcomeMessage();
    }

    function geo_flag(): string {
        return GeolocationTemplateHelpers::flagEmoji();
    }

    function geo_price(float $amount): string {
        return GeolocationTemplateHelpers::formatPrice($amount);
    }

    function geo_gdpr(): bool {
        return GeolocationTemplateHelpers::needsGDPR();
    }
}

Template Usage Examples:

<?php require_once 'template-helpers.php'; ?>
<!DOCTYPE html>
<html lang="<?= geo_language() ?>">
<head>
    <title><?= geo_welcome() ?> - My Site</title>
</head>
<body>
    <header>
        <div class="location-info">
            <?= geo_flag() ?> <?= GeolocationTemplateHelpers::locationString() ?>
        </div>

        <div class="language-selector">
            <select onchange="changeLanguage(this.value)">
                <option value="en" <?= geo_language() === 'en' ? 'selected' : '' ?>>English</option>
                <option value="fr" <?= geo_language() === 'fr' ? 'selected' : '' ?>>Français</option>
                <option value="de" <?= geo_language() === 'de' ? 'selected' : '' ?>>Deutsch</option>
            </select>
        </div>
    </header>

    <main>
        <h1><?= geo_welcome() ?>!</h1>

        <?php if (GeolocationTemplateHelpers::isCountry('US')): ?>
            <div class="us-specific">
                <p>Special offer for US customers!</p>
                <p>Price: <?= geo_price(9.99) ?></p>
            </div>
        <?php elseif (GeolocationTemplateHelpers::inCountries(['DE', 'AT', 'CH'])): ?>
            <div class="dach-specific">
                <p>Spezialangebot für DACH-Region!</p>
                <p>Preis: <?= geo_price(8.99) ?></p>
            </div>
        <?php endif; ?>

        <?php if (geo_gdpr()): ?>
            <div class="gdpr-notice">
                <p>We use cookies to enhance your experience. By continuing to browse, you agree to our cookie policy.</p>
            </div>
        <?php endif; ?>

        <p>Your local time: <?= GeolocationTemplateHelpers::localTime('g:i A') ?></p>
    </main>

    <footer>
        <p>Detected location: <?= GeolocationTemplateHelpers::locationString() ?></p>
        <p>Language: <?= GeolocationTemplateHelpers::languageName() ?></p>
    </footer>
</body>
</html>

Legacy System Integration

WordPress Integration (without plugin)

<?php
// wp-content/themes/your-theme/geolocation-integration.php

class WordPressGeolocationIntegration
{
    private Geolocation $geo;

    public function __construct() {
        // Load composer autoloader if not already loaded
        if (!class_exists('Rumenx\Geolocation\Geolocation')) {
            require_once ABSPATH . 'vendor/autoload.php';
        }

        $this->geo = new Geolocation();
        $this->initHooks();
    }

    private function initHooks(): void {
        // Add geolocation data to WordPress
        add_action('wp', [$this, 'setGlobalGeoData']);
        add_action('wp_enqueue_scripts', [$this, 'enqueueGeoScripts']);
        add_filter('body_class', [$this, 'addGeoBodyClasses']);
        add_shortcode('geo_info', [$this, 'geoInfoShortcode']);
        add_shortcode('geo_content', [$this, 'geoContentShortcode']);
    }

    public function setGlobalGeoData(): void {
        global $wp_geo;
        $wp_geo = [
            'country' => $this->geo->getCountryCode(),
            'language' => $this->geo->getLanguage(),
            'city' => $this->geo->getCity(),
            'region' => $this->geo->getRegion()
        ];
    }

    public function enqueueGeoScripts(): void {
        // Pass geo data to JavaScript
        wp_localize_script('jquery', 'geoData', [
            'country' => $this->geo->getCountryCode(),
            'language' => $this->geo->getLanguage(),
            'city' => $this->geo->getCity(),
            'ajax_url' => admin_url('admin-ajax.php')
        ]);
    }

    public function addGeoBodyClasses(array $classes): array {
        $classes[] = 'country-' . strtolower($this->geo->getCountryCode());
        $classes[] = 'language-' . $this->geo->getLanguage();

        if ($this->isEurope()) {
            $classes[] = 'region-europe';
            $classes[] = 'gdpr-required';
        }

        return $classes;
    }

    public function geoInfoShortcode(array $atts): string {
        $atts = shortcode_atts([
            'show' => 'country,language,city',
            'format' => 'list'
        ], $atts);

        $show = explode(',', $atts['show']);
        $info = [];

        if (in_array('country', $show)) {
            $info['Country'] = $this->geo->getCountryCode();
        }
        if (in_array('language', $show)) {
            $info['Language'] = $this->geo->getLanguage();
        }
        if (in_array('city', $show)) {
            $info['City'] = $this->geo->getCity();
        }
        if (in_array('region', $show)) {
            $info['Region'] = $this->geo->getRegion();
        }

        if ($atts['format'] === 'list') {
            $output = '<ul class="geo-info">';
            foreach ($info as $label => $value) {
                $output .= "<li><strong>$label:</strong> $value</li>";
            }
            $output .= '</ul>';
            return $output;
        }

        return implode(', ', $info);
    }

    public function geoContentShortcode(array $atts, string $content = ''): string {
        $atts = shortcode_atts([
            'country' => '',
            'countries' => '',
            'language' => '',
            'exclude_countries' => ''
        ], $atts);

        $currentCountry = $this->geo->getCountryCode();
        $currentLanguage = $this->geo->getLanguage();

        // Check country restrictions
        if (!empty($atts['country']) && $currentCountry !== strtoupper($atts['country'])) {
            return '';
        }

        if (!empty($atts['countries'])) {
            $countries = array_map('strtoupper', explode(',', $atts['countries']));
            if (!in_array($currentCountry, $countries)) {
                return '';
            }
        }

        if (!empty($atts['exclude_countries'])) {
            $excludeCountries = array_map('strtoupper', explode(',', $atts['exclude_countries']));
            if (in_array($currentCountry, $excludeCountries)) {
                return '';
            }
        }

        // Check language restrictions
        if (!empty($atts['language']) && $currentLanguage !== strtolower($atts['language'])) {
            return '';
        }

        return do_shortcode($content);
    }

    private function isEurope(): bool {
        $euCountries = ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE'];
        return in_array($this->geo->getCountryCode(), $euCountries);
    }
}

// Initialize in functions.php
new WordPressGeolocationIntegration();

// Usage in WordPress templates:
global $wp_geo;
echo "Welcome visitor from " . $wp_geo['country'];

// In posts/pages:
// [geo_info show="country,city"]
// [geo_content country="US"]This content only shows to US visitors[/geo_content]
// [geo_content countries="DE,AT,CH"]DACH region content[/geo_content]

Drupal Integration

<?php
// sites/all/modules/custom/geolocation_integration/geolocation_integration.module

/**
 * Implements hook_init().
 */
function geolocation_integration_init() {
    // Load composer autoloader
    if (file_exists(DRUPAL_ROOT . '/vendor/autoload.php')) {
        require_once DRUPAL_ROOT . '/vendor/autoload.php';

        if (class_exists('Rumenx\Geolocation\Geolocation')) {
            $geo = new Rumenx\Geolocation\Geolocation();

            // Store in Drupal globals
            $GLOBALS['drupal_geo'] = [
                'country' => $geo->getCountryCode(),
                'language' => $geo->getLanguage(),
                'city' => $geo->getCity(),
                'region' => $geo->getRegion()
            ];

            // Add CSS classes to body
            drupal_add_css('.country-' . strtolower($geo->getCountryCode()) . ' { }', 'inline');
        }
    }
}

/**
 * Implements hook_preprocess_html().
 */
function geolocation_integration_preprocess_html(&$variables) {
    if (isset($GLOBALS['drupal_geo'])) {
        $geo = $GLOBALS['drupal_geo'];

        $variables['classes_array'][] = 'country-' . strtolower($geo['country']);
        $variables['classes_array'][] = 'language-' . $geo['language'];

        // Add geo data to JavaScript
        drupal_add_js(['geoData' => $geo], 'setting');
    }
}

/**
 * Get geolocation data for use in templates
 */
function geolocation_integration_get_geo_data() {
    return $GLOBALS['drupal_geo'] ?? null;
}

/**
 * Theme function for displaying geo info
 */
function theme_geolocation_info($variables) {
    $geo = geolocation_integration_get_geo_data();
    if (!$geo) return '';

    $output = '<div class="geolocation-info">';
    $output .= '<span class="country">' . $geo['country'] . '</span>';
    $output .= '<span class="city">' . $geo['city'] . '</span>';
    $output .= '</div>';

    return $output;
}

// Usage in Drupal templates:
// $geo = geolocation_integration_get_geo_data();
// echo theme('geolocation_info');

Microservice Patterns

RESTful Geolocation Service

<?php
// microservice.php - Standalone geolocation microservice

class GeolocationMicroservice
{
    private Geolocation $geo;

    public function __construct() {
        $this->geo = new Geolocation();
    }

    public function handleRequest(): void {
        header('Content-Type: application/json');
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type');

        if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
            http_response_code(200);
            exit;
        }

        try {
            $path = $_SERVER['PATH_INFO'] ?? '/';
            $method = $_SERVER['REQUEST_METHOD'];

            switch ($path) {
                case '/':
                case '/info':
                    $this->getGeoInfo();
                    break;

                case '/country':
                    $this->getCountry();
                    break;

                case '/language':
                    $this->getLanguage();
                    break;

                case '/full':
                    $this->getFullInfo();
                    break;

                case '/simulate':
                    if ($method === 'POST') {
                        $this->simulateLocation();
                    } else {
                        $this->methodNotAllowed();
                    }
                    break;

                default:
                    $this->notFound();
            }
        } catch (Exception $e) {
            $this->error(500, $e->getMessage());
        }
    }

    private function getGeoInfo(): void {
        $this->success([
            'country' => $this->geo->getCountryCode(),
            'language' => $this->geo->getLanguage()
        ]);
    }

    private function getCountry(): void {
        $this->success([
            'country' => $this->geo->getCountryCode()
        ]);
    }

    private function getLanguage(): void {
        $this->success([
            'language' => $this->geo->getLanguage()
        ]);
    }

    private function getFullInfo(): void {
        $this->success([
            'country' => $this->geo->getCountryCode(),
            'language' => $this->geo->getLanguage(),
            'city' => $this->geo->getCity(),
            'region' => $this->geo->getRegion(),
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
            'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
            'timestamp' => time()
        ]);
    }

    private function simulateLocation(): void {
        $input = json_decode(file_get_contents('php://input'), true);

        if (!isset($input['country'])) {
            $this->error(400, 'Country is required');
            return;
        }

        $simulated = Geolocation::simulate($input['country']);

        $this->success([
            'country' => $simulated->getCountryCode(),
            'language' => $simulated->getLanguage(),
            'city' => $simulated->getCity(),
            'region' => $simulated->getRegion(),
            'simulated' => true
        ]);
    }

    private function success(array $data): void {
        http_response_code(200);
        echo json_encode([
            'success' => true,
            'data' => $data,
            'timestamp' => time()
        ]);
    }

    private function error(int $code, string $message): void {
        http_response_code($code);
        echo json_encode([
            'success' => false,
            'error' => $message,
            'timestamp' => time()
        ]);
    }

    private function notFound(): void {
        $this->error(404, 'Endpoint not found');
    }

    private function methodNotAllowed(): void {
        $this->error(405, 'Method not allowed');
    }
}

// Run the microservice
$service = new GeolocationMicroservice();
$service->handleRequest();

Client Usage:

<?php
// Client for consuming the geolocation microservice

class GeolocationClient
{
    private string $baseUrl;
    private int $timeout;

    public function __construct(string $baseUrl, int $timeout = 5) {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->timeout = $timeout;
    }

    public function getGeoInfo(): ?array {
        return $this->request('/info');
    }

    public function getCountry(): ?string {
        $result = $this->request('/country');
        return $result['country'] ?? null;
    }

    public function getLanguage(): ?string {
        $result = $this->request('/language');
        return $result['language'] ?? null;
    }

    public function getFullInfo(): ?array {
        return $this->request('/full');
    }

    public function simulate(string $country): ?array {
        return $this->request('/simulate', 'POST', ['country' => $country]);
    }

    private function request(string $endpoint, string $method = 'GET', array $data = null): ?array {
        $url = $this->baseUrl . $endpoint;

        $context = [
            'http' => [
                'method' => $method,
                'timeout' => $this->timeout,
                'header' => [
                    'Content-Type: application/json',
                    'Accept: application/json'
                ]
            ]
        ];

        if ($data) {
            $context['http']['content'] = json_encode($data);
        }

        $response = @file_get_contents($url, false, stream_context_create($context));

        if ($response === false) {
            return null;
        }

        $decoded = json_decode($response, true);

        return $decoded['data'] ?? null;
    }
}

// Usage
$client = new GeolocationClient('https://geo-api.example.com');

$country = $client->getCountry();
$fullInfo = $client->getFullInfo();
$simulated = $client->simulate('DE');

Performance Optimization

Caching Strategies

<?php
class PerformantGeolocation
{
    private ?Geolocation $geo = null;
    private array $cache = [];
    private string $cacheKey;

    public function __construct() {
        $this->cacheKey = $this->generateCacheKey();
        $this->loadFromCache();
    }

    private function generateCacheKey(): string {
        $factors = [
            $_SERVER['HTTP_CF_IPCOUNTRY'] ?? '',
            $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '',
            $_SERVER['REMOTE_ADDR'] ?? ''
        ];

        return 'geo_' . hash('crc32', implode('|', $factors));
    }

    private function loadFromCache(): void {
        // Try APCu first
        if (function_exists('apcu_fetch')) {
            $cached = apcu_fetch($this->cacheKey);
            if ($cached !== false) {
                $this->cache = $cached;
                return;
            }
        }

        // Fallback to session cache
        if (session_status() === PHP_SESSION_ACTIVE && isset($_SESSION[$this->cacheKey])) {
            $cached = $_SESSION[$this->cacheKey];
            if ($cached['expires'] > time()) {
                $this->cache = $cached['data'];
                return;
            }
        }

        // Load fresh data
        $this->refreshCache();
    }

    private function refreshCache(): void {
        if ($this->geo === null) {
            $this->geo = new Geolocation();
        }

        $this->cache = [
            'country' => $this->geo->getCountryCode(),
            'language' => $this->geo->getLanguage(),
            'city' => $this->geo->getCity(),
            'region' => $this->geo->getRegion(),
            'timestamp' => time()
        ];

        $this->saveToCache();
    }

    private function saveToCache(): void {
        $ttl = 3600; // 1 hour

        // Save to APCu
        if (function_exists('apcu_store')) {
            apcu_store($this->cacheKey, $this->cache, $ttl);
        }

        // Save to session as fallback
        if (session_status() === PHP_SESSION_ACTIVE) {
            $_SESSION[$this->cacheKey] = [
                'data' => $this->cache,
                'expires' => time() + $ttl
            ];
        }
    }

    public function getCountryCode(): string {
        return $this->cache['country'] ?? 'US';
    }

    public function getLanguage(): string {
        return $this->cache['language'] ?? 'en';
    }

    public function getCity(): string {
        return $this->cache['city'] ?? '';
    }

    public function getRegion(): string {
        return $this->cache['region'] ?? '';
    }

    public function warmCache(array $countries): void {
        foreach ($countries as $country) {
            $simulated = Geolocation::simulate($country);
            $cacheKey = 'geo_simulated_' . $country;

            $data = [
                'country' => $simulated->getCountryCode(),
                'language' => $simulated->getLanguage(),
                'city' => $simulated->getCity(),
                'region' => $simulated->getRegion(),
                'timestamp' => time()
            ];

            if (function_exists('apcu_store')) {
                apcu_store($cacheKey, $data, 3600);
            }
        }
    }
}

// Usage
$geo = new PerformantGeolocation();

// Warm cache for common countries (run this during deployment)
$geo->warmCache(['US', 'GB', 'DE', 'FR', 'CA', 'AU', 'JP']);

echo $geo->getCountryCode(); // Fast, cached response

Next Steps


Previous: Troubleshooting | Next: CodeIgniter Integration

Clone this wiki locally