diff --git a/.gitignore b/.gitignore index de025d7..594e91d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ php-cs-fixer-v2.phar .idea/* php-cs-fixer.phar .php-cs-fixer.cache +*.Identifier diff --git a/README.md b/README.md index 9fdd534..a31cf08 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # WebFiori HTTP -A simple library for creating RESTful web APIs in adition to providing utilities for handling HTTP request and response. -It includes inputs feltering and data validation in addion to creating user-defined inputs filters. + +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.

@@ -20,7 +20,24 @@ It includes inputs feltering and data validation in addion to creating user-defi

+## Table of Contents + +- [Supported PHP Versions](#supported-php-versions) +- [Key Features](#key-features) +- [Installation](#installation) +- [Quick Start](#quick-start) +- [Core Concepts](#core-concepts) +- [Creating Web Services](#creating-web-services) +- [Parameter Management](#parameter-management) +- [Authentication & Authorization](#authentication--authorization) +- [Request & Response Handling](#request--response-handling) +- [Advanced Features](#advanced-features) +- [Testing](#testing) +- [Examples](#examples) +- [API Documentation](#api-documentation) + ## Supported PHP Versions + | Build Status | |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| | | @@ -28,42 +45,722 @@ It includes inputs feltering and data validation in addion to creating user-defi | | | | -## API Docs -This library is a part of WebFiori Framework. To access API docs of the library, you can visid the following link: https://webfiori.com/docs/webfiori/http . +## Key Features -## Terminology +- **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 -Following terminology is used by the library: -| Term | Definition| -|:------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| -| Web Service | A single end pont that implements a REST service. It is represented as an instance of the class `AbstractWebService`. | -| Services Manager | An entity which is used to manage a set of web services. Represented by the class `WebServicesManager`. | -| Request Parameter | A way to pass values from a client such as a web browser to the server. Represented by the class `RequestParameter`. | +## Installation -## The Idea +### Using Composer (Recommended) -The idea of the library is as follows, when a client performs a request to a web service, he is usually intersted in performing specific action. Related actions are kept in one place as a set of web services (e.g. CRUD operations on a reasorce). The client can pass arguments (or parameters) to the end point in request body as `POST` or `PUT` request method or as a query string when using `GET` or `DELETE`. +```bash +composer require webfiori/http +``` -An end point is represented by the class [`AbstractWebService`](https://webfiori.com/docs/webfiori/http/AbstractWebService) and a set of web service (or end ponts) are grouped using the class [`WebServicesManager`](https://webfiori.com/docs/webfiori/http/WebServicesManager). Also, body parameters represented by the class [`RequestParameter`](https://webfiori.com/docs/webfiori/http/RequestParameter). +### Manual Installation -## Features -* Full support for creating REST services that supports JSON as request and response. -* Support for basic data filtering and validation. -* The ability to create custom filters based on the need. +Download the latest release from [GitHub Releases](https://github.com/WebFiori/http/releases) and include the autoloader: -## Installation -If you are using composer to collect your dependencies, you can simply include the following entry in your 'composer.json' file to get the latest release of the library: +```php +require_once 'path/to/webfiori-http/vendor/autoload.php'; +``` + +## Quick Start + +Here's a simple example to get you started: + +```php +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 +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 + +```php +// Single method +$this->addRequestMethod(RequestMethod::POST); + +// Multiple methods +$this->setRequestMethods([ + RequestMethod::GET, + RequestMethod::POST, + RequestMethod::PUT +]); +``` + +#### Service Metadata + +```php +$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 -``` json -{ - "require": { - "webfiori/http":"*" +### Parameter Types + +The library supports various parameter types through `ParamType`: + +```php +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 + +```php +use WebFiori\Http\RequestParameter; + +$param = new RequestParameter('username', ParamType::STRING); +$this->addParameter($param); +``` + +#### Batch Parameter Addition + +```php +$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`: + +```php +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 + +```php +$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 + +```php +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 + +```php +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 + +```php +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 + +```php +public function __construct() { + parent::__construct('public-service'); + $this->setIsAuthRequired(false); // Skip authentication } ``` -Note that the WebFiori Json library will be included with the installation files as this library is depending on it. -Another option is to download the latest release manually from Release. +### Custom Error Messages + +```php +use WebFiori\Http\ResponseMessage; + +public function isAuthorized() { + ResponseMessage::set('401', 'Custom unauthorized message'); + + // Your authorization logic + return false; +} +``` + +## Request & Response Handling + +### Sending JSON Responses + +```php +// 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 + +```php +// Send XML response +$xmlData = '123John Doe'; +$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 + +```php +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`: + +```php +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: + +```php +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 + +```php +$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 + +```php +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 + +```php +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 +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 + +```php +// 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 +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 +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](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 + +- [`AbstractWebService`](https://webfiori.com/docs/webfiori/http/AbstractWebService) - Base class for web services +- [`WebServicesManager`](https://webfiori.com/docs/webfiori/http/WebServicesManager) - Services management +- [`RequestParameter`](https://webfiori.com/docs/webfiori/http/RequestParameter) - Parameter definition and validation +- [`APIFilter`](https://webfiori.com/docs/webfiori/http/APIFilter) - Input filtering and validation +- [`Request`](https://webfiori.com/docs/webfiori/http/Request) - HTTP request utilities +- [`Response`](https://webfiori.com/docs/webfiori/http/Response) - HTTP response utilities + +## 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](LICENSE) file for details. + +## Support + +- **Issues**: [GitHub Issues](https://github.com/WebFiori/http/issues) +- **Documentation**: [WebFiori Docs](https://webfiori.com/docs/webfiori/http) +- **Examples**: [Examples Directory](examples/) + +## Changelog -## Usage -For more information on how to use the library, [check here](https://github.com/WebFiori/wf-docs/blob/main/web-services.md) +See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version history. diff --git a/webfiori/http/APIFilter.php b/WebFiori/Http/APIFilter.php similarity index 98% rename from webfiori/http/APIFilter.php rename to WebFiori/Http/APIFilter.php index 1b6bbc6..67e63f0 100644 --- a/webfiori/http/APIFilter.php +++ b/WebFiori/Http/APIFilter.php @@ -7,10 +7,10 @@ * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE */ -namespace webfiori\http; +namespace WebFiori\Http; use Exception; -use webfiori\json\Json; +use WebFiori\Json\Json; /** * A class used to validate and sanitize request parameters. * @@ -19,7 +19,6 @@ * * @author Ibrahim * - * @version 1.2.3 */ class APIFilter { /** @@ -27,7 +26,6 @@ class APIFilter { * * @var string A string that indicates a given value is invalid. * - * @since 1.2.2 */ const INVALID = 'INV'; @@ -36,16 +34,19 @@ class APIFilter { * * @var array * - * @since 1.0 */ private $inputs = []; + /** + * The path to the input stream. + * + * @var string + */ private $inputStreamPath; /** * The non-filtered data (original). * * @var mixed * - * @since 1.2 */ private $nonFilteredInputs; /** @@ -53,7 +54,6 @@ class APIFilter { * * @var array * - * @since 1.0 */ private $paramDefs = []; /** @@ -61,7 +61,6 @@ class APIFilter { * * @param RequestParameter $reqParam The request parameter that will be added. * - * @since 1.1 */ public function addRequestParameter(RequestParameter $reqParam) { $paramIdx = 'parameter'; @@ -108,7 +107,6 @@ public function addRequestParameter(RequestParameter $reqParam) { /** * Clears the arrays that are used to store filtered and not-filtered variables. * - * @since 1.2.2 */ public function clearInputs() { $this->inputs = []; @@ -117,7 +115,6 @@ public function clearInputs() { /** * Clears filter parameters. * - * @since 1.1 */ public function clearParametersDef() { $this->paramDefs = []; @@ -148,7 +145,6 @@ public function clearParametersDef() { * has all values filtered. The index which has the key 'non-filtered' * will contain the original values. * - * @since 1.2.2 */ public static function filter(APIFilter $apiFilter, array $arr): array { $filteredIdx = 'filtered'; @@ -226,7 +222,6 @@ private static function decodeArray(array $array) { * * * - * @since 1.0 */ public final function filterGET() { $this->clearInputs(); @@ -254,7 +249,6 @@ public final function filterGET() { * * * @throws Exception - * @since 1.0 */ public final function filterPOST() { $this->clearInputs(); @@ -279,7 +273,6 @@ public final function filterPOST() { * * @return array An array that contains filter constraints. * - * @since 1.2.2 */ public function getFilterDef() : array { return $this->paramDefs; @@ -297,7 +290,6 @@ public function getFilterDef() : array { * 'application/json', the method will return an instance of the class * 'Json' that has all JSON information. * - * @since 1.0 */ public function getInputs() { return $this->inputs; @@ -307,7 +299,6 @@ public function getInputs() { * * @return string|null * - * @since 1.2.3 */ public function getInputStreamPath() { return $this->inputStreamPath; @@ -320,7 +311,6 @@ public function getInputStreamPath() { * type is 'application/json', the method will return an object of type * 'Json'. * - * @since 1.2 */ public final function getNonFiltered() { return $this->nonFilteredInputs; @@ -337,7 +327,6 @@ public final function getNonFiltered() { * @return bool If input stream is successfully set, the method will * return true. False otherwise. * - * @since 1.2.3 */ public function setInputStream($pathOrResource) : bool { if (is_resource($pathOrResource)) { @@ -571,7 +560,6 @@ private function cleanJsonStr($extraClean, $def, $toBeFiltered) { * which contains the values is returned. If it has invalid syntax, the * method will return the string 'APIFilter::INVALID'. * - * @since 1.2.1 */ private static function filterArray($arr) { if (gettype($arr) == 'array') { @@ -672,7 +660,6 @@ private static function filterArray($arr) { * * @return bool|string * - * @since 1.1 */ private static function filterBoolean($boolean) { if (gettype($boolean) == 'boolean') { @@ -911,7 +898,6 @@ private function parseJsonBody() { * @param string $stringEndChar * @return array * - * @since 1.2.1 */ private static function parseStringFromArray(string $stringAsArr,int $start,int $len, string $stringEndChar) : array { $retVal = [ diff --git a/webfiori/http/APITestCase.php b/WebFiori/Http/APITestCase.php similarity index 98% rename from webfiori/http/APITestCase.php rename to WebFiori/Http/APITestCase.php index 5e5f2f4..77bcfc5 100644 --- a/webfiori/http/APITestCase.php +++ b/WebFiori/Http/APITestCase.php @@ -7,11 +7,11 @@ * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE */ -namespace webfiori\http; +namespace WebFiori\Http; use PHPUnit\Framework\TestCase; -use webfiori\json\Json; -use webfiori\json\JsonException; +use WebFiori\Json\Json; +use WebFiori\Json\JsonException; /** * A helper class which is used to implement test cases for API calls. * @@ -24,6 +24,11 @@ class APITestCase extends TestCase { const NL = "\r\n"; const DEFAULT_OUTPUT_STREAM = __DIR__.DIRECTORY_SEPARATOR.'outputStream.txt'; + /** + * The path to the output stream file. + * + * @var string + */ private $outputStreamPath; /** * Sets the path to the file which is used to store API output temporarily. diff --git a/webfiori/http/AbstractWebService.php b/WebFiori/Http/AbstractWebService.php similarity index 97% rename from webfiori/http/AbstractWebService.php rename to WebFiori/Http/AbstractWebService.php index 3529966..25eae1f 100644 --- a/webfiori/http/AbstractWebService.php +++ b/WebFiori/Http/AbstractWebService.php @@ -7,10 +7,10 @@ * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE */ -namespace webfiori\http; +namespace WebFiori\Http; -use webfiori\json\Json; -use webfiori\json\JsonI; +use WebFiori\Json\Json; +use WebFiori\Json\JsonI; /** * A class that represents one web service. * @@ -21,23 +21,19 @@ * * @author Ibrahim * - * @version 1.0.3 * - * @since 1.5.0 */ abstract class AbstractWebService implements JsonI { /** * A constant which is used to indicate that the message that will be * sent is of type error. * - * @since 1.0.2 */ const E = 'error'; /** * A constant which is used to indicate that the message that will be * sent is of type info. * - * @since 1.0.2 */ const I = 'info'; @@ -46,7 +42,6 @@ abstract class AbstractWebService implements JsonI { * * @var string * - * @since 1.0 */ private $name; /** @@ -54,7 +49,6 @@ abstract class AbstractWebService implements JsonI { * * @var WebServicesManager * - * @since 1.0.1 */ private $owner; /** @@ -62,7 +56,6 @@ abstract class AbstractWebService implements JsonI { * * @var array * - * @since 1.0 */ private $parameters; /** @@ -70,7 +63,6 @@ abstract class AbstractWebService implements JsonI { * * @var array * - * @since 1.0 */ private $reqMethods; /** @@ -79,7 +71,6 @@ abstract class AbstractWebService implements JsonI { * * @var bool * - * @since 1.0.1 */ private $requireAuth; /** @@ -88,7 +79,6 @@ abstract class AbstractWebService implements JsonI { * * @var array * - * @since 1.0 */ private $responses; /** @@ -96,7 +86,6 @@ abstract class AbstractWebService implements JsonI { * * @var string * - * @since 1.0 */ private $serviceDesc; /** @@ -105,7 +94,6 @@ abstract class AbstractWebService implements JsonI { * * @var string * - * @since 1.0 */ private $sinceVersion; /** @@ -146,7 +134,6 @@ public function __construct(string $name) { * @return array An array that contains all possible requests methods at which the * service can be called using. * - * @since 1.0 */ public function &getRequestMethods() : array { return $this->reqMethods; @@ -156,7 +143,6 @@ public function &getRequestMethods() : array { * * @return array an array that contains an objects of type RequestParameter. * - * @since 1.0 */ public final function &getParameters() : array { return $this->parameters; @@ -165,7 +151,6 @@ public final function &getParameters() : array { * * @return string * - * @since 1.0 */ public function __toString() { $retVal = "APIAction[\n"; @@ -257,7 +242,6 @@ public function __toString() { * return true. If it was not added for any reason, the method will return * false. * - * @since 1.0 */ public function addParameter($param) : bool { if (gettype($param) == 'array') { @@ -280,7 +264,6 @@ public function addParameter($param) : bool { * then the key will represent the name of the web service and the value of the * key should be a sub-associative array that holds parameter options. * - * @since 1.0.3 */ public function addParameters(array $params) { foreach ($params as $paramIndex => $param) { @@ -305,7 +288,6 @@ public function addParameters(array $params) { * request method is already added or the method is unknown, the method * will return false. * - * @since 1.0 */ public final function addRequestMethod(string $method) : bool { $uMethod = strtoupper(trim($method)); @@ -327,7 +309,6 @@ public final function addRequestMethod(string $method) : bool { * @param string $description A paragraph that describes one of * the possible responses due to calling the service. * - * @since 1.0 */ public final function addResponseDescription(string $description) { $trimmed = trim($description); @@ -355,7 +336,6 @@ public function getAuthHeader() { * * @return string The description of the service. Default is empty string. * - * @since 1.0 */ public final function getDescription() : string { return $this->serviceDesc; @@ -375,7 +355,6 @@ public final function getDescription() : string { * be an object of type 'Json' if request content type was 'application/json'. * If no manager was associated with the service, the method will return null. * - * @since 1.0.1 */ public function getInputs() { $manager = $this->getManager(); @@ -400,7 +379,6 @@ public function getManager() { * * @return string The name of the service. * - * @since 1.0 */ public final function getName() : string { return $this->name; @@ -440,7 +418,6 @@ public function getObject(string $clazz, array $settersMap = []) { * @return RequestParameter|null Returns an objects of type RequestParameter if * a parameter with the given name was found. null if nothing is found. * - * @since 1.0 */ public final function getParameterByName(string $paramName) { $trimmed = trim($paramName); @@ -466,7 +443,6 @@ public final function getParameterByName(string $paramName) { * For optional parameters, if a default value is set for it, the method will * return that value. * - * @since 1.0.1 */ public function getParamVal(string $paramName) { $inputs = $this->getInputs(); @@ -490,7 +466,6 @@ public function getParamVal(string $paramName) { * * @return array An array that contains information about possible responses. * - * @since 1.0 */ public final function getResponsesDescriptions() : array { return $this->responses; @@ -504,7 +479,6 @@ public final function getResponsesDescriptions() : array { * @return string The version number at which the service was added to the API. * Default is '1.0.0'. * - * @since 1.0 */ public final function getSince() : string { return $this->sinceVersion; @@ -521,7 +495,6 @@ public final function getSince() : string { * to the service, the method will return true. Otherwise, the method will return * false. * - * @since 1.0 */ public function hasParameter(string $name) : bool { $trimmed = trim($name); @@ -546,7 +519,6 @@ public function hasParameter(string $name) : bool { * user is authorized to call the API. If WebFiori framework is used, it is * possible to perform the functionality of this method using middleware. * - * @since 1.0.1 */ public function isAuthorized() { } @@ -559,7 +531,6 @@ public function isAuthorized() { * @return bool The method will return true if authorization step required. * False if the authorization step will be skipped. Default return value is true. * - * @since 1.0.1 */ public function isAuthRequired() : bool { return $this->requireAuth; @@ -596,7 +567,6 @@ public static function isValidName(string $name): bool { * This method must be implemented in a way it sends back a response after * processing the request. * - * @since 1.0.1 */ abstract function processRequest(); /** @@ -609,7 +579,6 @@ abstract function processRequest(); * that represents the removed parameter. If nothing is removed, the * method will return null. * - * @since 1.0 */ public function removeParameter(string $paramName) { $trimmed = trim($paramName); @@ -647,7 +616,6 @@ public function removeParameter(string $paramName) { * @return bool If the given request method is remove, the method will * return true. Other than that, the method will return true. * - * @since 1.0 */ public function removeRequestMethod(string $method): bool { $uMethod = strtoupper(trim($method)); @@ -686,7 +654,6 @@ public function removeRequestMethod(string $method): bool { * @param int $code HTTP response code that will be used to send the data. * Default is HTTP code 200 - Ok. * - * @since 1.0.1 */ public function send(string $contentType, $data, int $code = 200) { $manager = $this->getManager(); @@ -722,7 +689,6 @@ public function send(string $contentType, $data, int $code = 200) { * string, an object... . If null is given, the parameter 'more-info' * will be not included in response. Default is empty string. Default is null. * - * @since 1.0.1 */ public function sendResponse(string $message, string $type = '', int $code = 200, mixed $otherInfo = '') { $manager = $this->getManager(); @@ -738,7 +704,6 @@ public function sendResponse(string $message, string $type = '', int $code = 200 * * @param string $desc Action description. * - * @since 1.0 */ public final function setDescription(string $desc) { $this->serviceDesc = trim($desc); @@ -752,7 +717,6 @@ public final function setDescription(string $desc) { * @param bool $bool True to make authorization step required. False to * skip the authorization step. * - * @since 1.0.1 */ public function setIsAuthRequired(bool $bool) { $this->requireAuth = $bool; @@ -791,7 +755,6 @@ public function setManager(?WebServicesManager $manager) { * true once the name is set. false is returned if the given * name is invalid. * - * @since 1.0 */ public final function setName(string $name) : bool { if (self::isValidName($name)) { @@ -820,7 +783,6 @@ public function setRequestMethods(array $methods) { * * @param string $sinceAPIv The version number at which the service was added to the API. * - * @since 1.0 */ public final function setSince(string $sinceAPIv) { $this->sinceVersion = $sinceAPIv; @@ -843,7 +805,6 @@ public final function setSince(string $sinceAPIv) { * * @return Json an object of type Json. * - * @since 1.0 */ public function toJSON() : Json { $json = new Json(); diff --git a/webfiori/http/AuthHeader.php b/WebFiori/Http/AuthHeader.php similarity index 86% rename from webfiori/http/AuthHeader.php rename to WebFiori/Http/AuthHeader.php index 7cf979f..a0b4aab 100644 --- a/webfiori/http/AuthHeader.php +++ b/WebFiori/Http/AuthHeader.php @@ -1,7 +1,7 @@ getHeaderAsObj($name); diff --git a/webfiori/http/HttpCookie.php b/WebFiori/Http/HttpCookie.php similarity index 96% rename from webfiori/http/HttpCookie.php rename to WebFiori/Http/HttpCookie.php index dfc84ca..babfff0 100644 --- a/webfiori/http/HttpCookie.php +++ b/WebFiori/Http/HttpCookie.php @@ -7,7 +7,7 @@ * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE */ -namespace webfiori\http; +namespace WebFiori\Http; /** * A class which is used to represent Http cookies. @@ -23,20 +23,37 @@ class HttpCookie { 'Strict', 'None' ]; + /** + * The name of the cookie. + * + * @var string + */ private $cookieName; private $domain; + /** + * The domain of the cookie. + * + * @var string + */ private $expires; private $httpOnly; + /** + * The expiration time of the cookie. + * + * @var int + */ private $path; private $sameSite; + /** + * A boolean value that indicates if the cookie is HTTP only. + * + * @var bool + */ private $secure; private $val; /** * Creates new instance of the class with default properties. - * * A newly created cookie will have the following properties by default: - * * - * @since 1.0 */ public function getComponents() : array { return $this->uriBroken; @@ -266,7 +256,6 @@ public function getComponents() : array { * @return string Fragment part of the URI. The fragment in the URI is * any string that comes after the character '#'. * - * @since 1.0 */ public function getFragment() : string { return $this->uriBroken['fragment']; @@ -276,7 +265,6 @@ public function getFragment() : string { * * @return string The host name such as 'www.webfiori.com'. * - * @since 1.0 */ public function getHost() : string { return $this->uriBroken['host']; @@ -304,7 +292,6 @@ public function getParameter(string $name) { * @return array An indexed array which contains URI parameters as * objects of type UriParameter. * - * @since 1.0 */ public function getParameters() : array { return $this->uriBroken['uri-vars']; @@ -331,7 +318,6 @@ public function getParametersNames() : array { * parameter if found. If the parameter is not set or the parameter * does not exist, the method will return null. * - * @since 1.0 */ public function getParameterValue(string $varName) { $param = $this->getParameter($varName); @@ -351,7 +337,6 @@ public function getParameterValue(string $varName) { * values for the parameter which was added using the method Router::addUriVarValue(). * If the parameter does not exist, the array will be empty. * - * @since 1.3.6 */ public function getParameterValues(string $varName) : array { $trimmed = trim($varName); @@ -367,7 +352,6 @@ public function getParameterValues(string $varName) : array { * * @return string A string such as '/path1/path2/path3'. * - * @since 1.0 */ public function getPath() : string { $retVal = ''; @@ -385,7 +369,6 @@ public function getPath() : string { * For example, if the path part of the URI is '/path1/path2', the * array will contain the value 'path1' at index 0 and 'path2' at index 1. * - * @since 1.0 */ public function getPathArray() : array { return $this->uriBroken['path']; @@ -396,7 +379,6 @@ public function getPathArray() : array { * @return string Port number of the authority part of the URI. If * port number was not specified, the method will return empty string. * - * @since 1.0 */ public function getPort() : string { return $this->uriBroken['port']; @@ -408,7 +390,6 @@ public function getPort() : string { * If the URI has no query string, the method will return empty * string. * - * @since 1.0 */ public function getQueryString() : string { return $this->uriBroken['query-string']; @@ -420,7 +401,6 @@ public function getQueryString() : string { * the keys will be acting as the names of the parameters and the values * of each parameter will be in its key. * - * @since 1.0 */ public function getQueryStringVars() : array { return $this->uriBroken['query-string-vars']; @@ -431,7 +411,6 @@ public function getQueryStringVars() : array { * * @return array An array that holds strings such as 'GET' or 'POST'. * - * @since 1.0.1 */ public function getRequestMethods() : array { return $this->allowedRequestMethods; @@ -442,7 +421,6 @@ public function getRequestMethods() : array { * @return string The scheme part of the URI. Usually, it is called protocol * (like http, ftp). * - * @since 1.0 */ public function getScheme() : string { return $this->uriBroken['scheme']; @@ -458,7 +436,6 @@ public function getScheme() : string { * * @return string The original requested URI. * - * @since 1.0 */ public function getUri(bool $incQueryStr = false, bool $incFragment = false) : string { $retVal = $this->getScheme().':'.$this->getAuthority().$this->getPath(); @@ -505,7 +482,6 @@ public function getUri(bool $incQueryStr = false, bool $incFragment = false) : s * @return boolean If the given parameter name is exist, the method will * return true. Other than that, the method will return false. * - * @since 1.0 */ public function hasParameter(string $varName) : bool { return in_array($varName, $this->getParametersNames()); @@ -519,7 +495,6 @@ public function hasParameter(string $varName) : bool { * @return bool If the URI has any parameters, the method will * return true. * - * @since 1.0 */ public function hasParameters() : bool { return count($this->getParameters()) != 0; @@ -530,7 +505,6 @@ public function hasParameters() : bool { * @return bool The method will return true if all non-optional URI * parameters have a value other than null. * - * @since 1.0 */ public function isAllParametersSet() : bool { $canRoute = true; @@ -550,7 +524,6 @@ public function isAllParametersSet() : bool { * in the allowed request methods. Other than that, the method will return * false. * - * @since 1.0.1 */ public function isRequestMethodAllowed() : bool { $methods = $this->getRequestMethods(); @@ -571,7 +544,6 @@ public function isRequestMethodAllowed() : bool { * @return bool The method will return true if the parameter * was set. If the parameter does not exist, the method will return false. * - * @since 1.0 */ public function setParameterValue(string $varName, string $value) : bool { $param = $this->getParameter($varName); @@ -590,7 +562,6 @@ public function setParameterValue(string $varName, string $value) : bool { * * @param array $methods An array that holds strings such as 'GET' or 'POST'. * - * @since 1.0.1 */ public function setRequestMethods(array $methods) { foreach ($methods as $m) { @@ -619,7 +590,6 @@ public function setRequestMethods(array $methods) { *
  • uri-vars: An array that contains URI path parameters and values.
  • * * - * @since 1.0 */ public static function splitURI(string $uri) { $validate = filter_var(str_replace(' ', '%20', $uri),FILTER_VALIDATE_URL); diff --git a/webfiori/http/UriParameter.php b/WebFiori/Http/UriParameter.php similarity index 91% rename from webfiori/http/UriParameter.php rename to WebFiori/Http/UriParameter.php index 9d42ca9..8e27fb2 100644 --- a/webfiori/http/UriParameter.php +++ b/WebFiori/Http/UriParameter.php @@ -7,7 +7,7 @@ * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE */ -namespace webfiori\http; +namespace WebFiori\Http; use InvalidArgumentException; @@ -17,8 +17,18 @@ * @author Ibrahim */ class UriParameter { + /** + * A boolean value that indicates if the parameter is optional or not. + * + * @var bool + */ private $isOptional; private $name; + /** + * The name of the parameter. + * + * @var string + */ private $value; /** * Creates new instance of the class. diff --git a/webfiori/http/WebServicesManager.php b/WebFiori/Http/WebServicesManager.php similarity index 97% rename from webfiori/http/WebServicesManager.php rename to WebFiori/Http/WebServicesManager.php index f388e93..bd08008 100644 --- a/webfiori/http/WebServicesManager.php +++ b/WebFiori/Http/WebServicesManager.php @@ -7,11 +7,11 @@ * For more information on the license, please visit: * https://github.com/WebFiori/http/blob/master/LICENSE */ -namespace webfiori\http; +namespace WebFiori\Http; use Exception; -use webfiori\json\Json; -use webfiori\json\JsonI; +use WebFiori\Json\Json; +use WebFiori\Json\JsonI; /** * A class that is used to manage multiple web services. * @@ -22,7 +22,6 @@ * When a request is made to the services set, An instance of the class must be created * and the method WebServicesManager::process() must be called. * - * @version 1.4.8 */ class WebServicesManager implements JsonI { /** @@ -47,7 +46,6 @@ class WebServicesManager implements JsonI { * * @var array An array that contains the supported 'POST' and 'PUT' request content types. * - * @since 1.1 */ const POST_CONTENT_TYPES = [ 'application/x-www-form-urlencoded', @@ -59,7 +57,6 @@ class WebServicesManager implements JsonI { * * @var string * - * @since 1.3 */ private $apiDesc; /** @@ -67,7 +64,6 @@ class WebServicesManager implements JsonI { * * @var string * - * @since 1.0 */ private $apiVersion; /** @@ -75,7 +71,6 @@ class WebServicesManager implements JsonI { * * @var APIFilter * - * @since 1.0 */ private $filter; /** @@ -83,7 +78,6 @@ class WebServicesManager implements JsonI { * * @var array * - * @since 1.4.1 */ private $invParamsArr; /** @@ -91,7 +85,6 @@ class WebServicesManager implements JsonI { * * @var array * - * @since 1.4.1 */ private $missingParamsArr; /** @@ -111,7 +104,6 @@ class WebServicesManager implements JsonI { * * @var array * - * @since 1.0 */ private $services; /** @@ -147,7 +139,6 @@ public function __construct(string $version = '1.0.0') { * @param AbstractWebService $service The web service that will be added. * * - * @since 1.0 */ public function addService(AbstractWebService $service) { $this->addAction($service); @@ -169,7 +160,6 @@ public function addService(AbstractWebService $service) { * @param string $cType The value of the header 'content-type' taken from * request header. * - * @since 1.1 */ public function contentTypeNotSupported(string $cType = '') { $j = new Json(); @@ -186,7 +176,6 @@ public function contentTypeNotSupported(string $cType = '') { * @return string|null The name of the service that was requested. If the name * of the service is not set, the method will return null. * - * @since 1.0 * */ public function getCalledServiceName() { @@ -199,7 +188,6 @@ public function getCalledServiceName() { * useful to describe what does the set of services can do. If the description is * not set, the method will return null. * - * @since 1.3 */ public function getDescription() { return $this->apiDesc; @@ -218,7 +206,6 @@ public function getDescription() { * @return array|Json An array of filtered request inputs. This also can * be an object of type 'Json' if request content type was 'application/json'. * - * @since 1.0 */ public function getInputs() { return $this->filter->getInputs(); @@ -228,7 +215,6 @@ public function getInputs() { * * @return array An array that contains the names of request parameters which have invalid values. * - * @since 1.4.1 */ public function getInvalidParameters() : array { return $this->invParamsArr; @@ -239,7 +225,6 @@ public function getInvalidParameters() : array { * * @return array An array that contains the names of missing required parameters. * - * @since 1.4.1 */ public function getMissingParameters() : array { return $this->missingParamsArr; @@ -254,7 +239,6 @@ public function getMissingParameters() : array { * * @return array An array of request parameters. * - * @since 1.4.3 */ public function getNonFiltered() : array { return $this->filter->getNonFiltered(); @@ -267,7 +251,6 @@ public function getNonFiltered() : array { * resource. The resource will be still open. If no custom stream is set, * the method will return null. * - * @since 1.4.7 */ public function getOutputStream() { return $this->outputStream; @@ -278,7 +261,6 @@ public function getOutputStream() { * @return string|null A string that represents the path of the custom output stream. * If no custom output stream is set, the method will return null. * - * @since 1.4.7 */ public function getOutputStreamPath() { return $this->outputStreamPath; @@ -292,7 +274,6 @@ public function getOutputStreamPath() { * if the service is found. If no service was found which has the given name, * The method will return null. * - * @since 1.3 */ public function getServiceByName(string $serviceName) { $trimmed = trim($serviceName); @@ -310,7 +291,6 @@ public function getServiceByName(string $serviceName) { * The indices of the array are services names and the values are objects * of type 'WebService'. * - * @since 1.0 */ public final function getServices() : array { return $this->services; @@ -320,7 +300,6 @@ public final function getServices() : array { * * @return string A string in the format 'X.X.X'. * - * @since 1.0 */ public final function getVersion() : string { return $this->apiVersion; @@ -337,7 +316,6 @@ public final function getVersion() : string { *

    * In addition to the message, The response will send HTTP code 404 - Not Found. * - * @since 1.3 */ public function invParams() { $val = ''; @@ -366,7 +344,6 @@ public function invParams() { * PUT and POST), the method will return true, false if not. Other than that, the method * will return true. * - * @since 1.1 */ public final function isContentTypeSupported() : bool { $c = Request::getContentType(); @@ -392,7 +369,6 @@ public final function isContentTypeSupported() : bool { *

    * In addition to the message, The response will send HTTP code 404 - Not Found. * - * @since 1.3 */ public function missingParams() { $val = ''; @@ -425,7 +401,6 @@ public function missingParams() { *

    * In addition to the message, The response will send HTTP code 404 - Not Found. * - * @since 1.3.1 */ public function missingServiceName() { $this->sendResponse(ResponseMessage::get('404-3'), self::E, 404); @@ -443,7 +418,6 @@ public function missingServiceName() { *

    * In addition to the message, The response will send HTTP code 401 - Not Authorized. * - * @since 1.0 */ public function notAuth() { $this->sendResponse(ResponseMessage::get('401'), self::E, 401); @@ -456,7 +430,6 @@ public function notAuth() { * new instance of the class in order to process user request. * * @throws Exception - * @since 1.0 */ public final function process() { $this->invParamsArr = []; @@ -499,7 +472,6 @@ public final function process() { * @return string|null If the content was taken from the stream, the method * will return it as a string. Other than that, the method will return null. * - * @since 1.4.7 */ public function readOutputStream() { $path = $this->getOutputStreamPath(); @@ -519,7 +491,6 @@ public function readOutputStream() { * and removed, the method will return an object that represent the removed * service. Other than that, the method will return null. * - * @since 1.4.8 */ public function removeService(string $name) { $trimmed = trim($name); @@ -538,7 +509,6 @@ public function removeService(string $name) { * This method will simply re-initialize the arrays that holds all web * services. * - * @since 1.4.5 */ public function removeServices() { $this->services = []; @@ -556,7 +526,6 @@ public function removeServices() { * * In addition to the message, The response will send HTTP code 405 - Method Not Allowed. * - * @since 1.0 */ public function requestMethodNotAllowed() { $this->sendResponse(ResponseMessage::get('405'), self::E, 405); @@ -589,7 +558,6 @@ public function send(string $contentType, $data, int $code = 200) { * and the value of each key will represent the value * of the header. * - * @since 1.4.3 */ public function sendHeaders(array $headersArr) { foreach ($headersArr as $header => $val) { @@ -623,7 +591,6 @@ public function sendHeaders(array $headersArr) { * string, an object... . If null is given, the parameter 'more-info' * will be not included in response. Default is empty string. Default is null. * - * @since 1.0 */ public function sendResponse(string $message, string $type = '', int $code = 200, mixed $otherInfo = '') { $json = new Json(); @@ -663,7 +630,6 @@ public function sendResponse(string $message, string $type = '', int $code = 200 *

    * In addition to the message, The response will send HTTP code 404 - Not Found. * - * @since 1.0 */ public function serviceNotImplemented() { $this->sendResponse(ResponseMessage::get('404-4'), self::E, 404); @@ -680,7 +646,6 @@ public function serviceNotImplemented() { *

    * In addition to the message, The response will send HTTP code 404 - Not Found. * - * @since 1.0 */ public function serviceNotSupported() { $this->sendResponse(ResponseMessage::get('404-5'), self::E, 404); @@ -691,7 +656,6 @@ public function serviceNotSupported() { * @param string $desc Set description. Used to help front-end to identify * the use of the services set. * - * @since 1.3 */ public function setDescription(string $desc) { $this->apiDesc = $desc; @@ -708,7 +672,6 @@ public function setDescription(string $desc) { * @return bool If input stream is successfully set, the method will * return true. False otherwise. * - * @since 1.4.8 */ public function setInputStream($pathOrResource) : bool { return $this->filter->setInputStream($pathOrResource); @@ -727,7 +690,6 @@ public function setInputStream($pathOrResource) : bool { * @param bool $new If set to true and the resource does not exist, the * method will attempt to create it. * - * @since 1.4.7 */ public function setOutputStream($stream, bool $new = false): bool { if (is_resource($stream)) { @@ -762,7 +724,6 @@ public function setOutputStream($stream, bool $new = false): bool { * * @return bool true if set. false otherwise. * - * @since 1.0 */ public final function setVersion(string $val) : bool { $nums = explode('.', $val); @@ -789,7 +750,6 @@ public final function setVersion(string $val) : bool { * * @return Json An object of type Json. * - * @since 1.0 */ public function toJSON() : Json { $json = new Json(); @@ -848,7 +808,6 @@ private function _AfterParamsCheck($processReq) { * * @return bool true if called service is valid. * - * @since 1.0 */ private function _checkAction(): bool { $serviceName = $this->getCalledServiceName(); @@ -921,7 +880,6 @@ private function _processNonJson($params) { * * @param AbstractWebService $service The web service that will be added. * - * @since 1.0 * * @deprecated since version 1.4.7 Use WebservicesSet::addService() */ @@ -957,7 +915,6 @@ private function filterInputsHelper() { * @return string|null The name of the service that was requested. If the name * of the service is not set, the method will return null. * - * @since 1.0 * * @deprecated since version 1.4.6 Use WebServicesManager::getCalledServiceName() instead. */ diff --git a/composer.json b/composer.json index 0ca9364..09d3f9a 100644 --- a/composer.json +++ b/composer.json @@ -16,14 +16,25 @@ "license": "MIT", "require": { "php": ">=7.0", - "webfiori/jsonx": "3.3.x", + "webfiori/jsonx": "4.0.x", "ext-json": "*", "ext-mbstring": "*" }, "autoload": { "psr-4": { - "webfiori\\http\\": "webfiori\\http" + "WebFiori\\Http\\": "WebFiori/Http" } }, - "require-dev": {} + "autoload-dev": { + "psr-4": { + "WebFiori\\Tests\\Http\\": "tests/WebFiori/Tests/Http/" + } + }, + "scripts": { + "test": "phpunit --configuration tests/phpunit.xml", + "test-10": "phpunit --configuration tests/phpunit10.xml" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + } } diff --git a/examples/GetRandomService.php b/examples/GetRandomService.php index 99a2e24..ae48520 100644 --- a/examples/GetRandomService.php +++ b/examples/GetRandomService.php @@ -2,19 +2,42 @@ require 'loader.php'; -use webfiori\http\AbstractWebService; -use webfiori\http\RequestParameter; +use WebFiori\Http\AbstractWebService; +use WebFiori\Http\ParamOption; +use WebFiori\Http\ParamType; +use WebFiori\Http\RequestMethod; class GetRandomService extends AbstractWebService { public function __construct() { parent::__construct('get-random-number'); - $this->setRequestMethods(['get', 'post']); - - $this->addParameter(new RequestParameter('min', 'integer', true)); - $this->addParameter(new RequestParameter('max', 'integer', true)); + $this->setRequestMethods([ + RequestMethod::GET, + RequestMethod::POST + ]); + + $this->addParameters([ + 'min' => [ + ParamOption::TYPE => ParamType::INT, + ParamOption::OPTIONAL => true + ], + 'max' => [ + ParamOption::TYPE => ParamType::INT, + ParamOption::OPTIONAL => true + ] + ]); } public function isAuthorized() { +// $authHeader = $this->getAuthHeader(); +// +// if ($authHeader === null) { +// return false; +// } +// +// $scheme = $authHeader->getScheme(); +// $credentials = $authHeader->getCredentials(); + + //Verify credentials based on auth scheme (e.g. 'Basic', 'Barear' } public function processRequest() { diff --git a/examples/HelloWithAuthService.php b/examples/HelloWithAuthService.php new file mode 100644 index 0000000..40fdd2a --- /dev/null +++ b/examples/HelloWithAuthService.php @@ -0,0 +1,51 @@ +setRequestMethods([RequestMethod::GET]); + + $this->addParameters([ + 'my-name' => [ + ParamOption::TYPE => ParamType::STRING, + ParamOption::OPTIONAL => true + ] + ]); + } + public function isAuthorized() { + //Change default response message to custom one + ResponseMessage::set('401', 'Not authorized to use this API.'); + + $authHeader = $this->getAuthHeader(); + + if ($authHeader === null) { + return false; + } + + $scheme = $authHeader->getScheme(); + $credentials = $authHeader->getCredentials(); + + if ($scheme != 'bearer') { + return false; + } + + return $credentials == 'abc123trX'; + } + + public function processRequest() { + $name = $this->getParamVal('my-name'); + + if ($name !== null) { + $this->sendResponse("Hello '$name'."); + } + $this->sendResponse('Hello World!'); + } +} diff --git a/examples/HelloWorldService.php b/examples/HelloWorldService.php index 4cd4ef2..44233e8 100644 --- a/examples/HelloWorldService.php +++ b/examples/HelloWorldService.php @@ -2,22 +2,32 @@ require 'loader.php'; -use webfiori\http\AbstractWebService; +use WebFiori\Http\AbstractWebService; +use WebFiori\Http\ParamOption; +use WebFiori\Http\ParamType; +use WebFiori\Http\RequestMethod; -/** - * Description of HelloWorldService - * - * @author Ibrahim - */ class HelloWorldService extends AbstractWebService { public function __construct() { parent::__construct('hello'); - $this->addRequestMethod('get'); + $this->setRequestMethods([RequestMethod::GET]); + + $this->addParameters([ + 'my-name' => [ + ParamOption::TYPE => ParamType::STRING, + ParamOption::OPTIONAL => true + ] + ]); } public function isAuthorized() { } public function processRequest() { - $this->getManager()->sendResponse('Hello World!'); + $name = $this->getParamVal('my-name'); + + if ($name !== null) { + $this->sendResponse("Hello '$name'."); + } + $this->sendResponse('Hello World!'); } } diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..1b22b36 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,337 @@ +# WebFiori HTTP Examples + +This folder contains practical examples demonstrating how to use the WebFiori HTTP library to create RESTful web services. The examples showcase different features including basic services, parameter handling, and authentication. + +## Table of Contents + +- [Prerequisites](#prerequisites) +- [Setup](#setup) +- [Available Services](#available-services) + - [1. Hello World Service](#1-hello-world-service-helloworldservicephp) + - [2. Random Number Service](#2-random-number-service-getrandomservicephp) + - [3. Hello with Authentication Service](#3-hello-with-authentication-service-hellowithAuthservicephp) +- [Main Application](#main-application-indexphp) +- [Loader Configuration](#loader-configuration-loaderphp) +- [Key Concepts Demonstrated](#key-concepts-demonstrated) +- [Testing All Services](#testing-all-services) +- [Notes](#notes) + +## Prerequisites + +- PHP 8.1 or higher +- Composer installed +- WebFiori HTTP library dependencies + +## Setup + +1. **Install dependencies** (run from the project root directory): + ```bash + composer install + ``` + +2. **Navigate to the examples directory**: + ```bash + cd examples + ``` + +3. **Start the PHP development server**: + ```bash + php -S localhost:8989 + ``` + +## Available Services + +### 1. Hello World Service (`HelloWorldService.php`) + +A basic service that demonstrates simple parameter handling. + +**Service Name**: `hello` +**HTTP Methods**: GET +**Parameters**: +- `my-name` (optional, string): Name to include in greeting + +**Code Example**: +```php +setRequestMethods([RequestMethod::GET]); + + $this->addParameters([ + 'my-name' => [ + ParamOption::TYPE => ParamType::STRING, + ParamOption::OPTIONAL => true + ] + ]); + } + public function isAuthorized() { + } + + public function processRequest() { + $name = $this->getParamVal('my-name'); + + if ($name !== null) { + $this->sendResponse("Hello '$name'."); + } + $this->sendResponse('Hello World!'); + } +} +``` + +**Test URLs**: +```bash +# Basic hello +curl "http://localhost:8989?service=hello" +# Response: {"message":"Hello World!","http-code":200} + +# Hello with name +curl "http://localhost:8989?service=hello&my-name=ibrahim" +# Response: {"message":"Hello 'ibrahim'.","http-code":200} +``` + +### 2. Random Number Service (`GetRandomService.php`) + +Demonstrates parameter validation and processing with optional integer parameters. + +**Service Name**: `get-random-number` +**HTTP Methods**: GET, POST +**Parameters**: +- `min` (optional, integer): Minimum value for random number +- `max` (optional, integer): Maximum value for random number + +**Code Example**: +```php +setRequestMethods([ + RequestMethod::GET, + RequestMethod::POST + ]); + + $this->addParameters([ + 'min' => [ + ParamOption::TYPE => ParamType::INT, + ParamOption::OPTIONAL => true + ], + 'max' => [ + ParamOption::TYPE => ParamType::INT, + ParamOption::OPTIONAL => true + ] + ]); + } + + public function isAuthorized() { +// $authHeader = $this->getAuthHeader(); +// +// if ($authHeader === null) { +// return false; +// } +// +// $scheme = $authHeader->getScheme(); +// $credentials = $authHeader->getCredentials(); + + //Verify credentials based on auth scheme (e.g. 'Basic', 'Barear' + } + + public function processRequest() { + $max = $this->getParamVal('max'); + $min = $this->getParamVal('min'); + + if ($max !== null && $min !== null) { + $random = rand($min, $max); + } else { + $random = rand(); + } + $this->sendResponse($random); + } +} +``` + +**Test URLs**: +```bash +# Random number without bounds +curl "http://localhost:8989?service=get-random-number" +# Response: {"message":"1255598581","http-code":200} + +# Random number between 1 and 10 +curl "http://localhost:8989?service=get-random-number&min=1&max=10" +# Response: {"message":"7","http-code":200} + +# Random number between -4 and 0 +curl "http://localhost:8989?service=get-random-number&min=-4&max=0" +# Response: {"message":"-1","http-code":200} + +# Invalid parameter type (demonstrates validation) +curl "http://localhost:8989?service=get-random-number&min=-4&max=Super" +# Response: {"message":"The following parameter(s) has invalid values: 'max'.","type":"error","http-code":404,"more-info":{"invalid":["max"]}} +``` + +### 3. Hello with Authentication Service (`HelloWithAuthService.php`) + +Demonstrates Bearer token authentication implementation. + +**Service Name**: `hello-with-auth` +**HTTP Methods**: GET +**Authentication**: Bearer token required (`abc123trX`) +**Parameters**: +- `my-name` (optional, string): Name to include in greeting + +**Code Example**: +```php +setRequestMethods([RequestMethod::GET]); + + $this->addParameters([ + 'my-name' => [ + ParamOption::TYPE => ParamType::STRING, + ParamOption::OPTIONAL => true + ] + ]); + } + public function isAuthorized() { + //Change default response message to custom one + ResponseMessage::set('401', 'Not authorized to use this API.'); + + $authHeader = $this->getAuthHeader(); + + if ($authHeader === null) { + return false; + } + + $scheme = $authHeader->getScheme(); + $credentials = $authHeader->getCredentials(); + + if ($scheme != 'bearer') { + return false; + } + + return $credentials == 'abc123trX'; + } + + public function processRequest() { + $name = $this->getParamVal('my-name'); + + if ($name !== null) { + $this->sendResponse("Hello '$name'."); + } + $this->sendResponse('Hello World!'); + } +} +``` + +**Test URLs**: +```bash +# Without authorization (will fail) +curl "http://localhost:8989?service=hello-with-auth&my-name=ibrahim" +# Response: {"message":"Not authorized to use this API.","type":"error","http-code":401} + +# With correct Bearer token +curl -H "Authorization: Bearer abc123trX" "http://localhost:8989?service=hello-with-auth&my-name=ibrahim" +# Response: {"message":"Hello 'ibrahim'.","http-code":200} +``` + +## Main Application (`index.php`) + +The main entry point that registers all services with the WebServicesManager: + +```php +addService(new HelloWorldService()); +$manager->addService(new GetRandomService()); +$manager->addService(new HelloWithAuthService()); +$manager->process(); +``` + +## Loader Configuration (`loader.php`) + +Sets up error reporting and autoloading: + +```php +addService(new GetRandomService()); - } -} - -$manager = new RandomGenerator(); -$manager->process(); diff --git a/examples/ServicesManager.php b/examples/ServicesManager.php deleted file mode 100644 index 72afbc1..0000000 --- a/examples/ServicesManager.php +++ /dev/null @@ -1,11 +0,0 @@ -addService(new HelloWorldService()); -$manager->process(); diff --git a/examples/index.php b/examples/index.php new file mode 100644 index 0000000..0bbe98e --- /dev/null +++ b/examples/index.php @@ -0,0 +1,17 @@ +addService(new HelloWorldService()); +$manager->addService(new GetRandomService()); +$manager->addService(new HelloWithAuthService()); +$manager->process(); diff --git a/examples/loader.php b/examples/loader.php index d3162b3..3290d0c 100644 --- a/examples/loader.php +++ b/examples/loader.php @@ -4,16 +4,4 @@ ini_set('display_errors', 1); error_reporting(-1); -require_once '../vendor/webfiori/jsonx/src/JsonI.php'; -require_once '../vendor/webfiori/jsonx/src/JsonTypes.php'; -require_once '../vendor/webfiori/jsonx/src/Json.php'; -require_once '../src/ParamTypes.php'; -require_once '../src/AbstractWebService.php'; -require_once '../src/APIFilter.php'; -require_once '../src/WebServicesManager.php'; -require_once '../src/RequestParameter.php'; -require_once '../src/Request.php'; -require_once '../src/Response.php'; -require_once '../src/ParamOption.php'; -require_once '../src/Uri.php'; -require_once 'GetRandomService.php'; +require_once '../vendor/autoload.php'; diff --git a/tests/webfiori/tests/http/APIFilterTest.php b/tests/WebFiori/Tests/Http/APIFilterTest.php similarity index 99% rename from tests/webfiori/tests/http/APIFilterTest.php rename to tests/WebFiori/Tests/Http/APIFilterTest.php index 989e704..bf96056 100644 --- a/tests/webfiori/tests/http/APIFilterTest.php +++ b/tests/WebFiori/Tests/Http/APIFilterTest.php @@ -1,11 +1,11 @@ - ../webfiori/http/AbstractWebService.php - ../webfiori/http/APIFilter.php - ../webfiori/http/RequestParameter.php - ../webfiori/http/WebServicesManager.php - ../webfiori/http/Request.php - ../webfiori/http/Response.php - ../webfiori/http/Uri.php - ../webfiori/http/HttpHeader.php - ../webfiori/http/HttpCookie.php - ../webfiori/http/HeadersPool.php - ../webfiori/http/UriParameter.php - ../webfiori/http/ObjectMapper.php - ../webfiori/http/AuthHeader.php + ../WebFiori/Http/AbstractWebService.php + ../WebFiori/Http/APIFilter.php + ../WebFiori/Http/RequestParameter.php + ../WebFiori/Http/WebServicesManager.php + ../WebFiori/Http/Request.php + ../WebFiori/Http/Response.php + ../WebFiori/Http/Uri.php + ../WebFiori/Http/HttpHeader.php + ../WebFiori/Http/HttpCookie.php + ../WebFiori/Http/HeadersPool.php + ../WebFiori/Http/UriParameter.php + ../WebFiori/Http/ObjectMapper.php + ../WebFiori/Http/AuthHeader.php @@ -24,7 +24,7 @@ - ./webfiori/tests/http + ./WebFiori/Tests/Http \ No newline at end of file diff --git a/tests/phpunit10.xml b/tests/phpunit10.xml index 4214706..c6ee333 100644 --- a/tests/phpunit10.xml +++ b/tests/phpunit10.xml @@ -10,24 +10,24 @@ - ./webfiori/tests/http + ./WebFiori/Tests/Http - ../webfiori/http/AbstractWebService.php - ../webfiori/http/APIFilter.php - ../webfiori/http/RequestParameter.php - ../webfiori/http/WebServicesManager.php - ../webfiori/http/Request.php - ../webfiori/http/Response.php - ../webfiori/http/Uri.php - ../webfiori/http/HttpHeader.php - ../webfiori/http/HttpCookie.php - ../webfiori/http/HeadersPool.php - ../webfiori/http/UriParameter.php - ../webfiori/http/ObjectMapper.php - ../webfiori/http/AuthHeader.php + ../WebFiori/Http/AbstractWebService.php + ../WebFiori/Http/APIFilter.php + ../WebFiori/Http/RequestParameter.php + ../WebFiori/Http/WebServicesManager.php + ../WebFiori/Http/Request.php + ../WebFiori/Http/Response.php + ../WebFiori/Http/Uri.php + ../WebFiori/Http/HttpHeader.php + ../WebFiori/Http/HttpCookie.php + ../WebFiori/Http/HeadersPool.php + ../WebFiori/Http/UriParameter.php + ../WebFiori/Http/ObjectMapper.php + ../WebFiori/Http/AuthHeader.php