发布于 2026-01-06 1 阅读
0

使用 PHP 构建一个简单的 REST API PHP REST API 框架 为您的 PHP REST API 配置数据库 为 Post 表添加类并实现 PHP REST API 完成 下一步 0 /Applications/XAMPP/xamppfiles/htdocs/api/start.php(7): Dotenv\Dotenv->__construct('/Applications/X...') 1 {main}

用 PHP 构建一个简单的 REST API

PHP REST API 框架

配置 PHP REST API 的数据库

为帖子表添加类并实现 PHP REST API

完毕

接下来会发生什么?

0 /Applications/XAMPP/xamppfiles/htdocs/api/start.php(7): Dotenv\Dotenv->__construct('/Applications/X...')

1 {主}

REST (Representational State Transfer 是一种软件架构风格,它定义了一组用于创建 Web 服务的约束条件。REST API 是现代 Web 开发的支柱之一。如今,大多数 Web 应用程序的前端都是单页应用程序 ( SPA ),后端则使用不同的语言编写 API。虽然有很多 PHP 框架可以帮助您在几分钟内构建 REST API,但让我们先来学习如何使用纯 PHP 构建一个简单的 REST API。

这是本系列的第一部分Learn PHP,您将学习如何使用 PHP 核心代码构建 REST API。

接下来两个:

先决条件

PHP REST API 框架

创建一个/src目录,并composer.json在顶层目录中创建一个文件,该文件包含一个依赖项:DotEnv库,该库允许在文件中存储受保护的信息片段.env

composer.json



{
  "require": {
    "vlucas/phpdotenv": "^5.3"
  },
  "autoload": {
    "psr-4": {
      "Src\\": "src/"
    }
  }
}


Enter fullscreen mode Exit fullscreen mode

PSR-4 自动加载器会自动在目录中查找 PHP 类/src

立即安装依赖项:



composer install


Enter fullscreen mode Exit fullscreen mode

它将创建一个/vendor目录,并安装 DotEnv 依赖项(自动加载器将从该目录加载类,/src无需任何include()调用)。

为你的项目创建一个.gitignore文件,文件中只包含两行内容,这样/vendor目录和本地.env文件就会被忽略:

.gitignore



vendor/
.env


Enter fullscreen mode Exit fullscreen mode

接下来,创建一个.env.example用于存放秘密变量的文件:

.env.example



DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=


Enter fullscreen mode Exit fullscreen mode

还有一个.env文件,您可以在其中稍后填写您的实际详细信息(Git 会忽略该文件,因此它不会出现在您的存储库中)。

创建一个start.php加载环境变量的文件。

start.php



<?php
require 'vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->safeLoad();

echo $_ENV['DB_HOST'];

// test code:
// it will output: localhost
// when you run $ php start.php


Enter fullscreen mode Exit fullscreen mode

配置 PHP REST API 的数据库

我们将使用 MySQL 来驱动我们简单的 API。
请为您的应用创建一个新的数据库和用户:



mysql -u root -p
CREATE DATABASE blog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'rest_api_user'@'localhost' identified by 'rest_api_password';
GRANT ALL on blog.* to 'rest_api_user'@'localhost';
quit


Enter fullscreen mode Exit fullscreen mode

REST API 将包含我们Blog应用程序的帖子,包含以下字段:,,,,,id它允许用户在我们的应用程序上发布他们的博客titlebodyauthorauthor_picturecreated_atBlog

在MySQL中创建数据库表。



mysql -u rest_api_user -p;
// Enter your password
use blog;

CREATE TABLE `post` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `body` text NOT NULL,
  `author` varchar(255),
  `author_picture` varchar(255),
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
);



Enter fullscreen mode Exit fullscreen mode

将数据库连接变量添加到您的.env文件中:

.env



DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=blog
DB_USERNAME=rest_api_user
DB_PASSWORD=rest_api_password


Enter fullscreen mode Exit fullscreen mode

创建一个类来保存数据库连接,并将连接的初始化代码添加到start.php文件中。

src/Database.php



<?php
namespace Src;

class Database {

  private $dbConnection = null;

