请求模块

从零开始编写一个PHP框架 系列的《请求模块》

项目地址:terse

前言

当我们打开一个网页,浏览器会发出很多请求,有的是请求文件,有的是请求接口。

在编写和处理接口相关问题的时候,难免会需要分析请求相关的事情,比如:请求类型、请求头、请求相关的数据等。

需求分析

主要从几个大的方面来区分。

  • 服务器相关变量的获取
  • 请求类型的获取
  • 请求参数的获取

文件结构

├── Interfaces
│   └── Request.php    [接口]
└── Request.php        [请求类]

为了规范一点,以后的模块尽量都加上相关接口。

由于这里接口的函数和子类的函数一致,这里就不重复写了。

服务器相关变量的获取

众所周知,服务端相关的变量都是从 $_SERVER 里获取的。所以我们需要对外提供一个 getServer 的接口。

为了使用方便,我们要将一些经常用到的参数,封装好方法给暴露给外界。

<?php
class Request implements RequestInterface
{
    /**
     * 获取 $_SERVER 参数
     *
     * @param string $name
     * @return mixed
     */
    public function getServer($name)
    {
        if (isset($_SERVER[$name])) {
            return $_SERVER[$name];
        }
        return null;
    }

    /**
     * 获取 Header 信息
     *
     * @param string $name
     * @return string
     */
    public function getHeader($header)
    {
        $name = strtoupper(strtr($header, "-", "_"));
        $result = $this->getServer($name);
        if ($result) {
            return $result;
        }
        return $this->getServer('HTTP_' . $name);
    }

    /**
     * 获取 HTTP schema (http/https)
     *
     * @return string
     */
    public function getScheme()
    {
        $https = $this->getServer('HTTPS');
        return $https && $https != 'off' ? 'https' : 'http';
    }

    /**
     * 获取服务器IP
     *
     * @return string
     */
    public function getServerAddress()
    {
        $serverAddress = $this->getServer('SERVER_ADDR');
        return $serverAddress ? $serverAddress : gethostbyname('localhost');
    }

    /**
     * 获取服务器名称
     *
     * @return string
     */
    public function getServerName()
    {
        $serverName = $this->getServer('SERVER_NAME');
        return $serverName ? $serverName : 'localhost';
    }

    /**
     * 获取请求头中 Host: 项的内容
     *
     * @return string
     */
    public function getHttpHost()
    {
        return $this->getServer('HTTP_HOST');
    }

    /**
     * 获取端口
     *
     * @return int
     */
    public function getPort()
    {
        return $this->getServer('SERVER_PORT');
    }

    /**
     * 获取URI
     * 
     * @return string
     */
    public final function getURI()
    {
        return $this->getServer('REQUEST_URI');
    }

    /**
     * 获取客户端IP
     *
     * @return string
     */
    public function getClientAddress()
    {
        return $this->getServer('REMOTE_ADDR');
    }

    /**
     * 获取 UA
     *
     * @return string
     */
    public function getUserAgent()
    {
        return $this->getServer('HTTP_USER_AGENT');
    }

    /**
     * 获取 http referer
     *
     * @return string
     */
    public function getHttpReferer()
    {
        return $this->getServer('HTTP_REFERER');
    }

    /**
     * 获取请求方式
     *
     * @return string
     */
    public function getMethod()
    {
        $method = $this->getServer('REQUEST_METHOD');
        // 判断 是否存在
        return $this->isValidMethod($method) ? $method : self::METHOD_GET;
    }
}

请求类型的获取

关于请求类型,除了我们常用的 GETPOST 之外,还有一些像 OPTIONSPUTDELETEPATCHHEAD 等。

<?php
class Request implements RequestInterface
{
    const METHOD_GET     = 'GET';
    const METHOD_POST    = 'POST';
    const METHOD_PUT     = 'PUT';
    const METHOD_DELETE  = 'DELETE';
    const METHOD_HEAD    = 'HEAD';
    const METHOD_OPTIONS = 'OPTIONS';
    const METHOD_PATCH   = 'PATCH';
}

