Skip to content

WebFiori/http

Repository files navigation

WebFiori HTTP

A powerful and flexible PHP library for creating RESTful web APIs with built-in input filtering, data validation, and comprehensive HTTP utilities. The library provides a clean, object-oriented approach to building web services with automatic parameter validation, authentication support, and JSON response handling.

Table of Contents

Supported PHP Versions

Build Status

Key Features

  • RESTful API Development: Full support for creating REST services with JSON request/response handling
  • Automatic Input Validation: Built-in parameter validation with support for multiple data types
  • Custom Filtering: Ability to create user-defined input filters and validation rules
  • Authentication Support: Built-in support for various authentication schemes (Basic, Bearer, etc.)
  • HTTP Method Support: Support for all standard HTTP methods (GET, POST, PUT, DELETE, etc.)
  • Content Type Handling: Support for application/json, application/x-www-form-urlencoded, and multipart/form-data
  • Object Mapping: Automatic mapping of request parameters to PHP objects
  • Comprehensive Testing: Built-in testing utilities with APITestCase class
  • Error Handling: Structured error responses with appropriate HTTP status codes
  • Stream Support: Custom input/output stream handling for advanced use cases

Installation

Using Composer (Recommended)

composer require webfiori/http

Manual Installation

Download the latest release from GitHub Releases and include the autoloader:

require_once 'path/to/webfiori-http/vendor/autoload.php';

Quick Start

Here's a simple example to get you started:

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

use WebFiori\Http\AbstractWebService;
use WebFiori\Http\WebServicesManager;
use WebFiori\Http\RequestMethod;
use WebFiori\Http\ParamType;
use WebFiori\Http\ParamOption;

// Create a simple web service
class HelloService extends AbstractWebService {
    public function __construct() {
        parent::__construct('hello');
        $this->setRequestMethods([RequestMethod::GET]);
        
        $this->addParameters([
            'name' => [
                ParamOption::TYPE => ParamType::STRING,
                ParamOption::OPTIONAL => true
            ]
        ]);
    }
    
    public function isAuthorized() {
        // No authentication required
        return true;
    }
    
    public function processRequest() {
        $name = $this->getParamVal('name');
        
        if ($name !== null) {
            $this->sendResponse("Hello, $name!");
        } else {
            $this->sendResponse("Hello, World!");
        }
    }
}

// Set up the services manager
$manager = new WebServicesManager();
$manager->addService(new HelloService());
$manager->process();

Core Concepts

Terminology

Term Definition
Web Service A single endpoint that implements a REST service, represented by AbstractWebService
Services Manager An entity that manages multiple web services, represented by WebServicesManager
Request Parameter A way to pass values from client to server, represented by RequestParameter
API Filter A component that validates and sanitizes request parameters

Architecture Overview

The library follows a service-oriented architecture:

  1. AbstractWebService: Base class for all web services
  2. WebServicesManager: Manages multiple services and handles request routing
  3. RequestParameter: Defines and validates individual parameters
  4. APIFilter: Handles parameter filtering and validation
  5. Request/Response: Utilities for handling HTTP requests and responses

Creating Web Services

Basic Service Structure

Every web service must extend AbstractWebService and implement the processRequest() method:

<?php
use WebFiori\Http\AbstractWebService;
use WebFiori\Http\RequestMethod;

class MyService extends AbstractWebService {
    public function __construct() {
        parent::__construct('my-service');
        $this->setRequestMethods([RequestMethod::GET, RequestMethod::POST]);
        $this->setDescription('A sample web service');
    }
    
    public function isAuthorized() {
        // Implement authorization logic
        return true;
    }
    
    public function processRequest() {
        // Implement service logic
        $this->sendResponse('Service executed successfully');
    }
}

Service Configuration

Setting Request Methods

// Single method
$this->addRequestMethod(RequestMethod::POST);

// Multiple methods
$this->setRequestMethods([
    RequestMethod::GET,
    RequestMethod::POST,
    RequestMethod::PUT
]);

Service Metadata

$this->setDescription('Creates a new user profile');
$this->setSince('1.2.0');
$this->addResponseDescription('Returns user profile data on success');
$this->addResponseDescription('Returns error message on failure');

