Skip to content

Client Detection

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

Client Information Detection

Comprehensive guide to detecting and using browser, device, operating system, and resolution information.

Table of Contents

Overview

The php-geolocation package provides comprehensive client detection capabilities beyond just country detection. This information helps you:

  • Optimize User Experience: Serve appropriate layouts for different devices
  • Feature Detection: Enable/disable features based on browser capabilities
  • Analytics: Track visitor demographics and technology usage
  • Performance: Optimize content delivery based on device capabilities
  • Security: Identify potential security threats or unusual patterns

Available Information

use Rumenx\Geolocation\Geolocation;

$geo = new Geolocation();
$clientInfo = $geo->getGeoInfo([
    'browser',      // Browser name and version
    'os',          // Operating system
    'device',      // Device type (desktop/mobile/tablet)
    'resolution'   // Screen resolution
]);

print_r($clientInfo);
/* Output:
Array (
    [browser] => Array (
        [browser] => Chrome
        [version] => 91.0.4472.124
    )
    [os] => Windows 10
    [device] => desktop
    [resolution] => Array (
        [width] => 1920
        [height] => 1080
    )
)
*/

Browser Detection

Basic Browser Information

$geo = new Geolocation();
$browser = $geo->getBrowser();

if ($browser) {
    echo "Browser: {$browser['browser']}\n";
    echo "Version: {$browser['version']}\n";

    // Browser-specific logic
    switch ($browser['browser']) {
        case 'Chrome':
            $features = ['webp', 'modern-js', 'css-grid'];
            break;
        case 'Firefox':
            $features = ['webp', 'modern-js', 'css-grid'];
            break;
        case 'Safari':
            $features = ['webp', 'modern-js', 'css-grid'];
            $safariVersion = floatval($browser['version']);
            if ($safariVersion < 14) {
                // Remove features not supported in older Safari
                $features = array_diff($features, ['webp']);
            }
            break;
        case 'Internet Explorer':
            $features = ['legacy-support'];
            break;
        default:
            $features = ['basic'];
    }

    echo "Supported features: " . implode(', ', $features) . "\n";
}

Browser Version Comparison

function compareBrowserVersion($browser, $requiredBrowser, $requiredVersion) {
    if (!$browser || $browser['browser'] !== $requiredBrowser) {
        return false;
    }

    return version_compare($browser['version'], $requiredVersion, '>=');
}

$geo = new Geolocation();
$browser = $geo->getBrowser();

// Check for modern browser features
$supportsModernJS =
    compareBrowserVersion($browser, 'Chrome', '60') ||
    compareBrowserVersion($browser, 'Firefox', '55') ||
    compareBrowserVersion($browser, 'Safari', '12') ||
    compareBrowserVersion($browser, 'Edge', '79');

if ($supportsModernJS) {
    echo '<script type="module" src="modern-app.js"></script>';
} else {
    echo '<script src="legacy-app.js"></script>';
}

Browser Capability Detection

class BrowserCapabilities
{
    private $browser;

    public function __construct($browser) {
        $this->browser = $browser;
    }

    public function supportsWebP(): bool {
        if (!$this->browser) return false;

        $name = $this->browser['browser'];
        $version = floatval($this->browser['version']);

        switch ($name) {
            case 'Chrome':
                return $version >= 23;
            case 'Firefox':
                return $version >= 65;
            case 'Safari':
                return $version >= 14;
            case 'Edge':
                return $version >= 18;
            default:
                return false;
        }
    }

    public function supportsAVIF(): bool {
        if (!$this->browser) return false;

        $name = $this->browser['browser'];
        $version = floatval($this->browser['version']);

        switch ($name) {
            case 'Chrome':
                return $version >= 85;
            case 'Firefox':
                return $version >= 93;
            default:
                return false;
        }
    }

    public function supportsCSSGrid(): bool {
        if (!$this->browser) return false;

        $name = $this->browser['browser'];
        $version = floatval($this->browser['version']);

        switch ($name) {
            case 'Chrome':
                return $version >= 57;
            case 'Firefox':
                return $version >= 52;
            case 'Safari':
                return $version >= 10.1;
            case 'Edge':
                return $version >= 16;
            default:
                return false;
        }
    }

    public function getImageFormat(): string {
        if ($this->supportsAVIF()) {
            return 'avif';
        } elseif ($this->supportsWebP()) {
            return 'webp';
        } else {
            return 'jpg';
        }
    }
}