为了辨别请求是不是相关类型,我们需要有个函数作为判断,比如 is*

<?php
class Request implements RequestInterface
{
    ...

    /**
     * 判断是否 GET
     *
     * @return bool
     */
    public function isGet()
    {
        return $this->getMethod() === self::METHOD_GET;
    }

    /**
     * 判断是否 POST
     *
     * @return bool
     */
    public function isPost()
    {
        return $this->getMethod() === self::METHOD_POST;
    }

    /**
     * 判断是否 PUT
     *
     * @return bool
     */
    public function isPut()
    {
        return $this->getMethod() === self::METHOD_PUT;
    }

    /**
     * 判断是否 DELETE
     *
     * @return bool
     */
    public function isDelete()
    {
        return $this->getMethod() === self::METHOD_DELETE;
    }

    /**
     * 判断是否 HEAD
     *
     * @return bool
     */
    public function isHead()
    {
        return $this->getMethod() === self::METHOD_HEAD;
    }

    /**
     * 判断是否 OPTIONS
     *
     * @return bool
     */
    public function isOptions()
    {
        return $this->getMethod() === self::METHOD_OPTIONS;
    }

    /**
     * 判断是否 PATCH
     *
     * @return bool
     */
    public function isPatch()
    {
        return $this->getMethod() === self::METHOD_PATCH;
    }

    /**
     * 获取请求方式
     *
     * @return string
     */
    public function getMethod()
    {
        $method = $this->getServer('REQUEST_METHOD');
        // 判断 是否存在
        return $this->isValidMethod($method) ? $method : self::METHOD_GET;
    }

    /**
     * 检查方法
     *
     * @param string $method
     * @return bool
     */
    protected function isValidMethod($method)
    {
        switch ($method) {
            case self::METHOD_GET:
            case self::METHOD_POST:
            case self::METHOD_PUT:
            case self::METHOD_DELETE:
            case self::METHOD_HEAD:
            case self::METHOD_OPTIONS:
            case self::METHOD_PATCH:
            case self::METHOD_PURGE:
            case self::METHOD_TRACE:
            case self::METHOD_CONNECT:
                return true;
        }
        return false;
    }
}

请求参数的获取

关于请求参数,这里有几种类型:REQUESTGETPOSTPUTRAW INPUTFILES

<?php
class Request implements RequestInterface
{
    /**
     * 获取 $_REQUEST 参数
     *
     * @param string       $name
     * @param string|array $filters
     * @param mixed        $default
     * @return mixed
     */
    public function get($name = null, $filters = null, $default = null)
    {
        return $this->getHelper($_REQUEST, $name, $filters, $default);
    }

    /**
     * 获取 $_POST 参数
     *
     * @param string       $name
     * @param string|array $filters
     * @param mixed        $default
     * @return mixed
     */
    public function getPost($name = null, $filters = null, $default = null)
    {
        return $this->getHelper($_POST, $name, $filters, $default);
    }

    /**
     * 获取 $_GET 参数
     *
     * @param string       $name
     * @param string|array $filters
     * @param mixed        $default
     * @return mixed
     */
    public function getQuery($name = null, $filters = null, $default = null)
    {
        return $this->getHelper($_GET, $name, $filters, $default);
    }

    /**
     * 获取上传的文件
     *
     * @return boolean
     */
    public function getFiles()
    {
        return $_FILES;
    }

    /**
     * Gets HTTP raw request body
     *
     * @return string
     */
    public function getRawBody()
    {
        if (!$this->_rawBody) {
            $this->_rawBody = @file_get_contents('php://input');
        }
        return $this->_rawBody;
    }

    /**
     * Gets HTTP raw request body
     *
     * @return string
     */
    public function getJsonRawBody()
    {
        $rawBody = $this->getRawBody();
        return @json_decode($rawBody, true);
    }

