123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- <?php
- namespace Typecho;
- /**
- * 服务器请求处理类
- *
- * @package Request
- */
- class Request
- {
- /**
- * 单例句柄
- *
- * @access private
- * @var Request
- */
- private static $instance;
- /**
- * 沙箱参数
- *
- * @access private
- * @var Config|null
- */
- private $sandbox;
- /**
- * 用户参数
- *
- * @access private
- * @var Config|null
- */
- private $params;
- /**
- * 路径信息
- *
- * @access private
- * @var string
- */
- private $pathInfo = null;
- /**
- * requestUri
- *
- * @var string
- * @access private
- */
- private $requestUri = null;
- /**
- * requestRoot
- *
- * @var mixed
- * @access private
- */
- private $requestRoot = null;
- /**
- * 获取baseurl
- *
- * @var string
- * @access private
- */
- private $baseUrl = null;
- /**
- * 客户端ip地址
- *
- * @access private
- * @var string
- */
- private $ip = null;
- /**
- * 域名前缀
- *
- * @var string
- */
- private $urlPrefix = null;
- /**
- * 获取单例句柄
- *
- * @access public
- * @return Request
- */
- public static function getInstance(): Request
- {
- if (!isset(self::$instance)) {
- self::$instance = new self();
- }
- return self::$instance;
- }
- /**
- * 初始化变量
- *
- * @return $this
- */
- public function beginSandbox(Config $sandbox): Request
- {
- $this->sandbox = $sandbox;
- return $this;
- }
- /**
- * @return $this
- */
- public function endSandbox(): Request
- {
- $this->sandbox = null;
- return $this;
- }
- /**
- * @param Config $params
- * @return $this
- */
- public function proxy(Config $params): Request
- {
- $this->params = $params;
- return $this;
- }
- /**
- * 获取实际传递参数
- *
- * @param string $key 指定参数
- * @param mixed $default 默认参数 (default: NULL)
- * @param bool|null $exists detect exists
- * @return mixed
- */
- public function get(string $key, $default = null, ?bool &$exists = true)
- {
- $exists = true;
- $value = null;
- switch (true) {
- case isset($this->params) && isset($this->params[$key]):
- $value = $this->params[$key];
- break;
- case isset($this->sandbox):
- if (isset($this->sandbox[$key])) {
- $value = $this->sandbox[$key];
- } else {
- $exists = false;
- }
- break;
- case $key === '@json':
- $exists = false;
- if ($this->isJson()) {
- $body = file_get_contents('php://input');
- if (false !== $body) {
- $exists = true;
- $value = json_decode($body, true, 16);
- $default = $default ?? $value;
- }
- }
- break;
- case isset($_GET[$key]):
- $value = $_GET[$key];
- break;
- case isset($_POST[$key]):
- $value = $_POST[$key];
- break;
- default:
- $exists = false;
- break;
- }
- // reset params
- if (isset($this->params)) {
- $this->params = null;
- }
- if (isset($value)) {
- return is_array($default) == is_array($value) ? $value : $default;
- } else {
- return $default;
- }
- }
- /**
- * 获取实际传递参数(magic)
- *
- * @param string $key 指定参数
- * @return mixed
- */
- public function __get(string $key)
- {
- return $this->get($key);
- }
- /**
- * 判断参数是否存在
- *
- * @param string $key 指定参数
- * @return boolean
- */
- public function __isset(string $key)
- {
- $this->get($key, null, $exists);
- return $exists;
- }
- /**
- * 获取一个数组
- *
- * @param $key
- * @return array
- */
- public function getArray($key): array
- {
- $result = $this->get($key, [], $exists);
- if (!empty($result) || !$exists) {
- return $result;
- }
- return [$this->get($key)];
- }
- /**
- * 从参数列表指定的值中获取http传递参数
- *
- * @param mixed $params 指定的参数
- * @return array
- */
- public function from($params): array
- {
- $result = [];
- $args = is_array($params) ? $params : func_get_args();
- foreach ($args as $arg) {
- $result[$arg] = $this->get($arg);
- }
- return $result;
- }
- /**
- * getRequestRoot
- *
- * @return string
- */
- public function getRequestRoot(): string
- {
- if (null === $this->requestRoot) {
- $root = rtrim($this->getUrlPrefix() . $this->getBaseUrl(), '/') . '/';
- $pos = strrpos($root, '.php/');
- if ($pos) {
- $root = dirname(substr($root, 0, $pos));
- }
- $this->requestRoot = rtrim($root, '/');
- }
- return $this->requestRoot;
- }
- /**
- * 获取当前请求url
- *
- * @return string
- */
- public function getRequestUrl(): string
- {
- return $this->getUrlPrefix() . $this->getRequestUri();
- }
- /**
- * 根据当前uri构造指定参数的uri
- *
- * @param mixed $parameter 指定的参数
- * @return string
- */
- public function makeUriByRequest($parameter = null): string
- {
- /** 初始化地址 */
- $requestUri = $this->getRequestUrl();
- $parts = parse_url($requestUri);
- /** 初始化参数 */
- if (is_string($parameter)) {
- parse_str($parameter, $args);
- } elseif (is_array($parameter)) {
- $args = $parameter;
- } else {
- return $requestUri;
- }
- /** 构造query */
- if (isset($parts['query'])) {
- parse_str($parts['query'], $currentArgs);
- $args = array_merge($currentArgs, $args);
- }
- $parts['query'] = http_build_query($args);
- /** 返回地址 */
- return Common::buildUrl($parts);
- }
- /**
- * 获取当前pathinfo
- *
- * @return string
- */
- public function getPathInfo(): ?string
- {
- /** 缓存信息 */
- if (null !== $this->pathInfo) {
- return $this->pathInfo;
- }
- //参考Zend Framework对pathinfo的处理, 更好的兼容性
- $pathInfo = null;
- //处理requestUri
- $requestUri = $this->getRequestUri();
- $finalBaseUrl = $this->getBaseUrl();
- // Remove the query string from REQUEST_URI
- if ($pos = strpos($requestUri, '?')) {
- $requestUri = substr($requestUri, 0, $pos);
- }
- if (
- (null !== $finalBaseUrl)
- && (false === ($pathInfo = substr($requestUri, strlen($finalBaseUrl))))
- ) {
- // If substr() returns false then PATH_INFO is set to an empty string
- $pathInfo = '/';
- } elseif (null === $finalBaseUrl) {
- $pathInfo = $requestUri;
- }
- if (!empty($pathInfo)) {
- //针对iis的utf8编码做强制转换
- $pathInfo = defined('__TYPECHO_PATHINFO_ENCODING__') ?
- mb_convert_encoding($pathInfo, 'UTF-8', __TYPECHO_PATHINFO_ENCODING__) : $pathInfo;
- } else {
- $pathInfo = '/';
- }
- // fix issue 456
- return ($this->pathInfo = '/' . ltrim(urldecode($pathInfo), '/'));
- }
- /**
- * 获取环境变量
- *
- * @param string $name 获取环境变量名
- * @param string|null $default
- * @return string|null
- */
- public function getServer(string $name, string $default = null): ?string
- {
- return $_SERVER[$name] ?? $default;
- }
- /**
- * 获取ip地址
- *
- * @return string
- */
- public function getIp(): string
- {
- if (null === $this->ip) {
- $header = defined('__TYPECHO_IP_SOURCE__') ? __TYPECHO_IP_SOURCE__ : 'X-Forwarded-For';
- $ip = $this->getHeader($header, $this->getHeader('Client-Ip', $this->getServer('REMOTE_ADDR')));
- if (!empty($ip)) {
- [$ip] = array_map('trim', explode(',', $ip));
- $ip = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6);
- }
- if (!empty($ip)) {
- $this->ip = $ip;
- } else {
- $this->ip = 'unknown';
- }
- }
- 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);
- }
- /**
- * 获取客户端
- *
- * @return string
- */
- public function getAgent(): ?string
- {
- return $this->getHeader('User-Agent');
- }
- /**
- * 获取客户端
- *
- * @return string|null
- */
- public function getReferer(): ?string
- {
- return $this->getHeader('Referer');
- }
- /**
- * 判断是否为https
- *
- * @return bool
- */
- public function isSecure(): bool
- {
- return (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && !strcasecmp('https', $_SERVER['HTTP_X_FORWARDED_PROTO']))
- || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && !strcasecmp('quic', $_SERVER['HTTP_X_FORWARDED_PROTO']))
- || (!empty($_SERVER['HTTP_X_FORWARDED_PORT']) && 443 == $_SERVER['HTTP_X_FORWARDED_PORT'])
- || (!empty($_SERVER['HTTPS']) && 'off' != strtolower($_SERVER['HTTPS']))
- || (!empty($_SERVER['SERVER_PORT']) && 443 == $_SERVER['SERVER_PORT'])
- || (defined('__TYPECHO_SECURE__') && __TYPECHO_SECURE__);
- }
- /**
- * @return bool
- */
- public function isCli(): bool
- {
- return php_sapi_name() == 'cli';
- }
- /**
- * 判断是否为get方法
- *
- * @return boolean
- */
- public function isGet(): bool
- {
- return 'GET' == $this->getServer('REQUEST_METHOD');
- }
- /**
- * 判断是否为post方法
- *
- * @return boolean
- */
- public function isPost(): bool
- {
- return 'POST' == $this->getServer('REQUEST_METHOD');
- }
- /**
- * 判断是否为put方法
- *
- * @return boolean
- */
- public function isPut(): bool
- {
- return 'PUT' == $this->getServer('REQUEST_METHOD');
- }
- /**
- * 判断是否为ajax
- *
- * @return boolean
- */
- public function isAjax(): bool
- {
- return 'XMLHttpRequest' == $this->getHeader('X-Requested-With');
- }
- /**
- * 判断是否为Json请求
- *
- * @return bool
- */
- public function isJson(): bool
- {
- return !!preg_match("/^\s*application\/json(;|$)/i", $this->getHeader('Content-Type', ''));
- }
- /**
- * 判断输入是否满足要求
- *
- * @param mixed $query 条件
- * @return boolean
- */
- public function is($query): bool
- {
- $validated = false;
- /** 解析串 */
- if (is_string($query)) {
- parse_str($query, $params);
- } elseif (is_array($query)) {
- $params = $query;
- }
- /** 验证串 */
- if (!empty($params)) {
- $validated = true;
- foreach ($params as $key => $val) {
- $param = $this->get($key, null, $exists);
- $validated = empty($val) ? $exists : ($val == $param);
- if (!$validated) {
- break;
- }
- }
- }
- return $validated;
- }
- /**
- * 获取请求资源地址
- *
- * @return string|null
- */
- public function getRequestUri(): ?string
- {
- if (!empty($this->requestUri)) {
- return $this->requestUri;
- }
- //处理requestUri
- $requestUri = '/';
- if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // check this first so IIS will catch
- $requestUri = $_SERVER['HTTP_X_REWRITE_URL'];
- } elseif (
- // IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem)
- isset($_SERVER['IIS_WasUrlRewritten'])
- && $_SERVER['IIS_WasUrlRewritten'] == '1'
- && isset($_SERVER['UNENCODED_URL'])
- && $_SERVER['UNENCODED_URL'] != ''
- ) {
- $requestUri = $_SERVER['UNENCODED_URL'];
- } elseif (isset($_SERVER['REQUEST_URI'])) {
- $requestUri = $_SERVER['REQUEST_URI'];
- $parts = @parse_url($requestUri);
- if (isset($_SERVER['HTTP_HOST']) && strstr($requestUri, $_SERVER['HTTP_HOST'])) {
- if (false !== $parts) {
- $requestUri = (empty($parts['path']) ? '' : $parts['path'])
- . ((empty($parts['query'])) ? '' : '?' . $parts['query']);
- }
- } elseif (!empty($_SERVER['QUERY_STRING']) && empty($parts['query'])) {
- // fix query missing
- $requestUri .= '?' . $_SERVER['QUERY_STRING'];
- }
- } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0, PHP as CGI
- $requestUri = $_SERVER['ORIG_PATH_INFO'];
- if (!empty($_SERVER['QUERY_STRING'])) {
- $requestUri .= '?' . $_SERVER['QUERY_STRING'];
- }
- }
- return $this->requestUri = $requestUri;
- }
- /**
- * 获取url前缀
- *
- * @return string|null
- */
- public function getUrlPrefix(): ?string
- {
- if (empty($this->urlPrefix)) {
- if (defined('__TYPECHO_URL_PREFIX__')) {
- $this->urlPrefix = __TYPECHO_URL_PREFIX__;
- } elseif (php_sapi_name() != 'cli') {
- $this->urlPrefix = ($this->isSecure() ? 'https' : 'http') . '://'
- . ($_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME']);
- }
- }
- return $this->urlPrefix;
- }
- /**
- * getBaseUrl
- *
- * @return string
- */
- private function getBaseUrl(): ?string
- {
- if (null !== $this->baseUrl) {
- return $this->baseUrl;
- }
- //处理baseUrl
- $filename = (isset($_SERVER['SCRIPT_FILENAME'])) ? basename($_SERVER['SCRIPT_FILENAME']) : '';
- if (isset($_SERVER['SCRIPT_NAME']) && basename($_SERVER['SCRIPT_NAME']) === $filename) {
- $baseUrl = $_SERVER['SCRIPT_NAME'];
- } elseif (isset($_SERVER['PHP_SELF']) && basename($_SERVER['PHP_SELF']) === $filename) {
- $baseUrl = $_SERVER['PHP_SELF'];
- } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename) {
- $baseUrl = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
- } else {
- // Backtrack up the script_filename to find the portion matching
- // php_self
- $path = $_SERVER['PHP_SELF'] ?? '';
- $file = $_SERVER['SCRIPT_FILENAME'] ?? '';
- $segs = explode('/', trim($file, '/'));
- $segs = array_reverse($segs);
- $index = 0;
- $last = count($segs);
- $baseUrl = '';
- do {
- $seg = $segs[$index];
- $baseUrl = '/' . $seg . $baseUrl;
- ++$index;
- } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos));
- }
- // Does the baseUrl have anything in common with the request_uri?
- $finalBaseUrl = null;
- $requestUri = $this->getRequestUri();
- if (0 === strpos($requestUri, $baseUrl)) {
- // full $baseUrl matches
- $finalBaseUrl = $baseUrl;
- } elseif (0 === strpos($requestUri, dirname($baseUrl))) {
- // directory portion of $baseUrl matches
- $finalBaseUrl = rtrim(dirname($baseUrl), '/');
- } elseif (!strpos($requestUri, basename($baseUrl))) {
- // no match whatsoever; set it blank
- $finalBaseUrl = '';
- } 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
- $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
- }
- return ($this->baseUrl = (null === $finalBaseUrl) ? rtrim($baseUrl, '/') : $finalBaseUrl);
- }
- }
|