// Usage
$geo = new Geolocation();
$browser = $geo->getBrowser();
$capabilities = new BrowserCapabilities($browser);

$imageFormat = $capabilities->getImageFormat();
echo "Best image format: {$imageFormat}\n";

if ($capabilities->supportsCSSGrid()) {
    echo '<link rel="stylesheet" href="grid-layout.css">';
} else {
    echo '<link rel="stylesheet" href="flexbox-layout.css">';
}

Operating System Detection

Basic OS Information

$geo = new Geolocation();
$os = $geo->getOs();

if ($os) {
    echo "Operating System: {$os}\n";

    // OS-specific logic
    if (stripos($os, 'Windows') !== false) {
        $downloadLink = '/downloads/app-windows.exe';
        $installInstructions = 'Double-click to install';
    } elseif (stripos($os, 'Mac') !== false) {
        $downloadLink = '/downloads/app-macos.dmg';
        $installInstructions = 'Drag to Applications folder';
    } elseif (stripos($os, 'Linux') !== false) {
        $downloadLink = '/downloads/app-linux.tar.gz';
        $installInstructions = 'Extract and run install.sh';
    } elseif (stripos($os, 'Android') !== false) {
        $downloadLink = 'https://play.google.com/store/apps/details?id=com.example.app';
        $installInstructions = 'Install from Google Play Store';
    } elseif (stripos($os, 'iOS') !== false) {
        $downloadLink = 'https://apps.apple.com/app/example-app/id123456789';
        $installInstructions = 'Install from App Store';
    } else {
        $downloadLink = '/downloads/';
        $installInstructions = 'Choose your platform';
    }

    echo "Download: {$downloadLink}\n";
    echo "Instructions: {$installInstructions}\n";
}

OS Version Detection

function parseOSVersion($osString) {
    $patterns = [
        '/Windows NT (\d+\.\d+)/' => [
            '10.0' => 'Windows 10/11',
            '6.3' => 'Windows 8.1',
            '6.2' => 'Windows 8',
            '6.1' => 'Windows 7',
            '6.0' => 'Windows Vista'
        ],
        '/Mac OS X (\d+)[_.](\d+)/' => 'macOS',
        '/Android (\d+\.\d+)/' => 'Android',
        '/iPhone OS (\d+)[_.](\d+)/' => 'iOS',
        '/Ubuntu\/(\d+\.\d+)/' => 'Ubuntu Linux'
    ];

    foreach ($patterns as $pattern => $mapping) {
        if (preg_match($pattern, $osString, $matches)) {
            if (is_array($mapping)) {
                return $mapping[$matches[1]] ?? "Windows {$matches[1]}";
            } else {
                return "{$mapping} {$matches[1]}";
            }
        }
    }

    return $osString;
}

$geo = new Geolocation();
$os = $geo->getOs();
$detailedOS = parseOSVersion($_SERVER['HTTP_USER_AGENT'] ?? '');

echo "Basic OS: {$os}\n";
echo "Detailed OS: {$detailedOS}\n";

OS-Specific Features

class OSFeatures
{
    private $os;

    public function __construct($os) {
        $this->os = strtolower($os ?? '');
    }

    public function supportsPWA(): bool {
        // Progressive Web App support
        return strpos($this->os, 'android') !== false ||
               strpos($this->os, 'windows 10') !== false ||
               strpos($this->os, 'macos') !== false;
    }

    public function supportsNotifications(): bool {
        return strpos($this->os, 'windows') !== false ||
               strpos($this->os, 'macos') !== false ||
               strpos($this->os, 'linux') !== false;
    }

    public function getKeyboardShortcuts(): array {
        if (strpos($this->os, 'mac') !== false) {
            return [
                'copy' => 'Cmd+C',
                'paste' => 'Cmd+V',
                'save' => 'Cmd+S'
            ];
        } else {
            return [
                'copy' => 'Ctrl+C',
                'paste' => 'Ctrl+V',
                'save' => 'Ctrl+S'
            ];
        }
    }

    public function getFileManager(): string {
        if (strpos($this->os, 'windows') !== false) {
            return 'File Explorer';
        } elseif (strpos($this->os, 'mac') !== false) {
            return 'Finder';
        } else {
            return 'File Manager';
        }
    }
}

// Usage
$geo = new Geolocation();
$osFeatures = new OSFeatures($geo->getOs());

