PHP LogoRESTful API Server

A RESTful API Server is an application that provides data or functionality as 'resources' over the internet, adhering to the principles of REST (Representational State Transfer) architectural style. It allows different client applications (web browsers, mobile apps, other servers) to interact with these resources using standard HTTP methods.

Core Principles of REST:
1. Client-Server Architecture: Separation of concerns between the client (front-end) and the server (back-end). The client handles the user interface and user experience, while the server manages data and application logic. This separation allows independent development and scaling.
2. Stateless: Each request from the client to the server must contain all the information needed to understand the request. The server does not store any client context between requests. This improves scalability and reliability, as any server can handle any request.
3. Cacheable: Responses from the server can be cached by clients or intermediaries (like proxy servers) to improve performance and network efficiency. Responses must explicitly or implicitly define themselves as cacheable or non-cacheable.
4. Layered System: A client cannot ordinarily tell whether it is connected directly to the end server, or to an intermediary along the way. This allows for intermediate servers (proxies, load balancers, security layers) to be introduced without affecting client-server interactions, enhancing scalability and security.
5. Uniform Interface: This is the most crucial constraint in REST. It simplifies the overall system architecture and improves visibility and independent evolvability. It includes four sub-constraints:
* Identification of resources: Resources are identified by unique URIs (Uniform Resource Identifiers). For example, `/products` identifies a collection of products, and `/products/123` identifies a specific product.
* Manipulation of resources through representations: Clients interact with resources by sending representations (e.g., JSON, XML) in the request body, which describe the desired state of the resource. The server responds with representations of the resource's current state.
* Self-descriptive messages: Each message (request or response) contains enough information to describe how to process the message. This includes using appropriate HTTP methods (verbs) and headers.
* Hypermedia as the Engine of Application State (HATEOAS): (Often considered an advanced or optional principle) Resources return links within their representations to related resources or available actions, guiding the client on how to proceed. This makes the API more discoverable and self-documenting.

How a RESTful API Server Works:
* Resources: Everything is treated as a resource, identified by a URI (e.g., `/products`, `/users/123`). These resources are conceptual mappings to real-world entities or collections of entities.
* HTTP Methods (Verbs): Standard HTTP methods are used to perform operations on these resources:
* GET: Retrieve a resource or a collection of resources. (Idempotent and safe - doesn't change server state).
* POST: Create a new resource or submit data that results in a new resource. (Not idempotent).
* PUT: Update an existing resource, typically by replacing the entire resource with a new representation. (Idempotent).
* PATCH: Partially update an existing resource. (Not necessarily idempotent, though often designed to be).
* DELETE: Remove a resource. (Idempotent).
* Representations: Data is exchanged between client and server in standard formats like JSON (JavaScript Object Notation) or XML (Extensible Markup Language). JSON is most commonly used due to its lightweight nature and ease of parsing in web applications.
* Status Codes: HTTP status codes are used to indicate the outcome of an API request (e.g., `200 OK` for success, `201 Created` for successful resource creation, `404 Not Found` for a missing resource, `500 Internal Server Error` for server-side issues).

Benefits of RESTful APIs:
* Scalability: Statelessness and caching improve scalability and reliability.
* Flexibility & Interoperability: Uses standard HTTP, making it language and platform-agnostic, allowing diverse clients to interact.
* Simplicity: Easier to understand and implement compared to other architectural styles like SOAP due to its reliance on standard web technologies.
* Ease of Integration: Widely supported by web technologies and tools, facilitating faster development.

In essence, a RESTful API server exposes its data and functionalities through a set of URLs, where each URL represents a resource, and clients interact with these resources using standard HTTP operations to retrieve, create, update, or delete data.

Example Code

<?php
// Set the content type header to JSON for all responses
header('Content-Type: application/json');

// Simulate a database (in a real application, this would interact with a persistent store like a SQL or NoSQL database)
// For this example, data is reset with each request, demonstrating API logic for individual operations.
$products = [
    1 => ['id' => 1, 'name' => 'Laptop', 'price' => 1200],
    2 => ['id' => 2, 'name' => 'Mouse', 'price' => 25],
    3 => ['id' => 3, 'name' => 'Keyboard', 'price' => 75]
];
// In a real database, IDs would typically be auto-incremented.
// Here, we manually track a 'next ID' for new product creation based on existing keys.
$nextId = count($products) > 0 ? max(array_keys($products)) + 1 : 1;

// --- Request Parsing ---
$method = $_SERVER['REQUEST_METHOD']; // Get the HTTP method (GET, POST, PUT, DELETE)
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); // Get the path part of the URL

