從零開始編寫一個(gè)PHP框架 系列的《請(qǐng)求模塊》
項(xiàng)目地址:terse
前言
當(dāng)我們打開一個(gè)網(wǎng)頁弥臼,瀏覽器會(huì)發(fā)出很多請(qǐng)求兔沃,有的是請(qǐng)求文件脖祈,有的是請(qǐng)求接口乙墙。
在編寫和處理接口相關(guān)問題的時(shí)候逻谦,難免會(huì)需要分析請(qǐng)求相關(guān)的事情掌实,比如:請(qǐng)求類型、請(qǐng)求頭邦马、請(qǐng)求相關(guān)的數(shù)據(jù)等潮峦。
需求分析
主要從幾個(gè)大的方面來區(qū)分。
- 服務(wù)器相關(guān)變量的獲取
- 請(qǐng)求類型的獲取
- 請(qǐng)求參數(shù)的獲取
文件結(jié)構(gòu)
├── Interfaces
│ └── Request.php [接口]
└── Request.php [請(qǐng)求類]
為了規(guī)范一點(diǎn)勇婴,以后的模塊盡量都加上相關(guān)接口忱嘹。
由于這里接口的函數(shù)和子類的函數(shù)一致,這里就不重復(fù)寫了耕渴。
服務(wù)器相關(guān)變量的獲取
眾所周知拘悦,服務(wù)端相關(guān)的變量都是從 $_SERVER
里獲取的。所以我們需要對(duì)外提供一個(gè) getServer
的接口橱脸。
為了使用方便础米,我們要將一些經(jīng)常用到的參數(shù),封裝好方法給暴露給外界添诉。
<?php
class Request implements RequestInterface
{
/**
* 獲取 $_SERVER 參數(shù)
*
* @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';
}
/**
* 獲取服務(wù)器IP
*
* @return string
*/
public function getServerAddress()
{
$serverAddress = $this->getServer('SERVER_ADDR');
return $serverAddress ? $serverAddress : gethostbyname('localhost');
}
/**
* 獲取服務(wù)器名稱
*
* @return string
*/
public function getServerName()
{
$serverName = $this->getServer('SERVER_NAME');
return $serverName ? $serverName : 'localhost';
}
/**
* 獲取請(qǐng)求頭中 Host: 項(xiàng)的內(nèi)容
*
* @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');
}
/**
* 獲取請(qǐng)求方式
*
* @return string
*/
public function getMethod()
{
$method = $this->getServer('REQUEST_METHOD');
// 判斷 是否存在
return $this->isValidMethod($method) ? $method : self::METHOD_GET;
}
}
請(qǐng)求類型的獲取
關(guān)于請(qǐng)求類型屁桑,除了我們常用的 GET
和 POST
之外,還有一些像 OPTIONS
栏赴、PUT
蘑斧、DELETE
、PATCH
须眷、HEAD
等竖瘾。
<?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';
}
為了辨別請(qǐng)求是不是相關(guān)類型,我們需要有個(gè)函數(shù)作為判斷花颗,比如 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;
}
/**
* 獲取請(qǐng)求方式
*
* @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;
}
}
請(qǐng)求參數(shù)的獲取
關(guān)于請(qǐng)求參數(shù),這里有幾種類型:REQUEST
扩劝、GET
庸论、POST
职辅、PUT
、RAW INPUT
聂示、FILES
罐农。
<?php
class Request implements RequestInterface
{
/**
* 獲取 $_REQUEST 參數(shù)
*
* @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 參數(shù)
*
* @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 參數(shù)
*
* @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);
}
/**
* 判斷參數(shù)是否存在,并啟用過濾
*
* @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的催什,這個(gè)放到第二期再做吧
if (!!$filters) {
# filter
}
return !$value && $default ? $default : $value;
}
}
有時(shí)候,我們需要判斷某個(gè)參數(shù)是否存在宰睡,所以我們需要提供 has*
方法蒲凶。
<?php
class Request implements RequestInterface
{
/**
* 檢查 $_REQUEST 中是否存在指定參數(shù)
*
* @param string $name
* @return bool
*/
public function has($name)
{
return isset($_REQUEST[$name]);
}
/**
* 檢查 $_POST 中是否存在指定參數(shù)
*
* @param string $name
* @return bool
*/
public function hasPost($name)
{
return isset($_POST[$name]);
}
/**
* 檢查 $_GET 中是否存在指定參數(shù)
*
* @param string $name
* @return bool
*/
public function hasQuery($name)
{
return isset($_GET[$name]);
}
/**
* 檢查 $_SERVER 中是否存在指定參數(shù)
*
* @param string $name
* @return bool
*/
public function hasServer($name)
{
return isset($_SERVER[$name]);
}
/**
* 判斷是否存在上傳文件
*
* @return boolean
*/
public function hasFiles()
{
return !!$_FILES;
}
}
其它
由于我們經(jīng)常使用 Ajax
,所以請(qǐng)求里面也提供了 isAjax
的接口供外界調(diào)用拆内。
<?php
class Request implements RequestInterface
{
/**
* 判斷是否 AJAX
*
* @return bool
*/
public function isAjax()
{
$httpXRequest = $this->getServer('HTTP_X_REQUESTED_WITH');
return strtoupper($httpXRequest) == "XMLHTTPREQUEST";
}
}
完整代碼
目前考慮的就是這些方法旋圆,在實(shí)際應(yīng)用中,如果不夠麸恍,則會(huì)不斷的添加方法灵巧。
<?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 參數(shù)
*
* @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 參數(shù)
*
* @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 參數(shù)
*
* @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 中是否存在指定參數(shù)
*
* @param string $name
* @return bool
*/
public function has($name)
{
return isset($_REQUEST[$name]);
}
/**
* 檢查 $_POST 中是否存在指定參數(shù)
*
* @param string $name
* @return bool
*/
public function hasPost($name)
{
return isset($_POST[$name]);
}
/**
* 檢查 $_GET 中是否存在指定參數(shù)
*
* @param string $name
* @return bool
*/
public function hasQuery($name)
{
return isset($_GET[$name]);
}
/**
* 檢查 $_SERVER 中是否存在指定參數(shù)
*
* @param string $name
* @return bool
*/
public function hasServer($name)
{
return isset($_SERVER[$name]);
}
/**
* 判斷是否存在上傳文件
*
* @return boolean
*/
public function hasFiles()
{
return !!$_FILES;
}
/**
* 獲取 $_SERVER 參數(shù)
*
* @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';
}
/**
* 獲取服務(wù)器IP
*
* @return string
*/
public function getServerAddress()
{
$serverAddress = $this->getServer('SERVER_ADDR');
return $serverAddress ? $serverAddress : gethostbyname('localhost');
}
/**
* 獲取服務(wù)器名稱
*
* @return string
*/
public function getServerName()
{
$serverName = $this->getServer('SERVER_NAME');
return $serverName ? $serverName : 'localhost';
}
/**
* 獲取請(qǐng)求頭中 Host: 項(xiàng)的內(nèi)容
*
* @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');
}
/**
* 獲取請(qǐng)求方式
*
* @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;
}
/**
* 判斷參數(shù)是否存在,并啟用過濾
*
* @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;
}
}
調(diào)用方式
<?php
$request = new Request();
$request->get();
$request->getPost();
$request->getQuery();
$request->getJsonRawBody();
$request->isGet();
$request->isPost();
$request->isAjax();
最后
通篇基本在貼代碼了抹沪,畢竟基本是判斷和獲取刻肄,沒有什么比較特別的東西在里面。
下一篇《路由模塊》