yf_client_utils class

Some useful utils for the client (browser).

Fileplugins/common/classes/common/yf_client_utils.class.php
Use_class('client_utils', 'classes/common/')

Public properties

A site sets them in its config with $PROJECT_CONF['client_utils']['NAME'] = ...;

PropertyDefaultDescription
$FORWARDED_HEADERS
array
['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP'] SERVER keys with the client address chain, used only when TRUSTED_PROXIES is not empty
$TRUSTED_PROXIES
array
legacy behaviour, headers are trusted blindly); falls back to conf('TRUSTED_PROXIES') */ Property [ public $TRUSTED_PROXIES = [] IPs or CIDR masks of reverse proxies whose forwarding headers are trusted (empty = legacy behaviour, headers are trusted blindly); falls back to conf('TRUSTED_PROXIES')

Public methods 8

None without a leading underscore.

Methods starting with _ hooks and helpers; not routable as page actions

_check_ip

public function _check_ip($ip, $ignore_ips = '', $check_type = 'force')
    public function _check_ip($ip, $ignore_ips = '', $check_type = 'force')
    {
        $masks = [
            '0.0.0.0/8',		// Current network (only valid as source address)	RFC 1700
            '10.0.0.0/8',		// Private network	RFC 1918
            '127.0.0.0/8',		// Loopback	RFC 3330
            '128.0.0.0/16',		// Reserved (IANA)	RFC 3330
            '169.254.0.0/16',	// Link-Local	RFC 3927
            '172.16.0.0/12',	// Private network	RFC 1918
            '191.255.0.0/16',	// Reserved (IANA)	RFC 3330
            '192.0.0.0/24',		// Reserved (IANA)	RFC 3330
            '192.0.2.0/24',		// Documentation and example code	RFC 3330
            '192.88.99.0/24',	// IPv6 to IPv4 relay	RFC 3068
            '192.168.0.0/16',	// Private network	RFC 1918
            '198.18.0.0/15',	// Network benchmark tests	RFC 2544
            '223.255.255.0/24',	// Reserved (IANA)	RFC 3330
            '224.0.0.0/4',		// Multicasts (former Class D network)	RFC 3171
            '240.0.0.0/4',		// Reserved (former Class E network)	RFC 1700
            '255.255.255.255/',	// Broadcast
        ];
        $ip = preg_replace('/[^\d\.]/', ',', $ip);
        $ips = explode(',', $ip);
        foreach ((array) $ips as $item) {
            if (empty($item) || ( ! empty($ignore_ips) && isset($ignore_ips[$item]))) {
                continue;
            }
            $flag = false;
            preg_match('/([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/', $item, $cur_ip);
            $ip = isset($cur_ip[1]) ? $cur_ip[1] : false;
            if ( ! $ip) {
                continue;
            }
            $flag = true;
            foreach ((array) $masks as $mask) {
                list($net_addr, $net_mask) = array_pad(explode('/', $mask), 2, null);
                if ($net_mask <= 0) {
                    continue;
                }
                if (empty($net_mask) && $item == $net_addr) {
                    $flag = false;
                    break;
                }
                $ip_binary_string = sprintf('%032b', ip2long($item));
                $net_binary_string = sprintf('%032b', ip2long($net_addr));
                if (substr_compare($ip_binary_string, $net_binary_string, 0, $net_mask) === 0) {
                    $flag = false;
                }
            }
            if ($flag) {
                if (function_exists('geoip_country_code_by_name') && strtoupper($check_type) == 'GEO') {
                    $test = @geoip_country_code_by_name($ip);
                    if ($test) {
                        return trim($ip);
                    }
                } else {
                    return trim($ip);
                }
            }
        }
        return false;
    }
public function _get_browser_info()

Get browser detailed info.

    public function _get_browser_info()
    {
        $log_version2 = null;
        if ( ! empty($_SERVER['HTTP_USER_AGENT'])) {
            $HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT'];
        } else {
            $HTTP_USER_AGENT = '';
        }
        // 1. Platform
        if (strstr($HTTP_USER_AGENT, 'Win')) {
            $USER_OS = 'Win';
        } elseif (strstr($HTTP_USER_AGENT, 'Mac')) {
            $USER_OS = 'Mac';
        } elseif (strstr($HTTP_USER_AGENT, 'Linux')) {
            $USER_OS = 'Linux';
        } elseif (strstr($HTTP_USER_AGENT, 'Android')) {
            $USER_OS = 'Android';
        } elseif (strstr($HTTP_USER_AGENT, 'IOS')) {
            $USER_OS = 'IOS';
        } elseif (strstr($HTTP_USER_AGENT, 'Unix')) {
            $USER_OS = 'Unix';
        } else {
            $USER_OS = 'Other';
        }
        // 2. browser and version
        // (must check everything else before Mozilla)
        if (preg_match('@Chrome/([0-9\.]+)@', $HTTP_USER_AGENT, $log_version)) {
            $USER_BROWSER_VER = $log_version[1];
            $USER_BROWSER_AGENT = 'CHROME';
        } elseif (preg_match('@Opera(/| )([0-9].[0-9]{1,2})@', $HTTP_USER_AGENT, $log_version)) {
            $USER_BROWSER_VER = $log_version[2];
            $USER_BROWSER_AGENT = 'OPERA';
        } elseif (preg_match('@MSIE ([0-9].[0-9]{1,2})@', $HTTP_USER_AGENT, $log_version)) {
            $USER_BROWSER_VER = $log_version[1];
            $USER_BROWSER_AGENT = 'IE';
        } elseif (preg_match('@Safari/([0-9]*)@', $HTTP_USER_AGENT, $log_version)) {
            $USER_BROWSER_VER = $log_version[1];
            $USER_BROWSER_AGENT = 'SAFARI';
        } elseif (preg_match('@Mozilla/([0-9].[0-9]{1,2})@', $HTTP_USER_AGENT, $log_version)) {
            $USER_BROWSER_VER = $log_version[1];
            $USER_BROWSER_AGENT = 'FIREFOX';
        } else {
            $USER_BROWSER_VER = 0;
            $USER_BROWSER_AGENT = 'OTHER';
        }
        $result = [
            'USER_OS' => $USER_OS,
            'USER_BROWSER_VER' => $USER_BROWSER_VER,
            'USER_BROWSER_AGENT' => $USER_BROWSER_AGENT,
        ];
        return $result;
    }
public function _get_ignore_ips()

This method can be overriden in project to contain method to list IPs to ignore.

    public function _get_ignore_ips()
    {
        return '';
    }

_get_ip

public function _get_ip($check_type = 'force')

Get user IP address.

    public function _get_ip($check_type = 'force')
    {
        $cache_name = '_CURRENT_' . $check_type . 'IP';
        if (isset($this->CACHE[$cache_name])) {
            return $this->CACHE[$cache_name];
        }
        $trusted = $this->_trusted_proxies();
        if ($trusted) {
            $current_ip = $this->_check_ip($this->_get_ip_trusted($trusted), $this->_get_ignore_ips(), $check_type);
            $ip = '';
            if ($current_ip) {
                $ip = $current_ip;
            } elseif ($check_type == 'force') {
                $ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
            }
            return $this->CACHE[$cache_name] = $ip;
        }
        $ip_storage = [
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_REAL_IP',
            'REMOTE_ADDR',
        ];
        $ignore_ips = $this->_get_ignore_ips();
        foreach ((array) $ip_storage as $element) {
            if ( ! empty($_SERVER[$element])) {
                $current_ip = $this->_check_ip(($_SERVER[$element] ?? null), $ignore_ips, $check_type);
                if ($current_ip) {
                    return $this->CACHE[$cache_name] = $current_ip;
                }
            }
        }
        $ip = '';
        if ($check_type == 'force') {
            $ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
        }
        $this->CACHE[$cache_name] = $ip;
        return $ip;
    }
public function _get_ip_trusted(array $trusted)

Client address when behind trusted proxies: forwarding headers are read right to left.

  • $trusted array
    public function _get_ip_trusted(array $trusted)
    {
        $remote = (string) ($_SERVER['REMOTE_ADDR'] ?? '');
        if ( ! $this->_ip_in_list($remote, $trusted)) {
            return $this->_normalize_addr($remote) ?: $remote;
        }
        foreach ((array) $this->FORWARDED_HEADERS as $header) {
            $chain = explode(',', (string) ($_SERVER[$header] ?? ''));
            foreach (array_reverse($chain) as $addr) {
                if (trim($addr) === '') {
                    continue;
                }
                $addr = $this->_normalize_addr($addr);
                if ($addr === '') {
                    break;
                }
                if ( ! $this->_ip_in_list($addr, $trusted)) {
                    return $addr;
                }
            }
        }
        return $this->_normalize_addr($remote) ?: $remote;
    }

_ip_in_list

public function _ip_in_list($ip, array $list)

Check if IP is equal to one of list items or inside of its CIDR masks (IPv4 and IPv6). Invalid items never match.

  • $list array
    public function _ip_in_list($ip, array $list)
    {
        $ip = $this->_normalize_addr($ip);
        $bin = $ip !== '' ? @inet_pton($ip) : false;
        if ($bin === false) {
            return false;
        }
        foreach ($list as $item) {
            $parts = explode('/', trim((string) $item), 2);
            $net = @inet_pton($parts[0]);
            if ($net === false || strlen($net) !== strlen($bin)) {
                continue;
            }
            $max_bits = strlen($net) * 8;
            if (isset($parts[1])) {
                if ( ! ctype_digit($parts[1]) || (int) $parts[1] > $max_bits) {
                    continue;
                }
                $bits = (int) $parts[1];
            } else {
                $bits = $max_bits;
            }
            $bytes = intdiv($bits, 8);
            if (substr($bin, 0, $bytes) !== substr($net, 0, $bytes)) {
                continue;
            }
            $rest = $bits % 8;
            if ($rest === 0) {
                return true;
            }
            $mask = (0xFF << (8 - $rest)) & 0xFF;
            if ((ord($bin[$bytes]) & $mask) === (ord($net[$bytes]) & $mask)) {
                return true;
            }
        }
        return false;
    }
public function _normalize_addr($addr)

Address as some proxies write it (with a port, bracketed IPv6, IPv4-mapped IPv6) reduced to a plain IP, or '' when it is not one.

    public function _normalize_addr($addr)
    {
        $addr = trim((string) $addr);
        if (preg_match('/^\[([0-9a-f:.]+)\](?::\d+)?$/i', $addr, $m)) {
            $addr = $m[1];
        } elseif (preg_match('/^(\d{1,3}(?:\.\d{1,3}){3}):\d+$/', $addr, $m)) {
            $addr = $m[1];
        }
        if (stripos($addr, '::ffff:') === 0 && strpos($addr, '.') !== false) {
            $addr = substr($addr, 7);
        }
        return @inet_pton($addr) === false ? '' : $addr;
    }
public function _trusted_proxies()

Normalised list: the property, else conf('TRUSTED_PROXIES') (array or comma-separated string); blanks dropped.

    public function _trusted_proxies()
    {
        $list = $this->TRUSTED_PROXIES ?: conf('TRUSTED_PROXIES');
        is_string($list) && $list = explode(',', $list);
        $result = [];
        foreach ((array) $list as $item) {
            is_string($item) && trim($item) !== '' && $result[] = trim($item);
        }
        return $result;
    }