    /**
     * 判断参数是否存在,并启用过滤
     * 
     * @param  array  $source
     * @param  string $name
     * @param  array  $filters
     * @param  mixed  $default
     * @return mixed
     */
    protected final function getHelper(array $source, $name, $filters, $default)
    {
        if (!$name) {
            return $source;
        }

        $value = $default;
        if (isset($source[$name])) {
            $value = $source[$name];
        }

        // 本来想写filter的,这个放到第二期再做吧
        if (!!$filters) {
            # filter
        }

        return !$value && $default ? $default : $value;
    }
}

有时候,我们需要判断某个参数是否存在,所以我们需要提供 has* 方法。

<?php
class Request implements RequestInterface
{
    /**
     * 检查 $_REQUEST 中是否存在指定参数
     *
     * @param string $name
     * @return bool
     */
    public function has($name)
    {
        return isset($_REQUEST[$name]);
    }

    /**
     * 检查 $_POST 中是否存在指定参数
     *
     * @param string $name
     * @return bool
     */
    public function hasPost($name)
    {
        return isset($_POST[$name]);
    }

    /**
     * 检查 $_GET 中是否存在指定参数
     *
     * @param string $name
     * @return bool
     */
    public function hasQuery($name)
    {
        return isset($_GET[$name]);
    }

    /**
     * 检查 $_SERVER 中是否存在指定参数
     *
     * @param string $name
     * @return bool
     */
    public function hasServer($name)
    {
        return isset($_SERVER[$name]);
    }

    /**
     * 判断是否存在上传文件
     *
     * @return boolean
     */
    public function hasFiles()
    {
        return !!$_FILES;
    }
}

其它

由于我们经常使用 Ajax,所以请求里面也提供了 isAjax 的接口供外界调用。

<?php
class Request implements RequestInterface
{
    /**
     * 判断是否 AJAX
     *
     * @return bool
     */
    public function isAjax()
    {
        $httpXRequest = $this->getServer('HTTP_X_REQUESTED_WITH');
        return strtoupper($httpXRequest) == "XMLHTTPREQUEST";
    }
}

完整代码

目前考虑的就是这些方法,在实际应用中,如果不够,则会不断的添加方法。

<?php
namespace Terse\Http;

use Terse\Http\Interfaces\Request as RequestInterface;
/**
* Terse\Http\Request
* 
* @link https://gitee.com/imjcw/terse
* @author imjcw <imjcw@imjcw.com>
*/
class Request implements RequestInterface
{
    const METHOD_GET = 'GET';
    const METHOD_POST = 'POST';
    const METHOD_PUT = 'PUT';
    const METHOD_DELETE = 'DELETE';
    const METHOD_HEAD = 'HEAD';
    const METHOD_OPTIONS = 'OPTIONS';
    const METHOD_PATCH = 'PATCH';
    const METHOD_PURGE = 'PURGE';
    const METHOD_TRACE = 'TRACE';
    const METHOD_CONNECT = 'CONNECT';

    protected $_rawBody;

    /**
     * 获取 $_REQUEST 参数
     *
     * @param string       $name
     * @param string|array $filters
     * @param mixed        $default
     * @return mixed
     */
    public function get($name = null, $filters = null, $default = null)
    {
        return $this->getHelper($_REQUEST, $name, $filters, $default);
    }

    /**
     * 获取 $_POST 参数
     *
     * @param string       $name
     * @param string|array $filters
     * @param mixed        $default
     * @return mixed
     */
    public function getPost($name = null, $filters = null, $default = null)
    {
        return $this->getHelper($_POST, $name, $filters, $default);
    }

    /**
     * 获取 $_GET 参数
     *
     * @param string       $name
     * @param string|array $filters
     * @param mixed        $default
     * @return mixed
     */
    public function getQuery($name = null, $filters = null, $default = null)
    {
        return $this->getHelper($_GET, $name, $filters, $default);
    }

    /**
     * 获取上传的文件
     *
     * @return boolean
     */
    public function getFiles()
    {
        return $_FILES;
    }

    /**
     * Gets HTTP raw request body
     *
     * @return string
     */
    public function getRawBody()
    {
        if (!$this->_rawBody) {
            $this->_rawBody = @file_get_contents('php://input');
        }
        return $this->_rawBody;
    }