Parameter Management

Parameter Types

The library supports various parameter types through ParamType:

ParamType::STRING    // String values
ParamType::INT       // Integer values
ParamType::DOUBLE    // Float/double values
ParamType::BOOL      // Boolean values
ParamType::EMAIL     // Email addresses (validated)
ParamType::URL       // URLs (validated)
ParamType::ARR       // Arrays
ParamType::JSON_OBJ  // JSON objects

Adding Parameters

Simple Parameter Addition

use WebFiori\Http\RequestParameter;

$param = new RequestParameter('username', ParamType::STRING);
$this->addParameter($param);

Batch Parameter Addition

$this->addParameters([
    'username' => [
        ParamOption::TYPE => ParamType::STRING,
        ParamOption::OPTIONAL => false
    ],
    'age' => [
        ParamOption::TYPE => ParamType::INT,
        ParamOption::OPTIONAL => true,
        ParamOption::MIN => 18,
        ParamOption::MAX => 120,
        ParamOption::DEFAULT => 25
    ],
    'email' => [
        ParamOption::TYPE => ParamType::EMAIL,
        ParamOption::OPTIONAL => false
    ]
]);

Parameter Options

Available options through ParamOption:

ParamOption::TYPE         // Parameter data type
ParamOption::OPTIONAL     // Whether parameter is optional
ParamOption::DEFAULT      // Default value for optional parameters
ParamOption::MIN          // Minimum value (numeric types)
ParamOption::MAX          // Maximum value (numeric types)
ParamOption::MIN_LENGTH   // Minimum length (string types)
ParamOption::MAX_LENGTH   // Maximum length (string types)
ParamOption::EMPTY        // Allow empty strings
ParamOption::FILTER       // Custom filter function
ParamOption::DESCRIPTION  // Parameter description

Custom Validation

$this->addParameters([
    'password' => [
        ParamOption::TYPE => ParamType::STRING,
        ParamOption::MIN_LENGTH => 8,
        ParamOption::FILTER => function($original, $basic) {
            // Custom validation logic
            if (strlen($basic) < 8) {
                return APIFilter::INVALID;
            }
            // Additional password strength checks
            return $basic;
        }
    ]
]);

Retrieving Parameter Values

public function processRequest() {
    $username = $this->getParamVal('username');
    $age = $this->getParamVal('age');
    $email = $this->getParamVal('email');
    
    // Get all inputs as array
    $allInputs = $this->getInputs();
}

Authentication & Authorization

Basic Authentication Implementation

public function isAuthorized() {
    $authHeader = $this->getAuthHeader();
    
    if ($authHeader === null) {
        return false;
    }
    
    $scheme = $authHeader->getScheme();
    $credentials = $authHeader->getCredentials();
    
    if ($scheme === 'basic') {
        // Decode base64 credentials
        $decoded = base64_decode($credentials);
        list($username, $password) = explode(':', $decoded);
        
        // Validate credentials
        return $this->validateUser($username, $password);
    }
    
    return false;
}

Bearer Token Authentication

public function isAuthorized() {
    $authHeader = $this->getAuthHeader();
    
    if ($authHeader === null) {
        return false;
    }
    
    if ($authHeader->getScheme() === 'bearer') {
        $token = $authHeader->getCredentials();
        return $this->validateToken($token);
    }
    
    return false;
}

Skipping Authentication

public function __construct() {
    parent::__construct('public-service');
    $this->setIsAuthRequired(false); // Skip authentication
}

Custom Error Messages

use WebFiori\Http\ResponseMessage;

public function isAuthorized() {
    ResponseMessage::set('401', 'Custom unauthorized message');
    
    // Your authorization logic
    return false;
}

Request & Response Handling

Sending JSON Responses

// Simple message response
$this->sendResponse('Operation completed successfully');

// Response with type and status code
$this->sendResponse('User created', 'success', 201);

// Response with additional data
$userData = ['id' => 123, 'name' => 'John Doe'];
$this->sendResponse('User retrieved', 'success', 200, $userData);

