前言
在现代Web开发中,API接口已成为前后端分离架构的核心。PHP作为最流行的服务器端脚本语言之一,以其高效、灵活的特性在API开发中占据重要地位。本教程将带你从零开始,使用PHP构建一个高性能的RESTful API,并集成JWT身份验证机制。
一、环境准备与项目结构
1.1 开发环境配置
首先确保你的系统已安装以下组件:
- PHP 7.4或更高版本(需开启openssl扩展)
- Composer(PHP依赖管理工具)
- Apache/Nginx Web服务器
- MySQL数据库
1.2 创建项目并初始化
通过Composer初始化项目并安装必要依赖:
mkdir php-api-project
cd php-api-project
composer init
composer require firebase/php-jwt
composer require vlucas/phpdotenv
1.3 项目目录结构
php-api-project/
├── config/
│ └── database.php # 数据库配置
├── controllers/ # 控制器目录
├── models/ # 模型目录
├── utils/ # 工具类目录
├── vendor/ # Composer依赖
├── .env # 环境变量
├── .htaccess # Apache重写规则
└── index.php # 入口文件
二、数据库设计与连接
2.1 创建数据库表
我们创建一个简单的用户系统和文章管理系统:
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 文章表
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
2.2 数据库配置文件
创建数据库配置文件,使用环境变量管理敏感信息:
// config/database.php
<?php
class Database {
private $host;
private $db_name;
private $username;
private $password;
public $conn;
public function __construct() {
$this->host = getenv('DB_HOST');
$this->db_name = getenv('DB_NAME');
$this->username = getenv('DB_USER');
$this->password = getenv('DB_PASS');
}
public function getConnection() {
$this->conn = null;
try {
$this->conn = new PDO(
"mysql:host=" . $this->host . ";dbname=" . $this->db_name,
$this->username,
$this->password
);
$this->conn->exec("set names utf8");
} catch(PDOException $exception) {
echo "Connection error: " . $exception->getMessage();
}
return $this->conn;
}
}
?>
2.3 环境变量配置
// .env文件
DB_HOST=localhost
DB_NAME=api_database
DB_USER=root
DB_PASS=password
JWT_SECRET=your_super_secret_key_here
三、实现JWT身份验证
3.1 JWT工具类
创建JWT处理工具类,用于生成和验证Token:
// utils/JWTHandler.php
<?php
require_once 'vendor/autoload.php';
use FirebaseJWTJWT;
use FirebaseJWTKey;
class JWTHandler {
private $secretKey;
public function __construct() {
$this->secretKey = getenv('JWT_SECRET');
}
public function generateToken($userId, $username) {
$issuedAt = time();
$expire = $issuedAt + (60 * 60); // 1小时有效期
$payload = array(
"iat" => $issuedAt,
"exp" => $expire,
"iss" => "php-api-server",
"data" => array(
"id" => $userId,
"username" => $username
)
);
return JWT::encode($payload, $this->secretKey, 'HS256');
}
public function validateToken($token) {
try {
$decoded = JWT::decode($token, new Key($this->secretKey, 'HS256'));
return (array) $decoded->data;
} catch (Exception $e) {
return false;
}
}
}
?>
3.2 用户认证控制器
实现用户注册和登录接口:
// controllers/AuthController.php
<?php
require_once 'config/database.php';
require_once 'utils/JWTHandler.php';
class AuthController {
private $db;
private $jwtHandler;
public function __construct() {
$database = new Database();
$this->db = $database->getConnection();
$this->jwtHandler = new JWTHandler();
}
public function register($data) {
// 验证输入数据
if (!isset($data->username) || !isset($data->email) || !isset($data->password)) {
return array("message" => "缺少必要字段", "status" => 400);
}
// 检查用户是否已存在
$query = "SELECT id FROM users WHERE username = :username OR email = :email";
$stmt = $this->db->prepare($query);
$stmt->bindParam(":username", $data->username);
$stmt->bindParam(":email", $data->email);
$stmt->execute();
if ($stmt->rowCount() > 0) {
return array("message" => "用户名或邮箱已存在", "status" => 409);
}
// 创建用户
$query = "INSERT INTO users SET username=:username, email=:email, password=:password";
$stmt = $this->db->prepare($query);
$stmt->bindParam(":username", $data->username);
$stmt->bindParam(":email", $data->email);
// 哈希密码
$password_hash = password_hash($data->password, PASSWORD_BCRYPT);
$stmt->bindParam(":password", $password_hash);
if ($stmt->execute()) {
return array(
"message" => "用户注册成功",
"status" => 201,
"user_id" => $this->db->lastInsertId()
);
} else {
return array("message" => "注册失败", "status" => 500);
}
}
public function login($data) {
// 验证输入
if (!isset($data->username) || !isset($data->password)) {
return array("message" => "请输入用户名和密码", "status" => 400);
}
// 查询用户
$query = "SELECT id, username, password FROM users WHERE username = :username";
$stmt = $this->db->prepare($query);
$stmt->bindParam(":username", $data->username);
$stmt->execute();
if ($stmt->rowCount() == 1) {
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$id = $row['id'];
$username = $row['username'];
$password = $row['password'];
// 验证密码
if (password_verify($data->password, $password)) {
// 生成JWT令牌
$token = $this->jwtHandler->generateToken($id, $username);
return array(
"message" => "登录成功",
"status" => 200,
"token" => $token
);
}
}
return array("message" => "登录失败,用户名或密码错误", "status" => 401);
}
}
?>
四、实现RESTful API接口
4.1 文章控制器
实现文章的增删改查接口:
// controllers/PostController.php
<?php
require_once 'config/database.php';
require_once 'utils/JWTHandler.php';
class PostController {
private $db;
private $table_name = "posts";
public function __construct() {
$database = new Database();
$this->db = $database->getConnection();
}
// 获取所有文章
public function read() {
$query = "SELECT p.id, p.title, p.content, p.created_at, u.username
FROM " . $this->table_name . " p
LEFT JOIN users u ON p.user_id = u.id
ORDER BY p.created_at DESC";
$stmt = $this->db->prepare($query);
$stmt->execute();
$posts = array();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
array_push($posts, $row);
}
return array("data" => $posts, "status" => 200);
}
// 创建文章
public function create($data, $user_id) {
if (!isset($data->title) || !isset($data->content)) {
return array("message" => "标题和内容不能为空", "status" => 400);
}
$query = "INSERT INTO " . $this->table_name . "
SET title=:title, content=:content, user_id=:user_id";
$stmt = $this->db->prepare($query);
$stmt->bindParam(":title", $data->title);
$stmt->bindParam(":content", $data->content);
$stmt->bindParam(":user_id", $user_id);
if ($stmt->execute()) {
return array(
"message" => "文章创建成功",
"status" => 201,
"post_id" => $this->db->lastInsertId()
);
} else {
return array("message" => "创建文章失败", "status" => 500);
}
}
// 更新文章
public function update($id, $data, $user_id) {
// 先检查文章是否存在且属于当前用户
$query = "SELECT id FROM " . $this->table_name . "
WHERE id = :id AND user_id = :user_id";
$stmt = $this->db->prepare($query);
$stmt->bindParam(":id", $id);
$stmt->bindParam(":user_id", $user_id);
$stmt->execute();
if ($stmt->rowCount() == 0) {
return array("message" => "文章不存在或无权操作", "status" => 404);
}
$query = "UPDATE " . $this->table_name . "
SET title=:title, content=:content
WHERE id=:id";
$stmt = $this->db->prepare($query);
$stmt->bindParam(":title", $data->title);
$stmt->bindParam(":content", $data->content);
$stmt->bindParam(":id", $id);
if ($stmt->execute()) {
return array("message" => "文章更新成功", "status" => 200);
} else {
return array("message" => "更新文章失败", "status" => 500);
}
}
// 删除文章
public function delete($id, $user_id) {
// 先检查文章是否存在且属于当前用户
$query = "SELECT id FROM " . $this->table_name . "
WHERE id = :id AND user_id = :user_id";
$stmt = $this->db->prepare($query);
$stmt->bindParam(":id", $id);
$stmt->bindParam(":user_id", $user_id);
$stmt->execute();
if ($stmt->rowCount() == 0) {
return array("message" => "文章不存在或无权操作", "status" => 404);
}
$query = "DELETE FROM " . $this->table_name . " WHERE id = :id";
$stmt = $this->db->prepare($query);
$stmt->bindParam(":id", $id);
if ($stmt->execute()) {
return array("message" => "文章删除成功", "status" => 200);
} else {
return array("message" => "删除文章失败", "status" => 500);
}
}
}
?>
4.2 API路由与入口文件
创建统一的入口文件处理所有API请求:
// index.php
<?php
// 允许跨域请求
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// 处理预检请求
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
exit(0);
}
// 加载环境变量和自动加载
require_once 'vendor/autoload.php';
$dotenv = DotenvDotenv::createImmutable(__DIR__);
$dotenv->load();
// 获取请求方法和路径
$requestMethod = $_SERVER["REQUEST_METHOD"];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$pathSegments = explode('/', trim($path, '/'));
// 路由分发
switch ($pathSegments[0]) {
case 'auth':
require_once 'controllers/AuthController.php';
$auth = new AuthController();
$data = json_decode(file_get_contents("php://input"));
if ($requestMethod == 'POST' && $pathSegments[1] == 'register') {
$result = $auth->register($data);
http_response_code($result['status']);
echo json_encode($result);
} elseif ($requestMethod == 'POST' && $pathSegments[1] == 'login') {
$result = $auth->login($data);
http_response_code($result['status']);
echo json_encode($result);
}
break;
case 'posts':
require_once 'controllers/PostController.php';
require_once 'utils/JWTHandler.php';
$post = new PostController();
// JWT验证
$headers = apache_request_headers();
$token = isset($headers['Authorization']) ? str_replace('Bearer ', '', $headers['Authorization']) : '';
$jwt = new JWTHandler();
$userData = $jwt->validateToken($token);
if (!$userData && $requestMethod != 'GET') {
http_response_code(401);
echo json_encode(array("message" => "访问被拒绝,需要有效令牌"));
exit;
}
if ($requestMethod == 'GET') {
$result = $post->read();
http_response_code($result['status']);
echo json_encode($result);
} elseif ($requestMethod == 'POST') {
$data = json_decode(file_get_contents("php://input"));
$result = $post->create($data, $userData['id']);
http_response_code($result['status']);
echo json_encode($result);
} elseif ($requestMethod == 'PUT' && isset($pathSegments[1])) {
$data = json_decode(file_get_contents("php://input"));
$result = $post->update($pathSegments[1], $data, $userData['id']);
http_response_code($result['status']);
echo json_encode($result);
} elseif ($requestMethod == 'DELETE' && isset($pathSegments[1])) {
$result = $post->delete($pathSegments[1], $userData['id']);
http_response_code($result['status']);
echo json_encode($result);
}
break;
default:
http_response_code(404);
echo json_encode(array("message" => "接口不存在"));
break;
}
?>
五、API测试与部署
5.1 使用Postman测试API
使用Postman或其他API测试工具测试我们的接口:
- 注册用户:POST /auth/register
{"username":"testuser","email":"test@example.com","password":"mypassword"} - 用户登录:POST /auth/login
{"username":"testuser","password":"mypassword"} - 获取文章列表:GET /posts
- 创建文章:POST /posts (需在Header中添加Authorization: Bearer [token])
{"title":"我的第一篇文章","content":"这是文章内容..."}
5.2 部署到生产环境
部署注意事项:
- 确保生产环境的PHP版本符合要求
- 设置正确的文件权限
- 配置Web服务器重写规则(.htaccess用于Apache)
- 使用环境变量管理敏感信息
- 启用HTTPS加密传输
5.3 .htaccess配置
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
结语
通过本教程,我们完成了一个完整的PHP API开发项目,包含了用户认证、JWT令牌管理、RESTful接口设计等核心功能。这个项目可以作为开发更复杂API系统的基础框架。
在实际项目中,你还可以进一步扩展功能,如添加API速率限制、数据验证、更复杂的权限管理系统、API文档生成等。PHP虽然是一门历史悠久的语言,但在现代Web开发中仍然具有强大的生命力和广泛的应用场景。

