An Interest In:
Web News this Week
- April 26, 2024
- April 25, 2024
- April 24, 2024
- April 23, 2024
- April 22, 2024
- April 21, 2024
- April 20, 2024
Build a Simple REST 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.
Upcoming two:
Prerequisites
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.2" }, "autoload": { "psr-4": { "Src\\": "src/" } }}
A PSR-4 autoloader will automatically look for PHP classes in the /src
directory.
Install dependencies now:
composer install
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
Next, create a .env.example
file for Secret variables:
.env.example
DB_HOST=localhostDB_PORT=3306DB_DATABASE=DB_USERNAME=DB_PASSWORD=
and a .env
file where you'll fill in your actual details later (it will be ignored by Git so it wont end up in your repository).
Create a start.php
file which loads the environment variables.
start.php
<?phprequire 'vendor/autoload.php';use Dotenv\Dotenv;use Src\Database;$dotenv = new DotEnv(__DIR__);$dotenv->load();// test code:// it will output: localhost// when you run $ php start.phpecho getenv('DB_HOST');
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 -pCREATE 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
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 passworduse 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`));
Add the database connection variables to your .env
file:
.env
DB_HOST=localhostDB_PORT=3306DB_DATABASE=blogDB_USERNAME=rest_api_userDB_PASSWORD=rest_api_password
Create a class to hold the database connections and add the initialization of the connection to the start.php
file.
src/Database.php
<?phpnamespace Src;class Database { private $dbConnection = null; public function __construct() { $host = getenv('DB_HOST'); $port = getenv('DB_PORT'); $db = getenv('DB_DATABASE'); $user = getenv('DB_USERNAME'); $pass = getenv('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; }}
start.php
<?phprequire 'vendor/autoload.php';use Dotenv\Dotenv;use Src\Database;$dotenv = new DotEnv(__DIR__);$dotenv->load();$dbConnection = (new Database())->connet();
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
API | CRUD | Description |
---|---|---|
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
<?phprequire "../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 );// all of our endpoints start with /post or /posts// everything else results in a 404 Not Foundif ($uri[1] !== 'post') { if($uri[1] !== 'posts'){ header("HTTP/1.1 404 Not Found"); exit(); }}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();
src/Post.php
<?phpnamespace 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 posts; "; 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 posts (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 posts 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 posts 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 posts 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; }}
Let's start the PHP Server and test your APIs with a tool like Postman.
php -S localhost:8008 -t api
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.
Secure your PHP Rest API with Magic.
Original Link: https://dev.to/shahbaz17/build-a-simple-rest-api-in-php-2edl
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To