Skip to content

Commit 4f75857

Browse files
authored
chore: Merge pull request #89 from WebFiori/dev
Release-As: v5.0.0
2 parents 64b67f7 + 56b62a2 commit 4f75857

173 files changed

Lines changed: 5116 additions & 1111 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ php-cs-fixer.phar
1717
/.vscode
1818
tests/WebFiori/Tests/Http/output-stream.txt
1919
/OpenAPI_files
20+
/examples/01-core/04-file-uploads/uploads

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright 2019 Ibrahim BinAlshikh, WebFiori HTTP.
3+
Copyright 2019-present, WebFiori Framework.
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 134 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@ A powerful and flexible PHP library for creating RESTful web APIs with built-in
2626
- [Key Features](#key-features)
2727
- [Installation](#installation)
2828
- [Quick Start](#quick-start)
29+
- [Modern Approach with Attributes](#modern-approach-with-attributes)
30+
- [Traditional Approach](#traditional-approach)
2931
- [Core Concepts](#core-concepts)
3032
- [Creating Web Services](#creating-web-services)
33+
- [Using Attributes (Recommended)](#using-attributes-recommended)
34+
- [Traditional Class-Based Approach](#traditional-class-based-approach)
3135
- [Parameter Management](#parameter-management)
3236
- [Authentication & Authorization](#authentication--authorization)
3337
- [Request & Response Handling](#request--response-handling)
3438
- [Advanced Features](#advanced-features)
39+
- [Object Mapping](#object-mapping-1)
40+
- [OpenAPI Documentation](#openapi-documentation)
3541
- [Testing](#testing)
3642
- [Examples](#examples)
3743
- [API Documentation](#api-documentation)
@@ -77,19 +83,53 @@ require_once 'path/to/webfiori-http/vendor/autoload.php';
7783

7884
## Quick Start
7985

80-
Here's a simple example to get you started:
86+
### Modern Approach with Attributes
87+
88+
PHP 8+ attributes provide a clean, declarative way to define web services:
8189

8290
```php
8391
<?php
84-
require_once 'vendor/autoload.php';
92+
use WebFiori\Http\WebService;
93+
use WebFiori\Http\Annotations\RestController;
94+
use WebFiori\Http\Annotations\GetMapping;
95+
use WebFiori\Http\Annotations\PostMapping;
96+
use WebFiori\Http\Annotations\Param;
97+
use WebFiori\Http\Annotations\ResponseBody;
98+
use WebFiori\Http\Annotations\AllowAnonymous;
99+
use WebFiori\Http\ParamType;
100+
101+
#[RestController('hello', 'A simple greeting service')]
102+
class HelloService extends WebService {
103+
104+
#[GetMapping]
105+
#[ResponseBody]
106+
#[AllowAnonymous]
107+
#[Param('name', ParamType::STRING, 'Your name')]
108+
public function sayHello(?string $name): string {
109+
return $name ? "Hello, $name!" : "Hello, World!";
110+
}
111+
112+
#[PostMapping]
113+
#[ResponseBody]
114+
#[AllowAnonymous]
115+
#[Param('message', ParamType::STRING, 'Custom message')]
116+
public function customGreeting(string $message): array {
117+
return ['greeting' => $message, 'timestamp' => time()];
118+
}
119+
}
120+
```
85121

122+
### Traditional Approach
123+
124+
The traditional approach using constructor configuration:
125+
126+
```php
127+
<?php
86128
use WebFiori\Http\AbstractWebService;
87-
use WebFiori\Http\WebServicesManager;
88129
use WebFiori\Http\RequestMethod;
89130
use WebFiori\Http\ParamType;
90131
use WebFiori\Http\ParamOption;
91132

92-
// Create a simple web service
93133
class HelloService extends AbstractWebService {
94134
public function __construct() {
95135
parent::__construct('hello');
@@ -104,22 +144,19 @@ class HelloService extends AbstractWebService {
104144
}
105145

106146
public function isAuthorized() {
107-
// No authentication required
108147
return true;
109148
}
110149

111150
public function processRequest() {
112151
$name = $this->getParamVal('name');
113-
114-
if ($name !== null) {
115-
$this->sendResponse("Hello, $name!");
116-
} else {
117-
$this->sendResponse("Hello, World!");
118-
}
152+
$this->sendResponse($name ? "Hello, $name!" : "Hello, World!");
119153
}
120154
}
155+
```
156+
157+
Both approaches work with `WebServicesManager`:
121158

122-
// Set up the services manager
159+
```php
123160
$manager = new WebServicesManager();
124161
$manager->addService(new HelloService());
125162
$manager->process();
@@ -148,7 +185,60 @@ The library follows a service-oriented architecture:
148185

149186
## Creating Web Services
150187

151-
### Basic Service Structure
188+
### Using Attributes (Recommended)
189+
190+
PHP 8+ attributes provide a modern, declarative approach:
191+
192+
```php
193+
<?php
194+
use WebFiori\Http\WebService;
195+
use WebFiori\Http\Annotations\RestController;
196+
use WebFiori\Http\Annotations\GetMapping;
197+
use WebFiori\Http\Annotations\PostMapping;
198+
use WebFiori\Http\Annotations\PutMapping;
199+
use WebFiori\Http\Annotations\DeleteMapping;
200+
use WebFiori\Http\Annotations\Param;
201+
use WebFiori\Http\Annotations\ResponseBody;
202+
use WebFiori\Http\Annotations\RequiresAuth;
203+
use WebFiori\Http\ParamType;
204+
205+
#[RestController('users', 'User management operations')]
206+
#[RequiresAuth]
207+
class UserService extends WebService {
208+
209+
#[GetMapping]
210+
#[ResponseBody]
211+
#[Param('id', ParamType::INT, 'User ID', min: 1)]
212+
public function getUser(?int $id): array {
213+
return ['id' => $id ?? 1, 'name' => 'John Doe'];
214+
}
215+
216+
#[PostMapping]
217+
#[ResponseBody]
218+
#[Param('name', ParamType::STRING, 'User name', minLength: 2)]
219+
#[Param('email', ParamType::EMAIL, 'User email')]
220+
public function createUser(string $name, string $email): array {
221+
return ['id' => 2, 'name' => $name, 'email' => $email];
222+
}
223+
224+
#[PutMapping]
225+
#[ResponseBody]
226+
#[Param('id', ParamType::INT, 'User ID')]
227+
#[Param('name', ParamType::STRING, 'User name')]
228+
public function updateUser(int $id, string $name): array {
229+
return ['id' => $id, 'name' => $name];
230+
}
231+
232+
#[DeleteMapping]
233+
#[ResponseBody]
234+
#[Param('id', ParamType::INT, 'User ID')]
235+
public function deleteUser(int $id): array {
236+
return ['deleted' => $id];
237+
}
238+
}
239+
```
240+
241+
### Traditional Class-Based Approach
152242

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

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

528618
// Additional processing
529619
return strtoupper($filtered);
530-
};
620+
];
531621

532622
$this->addParameters([
533623
'code' => [
@@ -537,6 +627,36 @@ $this->addParameters([
537627
]);
538628
```
539629

630+
### OpenAPI Documentation
631+
632+
Generate OpenAPI 3.1.0 specification for your API:
633+
634+
```php
635+
$manager = new WebServicesManager();
636+
$manager->setVersion('1.0.0');
637+
$manager->setDescription('My REST API');
638+
639+
// Add your services
640+
$manager->addService(new UserService());
641+
$manager->addService(new ProductService());
642+
643+
// Generate OpenAPI specification
644+
$openApiObj = $manager->toOpenAPI();
645+
646+
// Customize if needed
647+
$info = $openApiObj->getInfo();
648+
$info->setTermsOfService('https://example.com/terms');
649+
650+
// Output as JSON
651+
header('Content-Type: application/json');
652+
echo $openApiObj->toJSON();
653+
```
654+
655+
The generated specification can be used with:
656+
- Swagger UI for interactive documentation
657+
- Postman for API testing
658+
- Code generators for client SDKs
659+
540660
## Testing
541661

542662
### Using APITestCase
@@ -735,9 +855,6 @@ class FileUploadService extends AbstractWebService {
735855

736856
For more examples, check the [examples](examples/) directory in this repository.
737857

738-
## API Documentation
739-
740-
This library is part of the WebFiori Framework. For complete API documentation, visit: https://webfiori.com/docs/webfiori/http
741858

742859
### Key Classes Documentation
743860

WebFiori/Http/APIFilter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* This file is licensed under MIT License.
44
*
5-
* Copyright (c) 2019 WebFiori Framework
5+
* Copyright (c) 2019-present WebFiori Framework
66
*
77
* For more information on the license, please visit:
88
* https://github.com/WebFiori/http/blob/master/LICENSE

WebFiori/Http/APITestCase.php

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/**
33
* This file is licensed under MIT License.
44
*
5-
* Copyright (c) 2019 WebFiori Framework
5+
* Copyright (c) 2019-present WebFiori Framework
66
*
77
* For more information on the license, please visit:
88
* https://github.com/WebFiori/http/blob/master/LICENSE
@@ -128,13 +128,13 @@ public function addFile(string $fileIdx, string $filePath, bool $reset = false)
128128
* to mimic HTTP request headers. The keys of the array are names of headers
129129
* and the value of each key represents the value of the header.
130130
*
131-
* @param UserInterface|null $user Optional user to authenticate the request with.
131+
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
132132
* to mimic HTTP request headers. The keys of the array are names of headers
133133
* and the value of each key represents the value of the header.
134134
*
135135
* @return string The method will return the output of the endpoint.
136136
*/
137-
public function callEndpoint(WebServicesManager $manager, string $requestMethod, string $apiEndpointName, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
137+
public function callEndpoint(WebServicesManager $manager, string $requestMethod, string $apiEndpointName, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
138138
$method = strtoupper($requestMethod);
139139
$serviceName = $this->resolveServiceName($apiEndpointName);
140140

@@ -188,7 +188,7 @@ private function resolveServiceName(string $nameOrClass): string {
188188
* to mimic HTTP request headers. The keys of the array are names of headers
189189
* and the value of each key represents the value of the header.
190190
*
191-
* @param UserInterface|null $user Optional user to authenticate the request with.
191+
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
192192
*/
193193
private function setupRequest(string $method, string $serviceName, array $parameters, array $httpHeaders) {
194194
putenv('REQUEST_METHOD=' . $method);
@@ -267,14 +267,14 @@ public function format(string $output) {
267267
* to mimic HTTP request headers. The keys of the array are names of headers
268268
* and the value of each key represents the value of the header.
269269
*
270-
* @param UserInterface|null $user Optional user to authenticate the request with.
270+
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
271271
* to mimic HTTP request headers. The keys of the array are names of headers
272272
* and the value of each key represents the value of the header.
273273
*
274274
* @return string The method will return the output that was produced by
275275
* the endpoint as string.
276276
*/
277-
public function deleteRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
277+
public function deleteRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
278278
return $this->callEndpoint($manager, RequestMethod::DELETE, $endpoint, $parameters, $httpHeaders, $user);
279279
}
280280
/**
@@ -290,7 +290,7 @@ public function deleteRequest(WebServicesManager $manager, string $endpoint, arr
290290
* @return string The method will return the output that was produced by
291291
* the endpoint as string.
292292
*/
293-
public function getRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
293+
public function getRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
294294
return $this->callEndpoint($manager, RequestMethod::GET, $endpoint, $parameters, $httpHeaders, $user);
295295
}
296296
/**
@@ -307,14 +307,14 @@ public function getRequest(WebServicesManager $manager, string $endpoint, array
307307
* to mimic HTTP request headers. The keys of the array are names of headers
308308
* and the value of each key represents the value of the header.
309309
*
310-
* @param UserInterface|null $user Optional user to authenticate the request with.
310+
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
311311
* to mimic HTTP request headers. The keys of the array are names of headers
312312
* and the value of each key represents the value of the header.
313313
*
314314
* @return string The method will return the output that was produced by
315315
* the endpoint as string.
316316
*/
317-
public function postRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
317+
public function postRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
318318
return $this->callEndpoint($manager, RequestMethod::POST, $endpoint, $parameters, $httpHeaders, $user);
319319
}
320320
/**
@@ -331,14 +331,14 @@ public function postRequest(WebServicesManager $manager, string $endpoint, array
331331
* to mimic HTTP request headers. The keys of the array are names of headers
332332
* and the value of each key represents the value of the header.
333333
*
334-
* @param UserInterface|null $user Optional user to authenticate the request with.
334+
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
335335
* to mimic HTTP request headers. The keys of the array are names of headers
336336
* and the value of each key represents the value of the header.
337337
*
338338
* @return string The method will return the output that was produced by
339339
* the endpoint as string.
340340
*/
341-
public function putRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
341+
public function putRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
342342
return $this->callEndpoint($manager, RequestMethod::PUT, $endpoint, $parameters, $httpHeaders, $user);
343343
}
344344
/**
@@ -355,14 +355,14 @@ public function putRequest(WebServicesManager $manager, string $endpoint, array
355355
* to mimic HTTP request headers. The keys of the array are names of headers
356356
* and the value of each key represents the value of the header.
357357
*
358-
* @param UserInterface|null $user Optional user to authenticate the request with.
358+
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
359359
* to mimic HTTP request headers. The keys of the array are names of headers
360360
* and the value of each key represents the value of the header.
361361
*
362362
* @return string The method will return the output that was produced by
363363
* the endpoint as string.
364364
*/
365-
public function patchRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
365+
public function patchRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
366366
return $this->callEndpoint($manager, RequestMethod::PATCH, $endpoint, $parameters, $httpHeaders, $user);
367367
}
368368
/**
@@ -379,14 +379,14 @@ public function patchRequest(WebServicesManager $manager, string $endpoint, arra
379379
* to mimic HTTP request headers. The keys of the array are names of headers
380380
* and the value of each key represents the value of the header.
381381
*
382-
* @param UserInterface|null $user Optional user to authenticate the request with.
382+
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
383383
* to mimic HTTP request headers. The keys of the array are names of headers
384384
* and the value of each key represents the value of the header.
385385
*
386386
* @return string The method will return the output that was produced by
387387
* the endpoint as string.
388388
*/
389-
public function optionsRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
389+
public function optionsRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
390390
return $this->callEndpoint($manager, RequestMethod::OPTIONS, $endpoint, $parameters, $httpHeaders, $user);
391391
}
392392
/**
@@ -403,14 +403,14 @@ public function optionsRequest(WebServicesManager $manager, string $endpoint, ar
403403
* to mimic HTTP request headers. The keys of the array are names of headers
404404
* and the value of each key represents the value of the header.
405405
*
406-
* @param UserInterface|null $user Optional user to authenticate the request with.
406+
* @param SecurityPrincipal|null $user Optional user to authenticate the request with.
407407
* to mimic HTTP request headers. The keys of the array are names of headers
408408
* and the value of each key represents the value of the header.
409409
*
410410
* @return string The method will return the output that was produced by
411411
* the endpoint as string.
412412
*/
413-
public function headRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?UserInterface $user = null) : string {
413+
public function headRequest(WebServicesManager $manager, string $endpoint, array $parameters = [], array $httpHeaders = [], ?SecurityPrincipal $user = null) : string {
414414
return $this->callEndpoint($manager, RequestMethod::HEAD, $endpoint, $parameters, $httpHeaders, $user);
415415
}
416416
private function extractPathAndName($absPath): array {

0 commit comments

Comments
 (0)