PHP 用户请求数据获取与文件上传

我们上篇教程提到,要获取 HTTP 请求数据,可以通过 $_GET$_POST$_REQUEST 等 PHP 内置的超全局变量,如果要获取 Cookie 和文件上传信息,可以通过额外的 $_COOKIE$_FILES。今天,学院君就来给大家演示下如何使用这些超全局变量获取请求数据。

GET 请求参数

我们知道,HTTP GET 请求是没有请求实体(表单请求数据)的,所有对于 GET 请求来说,请求数据以 URL 查询字符串(Query String)的形式提供的,所谓查询字符串,就是 URL 中 ? 之后的请求参数,例如对下面这个 URL 请求来说:

https://xueyuanjun.com/search?term=laravel

term=laravel 就是查询字符串,也就是 GET 请求参数。

我们在 http/index.php 中通过 var_dump($_GET) 打印 GET 请求数据:

<?php

echo '<pre>';
//var_dump($_SERVER);

var_dump($_GET);  

http 目录下通过 php -S localhost:9000 启动 HTTP 服务器,然后在浏览器中访问 http://localhost:9000

-w616

当没有任何请求数据时,打印结果为空,如果请求 URL 中包含了查询字符串:

-w642

则对应的 $_GET 变量值是一个以参数名为键,参数值为值的关联数组。非常简单。显然,要获取某个具体参数值,通过键名获取即可:

$name = $_GET['name'];
$website = $_GET['website'];
printf("用户名: %s, 网站: %s\n", $name, $website);

要注意的是,$_SERVER$_GET$_POST 之类的超全局变量只能在 Web 模式下生效,如果通过命令行访问,因为不是 HTTP 请求,所以会报错:

-w966

POST 表单请求

看完 GET 请求,我们接着来看 POST 请求,对于 Web 页面而言,POST 请求通常就是表单请求,我们在 http 目录下新建一个 form.html 来编写这个 HTML 表单,并引入 Bootstrap CSS 框架来优化样式:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>POST 请求测试</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
    <style>
        div.container {
            margin-top: 50px;
        }
        form {
            margin: 20px;
        }
    </style>
</head>
<body>
<div class="container col-4">
    <h1 class="text-center">登录表单</h1>
    <form method="post" action="index.php">
        <div class="form-group">
            <label for="inputUserName">用户名</label>
            <input type="text" class="form-control" id="inputUserName" name="name">
        </div>
        <div class="form-group">
            <label for="inputPassword">密码</label>
            <input type="password" class="form-control" id="inputPassword" name="password">
        </div>
        <button type="submit" class="btn btn-primary">登录</button>
    </form>
</div>
</body>
</html>

我们在 form 标签中设置 method 属性值为 postaction 属性值为 index.php,即表示点击登录按钮后,表单数据会以 POST 方式提交到 index.php 这个脚本进行处理。

在浏览器中访问 http://localhost:9000/form.html,就可以看到登录表单了:

-w657

修改 index.php 代码如下:

<?php

echo '<pre>';
//var_dump($_SERVER);

//var_dump($_GET);

var_dump($_POST);
$name = $_POST['name'];
$password = $_POST['password'];
printf("用户名: %s, 密码: %s\n", $name, $password);

现在,我们改为通过 $_POST 获取请求数据,使用方式和 $_GET 一样,只不过它接收的是 POST 请求数据。我们切换到登录表单页面,输入数据,点击「登录」提交表单,页面就会跳转到 index.php,并打印出提交数据:

-w543

通过 $_REQUEST 获取请求数据

$_POST 超全局变量是无法获取 GET 请求数据的,同理,$_GET 超全局变量也无法获取 POST 请求数据,比如我们尝试在表单提交 action 对应 URL 中添加查询字符串:

<form method="post" action="index.php?website=https://xueyuanjun.com">

刷新表单页面,重写填写数据提交表单,可以看到打印 $_POST 结果中不包含 website 信息:

-w743

要同时获取 GET 和 POST 请求数据,可以通过 $_REQUEST 超全局变量,我们将 index.php 中的代码调整如下:

var_dump($_REQUEST);
$name = $_REQUEST['name'];
$password = $_REQUEST['password'];
$website = $_REQUEST['website'];
printf("用户名: %s, 密码: %s, 网站: %s\n", $name, $password, $website);