  public function __construct()
  {
    $host = $_ENV['DB_HOST'];
    $port = $_ENV['DB_PORT'];
    $db   = $_ENV['DB_DATABASE'];
    $user = $_ENV['DB_USERNAME'];
    $pass = $_ENV['DB_PASSWORD'];

    try {
      $this->dbConnection = new \PDO(
          "mysql:host=$host;port=$port;dbname=$db",
          $user,
          $pass
      );
    } catch (\PDOException $e) {
      exit($e->getMessage());
    }
  }

  public function connet()
  {
    return $this->dbConnection;
  }
}


Enter fullscreen mode Exit fullscreen mode

start.php



<?php
require 'vendor/autoload.php';

use Src\Database;

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->safeLoad();

$dbConnection = (new Database())->connet();


Enter fullscreen mode Exit fullscreen mode

为帖子表添加类并实现 PHP REST API

在面向对象的环境中,与数据库交互的方式有很多种,但是,让我们采用一种简单的方法,您将实现返回所有帖子、返回特定帖子以及添加/更新/删除帖子的方法。

此外,还有将由我们的前端处理的 API 端点api/index.php

REST API,包含以下端点:

您的 API

API 增删改查 描述
GET /posts post从表中获取所有帖子
GET /post/{id} post从表中获取单个帖子
POST /post 创造 创建帖子并插入到post表中
PUT /post/{id} 更新 post更新表格中的帖子
删除 /post/{id} 删除 post从表中删除帖子

api/index.php



<?php
require "../start.php";
use Src\Post;

header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: OPTIONS,GET,POST,PUT,DELETE");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = explode( '/', $uri );

// endpoints starting with `/post` or `/posts` for GET shows all posts
// everything else results in a 404 Not Found
if ($uri[1] !== 'post') {
  if($uri[1] !== 'posts'){
    header("HTTP/1.1 404 Not Found");
    exit();
  }
}

// endpoints starting with `/posts` for POST/PUT/DELETE results in a 404 Not Found
if ($uri[1] == 'posts' and isset($uri[2])) {
    header("HTTP/1.1 404 Not Found");
    exit();
}

// the post id is, of course, optional and must be a number
$postId = null;
if (isset($uri[2])) {
    $postId = (int) $uri[2];
}

$requestMethod = $_SERVER["REQUEST_METHOD"];

// pass the request method and post ID to the Post and process the HTTP request:
$controller = new Post($dbConnection, $requestMethod, $postId);
$controller->processRequest();


Enter fullscreen mode Exit fullscreen mode

src/Post.php



<?php
namespace Src;

class Post {
  private $db;
  private $requestMethod;
  private $postId;

  public function __construct($db, $requestMethod, $postId)
  {
    $this->db = $db;
    $this->requestMethod = $requestMethod;
    $this->postId = $postId;
  }

  public function processRequest()
  {
    switch ($this->requestMethod) {
      case 'GET':
        if ($this->postId) {
          $response = $this->getPost($this->postId);
        } else {
          $response = $this->getAllPosts();
        };
        break;
      case 'POST':
        $response = $this->createPost();
        break;
      case 'PUT':
        $response = $this->updatePost($this->postId);
        break;
      case 'DELETE':
        $response = $this->deletePost($this->postId);
        break;
      default:
        $response = $this->notFoundResponse();
        break;
    }
    header($response['status_code_header']);
    if ($response['body']) {
        echo $response['body'];
    }
  }

  private function getAllPosts()
  {
    $query = "
      SELECT
          id, title, body, author, author_picture, created_at
      FROM
          post;
    ";

    try {
      $statement = $this->db->query($query);
      $result = $statement->fetchAll(\PDO::FETCH_ASSOC);
    } catch (\PDOException $e) {
      exit($e->getMessage());
    }

    $response['status_code_header'] = 'HTTP/1.1 200 OK';
    $response['body'] = json_encode($result);
    return $response;
  }

  private function getPost($id)
  {
    $result = $this->find($id);
    if (! $result) {
        return $this->notFoundResponse();
    }
    $response['status_code_header'] = 'HTTP/1.1 200 OK';
    $response['body'] = json_encode($result);
    return $response;
  }

