Copyright (c) Rodolfo Berrios All rights reserved. Licensed under the MIT license http://opensource.org/licenses/MIT --------------------------------------------------------------------- */ namespace G { use Exception; /** * ROUTE HELPERS * ---------------------------------------------------------------------. */ // Returns true if the $route is current /route or mapped-route -> /route function is_route($route) { return Handler::$base_request == $route; } // Returns true if the $route is has route file function is_route_available($route) { $route_file = 'route.' . $route . '.php'; return file_exists(G_APP_PATH_ROUTES . $route_file) or file_exists(G_APP_PATH_ROUTES_OVERRIDES . $route_file); } // Returns true if the route is prevented function is_prevented_route() { return Handler::$prevented_route == true; } // Get the route path // $full=true returns route/and/sub/routes, $full=false returns just the /route base function get_route_path($full = false) { return Handler::getRoutePath($full); } // Get route name from route.name.php function get_route_name() { return Handler::getRouteName(); } // Get the route template basename file used. Useful when you do route mapping. function get_template_used() { return Handler::getTemplateUsed(); } /** * GLOBAL HELPERS * ---------------------------------------------------------------------. */ // Outputs a well formatted HTML output of anything (strings, arrays, comma separated "things", anything) function debug($arguments) { if (empty($arguments)) { return; } echo '
';
        foreach (func_get_args() as $value) {
            print_r($value);
        }
        echo '
