|
1 | | -# sbsedv/input-library |
| 1 | +# sbsedv/input-converter |
2 | 2 |
|
3 | | -A minimal PHP library to nativly support PUT, PATCH and DELETE user input in form of mutlipart/form-data, application/x-www-urlencoded and various JSON types like application/json with complex data structures. |
| 3 | +A minimal PHP component to nativly support user input parsing on http methods other than POST. |
4 | 4 |
|
5 | | -Normally, PHP only parses multipart/form-data and application/x-www-urlencoded on POST requests and does nothing with application/json. |
6 | | -Many libraries, like [symfony/http-foundation](https://symfony.com/doc/current/components/http_foundation.html) with `$request->toArray()`, have limited support for JSON and usually, but not always, only work on POST requests. |
| 5 | +PHP natively only supports the parsing of multipart/form-data and application/x-www-urlencoded on POST http requests. |
7 | 6 |
|
8 | | -The default workarounds are to use the "X-HTTP-METHOD-OVERRIDE" header or a hidden "_method" input field to send requests as method=POST that the server then knows to translate to eg. PUT or PATCH requests. |
| 7 | +Many modern web applications also want use / support a) other http methods |
| 8 | +like PUT or PATCH and b) other content encodings like JSON or XML. |
9 | 9 |
|
10 | | -This library provides all you need to nativly support these kinds of requests, including complex data structures like nested Arrays and Objects. Because internally this library uses the PHP native functions [json_decode](https://www.php.net/manual/en/function.json-decode) and [parse_str](https://www.php.net/manual/en/function.parse-str) (multpart/form-data gets translated to x-www-urlencoded), complex data structures are only limited by what those functions support. |
| 10 | +This component provides a very simple and extensible object oriented api to support just that. |
| 11 | + |
| 12 | +Internally this component uses the PHP native functions [json_decode](https://www.php.net/manual/en/function.json-decode) and [parse_str](https://www.php.net/manual/en/function.parse-str) (multpart/form-data gets "translated" to x-www-urlencoded) and therefore complex data structures (arrays and objects) are only limited by what those functions support. <br/> |
| 13 | +This effectifly means that HTMLForms like the following are `FULLY supported`. |
| 14 | + |
| 15 | +```html |
| 16 | +<form method="PUT"> |
| 17 | + <select name="select[]" multiple> |
| 18 | + ... |
| 19 | + </select> |
| 20 | + |
| 21 | + <input type="text" name="text" /> |
| 22 | + |
| 23 | + <input type="text" name="obj[key1]" /> |
| 24 | + <input type="text" name="obj[key2]" /> |
| 25 | + <select name="obj[key3][]" multiple> |
| 26 | + ... |
| 27 | + </select> |
| 28 | +</form> |
| 29 | +``` |
11 | 30 |
|
12 | 31 | --- |
13 | 32 |
|
14 | 33 | ## **How it Works** |
15 | 34 |
|
16 | | -You should call this library as early as possible in your application. |
| 35 | +You should instantiate and call this component as early in your app lifecycle as possible. |
17 | 36 |
|
18 | | -You **CAN** pass a [PSR-7](https://www.php-fig.org/psr/psr-7/) or [HTTP-Foundation](https://symfony.com/doc/current/components/http_foundation.html) to the constructor and the library will make use of that. |
19 | | -If you do not pass anything to the constructor, the library uses PHPs `$_SERVER` global. |
| 37 | +You **MUST** pass either a [PSR-7](https://www.php-fig.org/psr/psr-7/) or [HTTP-Foundation](https://symfony.com/doc/current/components/http_foundation.html) request object to the "convert" method. |
20 | 38 |
|
21 | 39 | ```php |
22 | 40 | <?php declare(strict_types=1); |
23 | 41 |
|
24 | | -use SBSEDV\InputLibrary\InputLibrary; |
25 | | -use SBSEDV\InputLibrary\Transformer\FormData; |
26 | | -use SBSEDV\InputLibrary\Transformer\JSON; |
27 | | -use SBSEDV\InputLibrary\Transformer\UrlEncoded; |
28 | | - |
29 | | -// PUT /test HTTP/1.1 |
30 | | -// HOST: example.com |
31 | | -// Content-Type: application/json |
32 | | -// {"key": "value", "array": ["value1","value2"]} |
33 | | -// |
34 | | -// === OR === |
35 | | -// |
36 | | -// Content-Type: application/x-www-urlencoded |
37 | | -// key=value&array[]=value1&array[]=value2 |
38 | | -// |
39 | | -// === OR === |
40 | | -// |
41 | | -// Content-Type: multipart/form-data; boundary=----WebKitFormBoundary1fownfown |
42 | | -// ------WebKitFormBoundary1fownfown |
43 | | -// Content-Disposition: form-data; name="key" |
44 | | -// value |
45 | | -// ------WebKitFormBoundary1fownfown |
46 | | -// Content-Disposition: form-data; name="array[]" |
47 | | -// value1 |
48 | | -// ------WebKitFormBoundary1fownfown |
49 | | -// Content-Disposition: form-data; name="array[]" |
50 | | -// value2 |
51 | | - |
52 | | -/** |
53 | | - * You can pass an instance of |
54 | | - * Psr\Http\Message\ServerRequestInterface |
55 | | - * OR |
56 | | - * Symfony\Component\HttpFoundation\Request |
57 | | - * to the constructor. |
58 | | - * |
59 | | - * Otherwise the library uses PHP globals. |
60 | | - * |
61 | | - * You can als set / remove the request object |
62 | | - * via setRequest in case you are having problems |
63 | | - * with your container builder. |
64 | | - * |
65 | | - * @return SBSEDV\InputLibrary\ParsedInput |
66 | | - */ |
67 | | -$parseInput = (new InputLibrary()) |
68 | | - ->registerTransformers( |
69 | | - new JSON([ |
70 | | - 'application/json', |
71 | | - 'application/ld+json' |
72 | | - ]), // support multiple json types |
73 | | - new UrlEncoded(['PUT', 'PATCH']), // allow only PUT and PATCH |
74 | | - new FormData() // default options |
75 | | - )->run(); |
| 42 | +use SBSEDV\Component\InputConverter\InputConverter; |
| 43 | +use SBSEDV\Component\InputConverter\ParsedInput; |
| 44 | + |
| 45 | +try { |
| 46 | + /** @var ParsedInput $parsedInput */ |
| 47 | + $parsedInput = (new InputConverter()) |
| 48 | + ->addConverter(...) // your converter instance |
| 49 | + ->convert($request); |
| 50 | +} catch (MalformedContentException $e) { |
| 51 | + // a converter supported the request |
| 52 | + // but encountered an error while parsing |
| 53 | + |
| 54 | + http_status_code(400); |
| 55 | + exit(); |
| 56 | +} catch (UnsupportedRequestException) { |
| 57 | + // no converter supported the request |
| 58 | +} |
| 59 | + |
76 | 60 |
|
77 | 61 | // update $_POST and $_FILES with parsed values |
78 | | -$parseInput->toGlobals(): void; |
79 | | -// populate $request->request and $request->files |
80 | | -$parseInput->toHttpFoundation( |
81 | | - Symfony\Component\HttpFoundation\Request $request |
82 | | -): void; |
| 62 | +$parseInput->toGlobals(); |
| 63 | + |
| 64 | +// OR populate $request->request and $request->files |
| 65 | +$parseInput->applyOnHttpFoundationRequest($request); |
83 | 66 |
|
84 | | -// or access the data directly |
| 67 | +// OR access the data directly |
85 | 68 | $values = $parseInput->getValues(): array; // like $_POST |
86 | 69 | $files = $fileInput->getFiles(): array // like $_FILES |
87 | 70 | ``` |
88 | 71 |
|
89 | 72 | --- |
90 | 73 |
|
91 | | -### **CAUTION WITH FILE UPLOADS**: |
| 74 | +## **Converters** |
| 75 | + |
| 76 | +The actual parsing is handled by converter classes that implement |
| 77 | +[SBSEDV\Component\InputConverter\Converter\ConverterInterface](src/Converter/ConverterInterface.php). |
| 78 | + |
| 79 | +You can always implement your own converter. |
| 80 | + |
| 81 | +By default we support three customisable converters: |
| 82 | + |
| 83 | +### `SBSEDV\Component\InputConverter\Converter\UrlEncoded` |
| 84 | + |
| 85 | +Via its constructor you can influence which content types and http methods it supports. |
| 86 | + |
| 87 | +```php |
| 88 | +public function __construct( |
| 89 | + array $contentTypes = ['application/x-www-urlencoded'], |
| 90 | + array $methods = ['PUT', 'PATCH', 'DELETE'] |
| 91 | +); |
| 92 | +``` |
| 93 | + |
| 94 | +--- |
| 95 | + |
| 96 | +### `SBSEDV\Component\InputConverter\Converter\JSON` |
| 97 | + |
| 98 | +Via its constructor you can influence which content types and http methods it supports. |
| 99 | + |
| 100 | +```php |
| 101 | +public function __construct( |
| 102 | + array $contentTypes = ['application/json'], |
| 103 | + array $methods = ['POST', 'PUT', 'PATCH', 'DELETE'] |
| 104 | +); |
| 105 | +``` |
| 106 | + |
| 107 | +--- |
| 108 | + |
| 109 | +### `SBSEDV\Component\InputConverter\Converter\FormData` |
| 110 | + |
| 111 | +Via its constructor you can influence which content types and http methods it supports. |
| 112 | + |
| 113 | +Internally this uses the [riverline/multipart-parser](https://github.com/Riverline/multipart-parser) library for parsing. |
| 114 | + |
| 115 | +```php |
| 116 | +public function __construct( |
| 117 | + array $methods = ['PUT', 'PATCH', 'DELETE'], |
| 118 | + bool $fileSupport = false |
| 119 | +); |
| 120 | +``` |
| 121 | + |
| 122 | +#### **CAUTION WITH FILE UPLOADS**: |
92 | 123 |
|
93 | | -Even though file uploads via mulitpart/form-data are fully supported, they are **NOT** recommended because the whole file will be loaded into memory which may become a problem with large files and shared hosting plans. You should instead use POST request for file uploads and let PHP handle that mess. |
| 124 | +Even though file uploads via mulitpart/form-data are fully supported, they are **NOT** recommended because the whole file will be loaded into memory. You should instead use POST request for file uploads and let PHP handle that mess natively. |
94 | 125 |
|
95 | | -#### ***COMPATIBILITY***: |
| 126 | +#### **_COMPATIBILITY_**: |
96 | 127 |
|
97 | | -The returned file format is not 100 percent compatibile with the native [$_FILES](https://www.php.net/manual/en/features.file-upload.post-method.php#example-420) global. |
| 128 | +Also, the returned file format is not 100 percent compatibile with the native [$\_FILES](https://www.php.net/manual/en/features.file-upload.post-method.php#example-420) global. |
98 | 129 |
|
99 | 130 | If you upload an array / of images like: |
100 | 131 |
|
@@ -136,63 +167,4 @@ $_FILES_ = [ |
136 | 167 | ]; |
137 | 168 | ``` |
138 | 169 |
|
139 | | -For sanity reasons, this library returns the **expected** behaviour. |
140 | | - |
141 | | ---- |
142 | | - |
143 | | -## **Transformers** |
144 | | - |
145 | | -The actual parsing and extracting is handled by `Transformer` classes that implement `SBSEDV\InputLibrary\TransformerInterface`. |
146 | | - |
147 | | -You can always implement your own transformer to handle your custom content types and register it with the library via the `SBSEDV\InputLibrary\InputLibrary::registerTransformer` method. |
148 | | - |
149 | | -By default, this library comes with three transformers: |
150 | | - |
151 | | -##### `SBSEDV\InputLibrary\Transformer\UrlEncoded` |
152 | | - |
153 | | -This transformer handles application/x-www-urlencoded. |
154 | | -Under the hood, this uses PHPs native [parse_str](https://www.php.net/manual/en/function.parse-str) function. |
155 | | -Via its constructor you can influence on which methods it should work. |
156 | | - |
157 | | -```php |
158 | | -// SBSEDV\InputLibrary\Transformer\UrlEncoded |
159 | | - |
160 | | -public function __construct( |
161 | | - array $methods = ['PUT', 'PATCH', 'DELETE'] |
162 | | -); |
163 | | -``` |
164 | | - |
165 | | -##### `SBSEDV\InputLibrary\Transformer\JSON` |
166 | | - |
167 | | -This transformer handles JSON. |
168 | | -Internally this uses PHPs native [json_decode](https://www.php.net/manual/en/function.json-decode) function. |
169 | | -Via its constructor you can influence on which content-types and methods it should work. |
170 | | - |
171 | | -Because PHP does not parse JSON nativly, this also registers for the POST http method. |
172 | | - |
173 | | -```php |
174 | | -// SBSEDV\InputLibrary\Transformer\JSON |
175 | | - |
176 | | -public function __construct( |
177 | | - array $contentTypes = ['application/json'], |
178 | | - array $methods = ['POST', 'PUT', 'PATCH', 'DELETE'] |
179 | | -); |
180 | | -``` |
181 | | - |
182 | | -##### `SBSEDV\InputLibrary\Transformer\FormData` |
183 | | - |
184 | | -This transformer handles mulitpart/form-data and is by far the most complicated, but also the most convenient because of JavaScripts awesome [FormData API](https://developer.mozilla.org/en-US/docs/Web/API/FormData). |
185 | | - |
186 | | -Internally this uses the awesome [riverline/multipart-parser](https://github.com/Riverline/multipart-parser) library for parsing. Then, those key-value pairs are concatenated to a x-www-urlencoded string and then passed to the native [parse_str](https://www.php.net/manual/en/function.parse-str) function to keep complex data structures. |
187 | | - |
188 | | - |
189 | | -Via its constructor you can influence on which methods it should work and if file uploads should be supported (defaults to false). |
190 | | - |
191 | | -```php |
192 | | -// SBSEDV\InputLibrary\Transformer\FormData |
193 | | - |
194 | | -public function __construct( |
195 | | - array $methods = ['PUT', 'PATCH', 'DELETE'], |
196 | | - bool $fileSupport = false |
197 | | -); |
198 | | -``` |
| 170 | +For sanity reasons we return the **expected** behaviour. |
0 commit comments