Custom Content Type Responses

// Send XML response
$xmlData = '<user><id>123</id><name>John Doe</name></user>';
$this->send('application/xml', $xmlData, 200);

// Send plain text
$this->send('text/plain', 'Hello, World!', 200);

// Send file download
$this->send('application/octet-stream', $fileContent, 200);

Handling Different Request Methods

public function processRequest() {
    $method = $this->getManager()->getRequestMethod();
    
    switch ($method) {
        case RequestMethod::GET:
            $this->handleGet();
            break;
        case RequestMethod::POST:
            $this->handlePost();
            break;
        case RequestMethod::PUT:
            $this->handlePut();
            break;
        case RequestMethod::DELETE:
            $this->handleDelete();
            break;
    }
}

JSON Request Handling

The library automatically handles JSON requests when Content-Type: application/json:

public function processRequest() {
    $inputs = $this->getInputs();
    
    if ($inputs instanceof \WebFiori\Json\Json) {
        // Handle JSON input
        $name = $inputs->get('name');
        $email = $inputs->get('email');
    } else {
        // Handle form data
        $name = $inputs['name'] ?? null;
        $email = $inputs['email'] ?? null;
    }
}

Advanced Features

Object Mapping

Automatically map request parameters to PHP objects:

class User {
    private $name;
    private $email;
    private $age;
    
    public function setName($name) { $this->name = $name; }
    public function setEmail($email) { $this->email = $email; }
    public function setAge($age) { $this->age = $age; }
    
    // Getters...
}

public function processRequest() {
    // Automatic mapping
    $user = $this->getObject(User::class);
    
    // Custom setter mapping
    $user = $this->getObject(User::class, [
        'full-name' => 'setName',
        'email-address' => 'setEmail'
    ]);
}

Services Manager Configuration

$manager = new WebServicesManager();

// Set API version and description
$manager->setVersion('2.1.0');
$manager->setDescription('User Management API');

// Add multiple services
$manager->addService(new CreateUserService());
$manager->addService(new GetUserService());
$manager->addService(new UpdateUserService());
$manager->addService(new DeleteUserService());

// Custom input/output streams
$manager->setInputStream('php://input');
$manager->setOutputStream(fopen('api-log.txt', 'a'));

// Process requests
$manager->process();

Error Handling

public function processRequest() {
    try {
        // Service logic
        $result = $this->performOperation();
        $this->sendResponse('Success', 'success', 200, $result);
    } catch (ValidationException $e) {
        $this->sendResponse($e->getMessage(), 'error', 400);
    } catch (AuthenticationException $e) {
        $this->sendResponse('Unauthorized', 'error', 401);
    } catch (Exception $e) {
        $this->sendResponse('Internal server error', 'error', 500);
    }
}

Custom Filters

use WebFiori\Http\APIFilter;

$customFilter = function($original, $filtered) {
    // Custom validation logic
    if (strlen($filtered) < 3) {
        return APIFilter::INVALID;
    }
    
    // Additional processing
    return strtoupper($filtered);
};

$this->addParameters([
    'code' => [
        ParamOption::TYPE => ParamType::STRING,
        ParamOption::FILTER => $customFilter
    ]
]);

Testing

Using APITestCase

<?php
use WebFiori\Http\APITestCase;

class MyServiceTest extends APITestCase {
    public function testGetRequest() {
        $manager = new WebServicesManager();
        $manager->addService(new MyService());
        
        $response = $this->getRequest($manager, 'my-service', [
            'param1' => 'value1',
            'param2' => 'value2'
        ]);
        
        $this->assertJson($response);
        $this->assertContains('success', $response);
    }
    
    public function testPostRequest() {
        $manager = new WebServicesManager();
        $manager->addService(new MyService());
        
        $response = $this->postRequest($manager, 'my-service', [
            'name' => 'John Doe',
            'email' => 'john@example.com'
        ]);
        
        $this->assertJson($response);
    }
}

Manual Testing

// Set up test environment
$_GET['service'] = 'my-service';
$_GET['param1'] = 'test-value';
$_SERVER['REQUEST_METHOD'] = 'GET';

