前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)

学习目标

本期接上期内容继续学习php基础知识。

HTTP

1. 概要

1.1. 定义

HTTP(HyperText Transfer Protocol,超文本传输协议)最早就是计算机与计算机之间沟通的一种标准协议,这 种协议限制了通讯内容的格式以及各项内容的含义。
前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第1张图片
随着时代的发展,技术的变迁,这种协议现在广泛的应用在各种领域,也不仅仅局限于计算机与计算机之间,手 机、电视等各种智能设备很多时候都在使用这种协议通讯,所以一般现在称 HTTP 为端与端之间的通讯协议。

Web 属于 B/S 架构的应用软件,在 B/S 架构中,浏览器与服务器沟通的协议就是 HTTP 协议,作为一个合格的
Web 开发者,了解 HTTP 协议中约定的内容是一门必修课。

应用软件架构一般分为两类:

B/S 架构:Browser(浏览器) ←→ Server(服务器),这种软件都是通过浏览器访问一个网站使用, 服务器提供数据存储等服务。
C/S 架构:Client(客户端) ←→ Server(服务器),这种软件通过安装一个软件到电脑,然后使用, 服务器提供数据存储等服务。

1.2. 约定内容

  1. 请求 / 响应报文格式 请求方法 —— GET / POST
  2. 响应状态 —— 200 / 404 / 302 / 304
  3. 预设的请求 / 响应头

1.3. 约定形式

  1. 客户端通过随机端口与服务端某个固定端口(一般为80)建立连接 三次握手
  2. 客户端通过这个连接发送请求到服务端(这里的请求是名词)
  3. 服务端监听端口得到的客户端发送过来的请求
  4. 服务端通过连接响应给客户端状态和内容
要求:接下来的一个月,每次上网打开任何一个页面时都要能够脑补这个画面,默念这个流程

2. 核心概念

2.1. 报文

2.1.1. 请求报文

前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第2张图片
前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第3张图片
请求行
GET /demo.php HTTP/1.1

请求方式 + 空格 + 请求路径 + 空格 + HTTP 协议版本

请求头 客户端想要告诉服务端的一些额外信息,以下为常见的请求头:

Host 请求的主机
Cache-Control 控制缓存(例如:max-age=60 缓存 60 秒)
Accept 客户端想要接收的文档类型,逗号分隔
User-Agent 标识什么客户端帮你发送的这次请求
Referer 这次请求的来源
Accept-Encoding 可以接受的压缩编码
Cookie 客户端本地的小票信息

请求体

这次请求客户端想要发送给服务端的数据正文,一般在 GET 请求时很少用到,因为 GET 请求主观上都是去“拿东 西”。

2.1.2. 响应报文

前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第4张图片
状态行

HTTP/1.1 200 OK

HTTP 协议版本 + 空格 + 状态码 + 空格 + 状态描述

响应头 服务端想要告诉客户端的一些额外信息,常见的有以下:

Date 响应时间
Server 服务器信息
Content-Type 响应体的内容类型
Content-Length 响应的内容大小
Set-Cookie 让客户端设置一个小票

如果需要在程序中设置自定义的响应头(不是预设的),建议使用 X-Property-Name

响应体

这次请求服务端想要返回给客户端的数据正文,一般返回的都是 HTML,也可以返回 JavaScript 或者 CSS(需要修 改响应头中的响应类型)。

2.1.3. 应用场景

设置响应文件类型

header('Content-Type: text/css');
HTTP MIME type 指的就是 像 text/css text/html text/plain applcation/javascript

重定向(跳转到其他网页)

header('Location: https://www.baidu.com');

下载文件

// 让文件下载
2	header('Content‐Type: application/octet‐stream'); 
3	// 设置默认下载文件名
4	header('Content‐Disposition: attachment; filename=demo.txt');

图片防盗链

通过判断请求来源 是否为本网站从而区分是否是合法请求

2.2. 请求方式

2.2.1. GET

字面意思:拿,获取

2.2.2. POST