// A simplified way to extract the resource and ID from the URL path.
// In a production environment, a dedicated routing library (e.g., FastRoute, or a full framework like Laravel/Symfony)
// with .htaccess rewrites would handle this more robustly, mapping clean URLs to actions.

// Example paths this script might handle (assuming your web server is configured to route all requests to this script):
// GET /products            -> get all products
// GET /products/1          -> get product with ID 1
// POST /products           -> create a new product
// PUT /products/1          -> update product with ID 1
// DELETE /products/1       -> delete product with ID 1

// Remove the script name from the path if present (e.g., '/api.php/products' -> '/products')
$scriptName = basename($_SERVER['SCRIPT_FILENAME']);
$scriptNamePos = strpos($path, $scriptName);

if ($scriptNamePos !== false) {
    $path = substr($path, $scriptNamePos + strlen($scriptName));
}

// Split the path into segments (e.g., ['', 'products', '1'] from '/products/1')
$pathSegments = explode('/', trim($path, '/'));

$resource = $pathSegments[0] ?? null; // The main resource, e.g., 'products'
$id = $pathSegments[1] ?? null;       // The ID of a specific resource, e.g., '1'

// Convert ID to integer if it exists for numerical indexing
if ($id !== null) {
    $id = (int)$id;
}

// --- API Logic based on Resource and Method ---
if ($resource === 'products') {
    switch ($method) {
        case 'GET':
            if ($id) {
                // Get a single product by ID
                if (isset($products[$id])) {
                    echo json_encode($products[$id]);
                } else {
                    http_response_code(404); // 404 Not Found
                    echo json_encode(['message' => 'Product not found.']);
                }
            } else {
                // Get all products
                // array_values is used to re-index the array numerically for JSON output (ensures a JSON array, not object if keys are non-sequential).
                echo json_encode(array_values($products));
            }
            break;

        case 'POST':
            // Create a new product
            $data = json_decode(file_get_contents('php://input'), true); // Get JSON data from request body
            if (!empty($data) && isset($data['name']) && isset($data['price'])) {
                $newProduct = [
                    'id' => $nextId,
                    'name' => $data['name'],
                    'price' => $data['price']
                ];
                $products[$nextId] = $newProduct; // Add to our simulated database
                http_response_code(201); // 201 Created status code for successful creation
                echo json_encode($newProduct);
            } else {
                http_response_code(400); // 400 Bad Request if data is missing or invalid
                echo json_encode(['message' => 'Invalid data provided for new product.']);
            }
            break;

        case 'PUT':
            // Update an existing product by ID
            if ($id && isset($products[$id])) {
                $data = json_decode(file_get_contents('php://input'), true);
                if (!empty($data) && isset($data['name']) && isset($data['price'])) {
                    $products[$id]['name'] = $data['name'];
                    $products[$id]['price'] = $data['price'];
                    echo json_encode($products[$id]);
                } else {
                    http_response_code(400); // 400 Bad Request
                    echo json_encode(['message' => 'Invalid data provided for product update.']);
                }
            } else {
                http_response_code(404); // 404 Not Found if product doesn't exist
                echo json_encode(['message' => 'Product not found for update.']);
            }
            break;

        case 'DELETE':
            // Delete a product by ID
            if ($id && isset($products[$id])) {
                unset($products[$id]); // Remove from our simulated database
                http_response_code(204); // 204 No Content (successful deletion with no response body)
            } else {
                http_response_code(404); // 404 Not Found if product doesn't exist
                echo json_encode(['message' => 'Product not found for deletion.']);
            }
            break;

        default:
            // Handle unsupported HTTP methods for the 'products' resource
            http_response_code(405); // 405 Method Not Allowed
            echo json_encode(['message' => 'Method Not Allowed for this resource.']);
            break;
    }
} else {
    // Handle requests for unknown resources
    http_response_code(404); // 404 Not Found
    echo json_encode(['message' => 'Resource Not Found.']);
}

?>