$manager = new WebServicesManager();
$manager->addService(new MyService());
$manager->process();

Examples

Complete CRUD Service Example

<?php
class UserService extends AbstractWebService {
    public function __construct() {
        parent::__construct('user');
        $this->setRequestMethods([
            RequestMethod::GET,
            RequestMethod::POST,
            RequestMethod::PUT,
            RequestMethod::DELETE
        ]);
        
        $this->addParameters([
            'id' => [
                ParamOption::TYPE => ParamType::INT,
                ParamOption::OPTIONAL => true
            ],
            'name' => [
                ParamOption::TYPE => ParamType::STRING,
                ParamOption::OPTIONAL => true,
                ParamOption::MIN_LENGTH => 2
            ],
            'email' => [
                ParamOption::TYPE => ParamType::EMAIL,
                ParamOption::OPTIONAL => true
            ]
        ]);
    }
    
    public function isAuthorized() {
        return true; // Implement your auth logic
    }
    
    public function processRequest() {
        $method = $this->getManager()->getRequestMethod();
        
        switch ($method) {
            case RequestMethod::GET:
                $this->getUser();
                break;
            case RequestMethod::POST:
                $this->createUser();
                break;
            case RequestMethod::PUT:
                $this->updateUser();
                break;
            case RequestMethod::DELETE:
                $this->deleteUser();
                break;
        }
    }
    
    private function getUser() {
        $id = $this->getParamVal('id');
        
        if ($id) {
            // Get specific user
            $user = $this->findUserById($id);
            $this->sendResponse('User found', 'success', 200, $user);
        } else {
            // Get all users
            $users = $this->getAllUsers();
            $this->sendResponse('Users retrieved', 'success', 200, $users);
        }
    }
    
    private function createUser() {
        $name = $this->getParamVal('name');
        $email = $this->getParamVal('email');
        
        $user = $this->createNewUser($name, $email);
        $this->sendResponse('User created', 'success', 201, $user);
    }
    
    private function updateUser() {
        $id = $this->getParamVal('id');
        $name = $this->getParamVal('name');
        $email = $this->getParamVal('email');
        
        $user = $this->updateExistingUser($id, $name, $email);
        $this->sendResponse('User updated', 'success', 200, $user);
    }
    
    private function deleteUser() {
        $id = $this->getParamVal('id');
        
        $this->removeUser($id);
        $this->sendResponse('User deleted', 'success', 200);
    }
}

File Upload Service

<?php
class FileUploadService extends AbstractWebService {
    public function __construct() {
        parent::__construct('upload');
        $this->setRequestMethods([RequestMethod::POST]);
        
        $this->addParameters([
            'file' => [
                ParamOption::TYPE => ParamType::STRING,
                ParamOption::OPTIONAL => false
            ],
            'description' => [
                ParamOption::TYPE => ParamType::STRING,
                ParamOption::OPTIONAL => true
            ]
        ]);
    }
    
    public function isAuthorized() {
        return true;
    }
    
    public function processRequest() {
        if (isset($_FILES['file'])) {
            $file = $_FILES['file'];
            
            if ($file['error'] === UPLOAD_ERR_OK) {
                $uploadPath = 'uploads/' . basename($file['name']);
                
                if (move_uploaded_file($file['tmp_name'], $uploadPath)) {
                    $this->sendResponse('File uploaded successfully', 'success', 200, [
                        'filename' => $file['name'],
                        'size' => $file['size'],
                        'path' => $uploadPath
                    ]);
                } else {
                    $this->sendResponse('Failed to move uploaded file', 'error', 500);
                }
            } else {
                $this->sendResponse('File upload error', 'error', 400);
            }
        } else {
            $this->sendResponse('No file uploaded', 'error', 400);
        }
    }
}

For more examples, check the examples directory in this repository.

API Documentation

This library is part of the WebFiori Framework. For complete API documentation, visit: https://webfiori.com/docs/webfiori/http

Key Classes Documentation

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Changelog

See CHANGELOG.md for a list of changes and version history.

About

HTTP handling helper library of WebFiori Framework.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages