Skip to content

Commit

Permalink
Added parser for raw form data
Browse files Browse the repository at this point in the history
  • Loading branch information
Simbiat committed Jun 24, 2022
1 parent d7c5835 commit f8dde45
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ _Notice:_ While some functions (like `zEcho` or different `Headers`) can replace
+ [links](doc/Headers.md#links)
+ [redirect](doc/Headers.md#redirect)
+ [notAccept](doc/Headers.md#notaccept)
+ [multiPartFormParse](doc/Headers.md#multiPartFormParse)
* [Meta](doc/Meta.md)
+ [twitter](doc/Meta.md#twitter)
+ [msTile](doc/Meta.md#mstile)
Expand Down
9 changes: 9 additions & 0 deletions doc/Headers.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [links](#links)
- [redirect](#redirect)
- [notAccept](#notaccept)
- [multiPartFormParse](#multiPartFormParse)

# Headers
Functions that send/handle different HTTP headers.
Expand Down Expand Up @@ -171,3 +172,11 @@ Client will send standard `Accept` HTTP header with list of acceptable MIME type
If none of your supported MIME types match `Accept` header, `406` header will be returned to client. If header is not provided by client, function will return `true`. It will also return `true`, if client provides `*/*` MIME type. This is why it will be niche: most browsers are sending it in their `Accept` headers. If you are using some custom API - this may be useful, still.
`$supported` - array of MIME types you support.
`$exit` if set to `true` will exit the script right after HTTP 406 is sent, otherwise will return `false`.

## multiPartFormParse
```php
multiPartFormParse()
```
This function parses `multipart/form-data` data for PUT, DELETE and PATCH methods and dumps the result as associative array to respective static variables `$_PUT`, `$_DELETE` and `$_PATCH` (accessed as `\Simbiat\HTTP20\Headers::$_PUT`).
The same logic can technically be used for POST as well, but PHP already parses it into native `$_POST`, so need to cover it.
Other HTTP verbs are not supposed to be accompanied by form data like this.
38 changes: 38 additions & 0 deletions src/HTTP20/Headers.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

class Headers
{
public static array $_PUT = [];
public static array $_DELETE = [];
public static array $_PATCH = [];

#Regex to validate Origins (essentially, an URI in https://examplecom:443 format)
public const originRegex = '(?<scheme>[a-zA-Z][a-zA-Z0-9+.-]+):\/\/(?<host>[a-zA-Z0-9.\-_~]+)(?<port>:\d+)?';
#Safe HTTP methods which can, generally, be allowed for processing
Expand Down Expand Up @@ -945,4 +949,38 @@ public function notAccept(array $supported = ['text/html'], bool $exit = true):
return true;
}
}

#Function to parse multipart/form-data for PUT/DELETE/PATCH methods
public function multiPartFormParse(): void
{
#Get method
$method = $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'] ?? $_SERVER['REQUEST_METHOD'] ?? null;
#Get Content-Type
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
#Exit if not one of the supported methods or wrong content-type
if (!in_array($method, ['PUT', 'DELETE', 'PATCH']) || preg_match('/^multipart\/form-data; boundary=.*$/ui', $contentType) !== 1) {
return;
}
#Get boundary value
$boundary = preg_replace('/(^multipart\/form-data; boundary=)(.*$)/ui', '$2', $contentType);
#Get input stream
$formData = file_get_contents('php://input');
#Exit if failed to get the input or if it's not compliant with the RFC2046
if ($formData === false || preg_match('/^\s*--'.$boundary.'.*\s*--'.$boundary.'--\s*$/muis', $formData) !== 1) {
return;
}
#Strip ending boundary
$formData = preg_replace('/(^\s*--'.$boundary.'.*)(\s*--'.$boundary.'--\s*$)/muis', '$1', $formData);
#Split data into array of fields
$formData = preg_split('/\s*--'.$boundary.'\s*Content-Disposition: form-data;\s*/muis', $formData, 0, PREG_SPLIT_NO_EMPTY);
#Convert to associative array
$parsedData = [];
foreach ($formData as $field) {
$name = preg_replace('/(name=")(?<name>[^"]+)("\s*)(?<value>.*$)/mui', '$2', $field);
$value = preg_replace('/(name=")(?<name>[^"]+)("\s*)(?<value>.*$)/mui', '$4', $field);
$parsedData[trim($name)] = trim($value);
}
#Update static variable based on method value
self::${'_'.strtoupper($method)} = $parsedData;
}
}

0 comments on commit f8dde45

Please sign in to comment.