'; } // Universal check for setted values for strings and arrays function check_value($anything) { if ((@count($anything) > 0 and !@empty($anything) and @isset($anything)) || $anything == '0') { return true; } } function get_global($var) { global ${$var}; return ${$var}; } function is_apache() { return isset($_SERVER['SERVER_SOFTWARE']) and preg_match('/Apache/i', $_SERVER['SERVER_SOFTWARE']); } // Fixed from the original (not working) at: http://stackoverflow.com/a/7859707 function random_values($min, $max, $limit) { // Numbers? if (!is_numeric($min) or !is_numeric($max)) { return null; } // Get the accurate min and max $min = min($min, $max); $max = max($min, $max); // Go home if ($min == $max) { return array($min); } // is the limit ok? $minmax_limit = abs($max - $min); if ($limit > $minmax_limit) { $limit = $minmax_limit; } $array = array(); for ($i = 0; $i < $limit; ++$i) { $rand = rand($min, $max); while (in_array($rand, $array)) { $rand = mt_rand($min, $max); } $array[$i] = $rand; } return $array; } /** * Generates a random string. * * @autor Baba * @url http://stackoverflow.com/a/17267718 */ function random_string($length) { switch (true) { case function_exists('random_bytes'): $r = random_bytes($length); break; case function_exists('openssl_random_pseudo_bytes'): $r = openssl_random_pseudo_bytes($length); break; case is_readable('/dev/urandom'): // deceze $r = file_get_contents('/dev/urandom', false, null, 0, $length); break; default: $i = 0; $r = ''; while ($i++ < $length) { $r .= chr(mt_rand(0, 255)); } break; } return substr(bin2hex($r), 0, $length); } /** * A timing safe equals comparison. * * To prevent leaking length information, it is important * that user input is always used as the second parameter. * * @param string $safe The internal (safe) value to be checked * @param string $user The user submitted (unsafe) value * * @return bool true if the two strings are identical */ function timing_safe_compare($safe, $user) { // Prevent issues if string length is 0 $safe .= chr(0); $user .= chr(0); $safeLen = strlen($safe); $userLen = strlen($user); // Set the result to the difference between the lengths $result = $safeLen - $userLen; // Note that we ALWAYS iterate over the user-supplied length // This is to prevent leaking length information for ($i = 0; $i < $userLen; ++$i) { // Using % here is a trick to prevent notices // It's safe, since if the lengths are different // $result is already non-0 $result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i])); } // They are only identical strings if $result is exactly 0... return $result === 0; } function str_replace_first($search, $replace, $subject) { $pos = strpos($subject, $search); if ($pos !== false) { $subject = substr_replace($subject, $replace, $pos, strlen($search)); } return $subject; } function str_replace_last($search, $replace, $subject) { $pos = strrpos($subject, $search); if ($pos !== false) { $subject = substr_replace($subject, $replace, $pos, strlen($search)); } return $subject; } // Taken from http://stackoverflow.com/a/834355 function starts_with($needle, $haystack) { $length = strlen($needle); return substr($haystack, 0, $length) === $needle; } // Taken from http://stackoverflow.com/a/834355 function ends_with($needle, $haystack) { $length = strlen($needle); if ($length == 0) { return true; } return substr($haystack, -$length) === $needle; } /** * DATA HANDLING * ---------------------------------------------------------------------. */ // Filter array with another array function array_filter_array($array, $filter_keys, $get = 'exclusion') { $arr = $array; $return = []; $get = strtolower($get); $default_get = 'exclusion'; foreach ($filter_keys as $k => $v) { switch ($get) { default: case $default_get: $get = $default_get; if (!array_key_exists($v, $array)) { continue 2; } $return[$v] = $array[$v]; break; case 'rest': unset($array[$v]); break; } } return $get == $default_get ? $return : $array; } function key_asort(&$array, $key) { $sorter = []; $ret = []; reset($array); foreach ($array as $ii => $va) { $sorter[$ii] = $va[$key]; } asort($sorter); foreach ($sorter as $ii => $va) { $ret[$ii] = $array[$ii]; } $array = $ret; } /** * Recursive UTF-8 encode array. */ function array_utf8encode(&$arr) { array_walk_recursive($arr, function (&$val, $key) { $encoding = mb_detect_encoding($val); if ($encoding == false) { $val = null; } else { $val = mb_convert_encoding($val, 'UTF-8', $encoding); } }); return $arr; } /** * Recursive remove empty properties. */ function array_remove_empty($haystack) { foreach ($haystack as $key => $value) { if (is_array($value)) { $haystack[$key] = array_remove_empty($haystack[$key]); } if (empty($haystack[$key])) { unset($haystack[$key]); } } return $haystack; } /** * Abbreviate a number with suffix output. */ function abbreviate_number($number) { if ($number === null) { $number = 0; } else { // strip any formatting $number = (0 + str_replace(',', '', $number)); } // Not a number, keep it "as is" if (!is_numeric($number) or $number == 0) { return $number; } $abbreviations = [ 24 => 'Y', 21 => 'Z', 18 => 'E', 15 => 'P', 12 => 'T', 9 => 'G', 6 => 'M', 3 => 'K', 0 => null, ]; foreach ($abbreviations as $exponent => $abbreviation) { if ($number >= pow(10, $exponent)) { return round(floatval($number / pow(10, $exponent))) . $abbreviation; } } } function nullify_string(&$string) { if (is_string($string) and $string == '') { $string = null; } } // http://bavotasan.com/2011/convert-hex-color-to-rgb-using-php/ function hex_to_rgb($hex) { $hex = str_replace('#', '', $hex); if (strlen($hex) == 3) { $r = hexdec(substr($hex, 0, 1) . substr($hex, 0, 1)); $g = hexdec(substr($hex, 1, 1) . substr($hex, 1, 1)); $b = hexdec(substr($hex, 2, 1) . substr($hex, 2, 1)); } else { $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); } $rgb = array($r, $g, $b); return $rgb; // returns an array with the rgb values } function rgb_to_hex($rgb) { $hex = '#'; $hex .= str_pad(dechex($rgb[0]), 2, '0', STR_PAD_LEFT); $hex .= str_pad(dechex($rgb[1]), 2, '0', STR_PAD_LEFT); $hex .= str_pad(dechex($rgb[2]), 2, '0', STR_PAD_LEFT); return $hex; // returns the hex value including the number sign (#) } /** * Convert HTML code to BBCode * http://kuikie.com/snippets/snippet.php/90-17/php-function-to-convert-bbcode-to-html. */ function html_to_bbcode($text) { $htmltags = array( '/\(.*?)\<\/b\>/is', '/\(.*?)\<\/i\>/is', '/\(.*?)\<\/u\>/is', '/\(.*?)\<\/ul\>/is', '/\(.*?)\<\/li\>/is', '/\/is', '/\/is', '/\/is', '/\
(.*?)\<\/div\>/is', '/\
(.*?)\<\/div\>/is', '/\
(.*?)\<\/div\>/is', '/\
(.*?)\<\/div\>/is', '/\(.*?)\<\/cite\>/is', '/\(.*?)\<\/blockquote\>/is', '/\(.*?)\<\/div\>/is', '/\(.*?)\<\/code\>/is', '/\/is', '/\(.*?)\<\/strong\>/is', '/\(.*?)\<\/em\>/is', '/\(.*?)\<\/a\>/is', '/\http:\/\/(.*?)\<\/a\>/is', '/\(.*?)\<\/a\>/is', ); $bbtags = array( '[b]$1[/b]', '[i]$1[/i]', '[u]$1[/u]', '[list]$1[/list]', '[*]$1', '$3', '[img]$2[/img]', ':$3', '\[quote\]$1\[/quote\]', '\[code\]$1\[/code\]', '', '', '', '\[quote\]$1\[/quote\]', '$1', '\[code\]$1\[/code\]', "\n", '[b]$1[/b]', '[i]$1[/i]', '[email=$1]$3[/email]', '[url]$1[/url]', '[url=$1]$3[/url]', ); $text = str_replace("\n", ' ', $text); $ntext = preg_replace($htmltags, $bbtags, $text); $ntext = preg_replace($htmltags, $bbtags, $ntext); // for too large text and cannot handle by str_replace if (!$ntext) { $ntext = str_replace(array('
', '
'), "\n", $text); $ntext = str_replace(array('', ''), array('[b]', '[/b]'), $ntext); $ntext = str_replace(array('', ''), array('[i]', '[/i]'), $ntext); } $ntext = strip_tags($ntext); $ntext = trim(html_entity_decode($ntext, ENT_QUOTES, 'UTF-8')); return $ntext; } // Linkify functions borrowed from https://github.com/misd-service-development/php-linkify function linkify($text, array $options = array()) { $attr = ''; if (true === array_key_exists('attr', $options)) { foreach ($options['attr'] as $key => $value) { if (true === is_array($value)) { $value = array_pop($value); } $attr .= sprintf(' %s="%s"', $key, $value); } } $options['attr'] = $attr; $ignoreTags = array('head', 'link', 'a', 'script', 'style', 'code', 'pre', 'select', 'textarea', 'button'); $chunks = preg_split('/(<.+?>)/is', $text, 0, PREG_SPLIT_DELIM_CAPTURE); $openTag = null; for ($i = 0; $i < count($chunks); ++$i) { if ($i % 2 === 0) { // even numbers are text // Only process this chunk if there are no unclosed $ignoreTags if (null === $openTag) { $chunks[$i] = linkify_urls($chunks[$i], $options); $chunks[$i] = linkify_emails($chunks[$i], $options); } } else { // odd numbers are tags // Only process this tag if there are no unclosed $ignoreTags if (null === $openTag) { // Check whether this tag is contained in $ignoreTags and is not self-closing if (preg_match('`<(' . implode('|', $ignoreTags) . ').*(?$`is', $chunks[$i], $matches)) { $openTag = $matches[1]; } } else { // Otherwise, check whether this is the closing tag for $openTag. if (preg_match('``i', $chunks[$i], $matches)) { $openTag = null; } } } } $text = implode($chunks); return $text; } function linkify_emails($text, $options = array('attr' => '')) { $pattern = '~(?xi) \b (?' . $match[0] . '
'; }; return preg_replace_callback($pattern, $callback, $text); } function linkify_urls($text, $options = array('attr' => '')) { $pattern = '~(?xi) (?: ((ht|f)tps?://) # scheme:// | # or www\d{0,3}\. # "www.", "www1.", "www2." ... "www999." | # or www\- # "www-" | # or [a-z0-9.\-]+\.[a-z]{2,4}(?=/) # looks like domain name followed by a slash ) (?: # Zero or more: [^\s()<>]+ # Run of non-space, non-()<> | # or \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels )* (?: # End with: \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels | # or [^\s`!\-()\[\]{};:\'".,<>?«»“”‘’] # not a space or one of these punct chars ) ~'; $callback = function ($match) use ($options) { $caption = $match[0]; $pattern = '~^(ht|f)tps?://~'; if (0 === preg_match($pattern, $match[0])) { $match[0] = 'http://' . $match[0]; } if (isset($options['callback'])) { $cb = $options['callback']($match[0], $caption, $options); if (!is_null($cb)) { return $cb; } } return '' . $caption . ''; }; return preg_replace_callback($pattern, $callback, $text); } function linkify_safe($text, $options = []) { $options = array_merge(['attr' => ['rel' => 'nofollow', 'target' => '_blank']], $options); return linkify(htmlspecialchars($text), $options); } /** * ERROR HANDLING * ---------------------------------------------------------------------. */ // Converts an Exception to a formatted PHP like error function exception_to_error($e, $die = true) { // Debug levels // 0:NONE 1:ERROR_LOG 2:PRINT(NO ERROR_LOG) 3:PRINT+ERROR_LOG $debug_level = get_app_setting('debug_level'); $internal_code = 500; $internal_error = 'Aw, snap! ' . get_set_status_header_desc($internal_code); $table = [ 0 => 'debug is disabled', 1 => 'debug @ `error_log`', 2 => 'debug @ print', 3 => 'debug @ print,`error_log`', ]; $internal_error .= ' [' . $table[$debug_level] . '] - https://v3-docs.chevereto.com/setup/debug.html'; set_status_header($internal_code); if (!in_array($debug_level, [0, 1, 2, 3])) { $debug_level = 1; } if (in_array($debug_level, [1, 3])) { error_log($e); } if (!in_array($debug_level, [2, 3])) { // No print here die($internal_error); } $message = [$internal_error]; $message[] = ''; $message[] = 'Fatal error [' . $e->getCode() . ']: ' . safe_html($e->getMessage()); $message[] = 'Triggered in ' . absolute_to_relative($e->getFile()) . ' at line ' . $e->getLine() . "\n"; $message[] = 'Stack trace:'; $rtn = ''; $count = 0; foreach ($e->getTrace() as $frame) { $args = ''; if (isset($frame['args'])) { $args = array(); foreach ($frame['args'] as $arg) { switch (true) { case is_string($arg): if (file_exists($arg)) { $arg = absolute_to_relative($arg); } $args[] = "'" . $arg . "'"; break; case is_array($arg): $args[] = 'Array'; break; case is_null($arg): $args[] = 'NULL'; break; case is_bool($arg): $args[] = ($arg) ? 'true' : 'false'; break; case is_object($arg): $args[] = get_class($arg); break; case is_resource($arg): $args[] = get_resource_type($arg); break; default: $args[] = $arg; break; } } $args = join(', ', $args); } $rtn .= sprintf( "#%s %s(%s): %s(%s)\n", $count, isset($frame['file']) ? absolute_to_relative($frame['file']) : 'unknown file', isset($frame['line']) ? $frame['line'] : 'unknown line', (isset($frame['class'])) ? $frame['class'] . $frame['type'] . $frame['function'] : $frame['function'], $args ); ++$count; } $message[] = $rtn; $message = implode("\n", $message); if ($die) { echo nl2br($message); die(); } else { return $message; } } /** * MATH * ---------------------------------------------------------------------. */ function fraction_to_decimal($fraction) { list($top, $bottom) = explode('/', $fraction); return $bottom == 0 ? $fraction : ($top / $bottom); } /** * TIME * ---------------------------------------------------------------------. */ // Returns current GMT datetime function datetimegmt($format = null) { return gmdate(!is_null($format) ? $format : 'Y-m-d H:i:s'); } // Returns current datetime (for the system timezone) function datetime($format = null) { return date(!is_null($format) ? $format : 'Y-m-d H:i:s'); } // Returns current datetime in the specified timezone function datetime_tz($tz, $format = null) { $date = date_create(null, timezone_open($tz)); return date_format($date, !is_null($format) ? $format : 'Y-m-d H:i:s'); } // http://stackoverflow.com/a/5878722 function is_valid_timezone($tzid) { $valid = []; $tza = timezone_abbreviations_list(); foreach ($tza as $zone) { foreach ($zone as $item) { $valid[$item['timezone_id']] = true; } } unset($valid['']); return (bool) $valid[$tzid]; } // http://stackoverflow.com/a/18602474 function time_elapsed_string($datetime, $full = false) { $now = new \DateTime(datetimegmt()); $ago = new \DateTime($datetime); $diff = $now->diff($ago); $diff->w = floor($diff->d / 7); $diff->d -= $diff->w * 7; $string = array( 'y' => 'year', 'm' => 'month', 'w' => 'week', 'd' => 'day', 'h' => 'hour', 'i' => 'minute', 's' => 'second', ); foreach ($string as $k => &$v) { if ($diff->$k) { $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : ''); } else { unset($string[$k]); } } if (!$full) { $string = array_slice($string, 0, 1); } return $string ? implode(', ', $string) . ' ago' : 'just now'; } function datetimegmt_convert_tz($datetimegmt, $tz) { if (!is_valid_timezone($tz)) { return $datetimegmt; } $date = new \DateTime($datetimegmt . '+00'); $date->setTimezone(new \DateTimeZone($tz)); return $date->format('Y-m-d H:i:s'); } /** * Returns the difference between two UTC dates in the given format (default seconds) * @return integer `$newer - $older` */ function datetime_diff($older, $newer = null, $format = 's') { if (!in_array($format, ['s', 'm', 'h', 'd'])) { $format = 's'; } if (!$newer or $newer == null) { $newer = datetimegmt(); } $tz = new \DateTimeZone('UTC'); $datetime1 = new \DateTime($older, $tz); $datetime2 = new \DateTime($newer, $tz); $diff = $datetime2->getTimestamp() - $datetime1->getTimestamp(); // In seconds $timeconstant = [ 's' => 1, 'm' => 60, 'h' => 3600, 'd' => 86400, ]; return intval($diff / $timeconstant[$format]); } function datetime_add($datetime, $add) { return datetime_alter($datetime, $add, 'add'); } function datetime_sub($datetime, $sub) { return datetime_alter($datetime, $sub, 'sub'); } function datetime_modify($datetime, $var) { return datetime_alter($datetime, $var, 'modify'); } function datetime_alter($datetime, $var, $action = 'add') { if (!in_array($action, ['add', 'sub', 'modify'])) { return $datetime; } try { $DateTime = new \DateTime($datetime); if ($action == 'modify') { $DateTime->$action($var); } else { $interval = dateinterval($var); // validation if (!$interval) { return $datetime; } $DateTime->$action($interval); } return $DateTime->format('Y-m-d H:i:s'); } catch (Exception $e) { throw new Exception($e->getMessage() . ' in ' . __FUNCTION__ . ' (' . $action . ')', $e->getCode()); } } function dateinterval($var) { try { $di = new \DateInterval($var); return $di; } catch (Exception $e) { } return false; } /** * CLIENT * ---------------------------------------------------------------------. */ function get_client_ip() { return $_SERVER['REMOTE_ADDR']; } function get_client_languages($getSortedList = true, $acceptedLanguages = false) { if (empty($acceptedLanguages)) { $acceptedLanguages = $_SERVER['HTTP_ACCEPT_LANGUAGE']; } // regex inspired from @GabrielAnderson on http://stackoverflow.com/questions/6038236/http-accept-language preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})*)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $acceptedLanguages, $lang_parse); $langs = $lang_parse[1]; $ranks = $lang_parse[4]; // (create an associative array 'language' => 'preference') $lang2pref = array(); for ($i = 0; $i < count($langs); ++$i) { $lang2pref[$langs[$i]] = (float) (!empty($ranks[$i]) ? $ranks[$i] : 1); } $cmpLangs = function ($a, $b) use ($lang2pref) { if ($lang2pref[$a] > $lang2pref[$b]) { return -1; } elseif ($lang2pref[$a] < $lang2pref[$b]) { return 1; } elseif (strlen($a) > strlen($b)) { return -1; } elseif (strlen($a) < strlen($b)) { return 1; } else { return 0; } }; // Just in case the server is running eAccelerator if (is_callable($cmpLangs)) { uksort($lang2pref, $cmpLangs); } if ($getSortedList) { return $lang2pref; } // return the first value's key reset($lang2pref); return key($lang2pref); } /** * Parses a user agent string into its important parts. * * @author Jesse G. Donat * * @see https://github.com/donatj/PhpUserAgent * @see http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT * * @param string|null $u_agent * * @return array an array with browser, version and platform keys */ function parse_user_agent($u_agent = null) { if (is_null($u_agent) && isset($_SERVER['HTTP_USER_AGENT'])) { $u_agent = $_SERVER['HTTP_USER_AGENT']; } $platform = null; $browser = null; $version = null; $empty = array('platform' => $platform, 'browser' => $browser, 'version' => $version); if (!$u_agent) { return $empty; } if (preg_match('/\((.*?)\)/im', $u_agent, $parent_matches)) { preg_match_all('/(?PAndroid|CrOS|iPhone|iPad|Linux|Macintosh|Windows(\ Phone\ OS)?|Silk|linux-gnu|BlackBerry|PlayBook|Nintendo\ (WiiU?|3DS)|Xbox) (?:\ [^;]*)? (?:;|$)/imx', $parent_matches[1], $result, PREG_PATTERN_ORDER); $priority = array('Android', 'Xbox'); $result['platform'] = array_unique($result['platform']); if (count($result['platform']) > 1) { if ($keys = array_intersect($priority, $result['platform'])) { $platform = reset($keys); } else { $platform = $result['platform'][0]; } } elseif (isset($result['platform'][0])) { $platform = $result['platform'][0]; } } if ($platform == 'linux-gnu') { $platform = 'Linux'; } elseif ($platform == 'CrOS') { $platform = 'Chrome OS'; } preg_match_all( '%(?PCamino|Kindle(\ Fire\ Build)?|Firefox|Iceweasel|Safari|MSIE|Trident/.*rv|AppleWebKit|Chrome|IEMobile|Opera|OPR|Silk|Lynx|Midori|Version|Wget|curl|NintendoBrowser|PLAYSTATION\ (\d|Vita)+) (?:\)?;?) (?:(?:[:/ ])(?P[0-9A-Z.]+)|/(?:[A-Z]*))%ix', $u_agent, $result, PREG_PATTERN_ORDER ); // If nothing matched, return null (to avoid undefined index errors) if (!isset($result['browser'][0]) || !isset($result['version'][0])) { return $empty; } $browser = $result['browser'][0]; $version = $result['version'][0]; $find = function ($search, &$key) use ($result) { $xkey = array_search(strtolower($search), array_map('strtolower', $result['browser'])); if ($xkey !== false) { $key = $xkey; return true; } return false; }; $key = 0; if ($browser == 'Iceweasel') { $browser = 'Firefox'; } elseif ($find('Playstation Vita', $key)) { $platform = 'PlayStation Vita'; $browser = 'Browser'; } elseif ($find('Kindle Fire Build', $key) || $find('Silk', $key)) { $browser = $result['browser'][$key] == 'Silk' ? 'Silk' : 'Kindle'; $platform = 'Kindle Fire'; if (!($version = $result['version'][$key]) || !is_numeric($version[0])) { $version = $result['version'][array_search('Version', $result['browser'])]; } } elseif ($find('NintendoBrowser', $key) || $platform == 'Nintendo 3DS') { $browser = 'NintendoBrowser'; $version = $result['version'][$key]; } elseif ($find('Kindle', $key)) { $browser = $result['browser'][$key]; $platform = 'Kindle'; $version = $result['version'][$key]; } elseif ($find('OPR', $key)) { $browser = 'Opera Next'; $version = $result['version'][$key]; } elseif ($find('Opera', $key)) { $browser = 'Opera'; $find('Version', $key); $version = $result['version'][$key]; } elseif ($find('Chrome', $key)) { $browser = 'Chrome'; $version = $result['version'][$key]; } elseif ($find('Midori', $key)) { $browser = 'Midori'; $version = $result['version'][$key]; } elseif ($browser == 'AppleWebKit') { if (($platform == 'Android' && !($key = 0))) { $browser = 'Android Browser'; } elseif ($platform == 'BlackBerry' || $platform == 'PlayBook') { $browser = 'BlackBerry Browser'; } elseif ($find('Safari', $key)) { $browser = 'Safari'; } $find('Version', $key); $version = $result['version'][$key]; } elseif ($browser == 'MSIE' || strpos($browser, 'Trident') !== false) { if ($find('IEMobile', $key)) { $browser = 'IEMobile'; } else { $browser = 'MSIE'; $key = 0; } $version = $result['version'][$key]; } elseif ($key = preg_grep("/playstation \d/i", array_map('strtolower', $result['browser']))) { $key = reset($key); $platform = 'PlayStation ' . preg_replace('/[^\d]/i', '', $key); $browser = 'NetFront'; } return array('platform' => $platform, 'browser' => $browser, 'version' => $version); } /** * MISC. VALIDATIONS * ---------------------------------------------------------------------. */ // This checks for real email address. function is_real_email_address($email) { $valid = true; $atIndex = strrpos($email, '@'); if (is_bool($atIndex) && !$atIndex) { $valid = false; } else { $domain = substr($email, $atIndex + 1); $local = substr($email, 0, $atIndex); $localLen = strlen($local); $domainLen = strlen($domain); if ($localLen < 1 || $localLen > 64) { // local part length exceeded $valid = false; } elseif ($domainLen < 1 || $domainLen > 255) { // domain part length exceeded $valid = false; } elseif ($local[0] == '.' || $local[$localLen - 1] == '.') { // local part starts or ends with '.' $valid = false; } elseif (preg_match('/\\.\\./', $local)) { // local part has two consecutive dots $valid = false; } elseif (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) { // character not valid in domain part $valid = false; } elseif (preg_match('/\\.\\./', $domain)) { // domain part has two consecutive dots $valid = false; } elseif (!preg_match( '/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace('\\\\', '', $local) )) { // character not valid in local part unless // local part is quoted if (!preg_match('/^"(\\\\"|[^"])+"$/', str_replace('\\\\', '', $local))) { $valid = false; } } if ($valid && !(checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A'))) { // domain not found in DNS $valid = false; } } return $valid; } function is_valid_hex_color($string, $prefix = true) { return preg_match('/#' . ($prefix ? '?' : null) . '([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})/', $string); } function is_valid_ip($ip) { return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); } /** * SANITIZATION * ---------------------------------------------------------------------. */ // Removes all the spaces on a given string function remove_spaces($string) { return str_replace(' ', '', $string); } // Sanitizes double or more slahes in a path function sanitize_path_slashes($path) { return preg_replace('#/+#', '/', $path); } function sanitize_directory_separator($path) { return preg_replace('#' . DIRECTORY_SEPARATOR . '+#', DIRECTORY_SEPARATOR, $path); } function sanitize_relative_path($path) { $path = forward_slash($path); $path = sanitize_path_slashes($path); $path = preg_replace('#(\.+/)+#', '', $path); $path = sanitize_path_slashes($path); return $path; } function rrmdir($dir) { if (is_dir($dir)) { $objects = scandir($dir); foreach ($objects as $object) { if ($object != '.' && $object != '..') { if (is_dir($dir . '/' . $object)) { rrmdir($dir . '/' . $object); } else { unlink($dir . '/' . $object); } } } rmdir($dir); } } /** * Returns a sanitized string, typically for URLs * This function was borrowed from chyrp.net (MIT License). */ function sanitize_string($string, $force_lowercase = true, $only_alphanumerics = false, $truncate = 100) { $strip = array( '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '=', '+', '{', '}', '\\', '|', ';', ':', '\'', "'", '‘', '’', '“', '”', '–', '—', '—', '–', ',', '<', '.', '>', '/', '?', ); $clean = trim(str_replace($strip, '', strip_tags($string))); $clean = preg_replace('/\s+/', '-', $clean); $clean = ($only_alphanumerics ? preg_replace('/[^a-zA-Z0-9]/', '', $clean) : $clean); $clean = ($truncate ? substr($clean, 0, $truncate) : $clean); return ($force_lowercase) ? (function_exists('mb_strtolower')) ? mb_strtolower($clean, 'UTF-8') : strtolower($clean) : $clean; } // Original PHP code by Chirp Internet: www.chirp.com.au function truncate($string, $limit, $break = null, $pad = '...') { $encoding = 'UTF-8'; if (mb_strlen($string, $encoding) <= $limit) { return $string; } if (is_null($break) or $break == '') { $string = trim(mb_substr($string, 0, $limit - strlen($pad), $encoding)) . $pad; } else { if (false !== ($breakpoint = strpos($string, $break, $limit))) { if ($breakpoint < mb_strlen($string, $encoding) - 1) { $string = mb_substr($string, 0, $breakpoint, $encoding) . $pad; } } } return $string; } // Thanks to http://www.evaisse.net/2008/php-translit-remove-accent-unaccent-21001 function unaccent_string($string) { $string = (string) $string; if (function_exists('mb_detect_encoding')) { $utf8 = strtolower(mb_detect_encoding($string)) == 'utf-8'; } else { $length = strlen($string); $utf8 = true; for ($i = 0; $i < $length; ++$i) { $c = ord($string[$i]); if ($c < 0x80) { $n = 0; } // 0bbbbbbb elseif (($c & 0xE0) == 0xC0) { $n = 1; } // 110bbbbb elseif (($c & 0xF0) == 0xE0) { $n = 2; } // 1110bbbb elseif (($c & 0xF8) == 0xF0) { $n = 3; } // 11110bbb elseif (($c & 0xFC) == 0xF8) { $n = 4; } // 111110bb elseif (($c & 0xFE) == 0xFC) { $n = 5; } // 1111110b else { return false; } // Does not match any model for ($j = 0; $j < $n; ++$j) { // n bytes matching 10bbbbbb follow ? if ((++$i == $length) || ((ord($string[$i]) & 0xC0) != 0x80) ) { $utf8 = false; break; } } } } if (!$utf8) { $string = utf8_encode($string); } $transliteration = array( 'IJ' => 'I', 'Ö' => 'O', 'Œ' => 'O', 'Ü' => 'U', 'ä' => 'a', 'æ' => 'a', 'ij' => 'i', 'ö' => 'o', 'œ' => 'o', 'ü' => 'u', 'ß' => 's', 'ſ' => 's', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'A', 'Ā' => 'A', 'Ą' => 'A', 'Ă' => 'A', 'Ç' => 'C', 'Ć' => 'C', 'Č' => 'C', 'Ĉ' => 'C', 'Ċ' => 'C', 'Ď' => 'D', 'Đ' => 'D', 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ē' => 'E', 'Ę' => 'E', 'Ě' => 'E', 'Ĕ' => 'E', 'Ė' => 'E', 'Ĝ' => 'G', 'Ğ' => 'G', 'Ġ' => 'G', 'Ģ' => 'G', 'Ĥ' => 'H', 'Ħ' => 'H', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ī' => 'I', 'Ĩ' => 'I', 'Ĭ' => 'I', 'Į' => 'I', 'İ' => 'I', 'Ĵ' => 'J', 'Ķ' => 'K', 'Ľ' => 'K', 'Ĺ' => 'K', 'Ļ' => 'K', 'Ŀ' => 'K', 'Ł' => 'L', 'Ñ' => 'N', 'Ń' => 'N', 'Ň' => 'N', 'Ņ' => 'N', 'Ŋ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ø' => 'O', 'Ō' => 'O', 'Ő' => 'O', 'Ŏ' => 'O', 'Ŕ' => 'R', 'Ř' => 'R', 'Ŗ' => 'R', 'Ś' => 'S', 'Ş' => 'S', 'Ŝ' => 'S', 'Ș' => 'S', 'Š' => 'S', 'Ť' => 'T', 'Ţ' => 'T', 'Ŧ' => 'T', 'Ț' => 'T', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ū' => 'U', 'Ů' => 'U', 'Ű' => 'U', 'Ŭ' => 'U', 'Ũ' => 'U', 'Ų' => 'U', 'Ŵ' => 'W', 'Ŷ' => 'Y', 'Ÿ' => 'Y', 'Ý' => 'Y', 'Ź' => 'Z', 'Ż' => 'Z', 'Ž' => 'Z', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ā' => 'a', 'ą' => 'a', 'ă' => 'a', 'å' => 'a', 'ç' => 'c', 'ć' => 'c', 'č' => 'c', 'ĉ' => 'c', 'ċ' => 'c', 'ď' => 'd', 'đ' => 'd', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ē' => 'e', 'ę' => 'e', 'ě' => 'e', 'ĕ' => 'e', 'ė' => 'e', 'ƒ' => 'f', 'ĝ' => 'g', 'ğ' => 'g', 'ġ' => 'g', 'ģ' => 'g', 'ĥ' => 'h', 'ħ' => 'h', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ī' => 'i', 'ĩ' => 'i', 'ĭ' => 'i', 'į' => 'i', 'ı' => 'i', 'ĵ' => 'j', 'ķ' => 'k', 'ĸ' => 'k', 'ł' => 'l', 'ľ' => 'l', 'ĺ' => 'l', 'ļ' => 'l', 'ŀ' => 'l', 'ñ' => 'n', 'ń' => 'n', 'ň' => 'n', 'ņ' => 'n', 'ʼn' => 'n', 'ŋ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ø' => 'o', 'ō' => 'o', 'ő' => 'o', 'ŏ' => 'o', 'ŕ' => 'r', 'ř' => 'r', 'ŗ' => 'r', 'ś' => 's', 'š' => 's', 'ť' => 't', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ū' => 'u', 'ů' => 'u', 'ű' => 'u', 'ŭ' => 'u', 'ũ' => 'u', 'ų' => 'u', 'ŵ' => 'w', 'ÿ' => 'y', 'ý' => 'y', 'ŷ' => 'y', 'ż' => 'z', 'ź' => 'z', 'ž' => 'z', 'Α' => 'A', 'Ά' => 'A', 'Ἀ' => 'A', 'Ἁ' => 'A', 'Ἂ' => 'A', 'Ἃ' => 'A', 'Ἄ' => 'A', 'Ἅ' => 'A', 'Ἆ' => 'A', 'Ἇ' => 'A', 'ᾈ' => 'A', 'ᾉ' => 'A', 'ᾊ' => 'A', 'ᾋ' => 'A', 'ᾌ' => 'A', 'ᾍ' => 'A', 'ᾎ' => 'A', 'ᾏ' => 'A', 'Ᾰ' => 'A', 'Ᾱ' => 'A', 'Ὰ' => 'A', 'ᾼ' => 'A', 'Β' => 'B', 'Γ' => 'G', 'Δ' => 'D', 'Ε' => 'E', 'Έ' => 'E', 'Ἐ' => 'E', 'Ἑ' => 'E', 'Ἒ' => 'E', 'Ἓ' => 'E', 'Ἔ' => 'E', 'Ἕ' => 'E', 'Ὲ' => 'E', 'Ζ' => 'Z', 'Η' => 'I', 'Ή' => 'I', 'Ἠ' => 'I', 'Ἡ' => 'I', 'Ἢ' => 'I', 'Ἣ' => 'I', 'Ἤ' => 'I', 'Ἥ' => 'I', 'Ἦ' => 'I', 'Ἧ' => 'I', 'ᾘ' => 'I', 'ᾙ' => 'I', 'ᾚ' => 'I', 'ᾛ' => 'I', 'ᾜ' => 'I', 'ᾝ' => 'I', 'ᾞ' => 'I', 'ᾟ' => 'I', 'Ὴ' => 'I', 'ῌ' => 'I', 'Θ' => 'T', 'Ι' => 'I', 'Ί' => 'I', 'Ϊ' => 'I', 'Ἰ' => 'I', 'Ἱ' => 'I', 'Ἲ' => 'I', 'Ἳ' => 'I', 'Ἴ' => 'I', 'Ἵ' => 'I', 'Ἶ' => 'I', 'Ἷ' => 'I', 'Ῐ' => 'I', 'Ῑ' => 'I', 'Ὶ' => 'I', 'Κ' => 'K', 'Λ' => 'L', 'Μ' => 'M', 'Ν' => 'N', 'Ξ' => 'K', 'Ο' => 'O', 'Ό' => 'O', 'Ὀ' => 'O', 'Ὁ' => 'O', 'Ὂ' => 'O', 'Ὃ' => 'O', 'Ὄ' => 'O', 'Ὅ' => 'O', 'Ὸ' => 'O', 'Π' => 'P', 'Ρ' => 'R', 'Ῥ' => 'R', 'Σ' => 'S', 'Τ' => 'T', 'Υ' => 'Y', 'Ύ' => 'Y', 'Ϋ' => 'Y', 'Ὑ' => 'Y', 'Ὓ' => 'Y', 'Ὕ' => 'Y', 'Ὗ' => 'Y', 'Ῠ' => 'Y', 'Ῡ' => 'Y', 'Ὺ' => 'Y', 'Φ' => 'F', 'Χ' => 'X', 'Ψ' => 'P', 'Ω' => 'O', 'Ώ' => 'O', 'Ὠ' => 'O', 'Ὡ' => 'O', 'Ὢ' => 'O', 'Ὣ' => 'O', 'Ὤ' => 'O', 'Ὥ' => 'O', 'Ὦ' => 'O', 'Ὧ' => 'O', 'ᾨ' => 'O', 'ᾩ' => 'O', 'ᾪ' => 'O', 'ᾫ' => 'O', 'ᾬ' => 'O', 'ᾭ' => 'O', 'ᾮ' => 'O', 'ᾯ' => 'O', 'Ὼ' => 'O', 'ῼ' => 'O', 'α' => 'a', 'ά' => 'a', 'ἀ' => 'a', 'ἁ' => 'a', 'ἂ' => 'a', 'ἃ' => 'a', 'ἄ' => 'a', 'ἅ' => 'a', 'ἆ' => 'a', 'ἇ' => 'a', 'ᾀ' => 'a', 'ᾁ' => 'a', 'ᾂ' => 'a', 'ᾃ' => 'a', 'ᾄ' => 'a', 'ᾅ' => 'a', 'ᾆ' => 'a', 'ᾇ' => 'a', 'ὰ' => 'a', 'ᾰ' => 'a', 'ᾱ' => 'a', 'ᾲ' => 'a', 'ᾳ' => 'a', 'ᾴ' => 'a', 'ᾶ' => 'a', 'ᾷ' => 'a', 'β' => 'b', 'γ' => 'g', 'δ' => 'd', 'ε' => 'e', 'έ' => 'e', 'ἐ' => 'e', 'ἑ' => 'e', 'ἒ' => 'e', 'ἓ' => 'e', 'ἔ' => 'e', 'ἕ' => 'e', 'ὲ' => 'e', 'ζ' => 'z', 'η' => 'i', 'ή' => 'i', 'ἠ' => 'i', 'ἡ' => 'i', 'ἢ' => 'i', 'ἣ' => 'i', 'ἤ' => 'i', 'ἥ' => 'i', 'ἦ' => 'i', 'ἧ' => 'i', 'ᾐ' => 'i', 'ᾑ' => 'i', 'ᾒ' => 'i', 'ᾓ' => 'i', 'ᾔ' => 'i', 'ᾕ' => 'i', 'ᾖ' => 'i', 'ᾗ' => 'i', 'ὴ' => 'i', 'ῂ' => 'i', 'ῃ' => 'i', 'ῄ' => 'i', 'ῆ' => 'i', 'ῇ' => 'i', 'θ' => 't', 'ι' => 'i', 'ί' => 'i', 'ϊ' => 'i', 'ΐ' => 'i', 'ἰ' => 'i', 'ἱ' => 'i', 'ἲ' => 'i', 'ἳ' => 'i', 'ἴ' => 'i', 'ἵ' => 'i', 'ἶ' => 'i', 'ἷ' => 'i', 'ὶ' => 'i', 'ῐ' => 'i', 'ῑ' => 'i', 'ῒ' => 'i', 'ῖ' => 'i', 'ῗ' => 'i', 'κ' => 'k', 'λ' => 'l', 'μ' => 'm', 'ν' => 'n', 'ξ' => 'k', 'ο' => 'o', 'ό' => 'o', 'ὀ' => 'o', 'ὁ' => 'o', 'ὂ' => 'o', 'ὃ' => 'o', 'ὄ' => 'o', 'ὅ' => 'o', 'ὸ' => 'o', 'π' => 'p', 'ρ' => 'r', 'ῤ' => 'r', 'ῥ' => 'r', 'σ' => 's', 'ς' => 's', 'τ' => 't', 'υ' => 'y', 'ύ' => 'y', 'ϋ' => 'y', 'ΰ' => 'y', 'ὐ' => 'y', 'ὑ' => 'y', 'ὒ' => 'y', 'ὓ' => 'y', 'ὔ' => 'y', 'ὕ' => 'y', 'ὖ' => 'y', 'ὗ' => 'y', 'ὺ' => 'y', 'ῠ' => 'y', 'ῡ' => 'y', 'ῢ' => 'y', 'ῦ' => 'y', 'ῧ' => 'y', 'φ' => 'f', 'χ' => 'x', 'ψ' => 'p', 'ω' => 'o', 'ώ' => 'o', 'ὠ' => 'o', 'ὡ' => 'o', 'ὢ' => 'o', 'ὣ' => 'o', 'ὤ' => 'o', 'ὥ' => 'o', 'ὦ' => 'o', 'ὧ' => 'o', 'ᾠ' => 'o', 'ᾡ' => 'o', 'ᾢ' => 'o', 'ᾣ' => 'o', 'ᾤ' => 'o', 'ᾥ' => 'o', 'ᾦ' => 'o', 'ᾧ' => 'o', 'ὼ' => 'o', 'ῲ' => 'o', 'ῳ' => 'o', 'ῴ' => 'o', 'ῶ' => 'o', 'ῷ' => 'o', 'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'E', 'Ж' => 'Z', 'З' => 'Z', 'И' => 'I', 'Й' => 'I', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O', 'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 'Х' => 'K', 'Ц' => 'T', 'Ч' => 'C', 'Ш' => 'S', 'Щ' => 'S', 'Ы' => 'Y', 'Э' => 'E', 'Ю' => 'Y', 'Я' => 'Y', 'а' => 'A', 'б' => 'B', 'в' => 'V', 'г' => 'G', 'д' => 'D', 'е' => 'E', 'ё' => 'E', 'ж' => 'Z', 'з' => 'Z', 'и' => 'I', 'й' => 'I', 'к' => 'K', 'л' => 'L', 'м' => 'M', 'н' => 'N', 'о' => 'O', 'п' => 'P', 'р' => 'R', 'с' => 'S', 'т' => 'T', 'у' => 'U', 'ф' => 'F', 'х' => 'K', 'ц' => 'T', 'ч' => 'C', 'ш' => 'S', 'щ' => 'S', 'ы' => 'Y', 'э' => 'E', 'ю' => 'Y', 'я' => 'Y', 'ð' => 'd', 'Ð' => 'D', 'þ' => 't', 'Þ' => 'T', 'ა' => 'a', 'ბ' => 'b', 'გ' => 'g', 'დ' => 'd', 'ე' => 'e', 'ვ' => 'v', 'ზ' => 'z', 'თ' => 't', 'ი' => 'i', 'კ' => 'k', 'ლ' => 'l', 'მ' => 'm', 'ნ' => 'n', 'ო' => 'o', 'პ' => 'p', 'ჟ' => 'z', 'რ' => 'r', 'ს' => 's', 'ტ' => 't', 'უ' => 'u', 'ფ' => 'p', 'ქ' => 'k', 'ღ' => 'g', 'ყ' => 'q', 'შ' => 's', 'ჩ' => 'c', 'ც' => 't', 'ძ' => 'd', 'წ' => 't', 'ჭ' => 'c', 'ხ' => 'k', 'ჯ' => 'j', 'ჰ' => 'h', 'ḩ' => 'h', 'ừ' => 'u', 'ế' => 'e', 'ả' => 'a', 'ị' => 'i', 'ậ' => 'a', 'ệ' => 'e', 'ỉ' => 'i', 'ộ' => 'o', 'ồ' => 'o', 'ề' => 'e', 'ơ' => 'o', 'ạ' => 'a', 'ẵ' => 'a', 'ư' => 'u', 'ắ' => 'a', 'ằ' => 'a', 'ầ' => 'a', 'ḑ' => 'd', 'Ḩ' => 'H', 'Ḑ' => 'D', 'ḑ' => 'd', 'ş' => 's', 'ā' => 'a', 'ţ' => 't', 'ễ' => 'e', ); $string = str_replace(array_keys($transliteration), array_values($transliteration), $string); // Missing something? if (strpos($string = htmlentities($string, ENT_QUOTES, 'UTF-8'), '&') !== false) { $string = html_entity_decode(preg_replace('~&([a-z]{1,2})(?:acute|cedil|circ|grave|lig|orn|ring|slash|tilde|uml);~i', '$1', $string), ENT_QUOTES, 'UTF-8'); } return $string; } // Safe for HTML output function safe_html($var, $flag = ENT_QUOTES) { if (!is_array($var)) { return $var === null ? null : htmlspecialchars($var, $flag, 'UTF-8'); // htmlspecialchars keeps ñ, á and all the UTF-8 valid chars } $safe_array = array(); foreach ($var as $k => $v) { $safe_array[$k] = is_array($v) ? safe_html($v) : ($v === null ? null : htmlspecialchars($v, $flag, 'UTF-8')); } return $safe_array; } /** * BYTE AND NUMBER HANDLING * ---------------------------------------------------------------------. */ // Converts bytes to whatever function format_bytes($bytes, $round = 1) { if (!is_numeric($bytes)) { return false; } if ($bytes < 1000) { return "$bytes B"; } $units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; foreach ($units as $k => $v) { $multiplier = pow(1000, $k + 1); $threshold = $multiplier * 1000; if ($bytes < $threshold) { $size = round($bytes / $multiplier, $round); return "$size $v"; } } } // Returns bytes for SIZE + Suffix format (dec + bin) function get_bytes($size, $cut = null) { if ($cut == null) { $suffix = substr($size, -3); $suffix = preg_match('/([A-Za-z]){3}/', $suffix) ? $suffix : substr($size, -2); } else { $suffix = substr($size, $cut); } $suffix = strtoupper($suffix); $units = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; // Default dec units if (strlen($suffix) == 3) { // Convert units to bin foreach ($units as &$unit) { $split = str_split($unit); $unit = $split[0] . 'I' . $split[1]; } } if (strlen($suffix) == 1) { $suffix .= 'B'; // Adds missing "B" for shorthand ini notation (Turns 1G into 1GB) } if (!in_array($suffix, $units)) { return $size; } $pow_factor = array_search($suffix, $units) + 1; return $size * pow(strlen($suffix) == 2 ? 1000 : 1024, $pow_factor); } // Converts bytes to MB function bytes_to_mb($bytes) { return round($bytes / pow(10, 6)); } // Returns bytes (used for the ini_get functions) function get_ini_bytes($size) { return get_bytes($size, -1); } /** * PATHS AND URL HANDLING * ---------------------------------------------------------------------. */ // Add trailing slashes function add_trailing_slashes($string) { return add_ending_slash(add_starting_slash($string)); } function add_starting_slash($string) { return '/' . ltrim($string, '/'); } function add_ending_slash($string) { return rtrim($string, '/') . '/'; } // Converts backslash into forward slash function forward_slash($string) { return str_replace('\\', '/', $string); } // Converts relative path to absolute path function relative_to_absolute($filepath) { return str_replace(G_ROOT_PATH_RELATIVE, G_ROOT_PATH, forward_slash($filepath)); } // Converts relative path to url function relative_to_url($filepath, $root_url = null) { if (!check_value($root_url)) { $root_url = get_root_url(); } return str_replace(G_ROOT_PATH_RELATIVE, $root_url, forward_slash($filepath)); } // Converts app URL to relative path function url_to_relative($url, $root_url = null) { if (!check_value($root_url)) { $root_url = get_root_url(); } return str_replace($root_url, G_ROOT_PATH_RELATIVE, $url); } // Converts absolute path to relative path function absolute_to_relative($filepath) { return str_replace(G_ROOT_PATH, G_ROOT_PATH_RELATIVE, forward_slash($filepath)); } // Converts absolute path to URL function absolute_to_url($filepath, $root_url = null) { if (!check_value($root_url)) { $root_url = get_root_url(); } if (G_ROOT_PATH === G_ROOT_PATH_RELATIVE) { return $root_url . ltrim($filepath, '/'); } return str_replace(G_ROOT_PATH, $root_url, forward_slash($filepath)); } // Converts app URL to absolute path function url_to_absolute($url, $root_url = null) { if (!check_value($root_url)) { $root_url = get_root_url(); } return str_replace($root_url, G_ROOT_PATH, $url); } /** * GET AND FETCH SOME DATA * ---------------------------------------------------------------------. */ // Get current G\ version function get_version() { return G_VERSION; } // Get the current app version function get_app_version($full = true) { if ($full) { return G_APP_VERSION; } else { preg_match('/\d\.\d/', G_APP_VERSION, $return); return $return[0]; } } // Get the $settings value for the app function get_app_setting($key) { return get_global('settings')[$key]; } function get_base_url($path = '') { $path = sanitize_relative_path($path); $return = get_root_url() . ltrim($path, '/'); return rtrim($return, '/'); } function get_host() { return defined('APP_G_HTTP_HOST') ? APP_G_HTTP_HOST : G_HTTP_HOST; } function get_host_domain() { $url = get_root_url(); return parse_url($url)['host']; } function get_root_url() { return defined('APP_G_ROOT_URL') ? APP_G_ROOT_URL : G_ROOT_URL; } /** * @param string Querystring keys to remove (comma separated) */ function get_current_url($safe = true, $removeQs = []) { $request_uri = $_SERVER['REQUEST_URI']; $request_path = rtrim(strtok($request_uri, '?'), '/'); if ($_SERVER['QUERY_STRING'] && $removeQs) { parse_str($_SERVER['QUERY_STRING'], $parse); foreach ($removeQs as $v) { unset($parse[$v]); } $querystring = $parse ? http_build_query($parse) : null; $request_uri = $request_path; if ($querystring) { $request_uri .= '/?' . $querystring; } } $path = preg_replace('#' . G_ROOT_PATH_RELATIVE . '#', '', rtrim($request_uri, '/') . '/', 1); return get_base_url($path); } function settings_has_db_info() { $settings = get_global('settings'); if (!is_array($settings)) { return false; } $has = true; foreach (['db_driver', 'db_host', 'db_name', 'db_user'] as $k) { if (!array_key_exists($k, $settings)) { $has = false; break; } } return $has; } function get_regex_match($regex, $delimiter = '/', $subject, $key = null) { preg_match($delimiter . $regex . $delimiter, $subject, $matches); if (array_key_exists($key, $matches)) { return $matches[$key]; } else { return $matches; } } /** * Fetch the contents from an URL * if $file is set the downloaed file will be saved there. */ function fetch_url($url, $file = null, $options = []) { if (!$url) { throw new Exception('missing $url in ' . __FUNCTION__); return false; } if (ini_get('allow_url_fopen') !== 1 && !function_exists('curl_init')) { throw new Exception('Fatal error in ' . __FUNCTION__ . ": cURL isn't installed and allow_url_fopen is disabled. Can't perform HTTP requests."); return false; } // File get contents is the failover fn $fn = (!function_exists('curl_init') ? 'fgc' : 'curl'); if ($fn == 'curl') { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 60); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_FAILONERROR, 0); curl_setopt($ch, CURLOPT_ENCODING, 'gzip'); curl_setopt($ch, CURLOPT_VERBOSE, 0); if (is_array($options) && count($options) > 0) { foreach ($options as $k => $v) { curl_setopt($ch, $k, $v); } } if ($file) { // Save the file to $file destination $out = @fopen($file, 'wb'); if (!$out) { throw new Exception("Can't open " . __FUNCTION__ . '() file for read and write'); return false; } curl_setopt($ch, CURLOPT_FILE, $out); @curl_exec($ch); fclose($out); } else { // Return the file string $file_get_contents = @curl_exec($ch); } if (curl_errno($ch)) { $curl_error = curl_error($ch); curl_close($ch); throw new Exception('Curl error ' . $curl_error); return false; } if ($file == null) { curl_close($ch); return $file_get_contents; } } else { $context = stream_context_create([ 'http' => ['ignore_errors' => true], ]); $result = @file_get_contents($url, false, $context); if (!$result) { throw new Exception("Can't fetch target URL (file_get_contents)"); return false; } if ($file) { if (file_put_contents($file, $result) === false) { throw new Exception("Can't fetch target URL (file_put_contents)"); return false; } } else { return $result; } } } /* get complete raw, add success + error properties */ function getUrlHeaders($url, $options = []) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 1); curl_setopt($ch, CURLOPT_NOBODY, 1); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36'); // Inject custom options if (is_array($options)) { foreach ($options as $k => $v) { curl_setopt($ch, $k, $v); } } $raw = curl_exec($ch); if (curl_errno($ch)) { $return['error'] = curl_error($ch); $return['http_code'] = 500; } else { $return = curl_getinfo($ch); $return['raw'] = $raw; } curl_close($ch); return $return; } // Returns float Execution time at this point of call function get_execution_time() { return microtime(true) - G_APP_TIME_EXECUTION_START; } // Get bcrypt optimal cost function bcrypt_cost($time = 0.2, $cost = 9) { do { ++$cost; $inicio = microtime(true); password_hash('test', PASSWORD_BCRYPT, ['cost' => $cost]); $fin = microtime(true); } while (($fin - $inicio) < $time); return $cost; } /** * CONDITIONALS * ---------------------------------------------------------------------. */ // Checks if $var is a positive integer using filter_var function is_integer($var, $range = []) { $options = []; if (!empty($range) && is_array($range)) { foreach (['min', 'max'] as $k) { if (is_int($range[$k])) { $options['options'][$k . '_range'] = $range[$k]; } } } return filter_var($var, FILTER_VALIDATE_INT, $options) !== false; } // This will tell if the string is an URL function is_url($string) { // Only for strings if (!is_string($string)) { return false; } // Internal PHP system if (filter_var($string, FILTER_VALIDATE_URL)) { // Note: This doesn't validate any non-alphanumeric URL return true; } // Test if it has foreign chars if (strlen($string) !== strlen(utf8_encode($string))) { $parsed_url = parse_url($string); if (count($parsed_url) < 2) { // At least scheme and host return false; } $schemes = ['http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn', 'tel', 'fax', 'xmpp']; if (!in_array(strtolower($parsed_url['scheme']), $schemes)) { // Must be a valid scheme return false; } if (!array_key_exists('host', $parsed_url)) { // Host must be there return false; } // At this point this thing looks like an URL return true; } return false; } // Tells if the given url is https or not function is_https($string) { return strpos($string, 'https://') !== false; } // Tell if the string is an URL and if is valid function is_valid_url($string) { if (!is_url($string)) { return false; } $url = preg_replace('/^https/', 'http', $string, 1); if (function_exists('curl_init')) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_NOBODY, 1); curl_setopt($ch, CURLOPT_FAILONERROR, 0); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 120); $result = @curl_exec($ch); curl_close($ch); return $result !== false ? true : false; } else { if (ini_get('allow_url_fopen')) { $result = file_get_contents($url); return $result === false ? false : true; } } } // Tell if the string is an image URL function is_image_url($string) { if (!is_string($string)) { return false; } return preg_match('/(?:ftp|https?):\/\/(\w+:\w+@)?([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(:[0-9]{1,4}){0,1}|(?:[\w\-]+\.)+[a-z]{2,6})(?:\/[^\/#\?]+)+\.(?:jpe?g|gif|png|bmp)/i', $string); } // Returns true if the system is in development mode // nota: buscar los is_localhost() function is_development_env() { return G_APP_ENV == 'develompent'; } function is_windows_os() { return strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? true : false; } function is_animated_image($filename) { $extension = get_file_extension($filename); switch ($extension) { case 'gif': return is_animated_gif($filename); break; case 'png': return is_animated_png($filename); break; case 'webp': return is_animated_webp($filename); break; default: return false; break; } } // Taken from http://php.net/manual/en/function.imagecreatefromgif.php#104473 function is_animated_gif($filename) { if (!($fh = @fopen($filename, 'rb'))) { return false; } $count = 0; while (!feof($fh) && $count < 2) { $chunk = fread($fh, 1024 * 100); //read 100kb at a time $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches); } fclose($fh); return $count > 1; } // Taken from https://stackoverflow.com/a/4525194/1145912 function is_animated_png($filename) { $img_bytes = file_get_contents($filename); if ($img_bytes) { if (strpos( substr($img_bytes, 0, strpos($img_bytes, 'IDAT')), 'acTL' ) !== false) { return true; } } return false; } function is_animated_webp($filename) { $result = false; $fh = fopen($filename, "rb"); fseek($fh, 12); if (fread($fh, 4) === 'VP8X') { fseek($fh, 16); $myByte = fread($fh, 1); $result = ((ord($myByte) >> 1) & 1) ? true : false; } fclose($fh); return $result; } /** * FILE RELATED * ---------------------------------------------------------------------. */ // Get mimetype of $file according to your php version function get_mimetype($file) { if (function_exists('finfo_open')) { return finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file); } else { if (function_exists('mime_content_type')) { return mime_content_type($file); } else { return extension_to_mime(get_file_extension($file)); } } } // For now this only works for images function mime_to_extension($mime, $reverse = false) { $mime_to_extension = array( 'image/x-windows-bmp' => 'bmp', 'image/x-ms-bmp' => 'bmp', 'image/bmp' => 'bmp', 'image/gif' => 'gif', 'image/pjpeg' => 'jpg', 'image/jpeg' => 'jpg', 'image/x-png' => 'png', 'image/png' => 'png', 'image/x-tiff' => 'tiff', 'image/tiff' => 'tiff', 'image/x-icon' => 'ico', 'image/x-rgb' => 'rgb', 'image/webp' => 'webp', ); // Used to get ext -> mime if ($reverse) { $mime_to_extension = array_flip($mime_to_extension); } return $mime_to_extension[$mime]; } function extension_to_mime($ext) { return mime_to_extension($ext, true); } // Retrieves info about the current image file function get_image_fileinfo($file) { clearstatcache(true, $file); // http://php.net/manual/es/function.clearstatcache.php (add G\ magic for those) $info = getimagesize($file); $filesize = @filesize($file); if (!$info || $filesize === false) { return false; } $mime = strtolower($info['mime']); return array( 'filename' => basename($file), // image.jpg 'name' => basename($file, '.' . get_file_extension($file)), // image 'width' => intval($info[0]), 'height' => intval($info[1]), 'ratio' => $info[0] / $info[1], 'size' => intval($filesize), 'size_formatted' => format_bytes($filesize), 'mime' => $mime, 'extension' => mime_to_extension($mime), 'bits' => $info['bits'], 'channels' => $info['channels'], 'url' => absolute_to_url($file), 'md5' => md5_file($file), ); } function get_file_extension($file) { return strtolower(pathinfo($file, PATHINFO_EXTENSION)); } function get_filename($file) { return basename($file); } function get_basename_without_extension($filename) { $extension = pathinfo($filename, PATHINFO_EXTENSION); $filename = basename($filename); return str_replace_last(".$extension", null, $filename); } function get_pathname_without_extension($filename) { $extension = pathinfo($filename, PATHINFO_EXTENSION); return str_replace_last(".$extension", null, $filename); } function change_pathname_extension($filename, $extension) { $chop = get_pathname_without_extension($filename); if ($chop == $filename) { return $filename; } return "$chop.$extension"; } /** * This creates .htaccess file on the target dir using the param rules * This can be also used to add rules to a existing .htaccess file. */ function generate_htaccess($rules, $directory, $before = null, $output = false) { $htaccess = $directory . '.htaccess'; $rules_stock = [ 'static' => '' . "\n" . 'order allow,deny' . "\n" . 'deny from all' . "\n" . '' . "\n\n" . 'AddHandler cgi-script .php .php3 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi .fcgi' . "\n" . 'Options -ExecCGI', 'deny_php' => '' . "\n" . 'Order Deny,Allow' . "\n" . 'Deny from all' . "\n" . '', 'deny' => 'deny from all', ]; if (array_key_exists($rules, $rules_stock)) { $rules = $rules_stock[$rules]; } if (file_exists($htaccess)) { $fgc = file_get_contents($htaccess); if (strpos($fgc, $rules)) { $done = true; } else { if (!is_null($before)) { $rules = str_replace($before, $rules . "\n" . $before, $fgc); $f = 'w'; } else { $rules = "\n\n" . $rules; $f = 'a'; } } } else { $f = 'w'; } if (!$done) { $fh = @fopen($htaccess, $f); if (!$fh) { return false; } if (fwrite($fh, $rules)) { @fclose($fh); return $output ? $rules : true; } else { @fclose($fh); return $output ? $rules : false; } } else { return $output ? $rules : true; } } /** * Get a safe filename * $method: original | random | mixed | id * $filename: name of the original file. */ function get_filename_by_method($method, $filename) { $max_length = 200; // Safe limit, ideally this should be 255 - 4 $extension = get_file_extension($filename); $clean_filename = substr($filename, 0, - (strlen($extension) + 1)); $clean_filename = unaccent_string($clean_filename); // change áéíóú to aeiou $clean_filename = preg_replace('/\s+/', '-', $clean_filename); // change all spaces with dash $clean_filename = trim($clean_filename, '-'); // get rid of those ugly dashes $clean_filename = preg_replace('/[^\.\w\d-]/i', '', $clean_filename); // remove any non alphanumeric, non underscore, non hyphen and non dot // Non alphanumeric name uh.. if (strlen($clean_filename) == 0) { $clean_filename = random_string(32); } $unlimited_filename = $clean_filename; // No max_length limit $capped_filename = substr($clean_filename, 0, $max_length); // 1->200 switch ($method) { default: case 'original': $name = $capped_filename; break; case 'random': $name = random_string(32); break; case 'mixed': $mixed_chars_length = 16; $mixed_chars = random_string($mixed_chars_length); if (strlen($capped_filename) + $mixed_chars_length > $max_length) { // Bring the scissors Morty $capped_filename = substr($capped_filename, 0, $max_length - $mixed_chars_length - strlen($capped_filename)); // Well done Morty you little piece of shit } $name = $capped_filename . $mixed_chars; break; case 'id': $name = $unlimited_filename; break; } return $name . '.' . $extension; // 200 + 4 } function name_unique_file($path, $method = 'original', $filename) { $file = $path . get_filename_by_method($method, $filename); if ($method == 'id') { return $file; } while (file_exists($file)) { if ($method == 'original') { $method = 'mixed'; } $file = $path . get_filename_by_method($method, $filename); } return $file; } /** * IMAGE RELATED * ---------------------------------------------------------------------. */ /** * PNG ALPHA CHANNEL SUPPORT for imagecopymerge(); * by Sina Salek. * * Bugfix by Ralph Voigt (bug which causes it * to work only for $src_x = $src_y = 0. * Also, inverting opacity is not necessary.) * 08-JAN-2011 * **/ function imagecopymerge_alpha($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct, $dst_im_ext) { if ($dst_im_ext == 'jpg' && $pct == 100) { imagealphablending($dst_im, true); imagealphablending($src_im, true); imagecopy($dst_im, $src_im, $dst_x, $dst_y, 0, 0, $src_w, $src_h); } else { $transparent_index = imagecolortransparent($dst_im); $colors_total = imagecolorstotal($dst_im); $cut = imagecreatetruecolor($src_w, $src_h); if ($transparent_index >= 0) { $transparent_color = imagecolorsforindex($dst_im, $transparent_index); $transparent_index = imagecolorallocatealpha($cut, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue'], 127); imagefill($cut, 0, 0, $transparent_index); imagecolortransparent($cut, $transparent_index); } else { $color = imagecolorallocatealpha($cut, 0, 0, 0, 127); imagefill($cut, 0, 0, $color); } if ($dst_im_ext == 'png') { imagealphablending($dst_im, false); $imagesavealpha = true; } else { if ($dst_im_ext == 'webp') { imagepalettetotruecolor($dst_im); $imagesavealpha = true; } else { if ($dst_im_ext !== 'jpg') { imagetruecolortopalette($dst_im, true, 255); $imagesavealpha = false; } } } imagesavealpha($dst_im, $imagesavealpha); if (in_array($dst_im_ext, ['png', 'webp']) && $colors_total == 0) { if ($pct < 100) { imagefilteropacity($src_im, $pct); } imagealphablending($dst_im, true); imagecopy($dst_im, $src_im, $dst_x, $dst_y, 0, 0, $src_w, $src_h); imagealphablending($dst_im, false); } else { imagecopy($cut, $dst_im, 0, 0, $dst_x, $dst_y, $src_w, $src_h); imagecopy($cut, $src_im, 0, 0, $src_x, $src_y, $src_w, $src_h); imagecopymerge($dst_im, $cut, $dst_x, $dst_y, 0, 0, $src_w, $src_h, $pct); } imagedestroy($cut); } imagedestroy($src_im); } // Taken from http://www.php.net/manual/en/function.imagefilter.php#82162 function imagefilteropacity(&$img, $opacity) { if (!isset($opacity)) { return false; } $opacity /= 100; $w = imagesx($img); $h = imagesy($img); imagealphablending($img, false); //find the most opaque pixel in the image (the one with the smallest alpha value) $minalpha = 127; for ($x = 0; $x < $w; ++$x) { for ($y = 0; $y < $h; ++$y) { $alpha = (imagecolorat($img, $x, $y) >> 24) & 0xFF; if ($alpha < $minalpha) { $minalpha = $alpha; } } } //loop through image pixels and modify alpha for each for ($x = 0; $x < $w; ++$x) { for ($y = 0; $y < $h; ++$y) { //get current alpha value (represents the TANSPARENCY!) $colorxy = imagecolorat($img, $x, $y); $alpha = ($colorxy >> 24) & 0xFF; //calculate new alpha if ($minalpha !== 127) { $alpha = 127 + 127 * $opacity * ($alpha - 127) / (127 - $minalpha); } else { $alpha += 127 * $opacity; } //get the color index with new alpha $alphacolorxy = imagecolorallocatealpha($img, ($colorxy >> 16) & 0xFF, ($colorxy >> 8) & 0xFF, $colorxy & 0xFF, $alpha); //set pixel with the new color + opacity if (!imagesetpixel($img, $x, $y, $alphacolorxy)) { return false; } } } return true; } function image_allocate_transparency($image, $extension) { if ($extension == 'png') { imagealphablending($image, false); imagesavealpha($image, true); } else { imagetruecolortopalette($image, true, 255); imagesavealpha($image, false); } } function image_copy_transparency($image_source, $image_target) { $transparent_index = imagecolortransparent($image_source); $palletsize = imagecolorstotal($image_source); if ($transparent_index >= 0 and $transparent_index < $palletsize) { $transparent_color = imagecolorsforindex($image_source, $transparent_index); $transparent_index = imagecolorallocatealpha($image_target, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue'], 127); imagefill($image_target, 0, 0, $transparent_index); imagecolortransparent($image_target, $transparent_index); } else { $color = imagecolorallocatealpha($image_target, 0, 0, 0, 127); imagefill($image_target, 0, 0, $color); } } function get_mask_bit_shift($bits, $mask) { if ($bits == 16) { // 555 if ($mask == 0x7c00) { return 7; } if ($mask == 0x03e0) { return 2; } // 656 if ($mask == 0xf800) { return 8; } if ($mask == 0x07e0) { return 3; } } else { if ($mask == 0xff000000) { return 24; } if ($mask == 0x00ff0000) { return 16; } if ($mask == 0x0000ff00) { return 8; } } return 0; } // http://www.programmierer-forum.de/function-imagecreatefrombmp-welche-variante-laeuft-t143137.htm // Extra bits by https://github.com/djeman // https://github.com/rodber/chevereto-free/pull/35 function imagecreatefrombmp($file) { // if (function_exists('imagecreatefrombmp')) { // return imagecreatefrombmp($file); // } // version 1.00 if (!($fh = fopen($file, 'rb'))) { trigger_error('imagecreatefrombmp: Can not open ' . $file, E_USER_WARNING); return false; } // read file header $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14)); // check for bitmap if ($meta['type'] != 19778) { trigger_error('imagecreatefrombmp: ' . $file . ' is not a bitmap!', E_USER_WARNING); return false; } // read image header $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40)); $bytes_read = 40; // read additional bitfields if ($meta['headersize'] > $bytes_read) { // RGB bit field masks $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12)); $bytes_read += 12; } else { if ($meta['bits'] == 16) { $meta['rMask'] = 0x7c00; $meta['gMask'] = 0x03e0; $meta['bMask'] = 0x001f; } elseif ($meta['bits'] > 16) { $meta['rMask'] = 0x00ff0000; $meta['gMask'] = 0x0000ff00; $meta['bMask'] = 0x000000ff; } } // set bytes and padding $meta['bytes'] = $meta['bits'] / 8; $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4))); if ($meta['decal'] == 4) { $meta['decal'] = 0; } // obtain imagesize if ($meta['imagesize'] < 1) { $meta['imagesize'] = $meta['filesize'] - $meta['offset']; // in rare cases filesize is equal to offset so we need to read physical size if ($meta['imagesize'] < 1) { $meta['imagesize'] = @filesize($file) - $meta['offset']; if ($meta['imagesize'] < 1) { trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $file . '!', E_USER_WARNING); return false; } } } // calculate colors $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors']; // read color palette $palette = array(); if ($meta['bits'] < 16) { $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4)); // in rare cases the color value is signed if ($palette[1] < 0) { foreach ($palette as $i => $color) { $palette[$i] = $color + 16777216; } } } // ignore extra bitmap headers if ($meta['headersize'] > $bytes_read) { fread($fh, $meta['headersize'] - $bytes_read); } // create gd image $im = imagecreatetruecolor($meta['width'], $meta['height']); $data = fread($fh, $meta['imagesize']); $p = 0; $vide = chr(0); $y = $meta['height'] - 1; $error = 'imagecreatefrombmp: ' . $file . ' has not enough data!'; // loop through the image data beginning with the lower left corner while ($y >= 0) { $x = 0; while ($x < $meta['width']) { switch ($meta['bits']) { case 32: if (!($part = substr($data, $p, 4))) { trigger_error($error, E_USER_WARNING); return $im; } $color = unpack('V', $part); $color[1] = (($color[1] & $meta['rMask']) >> get_mask_bit_shift(32, $meta['rMask'])) * 65536 + (($color[1] & $meta['gMask']) >> get_mask_bit_shift(32, $meta['gMask'])) * 256 + (($color[1] & $meta['bMask']) >> get_mask_bit_shift(32, $meta['bMask'])); break; case 24: if (!($part = substr($data, $p, 3))) { trigger_error($error, E_USER_WARNING); return $im; } $color = unpack('V', $part . $vide); $color[1] = (($color[1] & $meta['rMask']) >> get_mask_bit_shift(24, $meta['rMask'])) * 65536 + (($color[1] & $meta['gMask']) >> get_mask_bit_shift(24, $meta['gMask'])) * 256 + (($color[1] & $meta['bMask']) >> get_mask_bit_shift(24, $meta['bMask'])); break; case 16: if (!($part = substr($data, $p, 2))) { trigger_error($error, E_USER_WARNING); return $im; } $color = unpack('v', $part); $color[1] = (($color[1] & $meta['rMask']) >> get_mask_bit_shift(16, $meta['rMask'])) * 65536 + (($color[1] & $meta['gMask']) >> get_mask_bit_shift(16, $meta['gMask'])) * 256 + (($color[1] & $meta['bMask']) << 3); break; case 8: $color = unpack('n', $vide . substr($data, $p, 1)); $color[1] = $palette[$color[1] + 1]; break; case 4: $color = unpack('n', $vide . substr($data, floor($p), 1)); $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F; $color[1] = $palette[$color[1] + 1]; break; case 1: $color = unpack('n', $vide . substr($data, floor($p), 1)); switch (($p * 8) % 8) { case 0: $color[1] = $color[1] >> 7; break; case 1: $color[1] = ($color[1] & 0x40) >> 6; break; case 2: $color[1] = ($color[1] & 0x20) >> 5; break; case 3: $color[1] = ($color[1] & 0x10) >> 4; break; case 4: $color[1] = ($color[1] & 0x8) >> 3; break; case 5: $color[1] = ($color[1] & 0x4) >> 2; break; case 6: $color[1] = ($color[1] & 0x2) >> 1; break; case 7: $color[1] = ($color[1] & 0x1); break; } $color[1] = $palette[$color[1] + 1]; break; default: trigger_error('imagecreatefrombmp: ' . $file . ' has ' . $meta['bits'] . ' bits and this is not supported!', E_USER_WARNING); return false; } imagesetpixel($im, $x, $y, $color[1]); ++$x; $p += $meta['bytes']; } --$y; $p += $meta['decal']; } fclose($fh); return $im; } /** * JSON * ---------------------------------------------------------------------. */ // Prepare json to only serve XMLHttpRequest function json_prepare() { if (is_development_env()) { return; } if ($_SERVER['HTTP_X_REQUESTED_WITH'] !== 'XMLHttpRequest') { Render\json_output(['status_code' => 400]); } } function json_error($args) { if (func_num_args() == 1 and is_object($args)) { if (method_exists($args, 'getMessage') and method_exists($args, 'getCode')) { $message = $args->getMessage(); $code = $args->getCode(); $context = get_class($args); error_log($message); // log class errors } else { return; } } else { if (func_num_args() == 1) { $message = $args; $code = null; $context = null; } else { $message = func_get_arg(0); $code = func_get_arg(1); $context = null; } } return array( 'status_code' => 400, 'error' => array( 'message' => $message, 'code' => $code, 'context' => $context, ), ); } /** * HTTP * ---------------------------------------------------------------------. */ /** * Redirects to another URL. */ function redirect($to = '', $status = 301) { if (!filter_var($to, FILTER_VALIDATE_URL)) { $to = get_base_url($to); } $to = preg_replace('|[^a-z0-9-~+_.?#=&;,/:%!]|i', '', $to); if (php_sapi_name() != 'cgi-fcgi') { set_status_header($status); } header("Location: $to"); die(); } /** * Set HTTP status header from status code. * * @Inspired from WordPress */ function set_status_header($code) { $desc = get_set_status_header_desc($code); if (empty($desc)) { return false; } $protocol = $_SERVER['SERVER_PROTOCOL']; if ('HTTP/1.1' != $protocol && 'HTTP/1.0' != $protocol) { $protocol = 'HTTP/1.0'; } $set_status_header = "$protocol $code $desc"; return @header($set_status_header, true, $code); } /** * Gets header description according to its code. * * @Inspired from WordPress */ function get_set_status_header_desc($code) { $codes_to_desc = array( 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-Status', 226 => 'IM Used', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Reserved', 307 => 'Temporary Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Long', 415 => 'Unsupported Media Type', 416 => 'Requested Range Not Satisfiable', 417 => 'Expectation Failed', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 426 => 'Upgrade Required', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 510 => 'Not Extended', ); if (check_value($codes_to_desc[$code])) { return $codes_to_desc[$code]; } } // @Inspired from WordPress function clean_header_comment($string) { return trim(preg_replace('/\s*(?:\*\/|\?>).*/', '', $string)); } /** * function xml2array. * * This function is part of the PHP manual. * * The PHP manual text and comments are covered by the Creative Commons * Attribution 3.0 License, copyright (c) the PHP Documentation Group * * @author k dot antczak at livedata dot pl * @date 2011-04-22 06:08 UTC * * @see http://www.php.net/manual/en/ref.simplexml.php#103617 * * @license http://www.php.net/license/index.php#doc-lic * @license http://creativecommons.org/licenses/by/3.0/ * @license CC-BY-3.0 */ function xml2array($xmlObject, $out = array()) { foreach ((array) $xmlObject as $index => $node) { $out[$index] = (is_object($node)) ? xml2array($node) : $node; } return $out; } /** * @param string $domain Pass $_SERVER['SERVER_NAME'] here * @param bool $debug * * @debug bool $debug * * @return string * * @see https://gist.github.com/pocesar/5366899 */ function get_domain($domain, $debug = false) { $original = $domain = strtolower($domain); if (filter_var($domain, FILTER_VALIDATE_IP)) { return $domain; } $debug ? print('» Parsing: ' . $original) : false; $arr = array_slice(array_filter(explode('.', $domain, 4), function ($value) { return $value !== 'www'; }), 0); //rebuild array indexes if (count($arr) > 2) { $count = count($arr); $_sub = explode('.', $count === 4 ? $arr[3] : $arr[2]); $debug ? print(" (parts count: {$count})") : false; if (count($_sub) === 2) { // two level TLD $removed = array_shift($arr); if ($count === 4) { // got a subdomain acting as a domain $removed = array_shift($arr); } $debug ? print("
\n" . '[*] Two level TLD: ' . join('.', $_sub) . ' ') : false; } elseif (count($_sub) === 1) { // one level TLD $removed = array_shift($arr); //remove the subdomain if (strlen($_sub[0]) === 2 && $count === 3) { // TLD domain must be 2 letters array_unshift($arr, $removed); } else { // non country TLD according to IANA $tlds = array( 'aero', 'arpa', 'asia', 'biz', 'cat', 'com', 'coop', 'edu', 'gov', 'info', 'jobs', 'mil', 'mobi', 'museum', 'name', 'net', 'org', 'post', 'pro', 'tel', 'travel', 'xxx', ); if (count($arr) > 2 && in_array($_sub[0], $tlds) !== false) { //special TLD don't have a country array_shift($arr); } } $debug ? print("
\n" . '[*] One level TLD: ' . join('.', $_sub) . ' ') : false; } else { // more than 3 levels, something is wrong for ($i = count($_sub); $i > 1; --$i) { $removed = array_shift($arr); } $debug ? print("
\n" . '[*] Three level TLD: ' . join('.', $_sub) . ' ') : false; } } elseif (count($arr) === 2) { $arr0 = array_shift($arr); if ( strpos(join('.', $arr), '.') === false && in_array($arr[0], array('localhost', 'test', 'invalid')) === false ) { // not a reserved domain $debug ? print("
\n" . 'Seems invalid domain: ' . join('.', $arr) . ' re-adding: ' . $arr0 . ' ') : false; // seems invalid domain, restore it array_unshift($arr, $arr0); } } $debug ? print("
\n" . '« Done parsing: ' . $original . ' as ' . join('.', $arr) . "
\n") : false; return join('.', $arr); } function getQsParams() { $a = array(); foreach (explode("&", $_SERVER["QUERY_STRING"]) as $q) { $p = explode('=', $q, 2); $a[$p[0]] = isset($p[1]) ? $p[1] : ''; } return $a; } } // G Namespace // Global namespace namespace { function class_autoloader($class) { $array = ['class' => $class, 'exists' => class_exists($class)]; $explode = explode('\\', $class); $last_key = key(array_slice($explode, -1, 1, true)); $file = ($explode[0] == 'G' ? G_PATH_CLASSES : G_APP_PATH_CLASSES) . 'class.' . strtolower($explode[$last_key]) . '.php'; if (file_exists($file)) { require_once $file; } else { //trigger_error("Can't autoload ".$class.' class. Class should be at '. $file, E_USER_ERROR); // Don't trigger an error so it allows multiple autoloaders to be called } } spl_autoload_register('class_autoloader'); /* * A Compatibility library with PHP 5.5's simplified password hashing API. * * @author Anthony Ferrara * @license http://www.opensource.org/licenses/mit-license.html MIT License * @copyright 2012 The Authors */ if (!defined('PASSWORD_DEFAULT')) { define('PASSWORD_BCRYPT', 1); define('PASSWORD_DEFAULT', PASSWORD_BCRYPT); /** * Hash the password using the specified algorithm. * * @param string $password The password to hash * @param int $algo The algorithm to use (Defined by PASSWORD_* constants) * @param array $options The options for the algorithm to use * * @return string|false the hashed password, or false on error */ function password_hash($password, $algo, array $options = array()) { if (!function_exists('crypt')) { trigger_error('Crypt must be loaded for password_hash to function', E_USER_WARNING); return null; } if (!is_string($password)) { trigger_error('password_hash(): Password must be a string', E_USER_WARNING); return null; } if (!is_int($algo)) { trigger_error('password_hash() expects parameter 2 to be long, ' . gettype($algo) . ' given', E_USER_WARNING); return null; } switch ($algo) { case PASSWORD_BCRYPT: // Note that this is a C constant, but not exposed to PHP, so we don't define it here. $cost = 10; if (isset($options['cost'])) { $cost = $options['cost']; if ($cost < 4 || $cost > 31) { trigger_error(sprintf('password_hash(): Invalid bcrypt cost parameter specified: %d', $cost), E_USER_WARNING); return null; } } // The length of salt to generate $raw_salt_len = 16; // The length required in the final serialization $required_salt_len = 22; $hash_format = sprintf('$2y$%02d$', $cost); break; default: trigger_error(sprintf('password_hash(): Unknown password hashing algorithm: %s', $algo), E_USER_WARNING); return null; } if (isset($options['salt'])) { switch (gettype($options['salt'])) { case 'NULL': case 'boolean': case 'integer': case 'double': case 'string': $salt = (string) $options['salt']; break; case 'object': if (method_exists($options['salt'], '__tostring')) { $salt = (string) $options['salt']; break; } // no break case 'array': case 'resource': default: trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING); return null; } if (strlen($salt) < $required_salt_len) { trigger_error(sprintf('password_hash(): Provided salt is too short: %d expecting %d', strlen($salt), $required_salt_len), E_USER_WARNING); return null; } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) { $salt = str_replace('+', '.', base64_encode($salt)); } } else { $buffer = ''; $buffer_valid = false; if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) { $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM); if ($buffer) { $buffer_valid = true; } } if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) { $buffer = openssl_random_pseudo_bytes($raw_salt_len); if ($buffer) { $buffer_valid = true; } } if (!$buffer_valid && is_readable('/dev/urandom')) { $f = fopen('/dev/urandom', 'r'); $read = strlen($buffer); while ($read < $raw_salt_len) { $buffer .= fread($f, $raw_salt_len - $read); $read = strlen($buffer); } fclose($f); if ($read >= $raw_salt_len) { $buffer_valid = true; } } if (!$buffer_valid || strlen($buffer) < $raw_salt_len) { $bl = strlen($buffer); for ($i = 0; $i < $raw_salt_len; ++$i) { if ($i < $bl) { $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255)); } else { $buffer .= chr(mt_rand(0, 255)); } } } $salt = str_replace('+', '.', base64_encode($buffer)); } $salt = substr($salt, 0, $required_salt_len); $hash = $hash_format . $salt; $ret = crypt($password, $hash); if (!is_string($ret) || strlen($ret) <= 13) { return false; } return $ret; } /** * Get information about the password hash. Returns an array of the information * that was used to generate the password hash. * * array( * 'algo' => 1, * 'algoName' => 'bcrypt', * 'options' => array( * 'cost' => 10, * ), * ) * * @param string $hash The password hash to extract info from * * @return array the array of information about the hash */ function password_get_info($hash) { $return = array( 'algo' => 0, 'algoName' => 'unknown', 'options' => array(), ); if (substr($hash, 0, 4) == '$2y$' && strlen($hash) == 60) { $return['algo'] = PASSWORD_BCRYPT; $return['algoName'] = 'bcrypt'; list($cost) = sscanf($hash, '$2y$%d$'); $return['options']['cost'] = $cost; } return $return; } /** * Determine if the password hash needs to be rehashed according to the options provided. * * If the answer is true, after validating the password using password_verify, rehash it. * * @param string $hash The hash to test * @param int $algo The algorithm used for new password hashes * @param array $options The options array passed to password_hash * * @return bool true if the password needs to be rehashed */ function password_needs_rehash($hash, $algo, array $options = array()) { $info = password_get_info($hash); if ($info['algo'] != $algo) { return true; } switch ($algo) { case PASSWORD_BCRYPT: $cost = isset($options['cost']) ? $options['cost'] : 10; if ($cost != $info['options']['cost']) { return true; } break; } return false; } /** * Verify a password against a hash using a timing attack resistant approach. * * @param string $password The password to verify * @param string $hash The hash to verify against * * @return bool If the password matches the hash */ function password_verify($password, $hash) { if (!function_exists('crypt')) { trigger_error('Crypt must be loaded for password_verify to function', E_USER_WARNING); return false; } $ret = crypt($password, $hash); if (!is_string($ret) || strlen($ret) != strlen($hash) || strlen($ret) <= 13) { return false; } $status = 0; for ($i = 0; $i < strlen($ret); ++$i) { $status |= (ord($ret[$i]) ^ ord($hash[$i])); } return $status === 0; } } }