MultipartFormDataParser.php 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. <?php
  2. namespace Libs;
  3. use Psr\Http\Message\ServerRequestInterface;
  4. class MultipartFormDataParser
  5. {
  6. public static function parse(ServerRequestInterface $request): array
  7. {
  8. $contentType = $request->getHeaderLine('Content-Type');
  9. if (!preg_match('/multipart\/form-data;\s*boundary=(?:(?:"([^"]+)")|([^;\s]+))/i', $contentType, $m)) {
  10. throw new \RuntimeException('Invalid multipart Content-Type');
  11. }
  12. $boundary = $m[1] !== '' ? $m[1] : $m[2];
  13. $rawBody = (string)$request->getBody();
  14. if ($rawBody === '') {
  15. $rawBody = (string)file_get_contents('php://input');
  16. }
  17. $delimiter = "--" . $boundary;
  18. $parts = explode($delimiter, $rawBody);
  19. $fields = [];
  20. $files = [];
  21. foreach ($parts as $part) {
  22. $part = ltrim($part, "\r\n");
  23. $part = rtrim($part, "\r\n");
  24. if ($part === '' || $part === '--') {
  25. continue;
  26. }
  27. $split = preg_split("/\r\n\r\n/", $part, 2);
  28. if (!$split || count($split) !== 2) {
  29. continue;
  30. }
  31. [$rawHeaders, $body] = $split;
  32. $headers = self::parseHeaders($rawHeaders);
  33. $cd = $headers['content-disposition'] ?? '';
  34. if (!preg_match('/name="([^"]+)"/i', $cd, $nm)) {
  35. continue;
  36. }
  37. $name = $nm[1];
  38. $filename = null;
  39. if (preg_match('/filename="([^"]*)"/i', $cd, $fm)) {
  40. $filename = $fm[1];
  41. }
  42. $body = preg_replace("/\r\n\z/", '', $body);
  43. if ($filename !== null && $filename !== '') {
  44. $files[$name] = [
  45. 'field' => $name,
  46. 'filename' => $filename,
  47. 'content_type' => $headers['content-type'] ?? 'application/octet-stream',
  48. 'content' => $body,
  49. ];
  50. } else {
  51. $fields[$name] = $body;
  52. }
  53. }
  54. return ['fields' => $fields, 'files' => $files];
  55. }
  56. private static function parseHeaders(string $rawHeaders): array
  57. {
  58. $headers = [];
  59. foreach (preg_split('/\r\n/', $rawHeaders) as $line) {
  60. $line = trim($line);
  61. if ($line === '' || strpos($line, ':') === false) {
  62. continue;
  63. }
  64. [$k, $v] = explode(':', $line, 2);
  65. $headers[strtolower(trim($k))] = trim($v);
  66. }
  67. return $headers;
  68. }
  69. }