FormDataPart.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Mime\Part\Multipart;
  11. use Symfony\Component\Mime\Exception\InvalidArgumentException;
  12. use Symfony\Component\Mime\Part\AbstractMultipartPart;
  13. use Symfony\Component\Mime\Part\DataPart;
  14. use Symfony\Component\Mime\Part\TextPart;
  15. /**
  16. * Implements RFC 7578.
  17. *
  18. * @author Fabien Potencier <fabien@symfony.com>
  19. */
  20. final class FormDataPart extends AbstractMultipartPart
  21. {
  22. private $fields = [];
  23. /**
  24. * @param (string|array|DataPart)[] $fields
  25. */
  26. public function __construct(array $fields = [])
  27. {
  28. parent::__construct();
  29. foreach ($fields as $name => $value) {
  30. if (!\is_string($value) && !\is_array($value) && !$value instanceof TextPart) {
  31. throw new InvalidArgumentException(sprintf('A form field value can only be a string, an array, or an instance of TextPart ("%s" given).', get_debug_type($value)));
  32. }
  33. $this->fields[$name] = $value;
  34. }
  35. // HTTP does not support \r\n in header values
  36. $this->getHeaders()->setMaxLineLength(\PHP_INT_MAX);
  37. }
  38. public function getMediaSubtype(): string
  39. {
  40. return 'form-data';
  41. }
  42. public function getParts(): array
  43. {
  44. return $this->prepareFields($this->fields);
  45. }
  46. private function prepareFields(array $fields): array
  47. {
  48. $values = [];
  49. $prepare = function ($item, $key, $root = null) use (&$values, &$prepare) {
  50. if (\is_int($key) && \is_array($item)) {
  51. if (1 !== \count($item)) {
  52. throw new InvalidArgumentException(sprintf('Form field values with integer keys can only have one array element, the key being the field name and the value being the field value, %d provided.', \count($item)));
  53. }
  54. $key = key($item);
  55. $item = $item[$key];
  56. }
  57. $fieldName = null !== $root ? sprintf('%s[%s]', $root, $key) : $key;
  58. if (\is_array($item)) {
  59. array_walk($item, $prepare, $fieldName);
  60. return;
  61. }
  62. $values[] = $this->preparePart($fieldName, $item);
  63. };
  64. array_walk($fields, $prepare);
  65. return $values;
  66. }
  67. private function preparePart(string $name, $value): TextPart
  68. {
  69. if (\is_string($value)) {
  70. return $this->configurePart($name, new TextPart($value, 'utf-8', 'plain', '8bit'));
  71. }
  72. return $this->configurePart($name, $value);
  73. }
  74. private function configurePart(string $name, TextPart $part): TextPart
  75. {
  76. static $r;
  77. if (null === $r) {
  78. $r = new \ReflectionProperty(TextPart::class, 'encoding');
  79. $r->setAccessible(true);
  80. }
  81. $part->setDisposition('form-data');
  82. $part->setName($name);
  83. // HTTP does not support \r\n in header values
  84. $part->getHeaders()->setMaxLineLength(\PHP_INT_MAX);
  85. $r->setValue($part, '8bit');
  86. return $part;
  87. }
  88. }