Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ad1c121
fix: PUT and PATCH Request Methods
usernane Dec 29, 2025
dc473db
feat: Do Not Allow Use of Reserved Names
usernane Dec 30, 2025
12e546a
feat: Auto-Discovery
usernane Dec 30, 2025
ef029a1
feat: Invoke Service Auto for Single Reg
usernane Dec 30, 2025
adbefc0
docs: Added Hello Example
usernane Dec 30, 2025
6a74e60
docs: Added Example 02
usernane Dec 30, 2025
fd6a106
fix: Populating PUT Data
usernane Dec 30, 2025
0262390
feat: Parameters Injection
usernane Dec 30, 2025
d28b048
Update GreetingService.php
usernane Dec 30, 2025
bb43e50
docs: Added Example 00-03
usernane Dec 30, 2025
3dc02c9
docs: Added Example 00-04
usernane Dec 30, 2025
58bccca
docs: Added 00 Readme
usernane Dec 30, 2025
7472ae0
feat: Add Support for Custom Filter in Attr
usernane Dec 30, 2025
806ac88
Create ValidationService.php
usernane Dec 30, 2025
949e6eb
docs: Added Example 01-02
usernane Dec 30, 2025
02f8fe4
docs: Added Example 01-03
usernane Dec 30, 2025
22c7233
Update WebServicesManager.php
usernane Dec 30, 2025
16b621f
Update README.md
usernane Dec 30, 2025
d1997e9
docs: Added Example 01-04
usernane Dec 30, 2025
b218200
feat: Added Content Type to `RequestBody`
usernane Dec 30, 2025
e4b9be3
docs: Added Example 01-05
usernane Dec 30, 2025
64d51c6
Create README.md
usernane Dec 30, 2025
7823b31
docs: Added Example 02-01
usernane Dec 30, 2025
bd39be2
Update README.md
usernane Dec 30, 2025
4ebd92c
docs: Added Example 02-02
usernane Dec 30, 2025
1f840b6
Update WebService.php
usernane Dec 30, 2025
a511b81
docs: Added Example 02-04
usernane Dec 30, 2025
b635249
feat: Auto Mapping
usernane Dec 30, 2025
562ecf9
refactor: Rename Class to `SecurityPrinciple`
usernane Dec 30, 2025
be8ca99
Create README.md
usernane Dec 30, 2025
57112df
docs: Added Example 04-01
usernane Dec 30, 2025
d388604
chore: No Longer Needed
usernane Dec 30, 2025
a56447d
Update README.md
usernane Dec 30, 2025
8b4c419
test: Updated Test Cases
usernane Dec 30, 2025
3624f83
test: Added More Tests
usernane Dec 30, 2025
03023ea
chore: Updated License Headers
usernane Dec 30, 2025
cb0a243
docs: Added Example 04-03
usernane Dec 30, 2025
bb39d7c
chore: Updated README + License
usernane Dec 30, 2025
56b62a2
chore: Merge pull request #88 from WebFiori/docs
usernane Dec 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ php-cs-fixer.phar
/.vscode
tests/WebFiori/Tests/Http/output-stream.txt
/OpenAPI_files
/examples/01-core/04-file-uploads/uploads
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright 2019 Ibrahim BinAlshikh, WebFiori HTTP.
Copyright 2019-present, WebFiori Framework.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
151 changes: 134 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,18 @@ A powerful and flexible PHP library for creating RESTful web APIs with built-in
- [Key Features](#key-features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Modern Approach with Attributes](#modern-approach-with-attributes)
- [Traditional Approach](#traditional-approach)
- [Core Concepts](#core-concepts)
- [Creating Web Services](#creating-web-services)
- [Using Attributes (Recommended)](#using-attributes-recommended)
- [Traditional Class-Based Approach](#traditional-class-based-approach)
- [Parameter Management](#parameter-management)
- [Authentication & Authorization](#authentication--authorization)
- [Request & Response Handling](#request--response-handling)
- [Advanced Features](#advanced-features)
- [Object Mapping](#object-mapping-1)
- [OpenAPI Documentation](#openapi-documentation)
- [Testing](#testing)
- [Examples](#examples)
- [API Documentation](#api-documentation)
Expand Down Expand Up @@ -77,19 +83,53 @@ require_once 'path/to/webfiori-http/vendor/autoload.php';

## Quick Start

Here's a simple example to get you started:
### Modern Approach with Attributes

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

```php
<?php
require_once 'vendor/autoload.php';
use WebFiori\Http\WebService;
use WebFiori\Http\Annotations\RestController;
use WebFiori\Http\Annotations\GetMapping;
use WebFiori\Http\Annotations\PostMapping;
use WebFiori\Http\Annotations\Param;
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]
#[Param('name', ParamType::STRING, 'Your name')]
public function sayHello(?string $name): string {
return $name ? "Hello, $name!" : "Hello, World!";
}

#[PostMapping]
#[ResponseBody]
#[AllowAnonymous]
#[Param('message', ParamType::STRING, 'Custom message')]
public function customGreeting(string $message): array {
return ['greeting' => $message, 'timestamp' => time()];
}
}
```

### Traditional Approach

The traditional approach using constructor configuration:

```php
<?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');
Expand All @@ -104,22 +144,19 @@ class HelloService extends AbstractWebService {
}

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!");
}
$this->sendResponse($name ? "Hello, $name!" : "Hello, World!");
}
}
```

Both approaches work with `WebServicesManager`:

// Set up the services manager
```php
$manager = new WebServicesManager();
$manager->addService(new HelloService());
$manager->process();
Expand Down Expand Up @@ -148,7 +185,60 @@ The library follows a service-oriented architecture:

## Creating Web Services

### Basic Service Structure
### Using Attributes (Recommended)

PHP 8+ attributes provide a modern, declarative approach:

```php
<?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\Param;
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]
#[Param('id', ParamType::INT, 'User ID', min: 1)]
public function getUser(?int $id): array {
return ['id' => $id ?? 1, 'name' => 'John Doe'];
}

#[PostMapping]
#[ResponseBody]
#[Param('name', ParamType::STRING, 'User name', minLength: 2)]
#[Param('email', ParamType::EMAIL, 'User email')]
public function createUser(string $name, string $email): array {
return ['id' => 2, 'name' => $name, 'email' => $email];
}

#[PutMapping]
#[ResponseBody]
#[Param('id', ParamType::INT, 'User ID')]
#[Param('name', ParamType::STRING, 'User name')]
public function updateUser(int $id, string $name): array {
return ['id' => $id, 'name' => $name];
}

#[DeleteMapping]
#[ResponseBody]
#[Param('id', ParamType::INT, 'User ID')]
public function deleteUser(int $id): array {
return ['deleted' => $id];
}
}
```

### Traditional Class-Based Approach

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

Expand Down Expand Up @@ -527,7 +617,7 @@ $customFilter = function($original, $filtered) {

// Additional processing
return strtoupper($filtered);
};
];

$this->addParameters([
'code' => [
Expand All @@ -537,6 +627,36 @@ $this->addParameters([
]);
```

### OpenAPI Documentation

Generate OpenAPI 3.1.0 specification for your API:

```php
$manager = new WebServicesManager();
$manager->setVersion('1.0.0');
$manager->setDescription('My REST API');

// Add your services
$manager->addService(new UserService());
$manager->addService(new ProductService());

// Generate OpenAPI specification
$openApiObj = $manager->toOpenAPI();

// Customize if needed
$info = $openApiObj->getInfo();
$info->setTermsOfService('https://example.com/terms');

// Output as JSON
header('Content-Type: application/json');
echo $openApiObj->toJSON();
```

The generated specification can be used with:
- Swagger UI for interactive documentation
- Postman for API testing
- Code generators for client SDKs

## Testing

### Using APITestCase
Expand Down Expand Up @@ -735,9 +855,6 @@ class FileUploadService extends AbstractWebService {

For more examples, check the [examples](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

Expand Down
2 changes: 1 addition & 1 deletion WebFiori/Http/APIFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This file is licensed under MIT License.
*
* Copyright (c) 2019 WebFiori Framework
* Copyright (c) 2019-present WebFiori Framework
*
* For more information on the license, please visit:
* https://github.com/WebFiori/http/blob/master/LICENSE
Expand Down
34 changes: 17 additions & 17 deletions WebFiori/Http/APITestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* This file is licensed under MIT License.
*
* Copyright (c) 2019 WebFiori Framework
* Copyright (c) 2019-present WebFiori Framework
*
* For more information on the license, please visit:
* https://github.com/WebFiori/http/blob/master/LICENSE
Expand Down Expand Up @@ -128,13 +128,13 @@ public function addFile(string $fileIdx, string $filePath, bool $reset = false)
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output of the endpoint.
*/
public function callEndpoint(WebServicesManager $manager, string $requestMethod, string $apiEndpointName, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
public function callEndpoint(WebServicesManager $manager, string $requestMethod, string $apiEndpointName, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
$method = strtoupper($requestMethod);
$serviceName = $this->resolveServiceName($apiEndpointName);

Expand Down Expand Up @@ -188,7 +188,7 @@ private function resolveServiceName(string $nameOrClass): string {
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
*/
private function setupRequest(string $method, string $serviceName, array $parameters, array $httpHeaders) {
putenv('REQUEST_METHOD=' . $method);
Expand Down Expand Up @@ -267,14 +267,14 @@ public function format(string $output) {
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function deleteRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
public function deleteRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::DELETE, $endpoint, $parameters, $httpHeaders, $user);
}
/**
Expand All @@ -290,7 +290,7 @@ public function deleteRequest(WebServicesManager $manager, string $endpoint, arr
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function getRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
public function getRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::GET, $endpoint, $parameters, $httpHeaders, $user);
}
/**
Expand All @@ -307,14 +307,14 @@ public function getRequest(WebServicesManager $manager, string $endpoint, array
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function postRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
public function postRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::POST, $endpoint, $parameters, $httpHeaders, $user);
}
/**
Expand All @@ -331,14 +331,14 @@ public function postRequest(WebServicesManager $manager, string $endpoint, array
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function putRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
public function putRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::PUT, $endpoint, $parameters, $httpHeaders, $user);
}
/**
Expand All @@ -355,14 +355,14 @@ public function putRequest(WebServicesManager $manager, string $endpoint, array
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function patchRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
public function patchRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::PATCH, $endpoint, $parameters, $httpHeaders, $user);
}
/**
Expand All @@ -379,14 +379,14 @@ public function patchRequest(WebServicesManager $manager, string $endpoint, arra
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function optionsRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
public function optionsRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::OPTIONS, $endpoint, $parameters, $httpHeaders, $user);
}
/**
Expand All @@ -403,14 +403,14 @@ public function optionsRequest(WebServicesManager $manager, string $endpoint, ar
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @param UserInterface|null $user Optional user to authenticate the request with.
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
* to mimic HTTP request headers. The keys of the array are names of headers
* and the value of each key represents the value of the header.
*
* @return string The method will return the output that was produced by
* the endpoint as string.
*/
public function headRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
public function headRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
return $this->callEndpoint($manager, RequestMethod::HEAD, $endpoint, $parameters, $httpHeaders, $user);
}
private function extractPathAndName($absPath): array {
Expand Down
Loading