讲在后面的话:
申请与响应是框架生命周期中两个重要的环节,是框架的收尾两端。申请负责接管客户端申请信息,并对外提供大量接口,获取更精细化数据。响应负责将业务逻辑执行后的后果以各种模式输入。本章节将从九个方面具体介绍申请与响应的实现细节,九大节别离是:申请信息、申请变量、申请类型、头信息、申请缓存、响应类型、响应输入、响应参数、重定向。接下来是具体内容:
2.1 申请信息:
什么是申请信息?申请信息是指客户端申请服务端,发送过去的所有信息,这些信息包含申请协定、域名、端口、申请办法、申请参数等等,对于这些信息PHP语言将它们存储在一些超全局数组中,所谓的申请对象接管申请信息就是接管这些超全局数组,请看具体代码:
<code class="php"> 1. // 这是 Request 类的初始化办法,接管了除$_SESSION之外的超全局数据 2. // vendortopthinkframeworksrcthinkRequest.php 3. public static function __make(App $app) 4. { 5. $request = new static(); 7. if (function_exists('apache_request_headers') && $result = apache_request_headers()) { 8. $header = $result; 9. } else { 10. $header = []; 11. $server = $_SERVER; 12. foreach ($server as $key => $val) { 13. if (0 === strpos($key, 'HTTP_')) { 14. $key = str_replace('_', '-', strtolower(substr($key, 5))); 15. $header[$key] = $val; 16. } 17. } 18. if (isset($server['CONTENT_TYPE'])) { 19. $header['content-type'] = $server['CONTENT_TYPE']; 20. } 21. if (isset($server['CONTENT_LENGTH'])) { 22. $header['content-length'] = $server['CONTENT_LENGTH']; 23. } 24. } 26. $request->header = array_change_key_case($header); 27. $request->server = $_SERVER; 28. $request->env = $app->env; 30. $inputData = $request->getInputData($request->input); 32. $request->get = $_GET; 33. $request->post = $_POST ?: $inputData; 34. $request->put = $inputData; 35. $request->request = $_REQUEST; 36. $request->cookie = $_COOKIE; 37. $request->file = $_FILES ?? []; 39. return $request; 40. }
接管了这一系列信息之后,Request 类通过各种精细化接口对这些信息的局部获取全副进行输入,具体对外提供了那些接口,大家能够参考手册,这里对几个重要的接口进行剖析。
以后拜访域名或者IP:host() 办法
<code class="php"> 1. // 获取拜访域名 vendortopthinkframeworksrcthinkRequest.php 1754 行 2. public function host(bool $strict = false): string 3. { 4. if ($this->host) { 5. $host = $this->host; 6. } else { 8. // 通过 $_SERVER 中的HTTP_X_FORWARDED_HOST属性或者HTTP_HOST来获取 9. $host = strval($this->server('HTTP_X_FORWARDED_HOST') ?: $this->server('HTTP_HOST')); 10. } 11. // 这里是判断要不要蕴含端口 12. return true === $strict && strpos($host, ':') ? strstr($host, ':', true) : $host; 13. }
以后残缺URL:url() 办法
<code class="php"> 1. // 获取以后残缺URL 包含QUERY_STRING 2. // vendortopthinkframeworksrcthinkRequest.php 460 行 3. public function url(bool $complete = false): string 4. { 5. if ($this->url) { 6. $url = $this->url; 7. } elseif ($this->server('HTTP_X_REWRITE_URL')) { 8. $url = $this->server('HTTP_X_REWRITE_URL'); 9. } elseif ($this->server('REQUEST_URI')) { 10. $url = $this->server('REQUEST_URI'); 11. } elseif ($this->server('ORIG_PATH_INFO')) { // web服务器重定向会呈现这个属性 12. $url = $this->server('ORIG_PATH_INFO') . (!empty($this->server('QUERY_STRING')) ? '?' . $this->server('QUERY_STRING') : ''); 13. } elseif (isset($_SERVER['argv'][1])) { 14. $url = $_SERVER['argv'][1]; 15. } else { 16. $url = ''; 17. } 19. return $complete ? $this->domain() . $url : $url; 20. }
2.2 输出变量:
输出变量是指能够变动的输出信息,那什么是能够变动的输出信息呢?比方,查问参数、上传文件、Post申请体这些都是能够变动的,用户都是通过这些能够变动的货色去向服务器获取或者保存信息,比方通过查问参数的变动去获取不同的商品信息,通过提交图片信息保留本人的照片,这些对于框架而言都是输出变量,Request(申请) 类提供了丰盛的API帮咱们去获取这些信息,请看具体代码:
获取 $_POST 变量: post() 办法
<code class="php"> 1. // 获取POST参数,传递属性名称就能够获取它对应的值 2. // vendortopthinkframeworksrcthinkRequest.php 961 行 3. public function post($name = '', $default = null, $filter = '') 4. { 5. if (is_array($name)) { 6. return $this->only($name, $this->post, $filter); 7. } 9. return $this->input($this->post, $name, $default, $filter); 10. } 13. // 获取变量,并且反对过滤 不论是get() post() 还是 param() 都依赖这个办法 14. // vendortopthinkframeworksrcthinkRequest.php 1241行 15. public function input(array $data = [], $name = '', $default = null, $filter = '') 16. { 17. if (false === $name) { 18. // 获取原始数据 19. return $data; 20. } 22. $name = (string) $name; 23. if ('' != $name) { 24. // 解析name 25. if (strpos($name, '/')) { 26. [$name, $type] = explode('/', $name); 27. } 29. $data = $this->getData($data, $name); 31. if (is_null($data)) { 32. return $default; 33. } 35. if (is_object($data)) { 36. return $data; 37. } 38. } 40. $data = $this->filterData($data, $filter, $name, $default); 42. if (isset($type) && $data !== $default) { 43. // 强制类型转换 44. $this->typeCast($data, $type); 45. } 47. return $data; 48. }
获取 $_FILES 变量(上传文件信息):file() 办法
<code class="php"> 1. // 获取上传文件的信息,这里返回的是一个对象数组,每一个上传文件都会生成一个上传对象 2. // vendortopthinkframeworksrcthinkRequest.php 1128 行 3. public function file(string $name = '') 4. { 5. $files = $this->file; // 6. if (!empty($files)) { 8. if (strpos($name, '.')) { 9. [$name, $sub] = explode('.', $name); 10. } 12. // 解决上传文件 13. $array = $this->dealUploadFile($files, $name); 15. if ('' === $name) { 16. // 获取全副文件 17. return $array; 18. } elseif (isset($sub) && isset($array[$name][$sub])) { 19. return $array[$name][$sub]; 20. } elseif (isset($array[$name])) { 21. return $array[$name]; 22. } 23. } 24. } 26. // 上传文件对象创立 vendortopthinkframeworksrcthinkRequest.php 1175 行 27. $item[] = new UploadedFile($temp['tmp_name'], $temp['name'], $temp['type'], $temp['error']); 29. // 文件上传依赖 flysystem 组件这个咱们前面再介绍
2.3 申请类型:
申请类型是指 HTTP 申请类型,HTTP 总共五种申请类型,别离是:GET、POST、PUT、 DELETE、 HEAD。Request 类为咱们提供的接口分为两类,一类是获取以后申请类型,一类是判断是否是某种类型,接下为大家剖析两个代表性API:
获取以后申请类型: method() 办法
<code class="php"> 1. // 获取申请类型 申请类型的获取反对类型伪造,如果伪造申请类型优先获取伪造类型 2. // vendortopthinkframeworksrcthinkRequest.php 717 行 3. public function method(bool $origin = false): string 4. { 5. if ($origin) { 6. // 获取原始申请类型 7. return $this->server('REQUEST_METHOD') ?: 'GET'; 8. } elseif (!$this->method) { 9. if (isset($this->post[$this->varMethod])) { 10. $method = strtolower($this->post[$this->varMethod]); 11. if (in_array($method, ['get', 'post', 'put', 'patch', 'delete'])) { 12. $this->method = strtoupper($method); 13. $this->{$method} = $this->post; 14. } else { 15. $this->method = 'POST'; 16. } 17. unset($this->post[$this->varMethod]); 18. } elseif ($this->server('HTTP_X_HTTP_METHOD_OVERRIDE')) { 19. $this->method = strtoupper($this->server('HTTP_X_HTTP_METHOD_OVERRIDE')); 20. } else { 21. $this->method = $this->server('REQUEST_METHOD') ?: 'GET'; 22. } 23. } 25. return $this->method; 26. }
判断是否AJAX申请: isAjax() 办法
<code class="php"> 1. // 判断是否为 ajax 申请 次要是通过 HTTP_X_REQUESTED_WITH 属性来判断,只有ajax申请才会有这个属性 2. // ajax 申请同样反对类型伪造,并且无限获取伪造类型。 3. // vendortopthinkframeworksrcthinkRequest.php 1545 行 4. public function isAjax(bool $ajax = false): bool 5. { 6. $value = $this->server('HTTP_X_REQUESTED_WITH'); 7. $result = $value && 'xmlhttprequest' == strtolower($value) ? true : false; 9. if (true === $ajax) { 10. return $result; 11. } 13. return $this->param($this->varAjax) ? true : $result; 14. }
2.4 申请头信息:
申请头信息就是指 HTTP 申请报文头,以 “属性名:属性值”的模式组织信息,服务端据此获取绝大部分客户端信息。Request 以两种形式获取此信息,第一种是从 $_SERVER 超全局数组中提取,第二种是 从 apache_request_headers这个办法中获取(前提是要采纳Apache作为web容器),看代码:
<code class="php"> 1. // 在初始化办法中获取 vendortopthinkframeworksrcthinkRequest.php 307 行 2. public static function __make(App $app) 3. { 4. $request = new static(); 6. // 只有在Apache 容器下才会有此办法 7. if (function_exists('apache_request_headers') && $result = apache_request_headers()) { 8. $header = $result; 9. } else { 10. // 没有的话从 $_SERVER 中提取 11. $header = []; 12. $server = $_SERVER; 13. foreach ($server as $key => $val) { 14. if (0 === strpos($key, 'HTTP_')) { 15. $key = str_replace('_', '-', strtolower(substr($key, 5))); 16. $header[$key] = $val; 17. } 18. } 19. if (isset($server['CONTENT_TYPE'])) { 20. $header['content-type'] = $server['CONTENT_TYPE']; 21. } 22. if (isset($server['CONTENT_LENGTH'])) { 23. $header['content-length'] = $server['CONTENT_LENGTH']; 24. } 25. } 26. }
申请信息能够通过 header 办法获取:
<code class="php"> 1. // 设置或者获取申请头 vendortopthinkframeworksrcthinkRequest.php 1221 行 2. public function header(string $name = '', string $default = null) 3. { 4. if ('' === $name) { 5. return $this->header; 6. } 8. $name = str_replace('_', '-', strtolower($name)); 10. return $this->header[$name] ?? $default; 11. }
2.5 申请缓存:
什么是申请缓存?就是将申请的内容缓存在客户端,下次申请的服务端,服务端只需响应状态码,无需响应内容,浏览器主动从缓存中读取,这样能大大晋升用户体验。这么好的性能是如何实现的呢?其实是通过全局中间件实现,那如何开启呢?看代码:
<code class="php"> 1. <?php 2. // 第一步:将全局缓存中间件的正文去掉 3. // 全局中间件定义文件 appmiddleware.php 4. return [ 5. // 全局申请缓存 6. thinkmiddlewareCheckRequestCache::class, 7. // 多语言加载 8. thinkmiddlewareLoadLangPack::class, 9. // Session初始化 10. thinkmiddlewareSessionInit::class 11. ]; 13. // 第二步:将 路由配置中此项设置为 true 14. // configroute.php 30 行 16. // 是否开启申请缓存 true主动缓存 反对设置申请缓存规定 17. 'request_cache_key' => true,
接下来揭晓实现原理,代码如下:
<code class="php"> 1. // 中间件入口函数 且只反对 get 申请 2. public function handle($request, Closure $next, $cache = null) 3. { 4. if ($request->isGet() && false !== $cache) { 5. $cache = $cache ?: $this->getRequestCache($request); 7. if ($cache) { 8. if (is_array($cache)) { 9. [$key, $expire, $tag] = $cache; 10. } else { 11. $key = str_replace('|', '/', $request->url()); 12. $expire = $cache; 13. $tag = null; 14. } 16. if (strtotime($request->server('HTTP_IF_MODIFIED_SINCE', '')) + $expire > $request->server('REQUEST_TIME')) { 17. // 读取缓存 18. return Response::create()->code(304); 19. } elseif (($hit = $this->cache->get($key)) !== null) { 20. [$content, $header, $when] = $hit; 21. if (null === $expire || $when + $expire > $request->server('REQUEST_TIME')) { 22. return Response::create($content)->header($header); 23. } 24. } 25. } 26. } 28. $response = $next($request); 30. if (isset($key) && 200 == $response->getCode() && $response->isAllowCache()) { 31. $header = $response->getHeader(); 32. $header['Cache-Control'] = 'max-age=' . $expire . ',must-revalidate'; 33. $header['Last-Modified'] = gmdate('D, d M Y H:i:s') . ' GMT'; 34. $header['Expires'] = gmdate('D, d M Y H:i:s', time() + $expire) . ' GMT'; 36. $this->cache->tag($tag)->set($key, [$response->getContent(), $header, time()], $expire); 37. } 39. return $response; 40. }
2.6 响应类型:
响应类型是服务端响应客户端内容的模式,框架实现七种响应类型别离是 File、Html、Json、Jsonp、Redirect、View、Xml。其实就是七个响应类,都继承 Response 类,并且重写了局部父类的办法,下图为七个类定义的地位:
接下来别离介绍一下这个七个响应类所代表的含意:
File : 通过批改响应头信息,实现文件下载。
Html: 就是响应 html 页面,这是绝大数响应模式。
Json: 响应json数据,用于 API 响应。
Jsonp: 这是跨域申请响应。
Redirect : 这是重定向。
View: 这是响应视图实质还是html页面,这样做的益处就是无需调用视图外面 fetch 办法也能够渲染模板。
Xml:这是响应 xml 数据。
2.7 响应输入:
响应输入其实在生命周期外面曾经介绍过了,这里再补充几个点。响应输入就是调用 Response 的 send 办法,在这个办法外面有一个重要的操作就是获取输入数据,看代码:
<code class="php"> 1. public function send(): void 2. { 3. // 解决输入数据 4. $data = $this->getContent(); 6. // 省略若干代码 7. } 9. public function getContent(): string 10. { 11. if (null == $this->content) { 12. $content = $this->output($this->data); 14. if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable([ 15. $content, 16. '__toString', 17. ]) 18. ) { 19. throw new InvalidArgumentException(sprintf('variable type error: %s', gettype($content))); 20. } 22. $this->content = (string) $content; 23. } 25. return $this->content; 26. }
output 办法,在除 Html 之外的六个类中都有被重写,这个办法决定输入内容的差别,这个给大家看一下 File 类的 output 办法。代码如下:
<code class="php"> 1. // 此代码实现了一个文件下载性能 vendortopthinkframeworksrcthinkresponseFile.php 2. protected function output($data) 3. { 4. if (!$this->isContent && !is_file($data)) { 5. throw new Exception('file not exists:' . $data); 6. } 8. ob_end_clean(); 10. if (!empty($this->name)) { 11. $name = $this->name; 12. } else { 13. $name = !$this->isContent ? pathinfo($data, PATHINFO_BASENAME) : ''; 14. } 16. if ($this->isContent) { 17. $mimeType = $this->mimeType; 18. $size = strlen($data); 19. } else { 20. $mimeType = $this->getMimeType($data); 21. $size = filesize($data); 22. } 24. $this->header['Pragma'] = 'public'; 25. $this->header['Content-Type'] = $mimeType ?: 'application/octet-stream'; 26. $this->header['Cache-control'] = 'max-age=' . $this->expire; 27. $this->header['Content-Disposition'] = ($this->force ? 'attachment; ' : '') . 'filename="' . $name . '"'; 28. $this->header['Content-Length'] = $size; 29. $this->header['Content-Transfer-Encoding'] = 'binary'; 30. $this->header['Expires'] = gmdate("D, d M Y H:i:s", time() + $this->expire) . ' GMT'; 32. $this->lastModified(gmdate('D, d M Y H:i:s', time()) . ' GMT'); 34. return $this->isContent ? $data : file_get_contents($data); 35. }
2.8 响应参数:
所谓的响应参数就是指响应内容,状态码,响应头。尽管这些信息能够对立设置,同时 Response 也提供了独自的办法就设置这些内容,看代码演示:
响应内容
<code class="php"> 1. // 输入数据设置 2. public function data($data) 3. { 4. $this->data = $data; 6. return $this; 7. }
状态码
<code class="php"> 1. // 设置 HTTP 状态码 2. public function code(int $code) 3. { 4. $this->code = $code; 6. return $this; 7. }
响应头
<code class="php"> 1. // 设置响应头 2. public function header(array $header = []) 3. { 4. $this->header = array_merge($this->header, $header); 6. return $this; 7. }
2.9 重定向:
重定向是属于响应类型的一种,这里就不多做介绍了,间接看框架的实现代码:
<code class="php"> 1. // 实现就是通过 location实现的 这里的 $data 为 url 2. protected function output($data): string 3. { 4. $this->header['Location'] = $data; 6. return ''; 7. }
各位读者,对于申请与响应的介绍到这里就完结了,感激大家的浏览,如果对于文字教程意犹未尽,能够移步上行地址观看视频教程,并且能够自己一对一交换。
教程地址:https://edu.csdn.net/course/detail/28045