yf_client_utils classSome useful utils for the client (browser).
| File | plugins/common/classes/common/yf_client_utils.class.php |
|---|---|
| Use | _class('client_utils', 'classes/common/') |
A site sets them in its config with $PROJECT_CONF['client_utils']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$FORWARDED_HEADERSarray |
['HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP'] |
SERVER keys with the client address chain, used only when TRUSTED_PROXIES is not empty |
$TRUSTED_PROXIESarray |
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') |
None without a leading underscore.
_ hooks and helpers; not routable as page actionspublic 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 '';
}
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;
}
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;
}