字面意思:发,给

2.2.3. 对比 GET 与 POST
GET POST
后退 按钮/ 刷新 无害 数据会被重新提交(浏览器应该告知用户 数据会被重新提交)。
书签 可收藏为书签 不可收藏为书签
缓存 能被缓存 不能缓存
编码 类型 application/x-www-form-urlencoded application/x-www-form-urlencoded 或 multipart/form-data。为二进制数据 使用多重编码。
历史 参数保留在浏览器历史中。 参数不会保存在浏览器历史中。
对数据长度的限制 是的。当发送数据时,GET 方法向 URL 添加数据; URL 的长度是受限制的(URL 的最大长度是 2048 个 字符)。 无限制。
对数据类型的限制 只允许 ASCII 字符。 没有限制。也允许二进制数据。
安全 性 与 POST 相比,GET 的安全性较差,因为所发送的数 据是 URL 的一部分。在发送密码或其他敏感信息时绝 不要使用 GET ! POST 比 GET 更安全,因为参数不会被 保存在浏览器历史或 web 服务器日志 中。
可见 性 数据在 URL 中对所有人都是可见的。 数据不会显示在 URL 中。

2.3. 状态码

了解即可,不用刻意去记忆,用多了自然就忘不了。
http://www.w3school.com.cn/tags/html_ref_httpmessages.asp

状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。

1xx:指示信息 —— 表示请求已接收,继续处理。

2xx:成功 —— 表示请求已被成功接收、理解、接受。

3xx:重定向 —— 要完成请求必须进行更进一步的操作。

4xx:客户端错误 —— 请求有语法错误或请求无法实现。

5xx:服务器端错误 —— 服务器未能实现合法的请求。

常见状态代码、状态描述的说明如下。

200 OK:客户端请求成功。

400 Bad Request:客户端请求有语法错误,不能被服务器所理解。

401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用。

403 Forbidden:服务器收到请求,但是拒绝提供服务。

404 Not Found:请求资源不存在,举个例子:输入了错误的URL。

500 Internal Server Error:服务器发生不可预期的错误。

503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常。

设置响应头的应用场景

首先我们先看:

<?php 
//php中Header函数专门用来设置响应头
	header('Content-Type:text/html');
?>
<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<title>这是一个网页</title>
</head>
<body>
	<h1>这是一个网页内容</h1>
</body>
</html>

前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第5张图片
报文里响应类型为html。

修改源代码:

<?php 
//php中Header函数专门用来设置响应头
	header('Content-Type:text/css');
?>
<!DOCTYPE html>
<html lang="zh">
<head>
	<meta charset="UTF-8">
	<title>这是一个网页</title>
</head>
<body>
	<h1>这是一个网页内容</h1>
</body>
</html>

前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第6张图片
页面不再渲染,响应类型为css。

所以,你想让某一个文件以什么类型来响应就设置header就行了。

设置响应头跳转:

<?php

// 这里是在 响应头中添加一个 location 的头信息
// header('Location: content-type.php');
// 客户端浏览器在接收到这个头信息过后会自动跳转到 指定的地址

// 切记不能循环重定向
header('Location: 本页面.php');

下载响应头:
a.php:

<?php 
header('Content-Type:application/octet-stream');
?>
下载内容。。。

Download.php:

<a href="a.php">下载</a>

前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第7张图片
以下继续完成上期的音乐列表案例:

5. 音乐列表案例

基于文件存储的音乐数据增删改查

5.1. 思路分析

绝大多数情况下我们编写的应用功能都是在围绕着某种类型的数据做增删改查(Create / Read / Update / Delete)

对于被增删改查的数据有几点是可以明确的:

  1. 结构相同的多条数据(可以认为是:一个数组,数组中的元素都具有相同的属性结构)

  2. 可以持久化(长久保存)

5.1.1. 数据放在哪?