if ($osFeatures->supportsPWA()) {
    echo '<link rel="manifest" href="/manifest.json">';
    echo '<meta name="theme-color" content="#000000">';
}

$shortcuts = $osFeatures->getKeyboardShortcuts();
echo "Copy shortcut: {$shortcuts['copy']}\n";

Device Type Detection

Basic Device Detection

$geo = new Geolocation();
$device = $geo->getDeviceType();

switch ($device) {
    case 'mobile':
        $viewport = '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
        $css = 'mobile.css';
        $jsFramework = 'mobile-framework.js';
        $features = ['touch', 'compact-ui', 'swipe-gestures'];
        break;

    case 'tablet':
        $viewport = '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
        $css = 'tablet.css';
        $jsFramework = 'touch-framework.js';
        $features = ['touch', 'medium-ui', 'orientation-change'];
        break;

    case 'desktop':
    default:
        $viewport = '';
        $css = 'desktop.css';
        $jsFramework = 'full-framework.js';
        $features = ['mouse', 'keyboard', 'full-ui', 'hover-effects'];
        break;
}

echo $viewport . "\n";
echo "<link rel=\"stylesheet\" href=\"{$css}\">\n";
echo "<script src=\"{$jsFramework}\"></script>\n";
echo "Available features: " . implode(', ', $features) . "\n";

Responsive Design Integration

class ResponsiveHelper
{
    private $device;
    private $resolution;

    public function __construct($device, $resolution) {
        $this->device = $device;
        $this->resolution = $resolution;
    }

    public function getLayoutColumns(): int {
        switch ($this->device) {
            case 'mobile':
                return 1;
            case 'tablet':
                return 2;
            case 'desktop':
                if ($this->resolution['width'] >= 1920) {
                    return 4;
                } elseif ($this->resolution['width'] >= 1200) {
                    return 3;
                } else {
                    return 2;
                }
            default:
                return 2;
        }
    }

    public function getImageSizes(): array {
        switch ($this->device) {
            case 'mobile':
                return [
                    'thumbnail' => '150x150',
                    'medium' => '300x200',
                    'large' => '600x400'
                ];
            case 'tablet':
                return [
                    'thumbnail' => '200x200',
                    'medium' => '400x300',
                    'large' => '800x600'
                ];
            case 'desktop':
                return [
                    'thumbnail' => '250x250',
                    'medium' => '500x400',
                    'large' => '1200x800'
                ];
            default:
                return [
                    'thumbnail' => '150x150',
                    'medium' => '300x200',
                    'large' => '600x400'
                ];
        }
    }

    public function shouldLazyLoad(): bool {
        // Enable lazy loading for mobile to save bandwidth
        return $this->device === 'mobile';
    }

    public function getNavigationType(): string {
        switch ($this->device) {
            case 'mobile':
                return 'hamburger';
            case 'tablet':
                return 'tabs';
            case 'desktop':
                return 'horizontal';
            default:
                return 'horizontal';
        }
    }
}

// Usage
$geo = new Geolocation();
$device = $geo->getDeviceType();
$resolution = $geo->getResolution();

$helper = new ResponsiveHelper($device, $resolution);

$columns = $helper->getLayoutColumns();
$imageSizes = $helper->getImageSizes();
$navigation = $helper->getNavigationType();

echo "Layout columns: {$columns}\n";
echo "Navigation type: {$navigation}\n";
echo "Large image size: {$imageSizes['large']}\n";

if ($helper->shouldLazyLoad()) {
    echo '<script src="lazy-loading.js"></script>';
}

Screen Resolution

Resolution-Based Optimization

$geo = new Geolocation();
$resolution = $geo->getResolution();

if ($resolution['width'] && $resolution['height']) {
    $width = $resolution['width'];
    $height = $resolution['height'];

    // Determine resolution category
    if ($width >= 3840) {
        $category = '4K';
        $imageQuality = 'ultra-high';
        $maxImageWidth = 2400;
    } elseif ($width >= 2560) {
        $category = '2K/QHD';
        $imageQuality = 'high';
        $maxImageWidth = 1800;
    } elseif ($width >= 1920) {
        $category = 'Full HD';
        $imageQuality = 'high';
        $maxImageWidth = 1200;
    } elseif ($width >= 1366) {
        $category = 'HD';
        $imageQuality = 'medium';
        $maxImageWidth = 800;
    } else {
        $category = 'Standard';
        $imageQuality = 'low';
        $maxImageWidth = 600;
    }

    echo "Resolution: {$width}x{$height} ({$category})\n";
    echo "Image quality: {$imageQuality}\n";
    echo "Max image width: {$maxImageWidth}px\n";

    // Responsive image srcset
    $imageSrcset = generateResponsiveImages('hero-image', $maxImageWidth);
    echo "Responsive images: {$imageSrcset}\n";
}