使用方式所有超全局变量都是一样的,只是现在通过 $_REQUEST 既可以获取 POST 请求数据,又可以获取 GET 请求数据,在表单提交页面重新提交表单,打印结果如下:

-w631

文件上传

表单数据除了可以包含普通的文本信息和密码信息外,还可以包含文件信息,不过对于通过表单上传的文件,不能通过之前的 $_GET$_POST$_REQUEST 超全局变量获取,只能通过专门的 $_FILES 超全局变量获取。

文件上传表单

下面我们来简单演示下如何在 PHP 中通过表单上传文件,首先在 http 子目录下新建 file.html 来编写对应的 HTML 表单:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>File Upload</title>
</head>
<body>
<form method="post" action="file.php" enctype="multipart/form-data">
    <input type="file" name="image">
    <input type="submit" value="上传">
</form>
</body>
</html>

需要注意的是文件上传只能通过 POST 请求完成,并且需要额外设置表单属性 enctype 值为 multipart/form-data(默认是 application/x-www-form-urlencoded)。

$_FILES 数据结构

我们在 http 目录下新建一个 PHP 脚本 file.php 来定义服务端逻辑:

<?php
echo '<pre>';
var_dump($_FILES);

首先打印 $_FILES 查看其数据结构,我们在文件上传表单中选择一个本地图片上传,上传成功后,服务端打印结果如下:

-w713

可以看到 $_FILES 是一个键值对关联数组,键名是文件上传组件设置的 name 属性,对应的值也是一个关联数组,其中包含了详细的文件信息,包含文件名、MIME 类型、文件默认上传位置(位于临时目录下)、错误信息以及文件尺寸,我们可以结合这些新息将上传文件保存到指定位置。在 PHP 中,可以通过内置函数 move_uploaded_file 将上传文件从临时目录移动到指定目录。

文件上传处理

下面我们在 file.php 中编写对应的文件上传处理代码:

<?php
//echo '<pre>';
//var_dump($_FILES);

// 获取上传文件
$image = $_FILES['image'];

// 处理文件上传过程中的错误
if ($image['error'] != UPLOAD_ERR_OK) {
    switch ($image['error']) {
        case UPLOAD_ERR_INI_SIZE:
        case UPLOAD_ERR_FORM_SIZE:
            trigger_error('上传文件过大', E_USER_ERROR);
            break;
        case UPLOAD_ERR_PARTIAL:
            trigger_error('上传文件不完整', E_USER_ERROR);
            break;
        case UPLOAD_ERR_NO_FILE:
            trigger_error('没有文件被上传', E_USER_ERROR);
            break;
        case UPLOAD_ERR_NO_TMP_DIR:
            trigger_error('未指定或找不到临时目录', E_USER_ERROR);
            break;
        case UPLOAD_ERR_CANT_WRITE:
            trigger_error('上传目录无法写入', E_USER_ERROR);
            break;
        default:
            trigger_error('其他文件上传错误', E_USER_ERROR);
            break;
    }
}

// 限定上传文件类型
if (!in_array($image['type'], ['image/jpeg', 'image/png', 'image/gif'])) {
    trigger_error('只支持 jpg/jpeg、png、gif 格式图片上传', E_USER_WARNING);
}

// 限定上传文件大小
if ($image['size'] > 1 * 1024 * 1024) {
    trigger_error('上传文件不能超过 1M', E_USER_WARNING);
}

// 设置文件上传路径为 Web 根目录下的 images 子目录
$uploaddir =  __DIR__ . '/images/';
$filepath = $uploaddir . $image['name'];

// 移动上传文件到指定位置
if (move_uploaded_file($image['tmp_name'], $filepath)) {
    // 上传成功,则在页面预览上传的图片
    echo '<font color="green">文件上传成功</font><hr>';
    $webpath = '/images/' . $image['name'];
    echo '<img src="' . $webpath . '">';
} else {
    echo '<font color="red">文件上传失败,请重试!</font>';
}

测试文件上传

最后,我们访问文件上传页面,选择一张本地图片上传,选择之后,点击「上传」按钮开始上传,上传成功后,会在 file.php 页面显示出上传的图片,表明上传成功:

-w588

-w815

好了关于文件上传,我们就简单介绍到这里,更多细节,请阅读 PHP 官方文档

上一篇: 基于 Nginx + PHP-FPM 作为 HTTP 服务器

下一篇: PHP 发送 HTTP 响应与文件下载