  private function createPost()
  {
    $input = (array) json_decode(file_get_contents('php://input'), TRUE);
    if (! $this->validatePost($input)) {
      return $this->unprocessableEntityResponse();
    }

    $query = "
      INSERT INTO post
          (title, body, author, author_picture)
      VALUES
          (:title, :body, :author, :author_picture);
    ";

    try {
      $statement = $this->db->prepare($query);
      $statement->execute(array(
        'title' => $input['title'],
        'body'  => $input['body'],
        'author' => $input['author'],
        'author_picture' => 'https://secure.gravatar.com/avatar/'.md5(strtolower($input['author'])).'.png?s=200',
      ));
      $statement->rowCount();
    } catch (\PDOException $e) {
      exit($e->getMessage());
    }

    $response['status_code_header'] = 'HTTP/1.1 201 Created';
    $response['body'] = json_encode(array('message' => 'Post Created'));
    return $response;
  }

  private function updatePost($id)
  {
    $result = $this->find($id);
    if (! $result) {
      return $this->notFoundResponse();
    }
    $input = (array) json_decode(file_get_contents('php://input'), TRUE);
    if (! $this->validatePost($input)) {
      return $this->unprocessableEntityResponse();
    }

    $statement = "
      UPDATE post
      SET
        title = :title,
        body  = :body,
        author = :author,
        author_picture = :author_picture
      WHERE id = :id;
    ";

    try {
      $statement = $this->db->prepare($statement);
      $statement->execute(array(
        'id' => (int) $id,
        'title' => $input['title'],
        'body'  => $input['body'],
        'author' => $input['author'],
        'author_picture' => 'https://secure.gravatar.com/avatar/'.md5($input['author']).'.png?s=200',
      ));
      $statement->rowCount();
    } catch (\PDOException $e) {
      exit($e->getMessage());
    }
    $response['status_code_header'] = 'HTTP/1.1 200 OK';
    $response['body'] = json_encode(array('message' => 'Post Updated!'));
    return $response;
  }

  private function deletePost($id)
  {
    $result = $this->find($id);
    if (! $result) {
      return $this->notFoundResponse();
    }

    $query = "
      DELETE FROM post
      WHERE id = :id;
    ";

    try {
      $statement = $this->db->prepare($query);
      $statement->execute(array('id' => $id));
      $statement->rowCount();
    } catch (\PDOException $e) {
      exit($e->getMessage());
    }
    $response['status_code_header'] = 'HTTP/1.1 200 OK';
    $response['body'] = json_encode(array('message' => 'Post Deleted!'));
    return $response;
  }

  public function find($id)
  {
    $query = "
      SELECT
          id, title, body, author, author_picture, created_at
      FROM
          post
      WHERE id = :id;
    ";

    try {
      $statement = $this->db->prepare($query);
      $statement->execute(array('id' => $id));
      $result = $statement->fetch(\PDO::FETCH_ASSOC);
      return $result;
    } catch (\PDOException $e) {
      exit($e->getMessage());
    }
  }

  private function validatePost($input)
  {
    if (! isset($input['title'])) {
      return false;
    }
    if (! isset($input['body'])) {
      return false;
    }

    return true;
  }

  private function unprocessableEntityResponse()
  {
    $response['status_code_header'] = 'HTTP/1.1 422 Unprocessable Entity';
    $response['body'] = json_encode([
      'error' => 'Invalid input'
    ]);
    return $response;
  }

  private function notFoundResponse()
  {
    $response['status_code_header'] = 'HTTP/1.1 404 Not Found';
    $response['body'] = null;
    return $response;
  }
}


Enter fullscreen mode Exit fullscreen mode

让我们启动 PHP 服务器,并使用Postman等工具测试您的 API



php -S localhost:8000 -t api


Enter fullscreen mode Exit fullscreen mode

完毕

恭喜!!您已成功构建 REST API。

GitHub:https://github.com/shahbaz17/php-rest-api

接下来会发生什么?

现在,既然您已经构建了这个应用程序,您可能已经意识到这些端点没有受到保护,互联网上的任何人都可以利用它们来更新/删除/插入数据。因此,让我们通过身份验证和授权来保护这个 API。

在下一篇文章中,我将介绍如何利用Magic 的无密码功能,只需对这些代码进行几行更改即可保护您的 API。

文章来源:https://dev.to/shahbaz17/build-a-simple-rest-api-in-php-2edl