    /**
     * Gets HTTP raw request body
     *
     * @return string
     */
    public function getJsonRawBody()
    {
        $rawBody = $this->getRawBody();
        return @json_decode($rawBody, true);
    }

    /**
     * 检查 $_REQUEST 中是否存在指定参数
     *
     * @param string $name
     * @return bool
     */
    public function has($name)
    {
        return isset($_REQUEST[$name]);
    }

    /**
     * 检查 $_POST 中是否存在指定参数
     *
     * @param string $name
     * @return bool
     */
    public function hasPost($name)
    {
        return isset($_POST[$name]);
    }

    /**
     * 检查 $_GET 中是否存在指定参数
     *
     * @param string $name
     * @return bool
     */
    public function hasQuery($name)
    {
        return isset($_GET[$name]);
    }

    /**
     * 检查 $_SERVER 中是否存在指定参数
     *
     * @param string $name
     * @return bool
     */
    public function hasServer($name)
    {
        return isset($_SERVER[$name]);
    }

    /**
     * 判断是否存在上传文件
     *
     * @return boolean
     */
    public function hasFiles()
    {
        return !!$_FILES;
    }

    /**
     * 获取 $_SERVER 参数
     *
     * @param string $name
     * @return mixed
     */
    public function getServer($name)
    {
        if (isset($_SERVER[$name])) {
            return $_SERVER[$name];
        }
        return null;
    }

    /**
     * 获取 Header 信息
     *
     * @param string $name
     * @return string
     */
    public function getHeader($header)
    {
        $name = strtoupper(strtr($header, "-", "_"));
        $result = $this->getServer($name);
        if ($result) {
            return $result;
        }
        return $this->getServer('HTTP_' . $name);
    }

    /**
     * 获取 HTTP schema (http/https)
     *
     * @return string
     */
    public function getScheme()
    {
        $https = $this->getServer('HTTPS');
        return $https && $https != 'off' ? 'https' : 'http';
    }

    /**
     * 获取服务器IP
     *
     * @return string
     */
    public function getServerAddress()
    {
        $serverAddress = $this->getServer('SERVER_ADDR');
        return $serverAddress ? $serverAddress : gethostbyname('localhost');
    }

    /**
     * 获取服务器名称
     *
     * @return string
     */
    public function getServerName()
    {
        $serverName = $this->getServer('SERVER_NAME');
        return $serverName ? $serverName : 'localhost';
    }

    /**
     * 获取请求头中 Host: 项的内容
     *
     * @return string
     */
    public function getHttpHost()
    {
        return $this->getServer('HTTP_HOST');
    }

    /**
     * 获取端口
     *
     * @return int
     */
    public function getPort()
    {
        return $this->getServer('SERVER_PORT');
    }

    /**
     * 获取URI
     * 
     * @return string
     */
    public final function getURI()
    {
        return $this->getServer('REQUEST_URI');
    }

    /**
     * 获取客户端IP
     *
     * @return string
     */
    public function getClientAddress()
    {
        return $this->getServer('REMOTE_ADDR');
    }

    /**
     * 获取 UA
     *
     * @return string
     */
    public function getUserAgent()
    {
        return $this->getServer('HTTP_USER_AGENT');
    }

    /**
     * 获取 http referer
     *
     * @return string
     */
    public function getHttpReferer()
    {
        return $this->getServer('HTTP_REFERER');
    }

    /**
     * 获取请求方式
     *
     * @return string
     */
    public function getMethod()
    {
        $method = $this->getServer('REQUEST_METHOD');
        // 判断 是否POST
        if ($method === self::METHOD_POST) {
            $method = $this->validPost();
        }
        // 判断 是否存在
        return $this->isValidMethod($method) ? $method : self::METHOD_GET;
    }

    /**
     * 判断是否 GET
     *
     * @return bool
     */
    public function isGet()
    {
        return $this->getMethod() === self::METHOD_GET;
    }