我们第一件事就是考虑数据放在哪(怎么存怎么取)?
目前我们接触到的技术方案中,只有文件可以持久化的保存内容(数据),所以一定是用文件存我们要操作的数 据。
但是由于我们要存放的是一个有着复杂结构的数据,并不是简简单单的值,所以我们必须设计一种能表示复杂结构 数据的方式。

例如,一行为一条数据,不同信息之间用 | 分割,同时约定好每个位置的数据含义:

59d632855434e | 错过 | 梁咏琪 | /uploads/img/1.jpg | /uploads/mp3/1.mp3

59d632855434f | 开始懂了 | 孙燕姿 | /uploads/img/2.jpg | /uploads/mp3/2.mp3

59d6328554350 | 一生中最爱 | 谭咏麟 | /uploads/img/3.jpg | /uploads/mp3/3.mp3

59d6328554351 | 爱在深秋 | 谭咏麟 | /uploads/img/4.jpg | /uploads/mp3/4.mp3

这种方式很简单,但是缺点也非常明显,所以我们迫切需要一个更好更方便的表示有结构数据的方式。

5.2. JSON

JSON(JavaScript Object Notation) 是一种通过普通字符串描述数据的手段,用于表示有结构的数据。类似于编 程语言中字面量的概念,语法上跟 JavaScript 的字面量非常类似。

5.2.1. 数据类型

  • null
null
  • string
"hello json"
  • number
2048
  • boolean
true
  • object
{   
"name": "zce",   
"age": 18,   
"gender": true,   
"girl_friend": null 
}
  • array
["zhangsan", "lisi", "wangwu"]

5.2.2. 注意

  1. JSON 中属性名称必须用双引号包裹
  2. JSON 中表述字符串必须使用双引号
  3. JSON 中不能有单行或多行注释
  4. JSON 没有 undefined 这个值

JSON 表述

有了 JSON 这种格式,我们就可以更加容易的表示拥有复杂结构的数据了。

[   {     "id": "59d632855434e",     "title": "错过",     "artist": "梁咏琪",     "images": ["/uploads/img/1.jpg"],     "source": "/uploads/mp3/1.mp3"   },   {     "id": "59d632855434f",     "title": "开始懂了",     "artist": "孙燕姿",     "images": ["/uploads/img/2.jpg"],     "source": "/uploads/mp3/2.mp3"   },   {     "id": "59d6328554350",     "title": "一生中最爱",     "artist": "谭咏麟",     "images": ["/uploads/img/3.jpg"],     "source": "/uploads/mp3/3.mp3"   },   {     "id": "59d6328554351",     "title": "爱在深秋",     "artist": "谭咏麟",     "images": ["/uploads/img/4.jpg"],     "source": "/uploads/mp3/4.mp3"   } ]

5.3. 功能实现

在服务端开发领域中所谓的渲染指的是经过程序执行得到最终的 HTML 字符串这个过程。

5.3.1. 列表数据展示(展示类)

  • 文件读取
  • JSON 反序列化
  • json_decode 需要注意第二个参数
  • 如果希望以关联数组的方式而非对象的方式操作数据,可以将 json_decode 的第二个参数设置为 true
  • 数组遍历 foreach
  • PHP 与 HTML 混编

5.3.2. 新增数据(表单类)

前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第8张图片

  • 表单使用(form action method enctype,input name label for id)

  • 服务端表单校验并提示错误消息
    empty 判断一个成员是否没定义或者值为 false(可以隐式转换为 false)

  • 上传文件

  1. 文件数量
  2. 文件种类
  3. 如果需要考虑文件重名的情况,可以给上传的文件重新命名(唯一名称)
  • 单文件域多文件上传
    name 一定 以 [] 结尾,服务端会接收到一个数组
  • JSON 序列化
  • 文件写入

5.3.3. 删除数据

  • 问号传参
    一般情况下,如果需要超链接点击发起的请求可以传递参数,我们可以采用 ? 的方式
<a href="/delete.php?id=123">删除</a>
  • 数组移除元素
    array_splice

源代码:

整体目录:
前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第9张图片
首先是bootstrap.css前端框架,可以去bootstarp官网下载,网络上也有资源。

JSON数据:storage.json

[{"id":"59d632855434e","title":"\u9519\u8fc7","artist":"\u6881\u548f\u742a","images":["\/uploads\/img\/1.jpg"],"source":"\/uploads\/mp3\/1.mp3"},{"id":"59d632855434f","title":"\u5f00\u59cb\u61c2\u4e86","artist":"\u5b59\u71d5\u59ff","images":["\/uploads\/img\/2.jpg"],"source":"\/uploads\/mp3\/2.mp3"},{"id":"59d6328554350","title":"\u4e00\u751f\u4e2d\u6700\u7231","artist":"\u8c2d\u548f\u9e9f","images":["\/uploads\/img\/3.jpg"],"source":"\/uploads\/mp3\/3.mp3"},{"id":"59d6328554351","title":"\u7231\u5728\u6df1\u79cb","artist":"\u8c2d\u548f\u9e9f","images":["\/uploads\/img\/4.jpg"],"source":"\/uploads\/mp3\/4.mp3"},{"id":"59f0592aa33d8","title":"123123","artist":"123123","images":"123","source":"1231"}]

list.php:

<?php

// 获取文件中记录的数据,并展示到表格中(动态生成表格的HTML标签)
$contents = file_get_contents('storage.json');
// $contents => JSON 格式的字符串
// 把 JSON 格式的字符串转换为对象的过程叫做反序列化

// json_decode 默认反序列化时 将 JSON 中的对象转换为 PHP 中 stdClass 类型的对象
$data = json_decode($contents, true);
// $data => []

?>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>音乐列表</title>
  <link rel="stylesheet" href="bootstrap.css">
</head>
<body>
  <div class="container py-5">
    <h1 class="display-4">音乐列表</h1>
    <hr>
    <div class="mb-3">
      <a href="add.php" class="btn btn-secondary btn-sm">添加</a>
    </div>
    <table class="table table-bordered table-striped table-hover">
      <thead class="thead-dark">
        <tr>
          <th class="text-center">标题</th>
          <th class="text-center">歌手</th>
          <th class="text-center">海报</th>
          <th class="text-center">音乐</th>
          <th class="text-center">操作</th>
        </tr>
      </thead>
      <tbody class="text-center">
        <?php foreach ($data as $item): ?>
        <tr>
          <td><?php echo $item['title'] ?></td>
          <td><?php echo $item['artist'] ?></td>
          <td><img src="$item['images'][0] ?>" alt=""></td>
          <td><audio src="$item['source'] ?>" controls></audio></td>
          <td><button class="btn btn-danger btn-sm">删除</button></td>
        </tr>
        <?php endforeach ?>
      </tbody>
    </table>
  </div>
</body>
</html>

add.php

<?php

/**
 * 只是在表单提交时执行
 */
function add_music () {
  // 目标
  //  将用户提交过来的数据保存到 storage.json 中
  // 步骤
  //  1. 接收并校验
  //  2. 持久化
  //  3. 响应

  // 文本框校验
  // =====================================================

  if (empty($_POST['title'])) {
    $GLOBALS['error_message'] = '请输入标题';
    return;
  }

  if (empty($_POST['artist'])) {
    $GLOBALS['error_message'] = '请输入歌手';
    return;
  }

  // 校验上传文件
  // =====================================================

  if (empty($_FILES['source'])) {
    // 客户端提交的表单中没有 source 文件域
    $GLOBALS['error_message'] = '请正确提交文件';
    return;
  }

  $source = $_FILES['source'];

  // 判断用户是否选择了文件
  if ($source['error'] !== UPLOAD_ERR_OK) {
    $GLOBALS['error_message'] = '请选择音乐文件';
    return;
  }

  // 校验文件的大小
  if ($source['size'] > 10 * 1024 * 1024) {
    $GLOBALS['error_message'] = '音乐文件过大';
    return;
  }

  if ($source['size'] < 1 * 1024 * 1024) {
    $GLOBALS['error_message'] = '音乐文件过小';
    return;
  }

  // 校验类型
  $allowed_types = array('audio/mp3', 'audio/wma');
  if (!in_array($source['type'], $allowed_types)) {
    $GLOBALS['error_message'] = '这是不支持的音乐格式';
    return;
  }

  // 音乐文件已经上传成功,但是还在临时目录中
  // 一般情况会将上传的文件重命名
  $target = './uploads/' . uniqid() . '-' . $source['name'];
  if (!move_uploaded_file($source['tmp_name'], $target)) {
    $GLOBALS['error_message'] = '上传音乐失败';
    return;
  }

  // =========== 接收图片 ==============

  if (empty($_FILES['images'])) {
    // 客户端提交的表单中没有 source 文件域
    $GLOBALS['error_message'] = '请正确提交文件';
    return;
  }

  $images = $_FILES['images'];

  // 判断用户是否选择了文件
  if ($images['error'] !== UPLOAD_ERR_OK) {
    $GLOBALS['error_message'] = '请选择图片文件';
    return;
  }

  // 校验文件的大小

  if ($images['size'] > 1 * 1024 * 1024) {
    $GLOBALS['error_message'] = '图片文件过大';
    return;
  }

  // 校验类型
  $allowed_types = array('image/jpeg', 'image/png', 'image/gif');
  if (!in_array($images['type'], $allowed_types)) {
    $GLOBALS['error_message'] = '这是不支持的图片格式';
    return;
  }

  // 音乐文件已经上传成功,但是还在临时目录中
  // 一般情况会将上传的文件重命名
  $target = './uploads/' . uniqid() . '-' . $images['name'];
  if (!move_uploaded_file($images['tmp_name'], $target)) {
    $GLOBALS['error_message'] = '上传图片失败';
    return;
  }

  // 图片音乐都上传成功
  $title = $_POST['title'];
  $artist = $_POST['artist'];
  $images = '图片';
  $source = '音乐';

  $origin = json_decode(file_get_contents('storage.json'), true);

  $origin[] = array(
    'id' => uniqid(),
    'title' => $_POST['title'],
    'artist' => $_POST['artist'],
    'images' => '123',
    'source' => '1231',
  );

  $json = json_encode($origin);

  file_put_contents('storage.json', $json);

  // 跳转回列表页
  header('Location: list.php');
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  add_music();
}

?>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>添加新音乐</title>
  <link rel="stylesheet" href="bootstrap.css">
</head>
<body>
  <div class="container py-5">
    <h1 class="display-4">添加新音乐</h1>
    <hr>
    <?php if (isset($error_message)): ?>
    <div class="alert alert-danger" role="alert">
      <?php echo $error_message; ?>
    </div>
    <?php endif ?>
    <form action="$_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data" autocomplete="off">
      <div class="form-group">
        <label for="title">标题</label>
        <input type="text" class="form-control" id="title" name="title">
      </div>
      <div class="form-group">
        <label for="artist">歌手</label>
        <input type="text" class="form-control" id="artist" name="artist">
      </div>
      <div class="form-group">
        <label for="images">海报</label>
        <input type="file" class="form-control" id="images" name="images">
      </div>
      <div class="form-group">
        <label for="source">音乐</label>
        <!-- accept 可以限制文件域能够选择的文件种类,值是 MIME Type -->
        <input type="file" class="form-control" id="source" name="source" accept="audio/*">
      </div>
      <button class="btn btn-primary btn-block">保存</button>
    </form>
  </div>
</body>
</html>

前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第10张图片
前端学习从入门到高级全程记录之41 (PHP基础Ⅳ)_第11张图片

参考链接

  • HTML 中的 form 标签:http://www.w3school.com.cn/html/html_forms.asp
  • PHP 中处理表单:http://www.w3school.com.cn/php/php_forms.asp
  • Emmet 手册:https://docs.emmet.io/cheat-sheet/

总结

本期学习到此结束,下期继续学习php。

你可能感兴趣的:(PHP,php,html)