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

Modern Approach with Attributes (Recommended)

PHP 8+ attributes provide a clean, declarative way to define web services:

<?php
use WebFiori\Http\WebService;
use WebFiori\Http\Annotations\RestController;
use WebFiori\Http\Annotations\GetMapping;
use WebFiori\Http\Annotations\PostMapping;
use WebFiori\Http\Annotations\RequestParam;
use WebFiori\Http\Annotations\ResponseBody;
use WebFiori\Http\Annotations\AllowAnonymous;
use WebFiori\Http\ParamType;

#[RestController('hello', 'A simple greeting service')]
class HelloService extends WebService {
    
    #[GetMapping]
    #[ResponseBody]
    #[AllowAnonymous]
    #[RequestParam('name', ParamType::STRING, true)]
    public function sayHello(?string $name): string {
        return $name ? "Hello, $name!" : "Hello, World!";
    }
    
    #[PostMapping]
    #[ResponseBody]
    #[AllowAnonymous]
    #[RequestParam('message', ParamType::STRING)]
    public function customGreeting(string $message): array {
        return ['greeting' => $message, 'timestamp' => time()];
    }
}

Traditional Approach

For comparison, here's the traditional approach using constructor configuration:

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

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() {
        return true;
    }
    
    public function processRequest() {
        $name = $this->getParamVal('name');
        $this->sendResponse($name ? "Hello, $name!" : "Hello, World!");
    }
}

Both approaches work with WebServicesManager:

$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

Using Attributes (Recommended)

PHP 8+ attributes provide a modern, declarative approach:

<?php
use WebFiori\Http\WebService;
use WebFiori\Http\Annotations\RestController;
use WebFiori\Http\Annotations\GetMapping;
use WebFiori\Http\Annotations\PostMapping;
use WebFiori\Http\Annotations\PutMapping;
use WebFiori\Http\Annotations\DeleteMapping;
use WebFiori\Http\Annotations\RequestParam;
use WebFiori\Http\Annotations\ResponseBody;
use WebFiori\Http\Annotations\RequiresAuth;
use WebFiori\Http\ParamType;

#[RestController('users', 'User management operations')]
#[RequiresAuth]
class UserService extends WebService {
    
    #[GetMapping]
    #[ResponseBody]
    #[RequestParam('id', ParamType::INT, true)]
    public function getUser(?int $id): array {
        return ['id' => $id ?? 1, 'name' => 'John Doe'];
    }
    
    #[PostMapping]
    #[ResponseBody]
    #[RequestParam('name', ParamType::STRING)]
    #[RequestParam('email', ParamType::EMAIL)]
    public function createUser(string $name, string $email): array {
        return ['id' => 2, 'name' => $name, 'email' => $email];
    }
    
    #[PutMapping]
    #[ResponseBody]
    #[RequestParam('id', ParamType::INT)]
    #[RequestParam('name', ParamType::STRING)]
    public function updateUser(int $id, string $name): array {
        return ['id' => $id, 'name' => $name];
    }
    
    #[DeleteMapping]
    #[ResponseBody]
    #[RequestParam('id', ParamType::INT)]
    public function deleteUser(int $id): array {
        return ['deleted' => $id];
    }
}

Traditional Class-Based Approach

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();
}

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' => '[email protected]'
        ]);
        
        $this->assertJson($response);
    }
}

Examples

Complete CRUD Service Example

<?php
use WebFiori\Http\WebService;
use WebFiori\Http\Annotations\RestController;
use WebFiori\Http\Annotations\GetMapping;
use WebFiori\Http\Annotations\PostMapping;
use WebFiori\Http\Annotations\PutMapping;
use WebFiori\Http\Annotations\DeleteMapping;
use WebFiori\Http\Annotations\RequestParam;
use WebFiori\Http\Annotations\ResponseBody;
use WebFiori\Http\Annotations\AllowAnonymous;
use WebFiori\Http\ParamType;

#[RestController('tasks', 'Task management service')]
class TaskService extends WebService {
    
    #[GetMapping]
    #[ResponseBody]
    #[AllowAnonymous]
    public function getTasks(): array {
        return [
            'tasks' => [
                ['id' => 1, 'title' => 'Task 1', 'completed' => false],
                ['id' => 2, 'title' => 'Task 2', 'completed' => true]
            ],
            'count' => 2
        ];
    }
    
    #[PostMapping]
    #[ResponseBody]
    #[AllowAnonymous]
    #[RequestParam('title', ParamType::STRING)]
    #[RequestParam('description', ParamType::STRING, true)]
    public function createTask(string $title, ?string $description): array {
        
        return [
            'id' => 3,
            'title' => $title,
            'description' => $description ?: '',
            'completed' => false
        ];
    }
    
    #[PutMapping]
    #[ResponseBody]
    #[AllowAnonymous]
    #[RequestParam('id', ParamType::INT)]
    #[RequestParam('title', ParamType::STRING, true)]
    public function updateTask(int $id, ?string $title): array {
        
        return [
            'id' => $id,
            'title' => $title,
            'updated_at' => date('Y-m-d H:i:s')
        ];
    }
    
    #[DeleteMapping]
    #[ResponseBody]
    #[AllowAnonymous]
    #[RequestParam('id', ParamType::INT)]
    public function deleteTask(int $id): array {
        return [
            'id' => $id,
            'deleted_at' => date('Y-m-d H:i:s')
        ];
    }
}

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

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