Can we create api in php?

Can we create api in php?

REpresentational State Transfer (REST) is a software architectural style that defines a set of constraints to be used for creating Web Services. REST APIs are one of the pillars of modern web development. Most web applications these days are developed as single-page applications on the frontend, connected to backend APIs written in different languages. There are various PHP frameworks which could help you build a REST API in minutes. But, let us learn how to build a simple REST API in core PHP.

This is the first part of the Learn PHP series, where you'll learn how to build a REST API in PHP using core PHP.

Next two:

  • Secure your API with Magic.
  • Deploy your API to Heroku.

Prerequisites

  • PHP
  • MySQL
  • Composer
  • Postman

PHP REST API Skeleton

Create a /src directory, and a composer.json file in the top directory with one dependency: the DotEnv library, which allows storing secured pieces of information in .env file.

composer.json

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

Enter fullscreen mode Exit fullscreen mode

A PSR-4 autoloader will automatically look for PHP classes in the /src directory.

Install dependencies now:

composer install

Enter fullscreen mode Exit fullscreen mode

It will create a /vendor directory, and the DotEnv dependency will be installed (autoloader will load classes from /src with no include() calls).

Create a .gitignore file for your project with two lines in it, so the /vendor directory and local .env file will be ignored:

.gitignore

vendor/
.env

Enter fullscreen mode Exit fullscreen mode

Next, create a .env.example file for Secret variables:

.env.example

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

Enter fullscreen mode Exit fullscreen mode

and a .env file where you'll fill in your actual details later (it will be ignored by Git so it won’t end up in your repository).

Create a start.php file which loads the environment variables.

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

Configure the Database for your PHP REST API

We will use MySQL to power our simple API.
Create a new database and user for your app:

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

The REST API will contain posts for our Blog Application, with the following fields: id, title, body, author, author_picture, created_at. It allows users to post their blog on our Blog application.

Create the database table in 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

Add the database connection variables to your .env file:

.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

Create a class to hold the database connections and add the initialization of the connection to the start.php file.

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

Add a Class for the Post Table and Implement PHP REST API

There are many ways to interact with the database in an object-oriented context, but, let us go with a simple method where you will implement methods to return all posts, return a specific post and add/update/delete a post.

Also, the API endpoints which will be handled by our frontend api/index.php.

REST API with the following endpoints:

Your APIs

APICRUDDescription
GET /posts READ Get all the Posts from post table
GET /post/{id} READ Get a single Post from post table
POST /post CREATE Create a Post and insert into post table
PUT /post/{id} UPDATE Update the Post in post table
DELETE /post/{id} DELETE Delete a Post from post table

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

Let's start the PHP Server and test your APIs with a tool like Postman.

php -S localhost:8000 -t api

Enter fullscreen mode Exit fullscreen mode

Done

Congratulations!! You have successfully built a REST API.

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

What's Next

Now, as you have built this application, you may be aware that these endpoints are not protected, and anyone on the internet could take advantage and update/delete/insert data. So, Let's secure this API with authentication and authorization.

In my next article, I will cover how you can take advantage of Magic's Passwordless feature and secure your API with just a few lines of changes to these codes.

Is PHP used for API?

PHP provides a rich set of functions to work with APIs, and we will discuss these functions in more detail in this article.

What is a REST API in PHP?

Rest API is an API that allows programmers to send and receive information from other programs using HTTP protocol commands such as GET and POST. Although REST API works with most protocols, it is specially designed for transmitting data through the HTTP protocol.

Can I create my own API?

Creating your own RESTful API can be a great way to build a business around data you've collected or a service you've created, or it can just be a fun personal project that allows you to learn a new skill. Here's a list of 20 tutorials on how to design your own REST API!

How create MySQL API in PHP?

In this step we will create PHP REST API Endpoints to insert or add a single record in MySQL table. ... Create create. php file in the api folder and add the following code..