PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
83: Building RESTful Routes Without a Framework
How do I even know which URL was requested and what method was used?
When you aren't using a framework like Laravel or Symfony, you don't have a Route::get() helper. You're on your own. The first thing you need to realize is that PHP doesn't automatically "route" anything; it just executes a file. To build a REST API, we use the Front Controller pattern. This means we tell the web server (via .htaccess in Apache or a location block in Nginx) to send every single request to a single file: index.php.
Once everything hits index.php, you can inspect the global $_SERVER array. I usually start by grabbing the URI and the request method like this:
$method = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uriSegments = explode('/', trim($uri, '/'));
// Example: GET /books/12
// $method = 'GET'
// $uriSegments = ['books', '12']
It's simple, but it's the foundation. You're basically manually doing what the framework does behind the scenes.
What's the best way to handle those dynamic IDs in the path?
This is where things get tricky. If you're building a Book API, /books might list everything, but /books/12 needs to fetch one specific book. You can't just hardcode every possible ID. I prefer using a basic switch or a mapping array combined with the segments we exploded earlier.
Here is how I'd structure a basic router for a Book resource:
if ($uriSegments[0] === 'books') {
$id = $uriSegments[1] ?? null;
switch ($method) {
case 'GET':
$id ? handleGetBook($id) : handleListBooks();
break;
case 'POST':
handleCreateBook();
break;
case 'PUT':
if (!$id) throw new Exception("Book ID required for update", 400);
handleUpdateBook($id);
break;
case 'DELETE':
if (!$id) throw new Exception("Book ID required for deletion", 400);
handleDeleteBook($id);
break;
default:
http_response_code(405); // Method Not Allowed
break;
}
} else {
http_response_code(404); // Not Found
}
I'll be honest: as your app grows, this switch statement will become a nightmare. But for a small-to-medium project, it's incredibly fast and easy to debug because there's no "magic" happening.
How do I actually get the JSON data out of a PUT or POST request?
Here is a common trap: many developers try to use $_POST. The problem is that $_POST only populates if the request is application/x-www-form-urlencoded or multipart/form-data. Most modern REST clients send application/json, which PHP ignores when filling the $_POST superglobal.
To get that data, you have to read the "raw" input stream from php://input. I usually wrap this in a small helper function so I don't have to rewrite the json_decode logic everywhere:
function getRequestBody() {
$rawInput = file_get_contents('php://input');
$data = json_decode($rawInput, true);
if (json_last_error() !== JSON_ERROR_NONE) {
// The JSON was malformed
http_response_code(400);
echo json_encode(['error' => 'Invalid JSON provided']);
exit;
}
return $data;
}
// Usage inside your PUT handler:
function handleUpdateBook($id) {
$input = getRequestBody();
$title = $input['title'] ?? 'Untitled';
// Now update the database...
echo json_encode(['message' => "Book $id updated to $title"]);
}
Remember that php://input is a read-only stream. You can only read it once in some older PHP versions, though in modern PHP it's generally fine. Just get it once, decode it, and pass it along.
📋 Practical Task
Build a "Product Inventory" Route Handler
Your task is to create a standalone index.php file that implements a RESTful routing system for a product inventory. You don't need a real database—you can use a hardcoded array of products to simulate one.
- Implement routing for the
/productsURI. - Handle a
GETrequest to/productsthat returns a JSON list of all products. - Handle a
GETrequest to/products/{id}that returns a specific product based on the ID provided in the URL. - Handle a
DELETErequest to/products/{id}that returns a JSON message confirming the deletion (e.g.,{"message": "Product 5 deleted"}). - Ensure that if a user requests a URI other than
/products, the server returns a404 Not Foundresponse. - Ensure that if a
DELETErequest is sent without an ID, the server returns a400 Bad Requestresponse.
There are no comments for now.