diff --git a/var/Typecho/Common.php b/var/Typecho/Common.php index bd454f57..575c59ee 100644 --- a/var/Typecho/Common.php +++ b/var/Typecho/Common.php @@ -9,341 +9,295 @@ * @version $Id$ */ -define('__TYPECHO_MB_SUPPORTED__', function_exists('mb_get_info') && function_exists('mb_regex_encoding')); - -/** - * I18n function - * - * @param string $string 需要翻译的文字 - * @param mixed ...$args 参数 - * - * @return string - */ -function _t(string $string, ...$args): string -{ - if (empty($args)) { - return Typecho_I18n::translate($string); - } else { - return vsprintf(Typecho_I18n::translate($string), $args); - } -} - -/** - * I18n function, translate and echo - * - * @param string $string 需要翻译的文字 - * @param mixed ...$args 参数 - */ -function _e(string $string, ...$args) -{ - array_unshift($args, $string); - echo call_user_func_array('_t', $args); -} - -/** - * 针对复数形式的翻译函数 - * - * @param string $single 单数形式的翻译 - * @param string $plural 复数形式的翻译 - * @param integer $number 数字 - * - * @return string - */ -function _n(string $single, string $plural, int $number): string -{ - return str_replace('%d', $number, Typecho_I18n::ngettext($single, $plural, $number)); -} - -/** - * Typecho公用方法 - * - * @category typecho - * @package Common - * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) - * @license GNU General Public License 2.0 - */ -class Typecho_Common -{ - /** 程序版本 */ - const VERSION = '1.2.0'; +namespace { + define('__TYPECHO_MB_SUPPORTED__', function_exists('mb_get_info') && function_exists('mb_regex_encoding')); /** - * 默认编码 + * I18n function * - * @access public - * @var string - */ - public static $charset = 'UTF-8'; - - /** - * 异常处理类 - * - * @access public - * @var string - */ - public static $exceptionHandle; - - /** - * 允许的属性 - * - * @access private - * @var array - */ - private static $_allowableAttributes = []; - - /** - * 将字符串变成大写的回调函数 - * - * @param array $matches - * - * @access public - * @return string - */ - public static function __strToUpper($matches) - { - return strtoupper($matches[0]); - } - - /** - * 将url中的非法xss去掉时的数组回调过滤函数 - * - * @access private - * - * @param string $string 需要过滤的字符串 + * @param string $string 需要翻译的文字 + * @param mixed ...$args 参数 * * @return string */ - public static function __removeUrlXss($string) + function _t(string $string, ...$args): string { - $string = str_replace(['%0d', '%0a'], '', strip_tags($string)); - return preg_replace([ - "/\(\s*(\"|')/i", //函数开头 - "/(\"|')\s*\)/i", //函数结尾 - ], '', $string); + if (empty($args)) { + return Typecho_I18n::translate($string); + } else { + return vsprintf(Typecho_I18n::translate($string), $args); + } } /** - * 检查是否为安全路径 + * I18n function, translate and echo * - * @access public - * - * @param string $path 检查是否为安全路径 - * - * @return boolean + * @param string $string 需要翻译的文字 + * @param mixed ...$args 参数 */ - public static function __safePath($path) + function _e(string $string, ...$args) { - $safePath = rtrim(__TYPECHO_ROOT_DIR__, '/'); - return 0 === strpos($path, $safePath); + array_unshift($args, $string); + echo call_user_func_array('_t', $args); } /** - * __filterAttrs + * 针对复数形式的翻译函数 * - * @param mixed $matches + * @param string $single 单数形式的翻译 + * @param string $plural 复数形式的翻译 + * @param integer $number 数字 * - * @static - * @access public - * @return bool + * @return string */ - public static function __filterAttrs($matches) + function _n(string $single, string $plural, int $number): string { - if (!isset($matches[2])) { - return $matches[0]; - } + return str_replace('%d', $number, Typecho_I18n::ngettext($single, $plural, $number)); + } - $str = trim($matches[2]); + // autoload class + spl_autoload_register(function ($className) { + $path = str_replace(['_', '\\'], '/', $className) . '.php'; + $defaultFile = __TYPECHO_ROOT_DIR__ . '/var/' . $path; - if (empty($str)) { - return $matches[0]; - } + if (file_exists($defaultFile)) { + include_once $defaultFile; + } else { + $pluginFile = __TYPECHO_ROOT_DIR__ . __TYPECHO_PLUGIN_DIR__ . '/' . $path; - $attrs = self::__parseAttrs($str); - $parsedAttrs = []; - $tag = strtolower($matches[1]); - - foreach ($attrs as $key => $val) { - if (in_array($key, self::$_allowableAttributes[$tag])) { - $parsedAttrs[] = " {$key}" . (empty($val) ? '' : "={$val}"); + if (file_exists($pluginFile)) { + include_once $pluginFile; + } else { + return; } } - return '<' . $tag . implode('', $parsedAttrs) . '>'; - } + // hook old class loader + if ( + strpos($className, '_') !== false + && !class_exists($className, false) + && !interface_exists($className, false) + && !trait_exists($className, false) + ) { + $aliasClass = '\\' . str_replace('_', '\\', $className); + class_alias($aliasClass, $className); + } + }); +} + +namespace Typecho { /** - * 解析属性 + * Typecho公用方法 * - * @access public - * - * @param string $attrs 属性字符串 - * - * @return array + * @category typecho + * @package Common + * @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) + * @license GNU General Public License 2.0 */ - public static function __parseAttrs($attrs) + class Common { - $attrs = trim($attrs); - $len = strlen($attrs); - $pos = - 1; - $result = []; - $quote = ''; - $key = ''; - $value = ''; + /** 程序版本 */ + public const VERSION = '1.2.0'; - for ($i = 0; $i < $len; $i ++) { - if ('=' != $attrs[$i] && !ctype_space($attrs[$i]) && - 1 == $pos) { - $key .= $attrs[$i]; + /** + * 默认编码 + * + * @access public + * @var string + */ + public static $charset = 'UTF-8'; - /** 最后一个 */ - if ($i == $len - 1) { + /** + * 异常处理类 + * + * @access public + * @var string + */ + public static $exceptionHandle; + + /** + * 将url中的非法xss去掉时的数组回调过滤函数 + * + * @access private + * + * @param string $string 需要过滤的字符串 + * + * @return string + */ + public static function __removeUrlXss($string) + { + $string = str_replace(['%0d', '%0a'], '', strip_tags($string)); + return preg_replace([ + "/\(\s*(\"|')/i", //函数开头 + "/(\"|')\s*\)/i", //函数结尾 + ], '', $string); + } + + /** + * 检查是否为安全路径 + * + * @access public + * + * @param string $path 检查是否为安全路径 + * + * @return boolean + */ + public static function __safePath($path) + { + $safePath = rtrim(__TYPECHO_ROOT_DIR__, '/'); + return 0 === strpos($path, $safePath); + } + + /** + * 解析属性 + * + * @access public + * + * @param string $attrs 属性字符串 + * + * @return array + */ + public static function __parseAttrs($attrs) + { + $attrs = trim($attrs); + $len = strlen($attrs); + $pos = -1; + $result = []; + $quote = ''; + $key = ''; + $value = ''; + + for ($i = 0; $i < $len; $i++) { + if ('=' != $attrs[$i] && !ctype_space($attrs[$i]) && -1 == $pos) { + $key .= $attrs[$i]; + + /** 最后一个 */ + if ($i == $len - 1) { + if ('' != ($key = trim($key))) { + $result[$key] = ''; + $key = ''; + $value = ''; + } + } + + } elseif (ctype_space($attrs[$i]) && -1 == $pos) { + $pos = -2; + } elseif ('=' == $attrs[$i] && 0 > $pos) { + $pos = 0; + } elseif (('"' == $attrs[$i] || "'" == $attrs[$i]) && 0 == $pos) { + $quote = $attrs[$i]; + $value .= $attrs[$i]; + $pos = 1; + } elseif ($quote != $attrs[$i] && 1 == $pos) { + $value .= $attrs[$i]; + } elseif ($quote == $attrs[$i] && 1 == $pos) { + $pos = -1; + $value .= $attrs[$i]; + $result[trim($key)] = $value; + $key = ''; + $value = ''; + } elseif ('=' != $attrs[$i] && !ctype_space($attrs[$i]) && -2 == $pos) { if ('' != ($key = trim($key))) { $result[$key] = ''; - $key = ''; - $value = ''; + } + + $key = ''; + $value = ''; + $pos = -1; + $key .= $attrs[$i]; + } + } + + return $result; + } + + /** + * 将路径转化为链接 + * + * @access public + * + * @param string $path 路径 + * @param string $prefix 前缀 + * + * @return string + */ + public static function url($path, $prefix) + { + $path = (0 === strpos($path, './')) ? substr($path, 2) : $path; + return rtrim( + rtrim($prefix, '/') . '/' + . str_replace('//', '/', ltrim($path, '/')), + '/' + ); + } + + /** + * 程序初始化方法 + * + * @access public + * @return void + */ + public static function init() + { + /** 设置异常截获函数 */ + set_exception_handler(function (\Throwable $exception) { + if (defined('__TYPECHO_DEBUG__') && __TYPECHO_DEBUG__) { + echo '
';
+                    echo '

' . htmlspecialchars($exception->getMessage()) . '

'; + echo htmlspecialchars($exception->__toString()); + echo '
'; + } else { + @ob_end_clean(); + if (404 == $exception->getCode() && !empty(self::$exceptionHandle)) { + $handleClass = self::$exceptionHandle; + new $handleClass($exception); + } else { + self::error($exception); } } - } elseif (ctype_space($attrs[$i]) && - 1 == $pos) { - $pos = - 2; - } elseif ('=' == $attrs[$i] && 0 > $pos) { - $pos = 0; - } elseif (('"' == $attrs[$i] || "'" == $attrs[$i]) && 0 == $pos) { - $quote = $attrs[$i]; - $value .= $attrs[$i]; - $pos = 1; - } elseif ($quote != $attrs[$i] && 1 == $pos) { - $value .= $attrs[$i]; - } elseif ($quote == $attrs[$i] && 1 == $pos) { - $pos = - 1; - $value .= $attrs[$i]; - $result[trim($key)] = $value; - $key = ''; - $value = ''; - } elseif ('=' != $attrs[$i] && !ctype_space($attrs[$i]) && - 2 == $pos) { - if ('' != ($key = trim($key))) { - $result[$key] = ''; + exit(1); + }); + } + + /** + * 输出错误页面 + * + * @param \Throwable $exception 错误信息 + */ + public static function error(\Throwable $exception) + { + $code = $exception->getCode() ?: 500; + $message = $exception->getMessage(); + $charset = self::$charset; + + if ($exception instanceof \Typecho_Db_Exception) { + $code = 500; + + //覆盖原始错误信息 + $message = 'Database Server Error'; + + if ($exception instanceof \Typecho_Db_Adapter_Exception) { + $code = 503; + $message = 'Error establishing a database connection'; + } elseif ($exception instanceof \Typecho_Db_Query_Exception) { + $message = 'Database Query Error'; } - - $key = ''; - $value = ''; - $pos = - 1; - $key .= $attrs[$i]; } - } - return $result; - } - - /** - * 自动载入类 - * - * @param $className - */ - public static function __autoLoad($className) - { - $classFile = str_replace(['\\', '_'], '/', $className) . '.php'; - $systemPath = self::url($classFile, __TYPECHO_ROOT_DIR__ . '/var'); - - if (file_exists($systemPath)) { - @include_once $systemPath; - } else { - $pluginPath = self::url($classFile, __TYPECHO_ROOT_DIR__ . __TYPECHO_PLUGIN_DIR__); - - if (file_exists($pluginPath)) { - @include_once $pluginPath; + /** 设置http code */ + if (is_numeric($code) && $code > 200) { + \Typecho_Response::setStatus($code); } - } - } - /** - * 将路径转化为链接 - * - * @access public - * - * @param string $path 路径 - * @param string $prefix 前缀 - * - * @return string - */ - public static function url($path, $prefix) - { - $path = (0 === strpos($path, './')) ? substr($path, 2) : $path; - return rtrim( - rtrim($prefix, '/') . '/' - . str_replace('//', '/', ltrim($path, '/')), - '/' - ); - } + $message = nl2br($message); - /** - * 程序初始化方法 - * - * @access public - * @return void - */ - public static function init() - { - /** 设置自动载入函数 */ - spl_autoload_register(['Typecho_Common', '__autoLoad']); - - /** 设置异常截获函数 */ - set_exception_handler(function (Throwable $exception) { - if (defined('__TYPECHO_DEBUG__') && __TYPECHO_DEBUG__) { - echo '
';
-                echo '

' . htmlspecialchars($exception->getMessage()) . '

'; - echo htmlspecialchars($exception->__toString()); - echo '
'; + if (defined('__TYPECHO_EXCEPTION_FILE__')) { + require_once __TYPECHO_EXCEPTION_FILE__; } else { - @ob_end_clean(); - if (404 == $exception->getCode() && !empty(self::$exceptionHandle)) { - $handleClass = self::$exceptionHandle; - new $handleClass($exception); - } else { - self::error($exception); - } - } - - exit(1); - }); - } - - /** - * 输出错误页面 - * - * @param Throwable $exception 错误信息 - */ - public static function error(Throwable $exception) - { - $code = $exception->getCode() ?: 500; - $message = $exception->getMessage(); - $charset = self::$charset; - - if ($exception instanceof Typecho_Db_Exception) { - $code = 500; - - //覆盖原始错误信息 - $message = 'Database Server Error'; - - if ($exception instanceof Typecho_Db_Adapter_Exception) { - $code = 503; - $message = 'Error establishing a database connection'; - } elseif ($exception instanceof Typecho_Db_Query_Exception) { - $message = 'Database Query Error'; - } - } - - /** 设置http code */ - if (is_numeric($code) && $code > 200) { - Typecho_Response::setStatus($code); - } - - $message = nl2br($message); - - if (defined('__TYPECHO_EXCEPTION_FILE__')) { - require_once __TYPECHO_EXCEPTION_FILE__; - } else { - echo - << @@ -386,651 +340,653 @@ class Typecho_Common EOF; + } + + exit(1); } - exit(1); - } + /** + * 判断类是否能被加载 + * 此函数会遍历所有的include目录, 所以会有一定的性能消耗, 但是不会很大 + * 可是我们依然建议你在必须检测一个类能否被加载时使用它, 它通常表现为以下两种情况 + * 1. 当需要被加载的类不存在时, 系统不会停止运行 (如果你不判断, 系统会因抛出严重错误而停止) + * 2. 你需要知道哪些类无法被加载, 以提示使用者 + * 除了以上情况, 你无需关注那些类无法被加载, 因为当它们不存在时系统会自动停止并报错 + * + * @access public + * + * @param string $className 类名 + * @param string $path 指定的路径名称 + * + * @return boolean + */ + public static function isAvailableClass($className, $path = null) + { + /** 获取所有include目录 */ + //增加安全目录检测 fix issue 106 + $dirs = array_map('realpath', array_filter(explode(PATH_SEPARATOR, get_include_path()), + ['Typecho_Common', '__safePath'])); - /** - * 判断类是否能被加载 - * 此函数会遍历所有的include目录, 所以会有一定的性能消耗, 但是不会很大 - * 可是我们依然建议你在必须检测一个类能否被加载时使用它, 它通常表现为以下两种情况 - * 1. 当需要被加载的类不存在时, 系统不会停止运行 (如果你不判断, 系统会因抛出严重错误而停止) - * 2. 你需要知道哪些类无法被加载, 以提示使用者 - * 除了以上情况, 你无需关注那些类无法被加载, 因为当它们不存在时系统会自动停止并报错 - * - * @access public - * - * @param string $className 类名 - * @param string $path 指定的路径名称 - * - * @return boolean - */ - public static function isAvailableClass($className, $path = null) - { - /** 获取所有include目录 */ - //增加安全目录检测 fix issue 106 - $dirs = array_map('realpath', array_filter(explode(PATH_SEPARATOR, get_include_path()), - ['Typecho_Common', '__safePath'])); + $file = str_replace('_', '/', $className) . '.php'; - $file = str_replace('_', '/', $className) . '.php'; + if (!empty($path)) { + $path = realpath($path); + if (in_array($path, $dirs)) { + $dirs = [$path]; + } else { + return false; + } + } - if (!empty($path)) { - $path = realpath($path); - if (in_array($path, $dirs)) { - $dirs = [$path]; + foreach ($dirs as $dir) { + if (!empty($dir)) { + if (file_exists($dir . '/' . $file)) { + return true; + } + } + } + + return false; + } + + /** + * @param array $value + * @param $key + * + * @return array + * @deprecated use array_column instead + * + */ + public static function arrayFlatten(array $value, $key): array + { + return array_column($value, $key); + } + + /** + * 根据count数目来输出字符 + * + * echo splitByCount(20, 10, 20, 30, 40, 50); + * + * + * @access public + * + * @param int $count + * + * @return string + */ + public static function splitByCount($count) + { + $sizes = func_get_args(); + array_shift($sizes); + + foreach ($sizes as $size) { + if ($count < $size) { + return $size; + } + } + + return 0; + } + + /** + * 自闭合html修复函数 + * 使用方法: + * + * $input = '这是一段被截断的html文本 + * + * @access public + * + * @param string $string 需要修复处理的字符串 + * + * @return string + */ + public static function fixHtml($string) + { + //关闭自闭合标签 + $startPos = strrpos($string, "<"); + + if (false == $startPos) { + return $string; + } + + $trimString = substr($string, $startPos); + + if (false === strpos($trimString, ">")) { + $string = substr($string, 0, $startPos); + } + + //非自闭合html标签列表 + preg_match_all("/<([_0-9a-zA-Z-\:]+)\s*([^>]*)>/is", $string, $startTags); + preg_match_all("/<\/([_0-9a-zA-Z-\:]+)>/is", $string, $closeTags); + + if (!empty($startTags[1]) && is_array($startTags[1])) { + krsort($startTags[1]); + $closeTagsIsArray = is_array($closeTags[1]); + foreach ($startTags[1] as $key => $tag) { + $attrLength = strlen($startTags[2][$key]); + if ($attrLength > 0 && "/" == trim($startTags[2][$key][$attrLength - 1])) { + continue; + } + + // 白名单 + if (preg_match("/^(area|base|br|col|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i", $tag)) { + continue; + } + + if (!empty($closeTags[1]) && $closeTagsIsArray) { + if (false !== ($index = array_search($tag, $closeTags[1]))) { + unset($closeTags[1][$index]); + continue; + } + } + $string .= ""; + } + } + + return preg_replace("/\\s*\<\/p\>/is", '

', $string); + } + + /** + * 去掉字符串中的html标签 + * 使用方法: + * + * $input = '
hello'; + * $output = Typecho_Common::stripTags($input, ); + * echo $output; + * //display: 'hello' + * + * + * @access public + * + * @param string $html 需要处理的字符串 + * @param string|null $allowableTags 需要忽略的html标签 + * + * @return string + */ + public static function stripTags(string $html, ?string $allowableTags = null): string + { + $normalizeTags = ''; + $allowableAttributes = []; + + if (!empty($allowableTags) && preg_match_all("/\<([_a-z0-9-]+)([^>]*)\>/is", $allowableTags, $tags)) { + $normalizeTags = '<' . implode('><', array_map('strtolower', $tags[1])) . '>'; + $attributes = array_map('trim', $tags[2]); + foreach ($attributes as $key => $val) { + $allowableAttributes[strtolower($tags[1][$key])] = + array_map('strtolower', array_keys(self::__parseAttrs($val))); + } + } + + $html = strip_tags($html, $normalizeTags); + return preg_replace_callback( + "/<([_a-z0-9-]+)(\s+[^>]+)?>/is", + function ($matches) use ($allowableAttributes) { + if (!isset($matches[2])) { + return $matches[0]; + } + + $str = trim($matches[2]); + + if (empty($str)) { + return $matches[0]; + } + + $attrs = self::__parseAttrs($str); + $parsedAttrs = []; + $tag = strtolower($matches[1]); + + foreach ($attrs as $key => $val) { + if (in_array($key, $allowableAttributes[$tag])) { + $parsedAttrs[] = " {$key}" . (empty($val) ? '' : "={$val}"); + } + } + + return '<' . $tag . implode('', $parsedAttrs) . '>'; + }, + $html + ); + } + + /** + * 过滤用于搜索的字符串 + * + * @access public + * + * @param string $query 搜索字符串 + * + * @return string + */ + public static function filterSearchQuery($query) + { + return str_replace('-', ' ', self::slugName($query)); + } + + /** + * 生成缩略名 + * + * @access public + * + * @param string $str 需要生成缩略名的字符串 + * @param string $default 默认的缩略名 + * @param integer $maxLength 缩略名最大长度 + * + * @return string + */ + public static function slugName($str, $default = null, $maxLength = 128) + { + $str = trim($str); + + if (!strlen($str)) { + return $default; + } + + if (__TYPECHO_MB_SUPPORTED__) { + mb_regex_encoding(self::$charset); + mb_ereg_search_init($str, "[\w" . preg_quote('_-') . "]+"); + $result = mb_ereg_search(); + $return = ''; + + if ($result) { + $regs = mb_ereg_search_getregs(); + $pos = 0; + do { + $return .= ($pos > 0 ? '-' : '') . $regs[0]; + $pos++; + } while ($regs = mb_ereg_search_regs()); + } + + $str = $return; + } elseif ('UTF-8' == strtoupper(self::$charset)) { + if (preg_match_all("/[\w" . preg_quote('_-') . "]+/u", $str, $matches)) { + $str = implode('-', $matches[0]); + } } else { - return false; + $str = str_replace(["'", ":", "\\", "/", '"'], "", $str); + $str = str_replace( + ["+", ",", ' ', ',', ' ', ".", "?", "=", "&", "!", "<", ">", "(", ")", "[", "]", "{", "}"], + "-", + $str + ); + } + + $str = trim($str, '-_'); + $str = !strlen($str) ? $default : $str; + return substr($str, 0, $maxLength); + } + + /** + * 将url中的非法字符串 + * + * @param string $url 需要过滤的url + * + * @return string + */ + public static function safeUrl($url) + { + //~ 针对location的xss过滤, 因为其特殊性无法使用removeXSS函数 + //~ fix issue 66 + $params = parse_url(str_replace(["\r", "\n", "\t", ' '], '', $url)); + + /** 禁止非法的协议跳转 */ + if (isset($params['scheme'])) { + if (!in_array($params['scheme'], ['http', 'https'])) { + return '/'; + } + } + + /** 过滤解析串 */ + $params = array_map(['Typecho_Common', '__removeUrlXss'], $params); + return self::buildUrl($params); + } + + /** + * 根据parse_url的结果重新组合url + * + * @access public + * + * @param array $params 解析后的参数 + * + * @return string + */ + public static function buildUrl($params) + { + return (isset($params['scheme']) ? $params['scheme'] . '://' : null) + . (isset($params['user']) ? $params['user'] + . (isset($params['pass']) ? ':' . $params['pass'] : null) . '@' : null) + . ($params['host'] ?? null) + . (isset($params['port']) ? ':' . $params['port'] : null) + . ($params['path'] ?? null) + . (isset($params['query']) ? '?' . $params['query'] : null) + . (isset($params['fragment']) ? '#' . $params['fragment'] : null); + } + + /** + * 处理XSS跨站攻击的过滤函数 + * + * @param string $val 需要处理的字符串 + * + * @return string + * @author kallahar@kallahar.com + * @link http://kallahar.com/smallprojects/php_xss_filter_function.php + * @access public + */ + public static function removeXSS($val) + { + // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed + // this prevents some character re-spacing such as + // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs + $val = preg_replace('/([\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19])/', '', $val); + + // straight replacements, the user should never need these since they're normal characters + // this prevents like + $search = 'abcdefghijklmnopqrstuvwxyz'; + $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $search .= '1234567890!@#$%^&*()'; + $search .= '~`";:?+/={}[]-_|\'\\'; + + for ($i = 0; $i < strlen($search); $i++) { + // ;? matches the ;, which is optional + // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars + + // @ @ search for the hex values + $val = preg_replace('/(&#[xX]0{0,8}' . dechex(ord($search[$i])) . ';?)/i', $search[$i], $val); // with a ; + // @ @ 0{0,7} matches '0' zero to seven times + $val = preg_replace('/(�{0,8}' . ord($search[$i]) . ';?)/', $search[$i], $val); // with a ; + } + + // now the only remaining whitespace attacks are \t, \n, and \r + $ra1 = ['javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base']; + $ra2 = [ + 'onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload' + ]; + $ra = array_merge($ra1, $ra2); + + $found = true; // keep replacing as long as the previous round replaced something + while ($found == true) { + $val_before = $val; + for ($i = 0; $i < sizeof($ra); $i++) { + $pattern = '/'; + for ($j = 0; $j < strlen($ra[$i]); $j++) { + if ($j > 0) { + $pattern .= '('; + $pattern .= '(&#[xX]0{0,8}([9ab]);)'; + $pattern .= '|'; + $pattern .= '|(�{0,8}([9|10|13]);)'; + $pattern .= ')*'; + } + $pattern .= $ra[$i][$j]; + } + $pattern .= '/i'; + $replacement = substr($ra[$i], 0, 2) . '' . substr($ra[$i], 2); // add in <> to nerf the tag + $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags + + if ($val_before == $val) { + // no replacements were made, so exit the loop + $found = false; + } + } + } + + return $val; + } + + /** + * 宽字符串截字函数 + * + * @access public + * + * @param string $str 需要截取的字符串 + * @param integer $start 开始截取的位置 + * @param integer $length 需要截取的长度 + * @param string $trim 截取后的截断标示符 + * + * @return string + */ + public static function subStr($str, $start, $length, $trim = "...") + { + if (!strlen($str)) { + return ''; + } + + $iLength = self::strLen($str) - $start; + $tLength = $length < $iLength ? ($length - self::strLen($trim)) : $length; + + if (__TYPECHO_MB_SUPPORTED__) { + $str = mb_substr($str, $start, $tLength, self::$charset); + } else { + if ('UTF-8' == strtoupper(self::$charset)) { + if (preg_match_all("/./u", $str, $matches)) { + $str = implode('', array_slice($matches[0], $start, $tLength)); + } + } else { + $str = substr($str, $start, $tLength); + } + } + + return $length < $iLength ? ($str . $trim) : $str; + } + + /** + * 获取宽字符串长度函数 + * + * @access public + * + * @param string $str 需要获取长度的字符串 + * + * @return integer + */ + public static function strLen($str) + { + if (__TYPECHO_MB_SUPPORTED__) { + return mb_strlen($str, self::$charset); + } else { + return 'UTF-8' == strtoupper(self::$charset) + ? strlen(utf8_decode($str)) : strlen($str); } } - foreach ($dirs as $dir) { - if (!empty($dir)) { - if (file_exists($dir . '/' . $file)) { + /** + * 判断hash值是否相等 + * + * @access public + * + * @param string|null $from 源字符串 + * @param string|null $to 目标字符串 + * + * @return boolean + */ + public static function hashValidate(?string $from, ?string $to): bool + { + if ($from === null || $to === null) { + return false; + } + + if ('$T$' == substr($to, 0, 3)) { + $salt = substr($to, 3, 9); + return self::hash($from, $salt) === $to; + } else { + return md5($from) === $to; + } + } + + /** + * 对字符串进行hash加密 + * + * @access public + * + * @param string|null $string 需要hash的字符串 + * @param string|null $salt 扰码 + * + * @return string + */ + public static function hash(?string $string, ?string $salt = null): string + { + if ($string === null) { + return ''; + } + + /** 生成随机字符串 */ + $salt = empty($salt) ? self::randString(9) : $salt; + $length = strlen($string); + + if ($length == 0) { + return ''; + } + + $hash = ''; + $last = ord($string[$length - 1]); + $pos = 0; + + /** 判断扰码长度 */ + if (strlen($salt) != 9) { + /** 如果不是9直接返回 */ + return ''; + } + + while ($pos < $length) { + $asc = ord($string[$pos]); + $last = ($last * ord($salt[($last % $asc) % 9]) + $asc) % 95 + 32; + $hash .= chr($last); + $pos++; + } + + return '$T$' . $salt . md5($hash); + } + + /** + * 生成随机字符串 + * + * @access public + * + * @param integer $length 字符串长度 + * @param boolean $specialChars 是否有特殊字符 + * + * @return string + */ + public static function randString($length, $specialChars = false) + { + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + if ($specialChars) { + $chars .= '!@#$%^&*()'; + } + + $result = ''; + $max = strlen($chars) - 1; + for ($i = 0; $i < $length; $i++) { + $result .= $chars[rand(0, $max)]; + } + return $result; + } + + /** + * 创建一个会过期的Token + * + * @param $secret + * + * @return string + */ + public static function timeToken($secret) + { + return sha1($secret . '&' . time()); + } + + /** + * 在时间范围内验证token + * + * @param $token + * @param $secret + * @param int $timeout + * + * @return bool + */ + public static function timeTokenValidate($token, $secret, $timeout = 5) + { + $now = time(); + $from = $now - $timeout; + + for ($i = $now; $i >= $from; $i--) { + if (sha1($secret . '&' . $i) == $token) { return true; } } + + return false; } - return false; - } - - /** - * @param array $value - * @param $key - * @deprecated use array_column instead - * - * @return array - */ - public static function arrayFlatten(array $value, $key): array - { - return array_column($value, $key); - } - - /** - * 根据count数目来输出字符 - * - * echo splitByCount(20, 10, 20, 30, 40, 50); - * - * - * @access public - * - * @param int $count - * - * @return string - */ - public static function splitByCount($count) - { - $sizes = func_get_args(); - array_shift($sizes); - - foreach ($sizes as $size) { - if ($count < $size) { - return $size; - } - } - - return 0; - } - - /** - * 自闭合html修复函数 - * 使用方法: - * - * $input = '这是一段被截断的html文本 - * - * @access public - * - * @param string $string 需要修复处理的字符串 - * - * @return string - */ - public static function fixHtml($string) - { - //关闭自闭合标签 - $startPos = strrpos($string, "<"); - - if (false == $startPos) { - return $string; - } - - $trimString = substr($string, $startPos); - - if (false === strpos($trimString, ">")) { - $string = substr($string, 0, $startPos); - } - - //非自闭合html标签列表 - preg_match_all("/<([_0-9a-zA-Z-\:]+)\s*([^>]*)>/is", $string, $startTags); - preg_match_all("/<\/([_0-9a-zA-Z-\:]+)>/is", $string, $closeTags); - - if (!empty($startTags[1]) && is_array($startTags[1])) { - krsort($startTags[1]); - $closeTagsIsArray = is_array($closeTags[1]); - foreach ($startTags[1] as $key => $tag) { - $attrLength = strlen($startTags[2][$key]); - if ($attrLength > 0 && "/" == trim($startTags[2][$key][$attrLength - 1])) { - continue; - } - - // 白名单 - if (preg_match("/^(area|base|br|col|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/i", $tag)) { - continue; - } - - if (!empty($closeTags[1]) && $closeTagsIsArray) { - if (false !== ($index = array_search($tag, $closeTags[1]))) { - unset($closeTags[1][$index]); - continue; - } - } - $string .= ""; - } - } - - return preg_replace("/\\s*\<\/p\>/is", '

', $string); - } - - /** - * 去掉字符串中的html标签 - * 使用方法: - * - * $input = '
hello'; - * $output = Typecho_Common::stripTags($input, ); - * echo $output; - * //display: 'hello' - * - * - * @access public - * - * @param string $html 需要处理的字符串 - * @param string $allowableTags 需要忽略的html标签 - * - * @return string - */ - public static function stripTags($html, $allowableTags = null) - { - $normalizeTags = ''; - $allowableAttributes = []; - - if (!empty($allowableTags) && preg_match_all("/\<([_a-z0-9-]+)([^>]*)\>/is", $allowableTags, $tags)) { - $normalizeTags = '<' . implode('><', array_map('strtolower', $tags[1])) . '>'; - $attributes = array_map('trim', $tags[2]); - foreach ($attributes as $key => $val) { - $allowableAttributes[strtolower($tags[1][$key])] = - array_map('strtolower', array_keys(self::__parseAttrs($val))); - } - } - - self::$_allowableAttributes = $allowableAttributes; - $html = strip_tags($html, $normalizeTags); - $html = preg_replace_callback("/<([_a-z0-9-]+)(\s+[^>]+)?>/is", - ['Typecho_Common', '__filterAttrs'], $html); - - return $html; - } - - /** - * 过滤用于搜索的字符串 - * - * @access public - * - * @param string $query 搜索字符串 - * - * @return string - */ - public static function filterSearchQuery($query) - { - return str_replace('-', ' ', self::slugName($query)); - } - - /** - * 生成缩略名 - * - * @access public - * - * @param string $str 需要生成缩略名的字符串 - * @param string $default 默认的缩略名 - * @param integer $maxLength 缩略名最大长度 - * - * @return string - */ - public static function slugName($str, $default = null, $maxLength = 128) - { - $str = trim($str); - - if (!strlen($str)) { - return $default; - } - - if (__TYPECHO_MB_SUPPORTED__) { - mb_regex_encoding(self::$charset); - mb_ereg_search_init($str, "[\w" . preg_quote('_-') . "]+"); - $result = mb_ereg_search(); - $return = ''; - - if ($result) { - $regs = mb_ereg_search_getregs(); - $pos = 0; - do { - $return .= ($pos > 0 ? '-' : '') . $regs[0]; - $pos ++; - } while ($regs = mb_ereg_search_regs()); - } - - $str = $return; - } elseif ('UTF-8' == strtoupper(self::$charset)) { - if (preg_match_all("/[\w" . preg_quote('_-') . "]+/u", $str, $matches)) { - $str = implode('-', $matches[0]); - } - } else { - $str = str_replace(["'", ":", "\\", "/", '"'], "", $str); - $str = str_replace(["+", ",", ' ', ',', ' ', ".", "?", "=", "&", "!", "<", ">", "(", ")", "[", "]", "{", "}"], "-", $str); - } - - $str = trim($str, '-_'); - $str = !strlen($str) ? $default : $str; - return substr($str, 0, $maxLength); - } - - /** - * 将url中的非法字符串 - * - * @param string $url 需要过滤的url - * - * @return string - */ - public static function safeUrl($url) - { - //~ 针对location的xss过滤, 因为其特殊性无法使用removeXSS函数 - //~ fix issue 66 - $params = parse_url(str_replace(["\r", "\n", "\t", ' '], '', $url)); - - /** 禁止非法的协议跳转 */ - if (isset($params['scheme'])) { - if (!in_array($params['scheme'], ['http', 'https'])) { - return '/'; - } - } - - /** 过滤解析串 */ - $params = array_map(['Typecho_Common', '__removeUrlXss'], $params); - return self::buildUrl($params); - } - - /** - * 根据parse_url的结果重新组合url - * - * @access public - * - * @param array $params 解析后的参数 - * - * @return string - */ - public static function buildUrl($params) - { - return (isset($params['scheme']) ? $params['scheme'] . '://' : null) - . (isset($params['user']) ? $params['user'] . (isset($params['pass']) ? ':' . $params['pass'] : null) . '@' : null) - . (isset($params['host']) ? $params['host'] : null) - . (isset($params['port']) ? ':' . $params['port'] : null) - . (isset($params['path']) ? $params['path'] : null) - . (isset($params['query']) ? '?' . $params['query'] : null) - . (isset($params['fragment']) ? '#' . $params['fragment'] : null); - } - - /** - * 处理XSS跨站攻击的过滤函数 - * - * @param string $val 需要处理的字符串 - * - * @return string - * @author kallahar@kallahar.com - * @link http://kallahar.com/smallprojects/php_xss_filter_function.php - * @access public - */ - public static function removeXSS($val) - { - // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed - // this prevents some character re-spacing such as - // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs - $val = preg_replace('/([\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19])/', '', $val); - - // straight replacements, the user should never need these since they're normal characters - // this prevents like - $search = 'abcdefghijklmnopqrstuvwxyz'; - $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $search .= '1234567890!@#$%^&*()'; - $search .= '~`";:?+/={}[]-_|\'\\'; - - for ($i = 0; $i < strlen($search); $i ++) { - // ;? matches the ;, which is optional - // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars - - // @ @ search for the hex values - $val = preg_replace('/(&#[xX]0{0,8}' . dechex(ord($search[$i])) . ';?)/i', $search[$i], $val); // with a ; - // @ @ 0{0,7} matches '0' zero to seven times - $val = preg_replace('/(�{0,8}' . ord($search[$i]) . ';?)/', $search[$i], $val); // with a ; - } - - // now the only remaining whitespace attacks are \t, \n, and \r - $ra1 = ['javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base']; - $ra2 = [ - 'onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload' - ]; - $ra = array_merge($ra1, $ra2); - - $found = true; // keep replacing as long as the previous round replaced something - while ($found == true) { - $val_before = $val; - for ($i = 0; $i < sizeof($ra); $i ++) { - $pattern = '/'; - for ($j = 0; $j < strlen($ra[$i]); $j ++) { - if ($j > 0) { - $pattern .= '('; - $pattern .= '(&#[xX]0{0,8}([9ab]);)'; - $pattern .= '|'; - $pattern .= '|(�{0,8}([9|10|13]);)'; - $pattern .= ')*'; - } - $pattern .= $ra[$i][$j]; - } - $pattern .= '/i'; - $replacement = substr($ra[$i], 0, 2) . '' . substr($ra[$i], 2); // add in <> to nerf the tag - $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags - - if ($val_before == $val) { - // no replacements were made, so exit the loop - $found = false; - } - } - } - - return $val; - } - - /** - * 宽字符串截字函数 - * - * @access public - * - * @param string $str 需要截取的字符串 - * @param integer $start 开始截取的位置 - * @param integer $length 需要截取的长度 - * @param string $trim 截取后的截断标示符 - * - * @return string - */ - public static function subStr($str, $start, $length, $trim = "...") - { - if (!strlen($str)) { - return ''; - } - - $iLength = self::strLen($str) - $start; - $tLength = $length < $iLength ? ($length - self::strLen($trim)) : $length; - - if (__TYPECHO_MB_SUPPORTED__) { - $str = mb_substr($str, $start, $tLength, self::$charset); - } else { - if ('UTF-8' == strtoupper(self::$charset)) { - if (preg_match_all("/./u", $str, $matches)) { - $str = implode('', array_slice($matches[0], $start, $tLength)); - } + /** + * 获取gravatar头像地址 + * + * @param string $mail + * @param int $size + * @param string $rating + * @param string $default + * @param bool $isSecure + * + * @return string + */ + public static function gravatarUrl($mail, $size, $rating, $default, $isSecure = false) + { + if (defined('__TYPECHO_GRAVATAR_PREFIX__')) { + $url = __TYPECHO_GRAVATAR_PREFIX__; } else { - $str = substr($str, $start, $tLength); + $url = $isSecure ? 'https://secure.gravatar.com' : 'http://www.gravatar.com'; + $url .= '/avatar/'; } - } - return $length < $iLength ? ($str . $trim) : $str; - } - - /** - * 获取宽字符串长度函数 - * - * @access public - * - * @param string $str 需要获取长度的字符串 - * - * @return integer - */ - public static function strLen($str) - { - if (__TYPECHO_MB_SUPPORTED__) { - return mb_strlen($str, self::$charset); - } else { - return 'UTF-8' == strtoupper(self::$charset) - ? strlen(utf8_decode($str)) : strlen($str); - } - } - - /** - * 获取大写字符串 - * - * @param string $str - * - * @access public - * @return string - */ - public static function strToUpper($str) - { - if (__TYPECHO_MB_SUPPORTED__) { - return mb_strtoupper($str, self::$charset); - } else { - return 'UTF-8' == strtoupper(self::$charset) - ? preg_replace_callback("/[a-z]+/u", ['Typecho_Common', '__strToUpper'], $str) : strtoupper($str); - } - } - - /** - * 检查是否为合法的编码数据 - * - * @param string|array $str - * - * @return boolean - */ - public static function checkStrEncoding($str) - { - if (is_array($str)) { - return array_map(['Typecho_Common', 'checkStrEncoding'], $str); - } - - if (__TYPECHO_MB_SUPPORTED__) { - return mb_check_encoding($str, self::$charset); - } else { - // just support utf-8 - return preg_match('//u', $str); - } - } - - /** - * 判断hash值是否相等 - * - * @access public - * - * @param string $from 源字符串 - * @param string $to 目标字符串 - * - * @return boolean - */ - public static function hashValidate($from, $to) - { - if ('$T$' == substr($to, 0, 3)) { - $salt = substr($to, 3, 9); - return self::hash($from, $salt) === $to; - } else { - return md5($from) === $to; - } - } - - /** - * 对字符串进行hash加密 - * - * @access public - * - * @param string $string 需要hash的字符串 - * @param string $salt 扰码 - * - * @return string - */ - public static function hash($string, $salt = null) - { - /** 生成随机字符串 */ - $salt = empty($salt) ? self::randString(9) : $salt; - $length = strlen($string); - $hash = ''; - $last = ord($string[$length - 1]); - $pos = 0; - - /** 判断扰码长度 */ - if (strlen($salt) != 9) { - /** 如果不是9直接返回 */ - return; - } - - while ($pos < $length) { - $asc = ord($string[$pos]); - $last = ($last * ord($salt[($last % $asc) % 9]) + $asc) % 95 + 32; - $hash .= chr($last); - $pos ++; - } - - return '$T$' . $salt . md5($hash); - } - - /** - * 生成随机字符串 - * - * @access public - * - * @param integer $length 字符串长度 - * @param boolean $specialChars 是否有特殊字符 - * - * @return string - */ - public static function randString($length, $specialChars = false) - { - $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - if ($specialChars) { - $chars .= '!@#$%^&*()'; - } - - $result = ''; - $max = strlen($chars) - 1; - for ($i = 0; $i < $length; $i ++) { - $result .= $chars[rand(0, $max)]; - } - return $result; - } - - /** - * 创建一个会过期的Token - * - * @param $secret - * - * @return string - */ - public static function timeToken($secret) - { - return sha1($secret . '&' . time()); - } - - /** - * 在时间范围内验证token - * - * @param $token - * @param $secret - * @param int $timeout - * - * @return bool - */ - public static function timeTokenValidate($token, $secret, $timeout = 5) - { - $now = time(); - $from = $now - $timeout; - - for ($i = $now; $i >= $from; $i --) { - if (sha1($secret . '&' . $i) == $token) { - return true; + if (!empty($mail)) { + $url .= md5(strtolower(trim($mail))); } + + $url .= '?s=' . $size; + $url .= '&r=' . $rating; + $url .= '&d=' . $default; + + return $url; } - return false; - } + /** + * 给javascript赋值加入扰码设计 + * + * @param string $value + * + * @return string + */ + public static function shuffleScriptVar($value) + { + $length = strlen($value); + $max = 3; + $offset = 0; + $result = []; + $cut = []; - /** - * 获取gravatar头像地址 - * - * @param string $mail - * @param int $size - * @param string $rating - * @param string $default - * @param bool $isSecure - * - * @return string - */ - public static function gravatarUrl($mail, $size, $rating, $default, $isSecure = false) - { - if (defined('__TYPECHO_GRAVATAR_PREFIX__')) { - $url = __TYPECHO_GRAVATAR_PREFIX__; - } else { - $url = $isSecure ? 'https://secure.gravatar.com' : 'http://www.gravatar.com'; - $url .= '/avatar/'; - } + while ($length > 0) { + $len = rand(0, min($max, $length)); + $rand = "'" . self::randString(rand(1, $max)) . "'"; - if (!empty($mail)) { - $url .= md5(strtolower(trim($mail))); - } - - $url .= '?s=' . $size; - $url .= '&r=' . $rating; - $url .= '&d=' . $default; - - return $url; - } - - /** - * 给javascript赋值加入扰码设计 - * - * @param string $value - * - * @return string - */ - public static function shuffleScriptVar($value) - { - $length = strlen($value); - $max = 3; - $offset = 0; - $result = []; - $cut = []; - - while ($length > 0) { - $len = rand(0, min($max, $length)); - $rand = "'" . self::randString(rand(1, $max)) . "'"; - - if ($len > 0) { - $val = "'" . substr($value, $offset, $len) . "'"; - $result[] = rand(0, 1) ? "//{$rand}\n{$val}" : "{$val}//{$rand}\n"; - } else { - if (rand(0, 1)) { - $result[] = rand(0, 1) ? "''///*{$rand}*/{$rand}\n" : "/* {$rand}//{$rand} */''"; + if ($len > 0) { + $val = "'" . substr($value, $offset, $len) . "'"; + $result[] = rand(0, 1) ? "//{$rand}\n{$val}" : "{$val}//{$rand}\n"; } else { - $result[] = rand(0, 1) ? "//{$rand}\n{$rand}" : "{$rand}//{$rand}\n"; - $cut[] = [$offset, strlen($rand) - 2 + $offset]; + if (rand(0, 1)) { + $result[] = rand(0, 1) ? "''///*{$rand}*/{$rand}\n" : "/* {$rand}//{$rand} */''"; + } else { + $result[] = rand(0, 1) ? "//{$rand}\n{$rand}" : "{$rand}//{$rand}\n"; + $cut[] = [$offset, strlen($rand) - 2 + $offset]; + } } + + $offset += $len; + $length -= $len; } - $offset += $len; - $length -= $len; - } - - $name = '_' . self::randString(rand(3, 7)); - $cutName = '_' . self::randString(rand(3, 7)); - $var = implode('+', $result); - $cutVar = Json::encode($cut); - return "(function () { + $name = '_' . self::randString(rand(3, 7)); + $cutName = '_' . self::randString(rand(3, 7)); + $var = implode('+', $result); + $cutVar = Json::encode($cut); + return "(function () { var {$name} = {$var}, {$cutName} = {$cutVar}; for (var i = 0; i < {$cutName}.length; i ++) { @@ -1039,582 +995,586 @@ EOF; return {$name}; })();"; - } - - /** - * 过滤字段名 - * - * @access private - * - * @param mixed $result - * - * @return array - */ - public static function filterSQLite2ColumnName($result) - { - /** 如果结果为空,直接返回 */ - if (empty($result)) { - return $result; } - $tResult = []; - - /** 遍历数组 */ - foreach ($result as $key => $val) { - /** 按点分隔 */ - if (false !== ($pos = strpos($key, '.'))) { - $key = substr($key, $pos + 1); + /** + * 过滤字段名 + * + * @access private + * + * @param mixed $result + * + * @return array + */ + public static function filterSQLite2ColumnName($result) + { + /** 如果结果为空,直接返回 */ + if (empty($result)) { + return $result; } - $tResult[trim($key, '"')] = $val; + $tResult = []; + + /** 遍历数组 */ + foreach ($result as $key => $val) { + /** 按点分隔 */ + if (false !== ($pos = strpos($key, '.'))) { + $key = substr($key, $pos + 1); + } + + $tResult[trim($key, '"')] = $val; + } + + return $tResult; } - return $tResult; - } + /** + * 处理sqlite2的distinct count + * + * @param $sql + * + * @return string + */ + public static function filterSQLite2CountQuery($sql) + { + if (preg_match("/SELECT\s+COUNT\(DISTINCT\s+([^\)]+)\)\s+(AS\s+[^\s]+)?\s*FROM\s+(.+)/is", $sql, $matches)) { + return 'SELECT COUNT(' . $matches[1] . ') ' . $matches[2] . ' FROM SELECT DISTINCT ' + . $matches[1] . ' FROM ' . $matches[3]; + } - /** - * 处理sqlite2的distinct count - * - * @param $sql - * - * @return string - */ - public static function filterSQLite2CountQuery($sql) - { - if (preg_match("/SELECT\s+COUNT\(DISTINCT\s+([^\)]+)\)\s+(AS\s+[^\s]+)?\s*FROM\s+(.+)/is", $sql, $matches)) { - return 'SELECT COUNT(' . $matches[1] . ') ' . $matches[2] . ' FROM SELECT DISTINCT ' - . $matches[1] . ' FROM ' . $matches[3]; + return $sql; } - return $sql; - } + /** + * 创建备份文件缓冲 + * + * @param $type + * @param $header + * @param $body + * + * @return string + */ + public static function buildBackupBuffer($type, $header, $body) + { + $buffer = ''; - /** - * 创建备份文件缓冲 - * - * @param $type - * @param $header - * @param $body - * - * @return string - */ - public static function buildBackupBuffer($type, $header, $body) - { - $buffer = ''; + $buffer .= pack('vvV', $type, strlen($header), strlen($body)); + $buffer .= $header . $body; + $buffer .= md5($buffer); - $buffer .= pack('vvV', $type, strlen($header), strlen($body)); - $buffer .= $header . $body; - $buffer .= md5($buffer); - - return $buffer; - } - - /** - * 从备份文件中解压 - * - * @param $fp - * @param bool $offset - * @param string $version - * - * @return array|bool - */ - public static function extractBackupBuffer($fp, &$offset, $version) - { - $realMetaLen = $version == 'FILE' ? 6 : 8; - - $meta = fread($fp, $realMetaLen); - $offset += $realMetaLen; - $metaLen = strlen($meta); - - if (false === $meta || $metaLen != $realMetaLen) { - return false; + return $buffer; } - [$type, $headerLen, $bodyLen] = array_values(unpack($version == 'FILE' ? 'v3' : 'v1type/v1headerLen/V1bodyLen', $meta)); + /** + * 从备份文件中解压 + * + * @param $fp + * @param bool $offset + * @param string $version + * + * @return array|bool + */ + public static function extractBackupBuffer($fp, &$offset, $version) + { + $realMetaLen = $version == 'FILE' ? 6 : 8; - $header = @fread($fp, $headerLen); - $offset += $headerLen; + $meta = fread($fp, $realMetaLen); + $offset += $realMetaLen; + $metaLen = strlen($meta); - if (false === $header || strlen($header) != $headerLen) { - return false; - } - - if ('FILE' == $version) { - $bodyLen = array_reduce(json_decode($header, true), function ($carry, $len) { - return null === $len ? $carry : $carry + $len; - }, 0); - } - - $body = @fread($fp, $bodyLen); - $offset += $bodyLen; - - if (false === $body || strlen($body) != $bodyLen) { - return false; - } - - $md5 = @fread($fp, 32); - $offset += 32; - - if (false === $md5 || $md5 != md5($meta . $header . $body)) { - return false; - } - - return [$type, $header, $body]; - } - - /** - * 检查是否是一个安全的主机名 - * - * @param $host - * - * @return bool - */ - public static function checkSafeHost($host) - { - if ('localhost' == $host) { - return false; - } - - $address = gethostbyname($host); - $inet = inet_pton($address); - - if (false === $inet) { - // 有可能是ipv6的地址 - $records = dns_get_record($host, DNS_AAAA); - - if (empty($records)) { + if (false === $meta || $metaLen != $realMetaLen) { return false; } - $address = $records[0]['ipv6']; - $inet = inet_pton($address); + [$type, $headerLen, $bodyLen] = array_values(unpack($version == 'FILE' ? 'v3' : 'v1type/v1headerLen/V1bodyLen', $meta)); + + $header = @fread($fp, $headerLen); + $offset += $headerLen; + + if (false === $header || strlen($header) != $headerLen) { + return false; + } + + if ('FILE' == $version) { + $bodyLen = array_reduce(json_decode($header, true), function ($carry, $len) { + return null === $len ? $carry : $carry + $len; + }, 0); + } + + $body = @fread($fp, $bodyLen); + $offset += $bodyLen; + + if (false === $body || strlen($body) != $bodyLen) { + return false; + } + + $md5 = @fread($fp, 32); + $offset += 32; + + if (false === $md5 || $md5 != md5($meta . $header . $body)) { + return false; + } + + return [$type, $header, $body]; } - if (strpos($address, '.')) { - // ipv4 - // 非公网地址 - $privateNetworks = [ - '10.0.0.0|10.255.255.255', - '172.16.0.0|172.31.255.255', - '192.168.0.0|192.168.255.255', - '169.254.0.0|169.254.255.255', - '127.0.0.0|127.255.255.255' - ]; + /** + * 检查是否是一个安全的主机名 + * + * @param $host + * + * @return bool + */ + public static function checkSafeHost($host) + { + if ('localhost' == $host) { + return false; + } - $long = ip2long($address); + $address = gethostbyname($host); + $inet = inet_pton($address); - foreach ($privateNetworks as $network) { - [$from, $to] = explode('|', $network); + if (false === $inet) { + // 有可能是ipv6的地址 + $records = dns_get_record($host, DNS_AAAA); - if ($long >= ip2long($from) && $long <= ip2long($to)) { + if (empty($records)) { + return false; + } + + $address = $records[0]['ipv6']; + $inet = inet_pton($address); + } + + if (strpos($address, '.')) { + // ipv4 + // 非公网地址 + $privateNetworks = [ + '10.0.0.0|10.255.255.255', + '172.16.0.0|172.31.255.255', + '192.168.0.0|192.168.255.255', + '169.254.0.0|169.254.255.255', + '127.0.0.0|127.255.255.255' + ]; + + $long = ip2long($address); + + foreach ($privateNetworks as $network) { + [$from, $to] = explode('|', $network); + + if ($long >= ip2long($from) && $long <= ip2long($to)) { + return false; + } + } + } else { + // ipv6 + // https://en.wikipedia.org/wiki/Private_network + $from = inet_pton('fd00::'); + $to = inet_pton('fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'); + + if ($inet >= $from && $inet <= $to) { return false; } } - } else { - // ipv6 - // https://en.wikipedia.org/wiki/Private_network - $from = inet_pton('fd00::'); - $to = inet_pton('fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'); - if ($inet >= $from && $inet <= $to) { - return false; + return true; + } + + /** + * 获取图片 + * + * @access public + * + * @param string $fileName 文件名 + * + * @return string + */ + public static function mimeContentType($fileName) + { + //改为并列判断 + if (function_exists('mime_content_type')) { + return mime_content_type($fileName); } - } - return true; - } + if (function_exists('finfo_open')) { + $fInfo = @finfo_open(FILEINFO_MIME_TYPE); - /** - * 获取图片 - * - * @access public - * - * @param string $fileName 文件名 - * - * @return string - */ - public static function mimeContentType($fileName) - { - //改为并列判断 - if (function_exists('mime_content_type')) { - return mime_content_type($fileName); - } - - if (function_exists('finfo_open')) { - $fInfo = @finfo_open(FILEINFO_MIME_TYPE); - - if (false !== $fInfo) { - $mimeType = finfo_file($fInfo, $fileName); - finfo_close($fInfo); - return $mimeType; + if (false !== $fInfo) { + $mimeType = finfo_file($fInfo, $fileName); + finfo_close($fInfo); + return $mimeType; + } } - } - $mimeTypes = [ - 'ez' => 'application/andrew-inset', - 'csm' => 'application/cu-seeme', - 'cu' => 'application/cu-seeme', - 'tsp' => 'application/dsptype', - 'spl' => 'application/x-futuresplash', - 'hta' => 'application/hta', - 'cpt' => 'image/x-corelphotopaint', - 'hqx' => 'application/mac-binhex40', - 'nb' => 'application/mathematica', - 'mdb' => 'application/msaccess', - 'doc' => 'application/msword', - 'dot' => 'application/msword', - 'bin' => 'application/octet-stream', - 'oda' => 'application/oda', - 'ogg' => 'application/ogg', - 'oga' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'prf' => 'application/pics-rules', - 'key' => 'application/pgp-keys', - 'pdf' => 'application/pdf', - 'pgp' => 'application/pgp-signature', - 'ps' => 'application/postscript', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'rss' => 'application/rss+xml', - 'rtf' => 'text/rtf', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'wp5' => 'application/wordperfect5.1', - 'xht' => 'application/xhtml+xml', - 'xhtml' => 'application/xhtml+xml', - 'zip' => 'application/zip', - 'cdy' => 'application/vnd.cinderella', - 'mif' => 'application/x-mif', - 'xls' => 'application/vnd.ms-excel', - 'xlb' => 'application/vnd.ms-excel', - 'cat' => 'application/vnd.ms-pki.seccat', - 'stl' => 'application/vnd.ms-pki.stl', - 'ppt' => 'application/vnd.ms-powerpoint', - 'pps' => 'application/vnd.ms-powerpoint', - 'pot' => 'application/vnd.ms-powerpoint', - 'sdc' => 'application/vnd.stardivision.calc', - 'sda' => 'application/vnd.stardivision.draw', - 'sdd' => 'application/vnd.stardivision.impress', - 'sdp' => 'application/vnd.stardivision.impress', - 'smf' => 'application/vnd.stardivision.math', - 'sdw' => 'application/vnd.stardivision.writer', - 'vor' => 'application/vnd.stardivision.writer', - 'sgl' => 'application/vnd.stardivision.writer-global', - 'sxc' => 'application/vnd.sun.xml.calc', - 'stc' => 'application/vnd.sun.xml.calc.template', - 'sxd' => 'application/vnd.sun.xml.draw', - 'std' => 'application/vnd.sun.xml.draw.template', - 'sxi' => 'application/vnd.sun.xml.impress', - 'sti' => 'application/vnd.sun.xml.impress.template', - 'sxm' => 'application/vnd.sun.xml.math', - 'sxw' => 'application/vnd.sun.xml.writer', - 'sxg' => 'application/vnd.sun.xml.writer.global', - 'stw' => 'application/vnd.sun.xml.writer.template', - 'sis' => 'application/vnd.symbian.install', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'wmlsc' => 'application/vnd.wap.wmlscriptc', - 'wk' => 'application/x-123', - 'dmg' => 'application/x-apple-diskimage', - 'bcpio' => 'application/x-bcpio', - 'torrent' => 'application/x-bittorrent', - 'cdf' => 'application/x-cdf', - 'vcd' => 'application/x-cdlink', - 'pgn' => 'application/x-chess-pgn', - 'cpio' => 'application/x-cpio', - 'csh' => 'text/x-csh', - 'deb' => 'application/x-debian-package', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'wad' => 'application/x-doom', - 'dms' => 'application/x-dms', - 'dvi' => 'application/x-dvi', - 'pfa' => 'application/x-font', - 'pfb' => 'application/x-font', - 'gsf' => 'application/x-font', - 'pcf' => 'application/x-font', - 'pcf.Z' => 'application/x-font', - 'gnumeric' => 'application/x-gnumeric', - 'sgf' => 'application/x-go-sgf', - 'gcf' => 'application/x-graphing-calculator', - 'gtar' => 'application/x-gtar', - 'tgz' => 'application/x-gtar', - 'taz' => 'application/x-gtar', - 'gz' => 'application/x-gtar', - 'hdf' => 'application/x-hdf', - 'phtml' => 'application/x-httpd-php', - 'pht' => 'application/x-httpd-php', - 'php' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'php3' => 'application/x-httpd-php3', - 'php3p' => 'application/x-httpd-php3-preprocessed', - 'php4' => 'application/x-httpd-php4', - 'ica' => 'application/x-ica', - 'ins' => 'application/x-internet-signup', - 'isp' => 'application/x-internet-signup', - 'iii' => 'application/x-iphone', - 'jar' => 'application/x-java-archive', - 'jnlp' => 'application/x-java-jnlp-file', - 'ser' => 'application/x-java-serialized-object', - 'class' => 'application/x-java-vm', - 'js' => 'application/x-javascript', - 'chrt' => 'application/x-kchart', - 'kil' => 'application/x-killustrator', - 'kpr' => 'application/x-kpresenter', - 'kpt' => 'application/x-kpresenter', - 'skp' => 'application/x-koan', - 'skd' => 'application/x-koan', - 'skt' => 'application/x-koan', - 'skm' => 'application/x-koan', - 'ksp' => 'application/x-kspread', - 'kwd' => 'application/x-kword', - 'kwt' => 'application/x-kword', - 'latex' => 'application/x-latex', - 'lha' => 'application/x-lha', - 'lzh' => 'application/x-lzh', - 'lzx' => 'application/x-lzx', - 'frm' => 'application/x-maker', - 'maker' => 'application/x-maker', - 'frame' => 'application/x-maker', - 'fm' => 'application/x-maker', - 'fb' => 'application/x-maker', - 'book' => 'application/x-maker', - 'fbdoc' => 'application/x-maker', - 'wmz' => 'application/x-ms-wmz', - 'wmd' => 'application/x-ms-wmd', - 'com' => 'application/x-msdos-program', - 'exe' => 'application/x-msdos-program', - 'bat' => 'application/x-msdos-program', - 'dll' => 'application/x-msdos-program', - 'msi' => 'application/x-msi', - 'nc' => 'application/x-netcdf', - 'pac' => 'application/x-ns-proxy-autoconfig', - 'nwc' => 'application/x-nwc', - 'o' => 'application/x-object', - 'oza' => 'application/x-oz-application', - 'pl' => 'application/x-perl', - 'pm' => 'application/x-perl', - 'p7r' => 'application/x-pkcs7-certreqresp', - 'crl' => 'application/x-pkcs7-crl', - 'qtl' => 'application/x-quicktimeplayer', - 'rpm' => 'audio/x-pn-realaudio-plugin', - 'shar' => 'application/x-shar', - 'swf' => 'application/x-shockwave-flash', - 'swfl' => 'application/x-shockwave-flash', - 'sh' => 'text/x-sh', - 'sit' => 'application/x-stuffit', - 'sv4cpio' => 'application/x-sv4cpio', - 'sv4crc' => 'application/x-sv4crc', - 'tar' => 'application/x-tar', - 'tcl' => 'text/x-tcl', - 'tex' => 'text/x-tex', - 'gf' => 'application/x-tex-gf', - 'pk' => 'application/x-tex-pk', - 'texinfo' => 'application/x-texinfo', - 'texi' => 'application/x-texinfo', - '~' => 'application/x-trash', - '%' => 'application/x-trash', - 'bak' => 'application/x-trash', - 'old' => 'application/x-trash', - 'sik' => 'application/x-trash', - 't' => 'application/x-troff', - 'tr' => 'application/x-troff', - 'roff' => 'application/x-troff', - 'man' => 'application/x-troff-man', - 'me' => 'application/x-troff-me', - 'ms' => 'application/x-troff-ms', - 'ustar' => 'application/x-ustar', - 'src' => 'application/x-wais-source', - 'wz' => 'application/x-wingz', - 'crt' => 'application/x-x509-ca-cert', - 'fig' => 'application/x-xfig', - 'au' => 'audio/basic', - 'snd' => 'audio/basic', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'kar' => 'audio/midi', - 'mpga' => 'audio/mpeg', - 'mpega' => 'audio/mpeg', - 'mp2' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mp4' => 'video/mp4', - 'm3u' => 'audio/x-mpegurl', - 'sid' => 'audio/prs.sid', - 'aif' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'gsm' => 'audio/x-gsm', - 'wma' => 'audio/x-ms-wma', - 'wax' => 'audio/x-ms-wax', - 'ra' => 'audio/x-realaudio', - 'rm' => 'audio/x-pn-realaudio', - 'ram' => 'audio/x-pn-realaudio', - 'pls' => 'audio/x-scpls', - 'sd2' => 'audio/x-sd2', - 'wav' => 'audio/x-wav', - 'pdb' => 'chemical/x-pdb', - 'xyz' => 'chemical/x-xyz', - 'bmp' => 'image/x-ms-bmp', - 'gif' => 'image/gif', - 'ief' => 'image/ief', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'pcx' => 'image/pcx', - 'png' => 'image/png', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'wbmp' => 'image/vnd.wap.wbmp', - 'ras' => 'image/x-cmu-raster', - 'cdr' => 'image/x-coreldraw', - 'pat' => 'image/x-coreldrawpattern', - 'cdt' => 'image/x-coreldrawtemplate', - 'djvu' => 'image/x-djvu', - 'djv' => 'image/x-djvu', - 'ico' => 'image/x-icon', - 'art' => 'image/x-jg', - 'jng' => 'image/x-jng', - 'psd' => 'image/x-photoshop', - 'pnm' => 'image/x-portable-anymap', - 'pbm' => 'image/x-portable-bitmap', - 'pgm' => 'image/x-portable-graymap', - 'ppm' => 'image/x-portable-pixmap', - 'rgb' => 'image/x-rgb', - 'xbm' => 'image/x-xbitmap', - 'xpm' => 'image/x-xpixmap', - 'xwd' => 'image/x-xwindowdump', - 'igs' => 'model/iges', - 'iges' => 'model/iges', - 'msh' => 'model/mesh', - 'mesh' => 'model/mesh', - 'silo' => 'model/mesh', - 'wrl' => 'x-world/x-vrml', - 'vrml' => 'x-world/x-vrml', - 'csv' => 'text/comma-separated-values', - 'css' => 'text/css', - '323' => 'text/h323', - 'htm' => 'text/html', - 'html' => 'text/html', - 'uls' => 'text/iuls', - 'mml' => 'text/mathml', - 'asc' => 'text/plain', - 'txt' => 'text/plain', - 'text' => 'text/plain', - 'diff' => 'text/plain', - 'rtx' => 'text/richtext', - 'sct' => 'text/scriptlet', - 'wsc' => 'text/scriptlet', - 'tm' => 'text/texmacs', - 'ts' => 'text/texmacs', - 'tsv' => 'text/tab-separated-values', - 'jad' => 'text/vnd.sun.j2me.app-descriptor', - 'wml' => 'text/vnd.wap.wml', - 'wmls' => 'text/vnd.wap.wmlscript', - 'xml' => 'text/xml', - 'xsl' => 'text/xml', - 'h++' => 'text/x-c++hdr', - 'hpp' => 'text/x-c++hdr', - 'hxx' => 'text/x-c++hdr', - 'hh' => 'text/x-c++hdr', - 'c++' => 'text/x-c++src', - 'cpp' => 'text/x-c++src', - 'cxx' => 'text/x-c++src', - 'cc' => 'text/x-c++src', - 'h' => 'text/x-chdr', - 'c' => 'text/x-csrc', - 'java' => 'text/x-java', - 'moc' => 'text/x-moc', - 'p' => 'text/x-pascal', - 'pas' => 'text/x-pascal', - '***' => 'text/x-pcs-***', - 'shtml' => 'text/x-server-parsed-html', - 'etx' => 'text/x-setext', - 'tk' => 'text/x-tcl', - 'ltx' => 'text/x-tex', - 'sty' => 'text/x-tex', - 'cls' => 'text/x-tex', - 'vcs' => 'text/x-vcalendar', - 'vcf' => 'text/x-vcard', - 'dl' => 'video/dl', - 'fli' => 'video/fli', - 'gl' => 'video/gl', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - 'mxu' => 'video/vnd.mpegurl', - 'dif' => 'video/x-dv', - 'dv' => 'video/x-dv', - 'lsf' => 'video/x-la-asf', - 'lsx' => 'video/x-la-asf', - 'mng' => 'video/x-mng', - 'asf' => 'video/x-ms-asf', - 'asx' => 'video/x-ms-asf', - 'wm' => 'video/x-ms-wm', - 'wmv' => 'video/x-ms-wmv', - 'wmx' => 'video/x-ms-wmx', - 'wvx' => 'video/x-ms-wvx', - 'avi' => 'video/x-msvideo', - 'movie' => 'video/x-sgi-movie', - 'ice' => 'x-conference/x-cooltalk', - 'vrm' => 'x-world/x-vrml', - 'rar' => 'application/x-rar-compressed', - 'cab' => 'application/vnd.ms-cab-compressed' - ]; + $mimeTypes = [ + 'ez' => 'application/andrew-inset', + 'csm' => 'application/cu-seeme', + 'cu' => 'application/cu-seeme', + 'tsp' => 'application/dsptype', + 'spl' => 'application/x-futuresplash', + 'hta' => 'application/hta', + 'cpt' => 'image/x-corelphotopaint', + 'hqx' => 'application/mac-binhex40', + 'nb' => 'application/mathematica', + 'mdb' => 'application/msaccess', + 'doc' => 'application/msword', + 'dot' => 'application/msword', + 'bin' => 'application/octet-stream', + 'oda' => 'application/oda', + 'ogg' => 'application/ogg', + 'oga' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'prf' => 'application/pics-rules', + 'key' => 'application/pgp-keys', + 'pdf' => 'application/pdf', + 'pgp' => 'application/pgp-signature', + 'ps' => 'application/postscript', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'rss' => 'application/rss+xml', + 'rtf' => 'text/rtf', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'wp5' => 'application/wordperfect5.1', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'zip' => 'application/zip', + 'cdy' => 'application/vnd.cinderella', + 'mif' => 'application/x-mif', + 'xls' => 'application/vnd.ms-excel', + 'xlb' => 'application/vnd.ms-excel', + 'cat' => 'application/vnd.ms-pki.seccat', + 'stl' => 'application/vnd.ms-pki.stl', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pps' => 'application/vnd.ms-powerpoint', + 'pot' => 'application/vnd.ms-powerpoint', + 'sdc' => 'application/vnd.stardivision.calc', + 'sda' => 'application/vnd.stardivision.draw', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdp' => 'application/vnd.stardivision.impress', + 'smf' => 'application/vnd.stardivision.math', + 'sdw' => 'application/vnd.stardivision.writer', + 'vor' => 'application/vnd.stardivision.writer', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sxc' => 'application/vnd.sun.xml.calc', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'sxd' => 'application/vnd.sun.xml.draw', + 'std' => 'application/vnd.sun.xml.draw.template', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sis' => 'application/vnd.symbian.install', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wk' => 'application/x-123', + 'dmg' => 'application/x-apple-diskimage', + 'bcpio' => 'application/x-bcpio', + 'torrent' => 'application/x-bittorrent', + 'cdf' => 'application/x-cdf', + 'vcd' => 'application/x-cdlink', + 'pgn' => 'application/x-chess-pgn', + 'cpio' => 'application/x-cpio', + 'csh' => 'text/x-csh', + 'deb' => 'application/x-debian-package', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'wad' => 'application/x-doom', + 'dms' => 'application/x-dms', + 'dvi' => 'application/x-dvi', + 'pfa' => 'application/x-font', + 'pfb' => 'application/x-font', + 'gsf' => 'application/x-font', + 'pcf' => 'application/x-font', + 'pcf.Z' => 'application/x-font', + 'gnumeric' => 'application/x-gnumeric', + 'sgf' => 'application/x-go-sgf', + 'gcf' => 'application/x-graphing-calculator', + 'gtar' => 'application/x-gtar', + 'tgz' => 'application/x-gtar', + 'taz' => 'application/x-gtar', + 'gz' => 'application/x-gtar', + 'hdf' => 'application/x-hdf', + 'phtml' => 'application/x-httpd-php', + 'pht' => 'application/x-httpd-php', + 'php' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'php3' => 'application/x-httpd-php3', + 'php3p' => 'application/x-httpd-php3-preprocessed', + 'php4' => 'application/x-httpd-php4', + 'ica' => 'application/x-ica', + 'ins' => 'application/x-internet-signup', + 'isp' => 'application/x-internet-signup', + 'iii' => 'application/x-iphone', + 'jar' => 'application/x-java-archive', + 'jnlp' => 'application/x-java-jnlp-file', + 'ser' => 'application/x-java-serialized-object', + 'class' => 'application/x-java-vm', + 'js' => 'application/x-javascript', + 'chrt' => 'application/x-kchart', + 'kil' => 'application/x-killustrator', + 'kpr' => 'application/x-kpresenter', + 'kpt' => 'application/x-kpresenter', + 'skp' => 'application/x-koan', + 'skd' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'ksp' => 'application/x-kspread', + 'kwd' => 'application/x-kword', + 'kwt' => 'application/x-kword', + 'latex' => 'application/x-latex', + 'lha' => 'application/x-lha', + 'lzh' => 'application/x-lzh', + 'lzx' => 'application/x-lzx', + 'frm' => 'application/x-maker', + 'maker' => 'application/x-maker', + 'frame' => 'application/x-maker', + 'fm' => 'application/x-maker', + 'fb' => 'application/x-maker', + 'book' => 'application/x-maker', + 'fbdoc' => 'application/x-maker', + 'wmz' => 'application/x-ms-wmz', + 'wmd' => 'application/x-ms-wmd', + 'com' => 'application/x-msdos-program', + 'exe' => 'application/x-msdos-program', + 'bat' => 'application/x-msdos-program', + 'dll' => 'application/x-msdos-program', + 'msi' => 'application/x-msi', + 'nc' => 'application/x-netcdf', + 'pac' => 'application/x-ns-proxy-autoconfig', + 'nwc' => 'application/x-nwc', + 'o' => 'application/x-object', + 'oza' => 'application/x-oz-application', + 'pl' => 'application/x-perl', + 'pm' => 'application/x-perl', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'crl' => 'application/x-pkcs7-crl', + 'qtl' => 'application/x-quicktimeplayer', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'shar' => 'application/x-shar', + 'swf' => 'application/x-shockwave-flash', + 'swfl' => 'application/x-shockwave-flash', + 'sh' => 'text/x-sh', + 'sit' => 'application/x-stuffit', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'tar' => 'application/x-tar', + 'tcl' => 'text/x-tcl', + 'tex' => 'text/x-tex', + 'gf' => 'application/x-tex-gf', + 'pk' => 'application/x-tex-pk', + 'texinfo' => 'application/x-texinfo', + 'texi' => 'application/x-texinfo', + '~' => 'application/x-trash', + '%' => 'application/x-trash', + 'bak' => 'application/x-trash', + 'old' => 'application/x-trash', + 'sik' => 'application/x-trash', + 't' => 'application/x-troff', + 'tr' => 'application/x-troff', + 'roff' => 'application/x-troff', + 'man' => 'application/x-troff-man', + 'me' => 'application/x-troff-me', + 'ms' => 'application/x-troff-ms', + 'ustar' => 'application/x-ustar', + 'src' => 'application/x-wais-source', + 'wz' => 'application/x-wingz', + 'crt' => 'application/x-x509-ca-cert', + 'fig' => 'application/x-xfig', + 'au' => 'audio/basic', + 'snd' => 'audio/basic', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'kar' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mpega' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'm3u' => 'audio/x-mpegurl', + 'sid' => 'audio/prs.sid', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'gsm' => 'audio/x-gsm', + 'wma' => 'audio/x-ms-wma', + 'wax' => 'audio/x-ms-wax', + 'ra' => 'audio/x-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'pls' => 'audio/x-scpls', + 'sd2' => 'audio/x-sd2', + 'wav' => 'audio/x-wav', + 'pdb' => 'chemical/x-pdb', + 'xyz' => 'chemical/x-xyz', + 'bmp' => 'image/x-ms-bmp', + 'gif' => 'image/gif', + 'ief' => 'image/ief', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'pcx' => 'image/pcx', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'wbmp' => 'image/vnd.wap.wbmp', + 'ras' => 'image/x-cmu-raster', + 'cdr' => 'image/x-coreldraw', + 'pat' => 'image/x-coreldrawpattern', + 'cdt' => 'image/x-coreldrawtemplate', + 'djvu' => 'image/x-djvu', + 'djv' => 'image/x-djvu', + 'ico' => 'image/x-icon', + 'art' => 'image/x-jg', + 'jng' => 'image/x-jng', + 'psd' => 'image/x-photoshop', + 'pnm' => 'image/x-portable-anymap', + 'pbm' => 'image/x-portable-bitmap', + 'pgm' => 'image/x-portable-graymap', + 'ppm' => 'image/x-portable-pixmap', + 'rgb' => 'image/x-rgb', + 'xbm' => 'image/x-xbitmap', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'igs' => 'model/iges', + 'iges' => 'model/iges', + 'msh' => 'model/mesh', + 'mesh' => 'model/mesh', + 'silo' => 'model/mesh', + 'wrl' => 'x-world/x-vrml', + 'vrml' => 'x-world/x-vrml', + 'csv' => 'text/comma-separated-values', + 'css' => 'text/css', + '323' => 'text/h323', + 'htm' => 'text/html', + 'html' => 'text/html', + 'uls' => 'text/iuls', + 'mml' => 'text/mathml', + 'asc' => 'text/plain', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'diff' => 'text/plain', + 'rtx' => 'text/richtext', + 'sct' => 'text/scriptlet', + 'wsc' => 'text/scriptlet', + 'tm' => 'text/texmacs', + 'ts' => 'text/texmacs', + 'tsv' => 'text/tab-separated-values', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'wml' => 'text/vnd.wap.wml', + 'wmls' => 'text/vnd.wap.wmlscript', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'h++' => 'text/x-c++hdr', + 'hpp' => 'text/x-c++hdr', + 'hxx' => 'text/x-c++hdr', + 'hh' => 'text/x-c++hdr', + 'c++' => 'text/x-c++src', + 'cpp' => 'text/x-c++src', + 'cxx' => 'text/x-c++src', + 'cc' => 'text/x-c++src', + 'h' => 'text/x-chdr', + 'c' => 'text/x-csrc', + 'java' => 'text/x-java', + 'moc' => 'text/x-moc', + 'p' => 'text/x-pascal', + 'pas' => 'text/x-pascal', + '***' => 'text/x-pcs-***', + 'shtml' => 'text/x-server-parsed-html', + 'etx' => 'text/x-setext', + 'tk' => 'text/x-tcl', + 'ltx' => 'text/x-tex', + 'sty' => 'text/x-tex', + 'cls' => 'text/x-tex', + 'vcs' => 'text/x-vcalendar', + 'vcf' => 'text/x-vcard', + 'dl' => 'video/dl', + 'fli' => 'video/fli', + 'gl' => 'video/gl', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'mxu' => 'video/vnd.mpegurl', + 'dif' => 'video/x-dv', + 'dv' => 'video/x-dv', + 'lsf' => 'video/x-la-asf', + 'lsx' => 'video/x-la-asf', + 'mng' => 'video/x-mng', + 'asf' => 'video/x-ms-asf', + 'asx' => 'video/x-ms-asf', + 'wm' => 'video/x-ms-wm', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wvx' => 'video/x-ms-wvx', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'ice' => 'x-conference/x-cooltalk', + 'vrm' => 'x-world/x-vrml', + 'rar' => 'application/x-rar-compressed', + 'cab' => 'application/vnd.ms-cab-compressed' + ]; - $part = explode('.', $fileName); - $size = count($part); + $part = explode('.', $fileName); + $size = count($part); - if ($size > 1) { - $ext = $part[$size - 1]; - if (isset($mimeTypes[$ext])) { - return $mimeTypes[$ext]; + if ($size > 1) { + $ext = $part[$size - 1]; + if (isset($mimeTypes[$ext])) { + return $mimeTypes[$ext]; + } } + + return 'application/octet-stream'; } - return 'application/octet-stream'; - } + /** + * 寻找匹配的mime图标 + * + * @access public + * + * @param string $mime mime类型 + * + * @return string + */ + public static function mimeIconType($mime) + { + $parts = explode('/', $mime); - /** - * 寻找匹配的mime图标 - * - * @access public - * - * @param string $mime mime类型 - * - * @return string - */ - public static function mimeIconType($mime) - { - $parts = explode('/', $mime); - - if (count($parts) < 2) { - return 'unknown'; - } - - [$type, $stream] = $parts; - - if (in_array($type, ['image', 'video', 'audio', 'text', 'application'])) { - switch (true) { - case in_array($stream, ['msword', 'msaccess', 'ms-powerpoint', 'ms-powerpoint']): - case 0 === strpos($stream, 'vnd.'): - return 'office'; - case false !== strpos($stream, 'html') || false !== strpos($stream, 'xml') || false !== strpos($stream, 'wml'): - return 'html'; - case false !== strpos($stream, 'compressed') || false !== strpos($stream, 'zip') || - in_array($stream, ['application/x-gtar', 'application/x-tar']): - return 'archive'; - case 'text' == $type && 0 === strpos($stream, 'x-'): - return 'script'; - default: - return $type; + if (count($parts) < 2) { + return 'unknown'; + } + + [$type, $stream] = $parts; + + if (in_array($type, ['image', 'video', 'audio', 'text', 'application'])) { + switch (true) { + case in_array($stream, ['msword', 'msaccess', 'ms-powerpoint', 'ms-powerpoint']): + case 0 === strpos($stream, 'vnd.'): + return 'office'; + case false !== strpos($stream, 'html') + || false !== strpos($stream, 'xml') + || false !== strpos($stream, 'wml'): + return 'html'; + case false !== strpos($stream, 'compressed') + || false !== strpos($stream, 'zip') + || in_array($stream, ['application/x-gtar', 'application/x-tar']): + return 'archive'; + case 'text' == $type && 0 === strpos($stream, 'x-'): + return 'script'; + default: + return $type; + } + } else { + return 'unknown'; } - } else { - return 'unknown'; } } -} +} \ No newline at end of file diff --git a/var/Typecho/Request.php b/var/Typecho/Request.php index 08d961d5..c1765a5c 100644 --- a/var/Typecho/Request.php +++ b/var/Typecho/Request.php @@ -1,21 +1,37 @@ 'intval', + 'integer' => 'intval', + 'search' => ['Typecho_Common', 'filterSearchQuery'], + 'xss' => ['Typecho_Common', 'removeXSS'], + 'url' => ['Typecho_Common', 'safeUrl'], + 'slug' => ['Typecho_Common', 'slugName'] + ]; + /** * 内部参数 * @@ -32,14 +48,6 @@ class Typecho_Request */ private $_pathInfo = null; - /** - * 服务端参数 - * - * @access private - * @var array - */ - private $_server = []; - /** * _requestUri * @@ -88,22 +96,6 @@ class Typecho_Request */ private $_referer = null; - /** - * 单例句柄 - * - * @access private - * @var Typecho_Request - */ - private static $_instance; - - /** - * 全部的http数据 - * - * @var bool|array - */ - private static $_httpParams = false; - - /** * 域名前缀 * @@ -120,99 +112,27 @@ class Typecho_Request private $_filter = []; /** - * 支持的过滤器列表 - * - * @access private - * @var string + * 初始化变量 */ - private static $_supportFilters = [ - 'int' => 'intval', - 'integer' => 'intval', - 'search' => ['Typecho_Common', 'filterSearchQuery'], - 'xss' => ['Typecho_Common', 'removeXSS'], - 'url' => ['Typecho_Common', 'safeUrl'], - 'slug' => ['Typecho_Common', 'slugName'] - ]; + public function __construct() + { + } /** * 获取单例句柄 * * @access public - * @return Typecho_Request + * @return Request */ - public static function getInstance(): Typecho_Request + public static function getInstance(): Request { if (!isset(self::$_instance)) { - self::$_instance = new Typecho_Request(); + self::$_instance = new Request(); } return self::$_instance; } - /** - * 应用过滤器 - * - * @access private - * - * @param mixed $value - * - * @return mixed - */ - private function _applyFilter($value) - { - if ($this->_filter) { - foreach ($this->_filter as $filter) { - $value = is_array($value) ? array_map($filter, $value) : - call_user_func($filter, $value); - } - - $this->_filter = []; - } - - return $value; - } - - /** - * 检查ip地址是否合法 - * - * @param string $ip ip地址 - * - * @return boolean - */ - private function _checkIp(string $ip): bool - { - if (__TYPECHO_FILTER_SUPPORTED__) { - return false !== (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) - || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); - } - - return preg_match("/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $ip) - || preg_match("/^[0-9a-f:]+$/i", $ip); - } - - /** - * 检查ua是否合法 - * - * @param string $agent ua字符串 - * - * @return boolean - */ - private function _checkAgent(string $agent): bool - { - return preg_match("/^[_a-z0-9- ,:;=#@\.\(\)\/\+\*\?]+$/i", $agent); - } - - /** - * 初始化变量 - */ - public function __construct() - { - if (false === self::$_httpParams) { - self::$_httpParams = array_filter(array_merge($_POST, $_GET), - ['Typecho_Common', 'checkStrEncoding']); - } - } - /** * 获取url前缀 * @@ -224,7 +144,7 @@ class Typecho_Request if (empty($this->_urlPrefix)) { if (defined('__TYPECHO_URL_PREFIX__')) { $this->_urlPrefix = __TYPECHO_URL_PREFIX__; - } else if (!defined('__TYPECHO_CLI__')) { + } elseif (php_sapi_name() != 'cli') { $this->_urlPrefix = ($this->isSecure() ? 'https' : 'http') . '://' . ($_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME']); } @@ -252,9 +172,9 @@ class Typecho_Request * 设置过滤器 * * @access public - * @return Typecho_Request + * @return Request */ - public function filter(): Typecho_Request + public function filter(): Request { $filters = func_get_args(); @@ -291,7 +211,8 @@ class Typecho_Request */ public function __isset(string $key) { - return isset(self::$_httpParams[$key]) + return isset($_GET[$key]) + || isset($_POST[$key]) || isset($this->_params[$key]); } @@ -311,15 +232,18 @@ class Typecho_Request case isset($this->_params[$key]): $value = $this->_params[$key]; break; - case isset(self::$_httpParams[$key]): - $value = self::$_httpParams[$key]; + case isset($_GET[$key]): + $value = $_GET[$key]; + break; + case isset($_POST[$key]): + $value = $_POST[$key]; break; default: $value = $default; break; } - $value = !is_array($value) && strlen($value) > 0 ? $value : $default; + $value = ((!is_array($value) && strlen($value) > 0) || is_array($default)) ? $value : $default; return $this->_applyFilter($value); } @@ -332,11 +256,10 @@ class Typecho_Request */ public function getArray($key): array { - $result = $this->_params[$key] ?? (self::$_httpParams[$key] ?? []); + $result = $this->get($key, []); - $result = is_array($result) ? $result + return is_array($result) ? $result : (strlen($result) > 0 ? [$result] : []); - return $this->_applyFilter($result); } /** @@ -372,9 +295,7 @@ class Typecho_Request */ public function setParam(string $name, $value) { - if (Typecho_Common::checkStrEncoding($value)) { - $this->_params[$name] = $value; - } + $this->_params[$name] = $value; } /** @@ -394,8 +315,7 @@ class Typecho_Request $params = $out; } - $this->_params = array_merge($this->_params, - array_filter($params, ['Typecho_Common', 'checkStrEncoding'])); + $this->_params = array_merge($this->_params, $params); } /** @@ -524,14 +444,16 @@ class Typecho_Request if (0 === strpos($requestUri, $baseUrl)) { // full $baseUrl matches $finalBaseUrl = $baseUrl; - } else if (0 === strpos($requestUri, dirname($baseUrl))) { + } elseif (0 === strpos($requestUri, dirname($baseUrl))) { // directory portion of $baseUrl matches $finalBaseUrl = rtrim(dirname($baseUrl), '/'); - } else if (!strpos($requestUri, basename($baseUrl))) { + } elseif (!strpos($requestUri, basename($baseUrl))) { // no match whatsoever; set it blank $finalBaseUrl = ''; - } else if ((strlen($requestUri) >= strlen($baseUrl)) - && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) { + } elseif ( + (strlen($requestUri) >= strlen($baseUrl)) + && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0)) + ) { // If using mod_rewrite or ISAPI_Rewrite strip the script filename // out of baseUrl. $pos !== 0 makes sure it is not matching a value // from PATH_INFO or QUERY_STRING @@ -559,7 +481,7 @@ class Typecho_Request /** 初始化参数 */ if (is_string($parameter)) { parse_str($parameter, $args); - } else if (is_array($parameter)) { + } elseif (is_array($parameter)) { $args = $parameter; } else { return $requestUri; @@ -621,7 +543,7 @@ class Typecho_Request || stripos($_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer') !== false)) { if (function_exists('mb_convert_encoding')) { $pathInfo = mb_convert_encoding($pathInfo, $outputEncoding, $inputEncoding); - } else if (function_exists('iconv')) { + } elseif (function_exists('iconv')) { $pathInfo = iconv($inputEncoding, $outputEncoding, $pathInfo); } } @@ -660,23 +582,11 @@ class Typecho_Request if (!empty($ip)) { $this->_ip = $ip; } else { - switch (true) { - case defined('__TYPECHO_IP_SOURCE__') - && null !== $this->getServer(__TYPECHO_IP_SOURCE__): - list($this->_ip) = array_map( - 'trim', - explode(',', - $this->getServer(__TYPECHO_IP_SOURCE__)) - ); - break; - case null !== $this->getServer('REMOTE_ADDR'): - $this->_ip = $this->getServer('REMOTE_ADDR'); - break; - case null !== $this->getServer('HTTP_CLIENT_IP'): - $this->_ip = $this->getServer('HTTP_CLIENT_IP'); - break; - default: - break; + $header = defined('__TYPECHO_IP_SOURCE__') ? __TYPECHO_IP_SOURCE__ : 'X-Forwarded-For' ; + $ips = $this->getHeader($header, $this->getServer('HTTP_CLIENT_IP', $this->getServer('REMOTE_ADDR'))); + + if (!empty($ips)) { + [$this->_ip] = array_map('trim', explode(',', $ips)); } } @@ -700,6 +610,20 @@ class Typecho_Request return $this->_ip; } + /** + * get header value + * + * @param string $key + * @param string|null $default + * + * @return string|null + */ + public function getHeader(string $key, ?string $default = null): ?string + { + $key = 'HTTP_' . strtoupper(str_replace('-', '_', $key)); + return $this->getServer($key, $default); + } + /** * 设置客户端 * @@ -819,7 +743,7 @@ class Typecho_Request /** 解析串 */ if (is_string($query)) { parse_str($query, $params); - } else if (is_array($query)) { + } elseif (is_array($query)) { $params = $query; } @@ -837,4 +761,55 @@ class Typecho_Request return $validated; } + + /** + * 应用过滤器 + * + * @param mixed $value + * + * @return mixed + */ + private function _applyFilter($value) + { + if ($this->_filter) { + foreach ($this->_filter as $filter) { + $value = is_array($value) ? array_map($filter, $value) : + call_user_func($filter, $value); + } + + $this->_filter = []; + } + + return $value; + } + + /** + * 检查ip地址是否合法 + * + * @param string $ip ip地址 + * + * @return boolean + */ + private function _checkIp(string $ip): bool + { + if (function_exists('filter_var')) { + return false !== (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) + || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); + } + + return preg_match("/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/", $ip) + || preg_match("/^[0-9a-f:]+$/i", $ip); + } + + /** + * 检查ua是否合法 + * + * @param string $agent ua字符串 + * + * @return boolean + */ + private function _checkAgent(string $agent): bool + { + return preg_match("/^[_a-z0-9- ,:;=#@\.\(\)\/\+\*\?]+$/i", $agent); + } }