name. */ const BROWSERS = array( '/Instagram/' => 'Instagram', '/FBAN|FBAV|FB_IAB/' => 'Facebook', '/musical_ly|BytedanceWebview|TikTok/i' => 'TikTok', '/LinkedInApp/' => 'LinkedIn', '/Snapchat/' => 'Snapchat', '/Pinterest/' => 'Pinterest', '/; wv\)|WebView/' => 'In-app browser', '/SamsungBrowser/' => 'Samsung Internet', '/EdgA?\/|EdgiOS\//' => 'Edge', '/OPR\/|Opera/' => 'Opera', '/YaBrowser/' => 'Yandex Browser', '/DuckDuckGo\//' => 'DuckDuckGo', '/Firefox\/|FxiOS\//' => 'Firefox', '/Chrome\/|CriOS\//' => 'Chrome', '/Safari\//' => 'Safari', ); /** * Operating systems, most specific first: pattern => name. */ const SYSTEMS = array( '/iPhone|iPad|iPod/' => 'iOS', '/Android/' => 'Android', '/CrOS/' => 'ChromeOS', '/Windows/' => 'Windows', '/Macintosh|Mac OS X/' => 'macOS', '/Linux/' => 'Linux', ); /** * Device of the current request. * * @return array{type: string, os: string, browser: string}|null */ public static function from_request() { $agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : ''; return self::from_user_agent( $agent ); } /** * Device of a user agent. * * @param string $agent User agent. * @return array{type: string, os: string, browser: string}|null */ public static function from_user_agent( $agent ) { $agent = (string) $agent; if ( '' === $agent || Visits::is_bot( $agent ) ) { return null; } if ( preg_match( '/iPad|Tablet|PlayBook|Silk|Kindle/i', $agent ) || ( false !== strpos( $agent, 'Android' ) && false === strpos( $agent, 'Mobile' ) ) ) { $type = 'tablet'; } elseif ( preg_match( '/Mobi|iPhone|iPod|Android|Windows Phone/i', $agent ) ) { $type = 'mobile'; } else { $type = 'desktop'; } return array( 'type' => $type, 'os' => self::first_match( self::SYSTEMS, $agent ), 'browser' => self::first_match( self::BROWSERS, $agent ), ); } /** * A stored device, limited to the known names. * * @param mixed $device Stored value. * @return array{type: string, os: string, browser: string}|null */ public static function sanitize( $device ) { if ( ! is_array( $device ) || ! in_array( $device['type'] ?? '', array( 'mobile', 'tablet', 'desktop' ), true ) ) { return null; } return array( 'type' => $device['type'], 'os' => in_array( $device['os'] ?? '', self::SYSTEMS, true ) ? $device['os'] : '', 'browser' => in_array( $device['browser'] ?? '', self::BROWSERS, true ) ? $device['browser'] : '', ); } /** * Name of a kind of device. * * @param string $type mobile, tablet or desktop. * @return string */ public static function type_label( $type ) { $labels = array( 'mobile' => __( 'Smartphone', 'dealroot' ), 'tablet' => __( 'Tablet', 'dealroot' ), 'desktop' => __( 'Computer', 'dealroot' ), ); return $labels[ $type ] ?? ''; } /** * Name of a browser for display. * * @param string $browser Stored name. * @return string */ public static function browser_label( $browser ) { return 'In-app browser' === $browser ? __( 'In-app browser', 'dealroot' ) : (string) $browser; } /** * Name of the first pattern that matches. * * @param array $patterns Pattern => name. * @param string $agent User agent. * @return string */ private static function first_match( array $patterns, $agent ) { foreach ( $patterns as $pattern => $name ) { if ( preg_match( $pattern, $agent ) ) { return $name; } } return ''; } }