    /**
     * 判断是否 POST
     *
     * @return bool
     */
    public function isPost()
    {
        return $this->getMethod() === self::METHOD_POST;
    }

    /**
     * 判断是否 PUT
     *
     * @return bool
     */
    public function isPut()
    {
        return $this->getMethod() === self::METHOD_PUT;
    }

    /**
     * 判断是否 DELETE
     *
     * @return bool
     */
    public function isDelete()
    {
        return $this->getMethod() === self::METHOD_DELETE;
    }

    /**
     * 判断是否 HEAD
     *
     * @return bool
     */
    public function isHead()
    {
        return $this->getMethod() === self::METHOD_HEAD;
    }

    /**
     * 判断是否 OPTIONS
     *
     * @return bool
     */
    public function isOptions()
    {
        return $this->getMethod() === self::METHOD_OPTIONS;
    }

    /**
     * 判断是否 PATCH
     *
     * @return bool
     */
    public function isPatch()
    {
        return $this->getMethod() === self::METHOD_PATCH;
    }

    /**
     * 判断是否 AJAX
     *
     * @return bool
     */
    public function isAjax()
    {
        $httpXRequest = $this->getServer('HTTP_X_REQUESTED_WITH');
        return strtoupper($httpXRequest) == "XMLHTTPREQUEST";
    }

    /**
     * 待写
     */
    protected function validPost()
    {
        return self::METHOD_POST;
    }

    /**
     * 检查方法
     *
     * @param string $method
     * @return bool
     */
    protected function isValidMethod($method)
    {
        switch ($method) {
            case self::METHOD_GET:
            case self::METHOD_POST:
            case self::METHOD_PUT:
            case self::METHOD_DELETE:
            case self::METHOD_HEAD:
            case self::METHOD_OPTIONS:
            case self::METHOD_PATCH:
            case self::METHOD_PURGE:
            case self::METHOD_TRACE:
            case self::METHOD_CONNECT:
                return true;
        }
        return false;
    }

    /**
     * 判断参数是否存在,并启用过滤
     * 
     * @param  array  $source
     * @param  string $name
     * @param  array  $filters
     * @param  mixed  $default
     * @return mixed
     */
    protected final function getHelper(array $source, $name, $filters, $default)
    {
        if (!$name) {
            return $source;
        }

        $value = $default;
        if (isset($source[$name])) {
            $value = $source[$name];
        }

        if (!!$filters) {
            # filter
        }

        return !$value && $default ? $default : $value;
    }
}

调用方式

<?php
$request = new Request();
$request->get();
$request->getPost();
$request->getQuery();
$request->getJsonRawBody();
$request->isGet();
$request->isPost();
$request->isAjax();

最后

通篇基本在贴代码了,毕竟基本是判断和获取,没有什么比较特别的东西在里面。

下一篇《路由模块》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容

  • 这么久了,有什么坚持做的事,我这样问自己。 我以为我没有,在飞驰的火车上,我想了想,我有。 至少做了有3年以上的事...
    郑州链家芮培豪阅读 286评论 1 1
  • 关于阳痿早泄,统一解答:性命性命,先有性才有命,反过来性没命也不远矣,藏象密宗修为身体好转的第一指标就是性归,男子...
    9c091b821d42阅读 1,122评论 0 0
  • 看着宝贝一天天的长大,每天自己独立完成作业,看到你的进步,我妈妈为你感到骄傲、自豪,宝贝你是最棒的 加油 ✊✊妈妈...
    小安宇阅读 249评论 0 0
  • 不知不觉喜欢上了画画,有了绘画,我不在孤单,没事的时候,拿起笔,一画就是一两个小时,不仅仅能度过没事的时间,还能够...
    梦想的翅膀_0bbf阅读 214评论 0 0
  • 认识你之前 我沉浸在一场梦中 一场美好却虚假的幻梦 你如侠客 从天而降义无反顾 打碎那笼罩我的水晶球 为你我愿离开...
    雪霁尔归阅读 266评论 0 3