function generateResponsiveImages($imageName, $maxWidth) {
    $sizes = [
        intval($maxWidth * 0.5),
        intval($maxWidth * 0.75),
        $maxWidth
    ];

    $srcset = [];
    foreach ($sizes as $size) {
        $srcset[] = "/images/{$imageName}-{$size}w.jpg {$size}w";
    }

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

High DPI Display Detection

function detectHighDPI($resolution, $device) {
    $width = $resolution['width'];
    $height = $resolution['height'];

    // Common high DPI scenarios
    $highDPIPatterns = [
        // Retina displays
        ['min_width' => 2880, 'device' => 'desktop'],   // 5K iMac
        ['min_width' => 2560, 'device' => 'desktop'],   // 4K/Retina
        ['min_width' => 1440, 'device' => 'mobile'],    // iPhone Plus/Pro
        ['min_width' => 1125, 'device' => 'mobile'],    // iPhone standard
        ['min_width' => 2048, 'device' => 'tablet'],    // iPad Retina
    ];

    foreach ($highDPIPatterns as $pattern) {
        if ($width >= $pattern['min_width'] &&
            $device === $pattern['device']) {
            return true;
        }
    }

    // Density-based detection (approximate)
    if ($device === 'mobile' && $width > 720) {
        return true;
    }

    if ($device === 'desktop' && $width >= 2560) {
        return true;
    }

    return false;
}

$geo = new Geolocation();
$resolution = $geo->getResolution();
$device = $geo->getDeviceType();

$isHighDPI = detectHighDPI($resolution, $device);

if ($isHighDPI) {
    echo '<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">';
    echo '<!-- High DPI optimizations -->';
    echo '<link rel="stylesheet" href="retina.css">';

    // Serve 2x images
    $imageMultiplier = 2;
} else {
    $imageMultiplier = 1;
}

echo "Image multiplier: {$imageMultiplier}x\n";

User Agent Analysis

Advanced User Agent Parsing

class UserAgentAnalyzer
{
    private $userAgent;

    public function __construct($userAgent) {
        $this->userAgent = $userAgent;
    }

    public function isMobile(): bool {
        $mobilePatterns = [
            '/Mobile/', '/Android/', '/iPhone/', '/iPad/',
            '/Windows Phone/', '/BlackBerry/', '/Opera Mini/'
        ];

        foreach ($mobilePatterns as $pattern) {
            if (preg_match($pattern, $this->userAgent)) {
                return true;
            }
        }

        return false;
    }

    public function isBot(): bool {
        $botPatterns = [
            '/Googlebot/', '/Bingbot/', '/Slurp/', '/DuckDuckBot/',
            '/Baiduspider/', '/YandexBot/', '/facebookexternalhit/',
            '/Twitterbot/', '/LinkedInBot/', '/WhatsApp/', '/Telegram/'
        ];

        foreach ($botPatterns as $pattern) {
            if (preg_match($pattern, $this->userAgent)) {
                return true;
            }
        }

        return false;
    }

    public function getBotType(): ?string {
        $bots = [
            'Googlebot' => 'Search Engine',
            'Bingbot' => 'Search Engine',
            'facebookexternalhit' => 'Social Media',
            'Twitterbot' => 'Social Media',
            'LinkedInBot' => 'Social Media',
            'WhatsApp' => 'Messaging',
            'Telegram' => 'Messaging'
        ];

        foreach ($bots as $bot => $type) {
            if (strpos($this->userAgent, $bot) !== false) {
                return $type;
            }
        }

        return null;
    }

    public function isHeadless(): bool {
        $headlessPatterns = [
            '/HeadlessChrome/', '/PhantomJS/', '/Selenium/',
            '/puppeteer/', '/playwright/'
        ];

        foreach ($headlessPatterns as $pattern) {
            if (preg_match($pattern, $this->userAgent)) {
                return true;
            }
        }

        return false;
    }

    public function getEngineVersion(): ?array {
        $engines = [
            'WebKit' => '/WebKit\/(\d+\.\d+)/',
            'Gecko' => '/Gecko\/(\d+)/',
            'Trident' => '/Trident\/(\d+\.\d+)/',
            'Blink' => '/Chrome\/(\d+)/'  // Blink is used in Chrome
        ];

        foreach ($engines as $engine => $pattern) {
            if (preg_match($pattern, $this->userAgent, $matches)) {
                return [
                    'engine' => $engine,
                    'version' => $matches[1]
                ];
            }
        }

        return null;
    }
}

// Usage
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$analyzer = new UserAgentAnalyzer($userAgent);

if ($analyzer->isBot()) {
    $botType = $analyzer->getBotType();
    echo "Bot detected: {$botType}\n";

    // Handle bots differently
    if ($botType === 'Search Engine') {
        // Serve SEO-optimized content
        include 'seo-optimized.php';
    } elseif ($botType === 'Social Media') {
        // Serve social media preview content
        include 'social-preview.php';
    }
} elseif ($analyzer->isHeadless()) {
    echo "Headless browser detected\n";
    // Handle automation/testing
} else {
    echo "Regular user detected\n";

    $engine = $analyzer->getEngineVersion();
    if ($engine) {
        echo "Browser engine: {$engine['engine']} v{$engine['version']}\n";
    }
}

Practical Applications

Content Delivery Optimization

class ContentOptimizer
{
    private $geo;

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

    public function optimizeForClient(): array {
        $device = $this->geo->getDeviceType();
        $browser = $this->geo->getBrowser();
        $resolution = $this->geo->getResolution();

        $optimizations = [
            'css' => $this->getCSSOptimizations($device, $browser),
            'js' => $this->getJSOptimizations($browser),
            'images' => $this->getImageOptimizations($device, $resolution),
            'layout' => $this->getLayoutOptimizations($device, $resolution)
        ];

        return $optimizations;
    }

    private function getCSSOptimizations($device, $browser): array {
        $css = ['base.css'];

        // Device-specific CSS
        switch ($device) {
            case 'mobile':
                $css[] = 'mobile.css';
                break;
            case 'tablet':
                $css[] = 'tablet.css';
                break;
            case 'desktop':
                $css[] = 'desktop.css';
                break;
        }

        // Browser-specific CSS
        if ($browser && $browser['browser'] === 'Safari') {
            $css[] = 'safari-fixes.css';
        } elseif ($browser && $browser['browser'] === 'Internet Explorer') {
            $css[] = 'ie-compatibility.css';
        }

        return $css;
    }

    private function getJSOptimizations($browser): array {
        $js = ['core.js'];

        if ($browser) {
            $browserName = $browser['browser'];
            $version = floatval($browser['version']);

            // Modern browsers get modern JS
            if (($browserName === 'Chrome' && $version >= 60) ||
                ($browserName === 'Firefox' && $version >= 55) ||
                ($browserName === 'Safari' && $version >= 12)) {
                $js[] = 'modern.js';
            } else {
                $js[] = 'legacy.js';
                $js[] = 'polyfills.js';
            }
        }

        return $js;
    }

    private function getImageOptimizations($device, $resolution): array {
        $maxWidth = 800; // Default

        if ($device === 'desktop' && $resolution['width'] >= 1920) {
            $maxWidth = 1200;
        } elseif ($device === 'tablet') {
            $maxWidth = 768;
        } elseif ($device === 'mobile') {
            $maxWidth = 480;
        }

        return [
            'max_width' => $maxWidth,
            'quality' => $device === 'mobile' ? 80 : 90,
            'format' => 'webp', // Could be dynamic based on browser support
            'lazy_load' => $device === 'mobile'
        ];
    }

    private function getLayoutOptimizations($device, $resolution): array {
        return [
            'columns' => $this->calculateColumns($device, $resolution),
            'sidebar' => $device !== 'mobile',
            'compact_header' => $device === 'mobile',
            'touch_targets' => in_array($device, ['mobile', 'tablet'])
        ];
    }

    private function calculateColumns($device, $resolution): int {
        switch ($device) {
            case 'mobile':
                return 1;
            case 'tablet':
                return 2;
            case 'desktop':
                if ($resolution['width'] >= 1600) {
                    return 4;
                } elseif ($resolution['width'] >= 1200) {
                    return 3;
                } else {
                    return 2;
                }
            default:
                return 2;
        }
    }
}

// Usage
$geo = new Geolocation();
$optimizer = new ContentOptimizer($geo);
$optimizations = $optimizer->optimizeForClient();

// Apply optimizations
foreach ($optimizations['css'] as $cssFile) {
    echo "<link rel=\"stylesheet\" href=\"/css/{$cssFile}\">\n";
}

foreach ($optimizations['js'] as $jsFile) {
    echo "<script src=\"/js/{$jsFile}\"></script>\n";
}

$imageOpts = $optimizations['images'];
echo "<!-- Image optimization: max-width={$imageOpts['max_width']}px, quality={$imageOpts['quality']}% -->\n";

Feature Detection and Polyfills

class FeatureDetector
{
    private $browser;
    private $device;

    public function __construct($browser, $device) {
        $this->browser = $browser;
        $this->device = $device;
    }

    public function getRequiredPolyfills(): array {
        $polyfills = [];

        if (!$this->supportsES6()) {
            $polyfills[] = 'babel-polyfill.js';
        }

        if (!$this->supportsFetch()) {
            $polyfills[] = 'fetch-polyfill.js';
        }

        if (!$this->supportsPromises()) {
            $polyfills[] = 'promise-polyfill.js';
        }

        if (!$this->supportsIntersectionObserver()) {
            $polyfills[] = 'intersection-observer-polyfill.js';
        }

        if ($this->device === 'mobile' && !$this->supportsPassiveListeners()) {
            $polyfills[] = 'passive-events-polyfill.js';
        }

        return $polyfills;
    }

    private function supportsES6(): bool {
        if (!$this->browser) return false;

        $name = $this->browser['browser'];
        $version = floatval($this->browser['version']);

        switch ($name) {
            case 'Chrome':
                return $version >= 51;
            case 'Firefox':
                return $version >= 54;
            case 'Safari':
                return $version >= 10;
            case 'Edge':
                return $version >= 14;
            default:
                return false;
        }
    }

    private function supportsFetch(): bool {
        if (!$this->browser) return false;

        $name = $this->browser['browser'];
        $version = floatval($this->browser['version']);

        switch ($name) {
            case 'Chrome':
                return $version >= 42;
            case 'Firefox':
                return $version >= 39;
            case 'Safari':
                return $version >= 10.1;
            case 'Edge':
                return $version >= 14;
            default:
                return false;
        }
    }

    private function supportsPromises(): bool {
        if (!$this->browser) return false;

        $name = $this->browser['browser'];
        $version = floatval($this->browser['version']);

        switch ($name) {
            case 'Chrome':
                return $version >= 32;
            case 'Firefox':
                return $version >= 29;
            case 'Safari':
                return $version >= 8;
            case 'Edge':
                return $version >= 12;
            default:
                return false;
        }
    }

    private function supportsIntersectionObserver(): bool {
        if (!$this->browser) return false;

        $name = $this->browser['browser'];
        $version = floatval($this->browser['version']);

        switch ($name) {
            case 'Chrome':
                return $version >= 58;
            case 'Firefox':
                return $version >= 55;
            case 'Safari':
                return $version >= 12.1;
            case 'Edge':
                return $version >= 15;
            default:
                return false;
        }
    }

    private function supportsPassiveListeners(): bool {
        if (!$this->browser) return false;

        $name = $this->browser['browser'];
        $version = floatval($this->browser['version']);

        switch ($name) {
            case 'Chrome':
                return $version >= 51;
            case 'Firefox':
                return $version >= 49;
            case 'Safari':
                return $version >= 10;
            default:
                return false;
        }
    }
}

// Usage
$geo = new Geolocation();
$detector = new FeatureDetector($geo->getBrowser(), $geo->getDeviceType());
$polyfills = $detector->getRequiredPolyfills();

foreach ($polyfills as $polyfill) {
    echo "<script src=\"/polyfills/{$polyfill}\"></script>\n";
}

Advanced Patterns

Client Fingerprinting

class ClientFingerprint
{
    private $geo;

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

    public function generateFingerprint(): string {
        $components = [
            'country' => $this->geo->getCountryCode(),
            'browser' => $this->geo->getBrowser(),
            'os' => $this->geo->getOs(),
            'device' => $this->geo->getDeviceType(),
            'resolution' => $this->geo->getResolution(),
            'language' => $this->geo->getPreferredLanguage(),
            'ip_hash' => $this->hashIp($this->geo->getIp())
        ];

        // Create a hash of all components
        $fingerprint = hash('sha256', serialize($components));

        return substr($fingerprint, 0, 16); // Short fingerprint
    }

    private function hashIp(?string $ip): string {
        if (!$ip) return '';

        // Hash IP for privacy (keep only network portion)
        $parts = explode('.', $ip);
        if (count($parts) === 4) {
            // IPv4: Keep first 3 octets, hash the last
            $networkPart = implode('.', array_slice($parts, 0, 3));
            return $networkPart . '.' . hash('crc32', $parts[3]);
        }

        return hash('crc32', $ip);
    }

    public function isUniqueVisitor(string $fingerprint): bool {
        // Check against stored fingerprints (implement your storage logic)
        $storedFingerprints = $this->getStoredFingerprints();
        return !in_array($fingerprint, $storedFingerprints);
    }

    private function getStoredFingerprints(): array {
        // Implement your storage logic (database, cache, etc.)
        // This is just a placeholder
        return [];
    }
}

// Usage
$geo = new Geolocation();
$fingerprinter = new ClientFingerprint($geo);
$fingerprint = $fingerprinter->generateFingerprint();

echo "Client fingerprint: {$fingerprint}\n";

if ($fingerprinter->isUniqueVisitor($fingerprint)) {
    echo "New visitor detected\n";
    // Track new visitor
} else {
    echo "Returning visitor\n";
    // Handle returning visitor
}

Testing and Validation

Client Detection Testing

function testClientDetection() {
    echo "Testing Client Detection...\n\n";

    $testCases = [
        [
            'name' => 'Desktop Chrome on Windows',
            'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
            'expected' => [
                'browser' => 'Chrome',
                'os' => 'Windows 10',
                'device' => 'desktop'
            ]
        ],
        [
            'name' => 'iPhone Safari',
            'user_agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
            'expected' => [
                'browser' => 'Safari',
                'os' => 'iOS',
                'device' => 'mobile'
            ]
        ],
        [
            'name' => 'Android Chrome',
            'user_agent' => 'Mozilla/5.0 (Linux; Android 11; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36',
            'expected' => [
                'browser' => 'Chrome',
                'os' => 'Android',
                'device' => 'mobile'
            ]
        ]
    ];

    foreach ($testCases as $case) {
        echo "Testing: {$case['name']}\n";

        // Simulate the user agent
        $_SERVER['HTTP_USER_AGENT'] = $case['user_agent'];
        $geo = new Geolocation($_SERVER);

        $browser = $geo->getBrowser();
        $os = $geo->getOs();
        $device = $geo->getDeviceType();

        // Check results
        $browserMatch = $browser && strpos($browser['browser'], $case['expected']['browser']) !== false;
        $osMatch = strpos($os ?? '', $case['expected']['os']) !== false;
        $deviceMatch = $device === $case['expected']['device'];

        echo "  Browser: " . ($browserMatch ? '' : '') . " {$browser['browser'] ?? 'Unknown'}\n";
        echo "  OS: " . ($osMatch ? '' : '') . " {$os ?? 'Unknown'}\n";
        echo "  Device: " . ($deviceMatch ? '' : '') . " {$device ?? 'Unknown'}\n";
        echo "\n";
    }
}

testClientDetection();

Performance Benchmarking

function benchmarkClientDetection() {
    echo "Benchmarking Client Detection Performance...\n\n";

    $iterations = 1000;
    $userAgents = [
        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1',
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
    ];

    $methods = [
        'getBrowser' => 'Browser Detection',
        'getOs' => 'OS Detection',
        'getDeviceType' => 'Device Detection',
        'getGeoInfo' => 'Complete Info'
    ];

    foreach ($methods as $method => $description) {
        $totalTime = 0;

        for ($i = 0; $i < $iterations; $i++) {
            $_SERVER['HTTP_USER_AGENT'] = $userAgents[$i % count($userAgents)];
            $geo = new Geolocation($_SERVER);

            $start = microtime(true);
            $geo->$method();
            $end = microtime(true);

            $totalTime += ($end - $start);
        }

        $avgTime = ($totalTime / $iterations) * 1000; // Convert to milliseconds
        echo "{$description}: {$avgTime:.3f}ms average\n";
    }
}

benchmarkClientDetection();

Next Steps


Previous: API Reference | Next: Configuration

Clone this wiki locally