酒店预订平台
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

159 lignes
4.7 KiB

  1. <?php
  2. namespace GuzzleHttp\Psr7;
  3. use Psr\Http\Message\StreamInterface;
  4. /**
  5. * Stream that when read returns bytes for a streaming multipart or
  6. * multipart/form-data stream.
  7. *
  8. * @final
  9. */
  10. class MultipartStream implements StreamInterface
  11. {
  12. use StreamDecoratorTrait;
  13. private $boundary;
  14. /**
  15. * @param array $elements Array of associative arrays, each containing a
  16. * required "name" key mapping to the form field,
  17. * name, a required "contents" key mapping to a
  18. * StreamInterface/resource/string, an optional
  19. * "headers" associative array of custom headers,
  20. * and an optional "filename" key mapping to a
  21. * string to send as the filename in the part.
  22. * @param string $boundary You can optionally provide a specific boundary
  23. *
  24. * @throws \InvalidArgumentException
  25. */
  26. public function __construct(array $elements = [], $boundary = null)
  27. {
  28. $this->boundary = $boundary ?: sha1(uniqid('', true));
  29. $this->stream = $this->createStream($elements);
  30. }
  31. /**
  32. * Get the boundary
  33. *
  34. * @return string
  35. */
  36. public function getBoundary()
  37. {
  38. return $this->boundary;
  39. }
  40. public function isWritable()
  41. {
  42. return false;
  43. }
  44. /**
  45. * Get the headers needed before transferring the content of a POST file
  46. */
  47. private function getHeaders(array $headers)
  48. {
  49. $str = '';
  50. foreach ($headers as $key => $value) {
  51. $str .= "{$key}: {$value}\r\n";
  52. }
  53. return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
  54. }
  55. /**
  56. * Create the aggregate stream that will be used to upload the POST data
  57. */
  58. protected function createStream(array $elements)
  59. {
  60. $stream = new AppendStream();
  61. foreach ($elements as $element) {
  62. $this->addElement($stream, $element);
  63. }
  64. // Add the trailing boundary with CRLF
  65. $stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n"));
  66. return $stream;
  67. }
  68. private function addElement(AppendStream $stream, array $element)
  69. {
  70. foreach (['contents', 'name'] as $key) {
  71. if (!array_key_exists($key, $element)) {
  72. throw new \InvalidArgumentException("A '{$key}' key is required");
  73. }
  74. }
  75. $element['contents'] = Utils::streamFor($element['contents']);
  76. if (empty($element['filename'])) {
  77. $uri = $element['contents']->getMetadata('uri');
  78. if (substr($uri, 0, 6) !== 'php://') {
  79. $element['filename'] = $uri;
  80. }
  81. }
  82. list($body, $headers) = $this->createElement(
  83. $element['name'],
  84. $element['contents'],
  85. isset($element['filename']) ? $element['filename'] : null,
  86. isset($element['headers']) ? $element['headers'] : []
  87. );
  88. $stream->addStream(Utils::streamFor($this->getHeaders($headers)));
  89. $stream->addStream($body);
  90. $stream->addStream(Utils::streamFor("\r\n"));
  91. }
  92. /**
  93. * @return array
  94. */
  95. private function createElement($name, StreamInterface $stream, $filename, array $headers)
  96. {
  97. // Set a default content-disposition header if one was no provided
  98. $disposition = $this->getHeader($headers, 'content-disposition');
  99. if (!$disposition) {
  100. $headers['Content-Disposition'] = ($filename === '0' || $filename)
  101. ? sprintf(
  102. 'form-data; name="%s"; filename="%s"',
  103. $name,
  104. basename($filename)
  105. )
  106. : "form-data; name=\"{$name}\"";
  107. }
  108. // Set a default content-length header if one was no provided
  109. $length = $this->getHeader($headers, 'content-length');
  110. if (!$length) {
  111. if ($length = $stream->getSize()) {
  112. $headers['Content-Length'] = (string) $length;
  113. }
  114. }
  115. // Set a default Content-Type if one was not supplied
  116. $type = $this->getHeader($headers, 'content-type');
  117. if (!$type && ($filename === '0' || $filename)) {
  118. if ($type = MimeType::fromFilename($filename)) {
  119. $headers['Content-Type'] = $type;
  120. }
  121. }
  122. return [$stream, $headers];
  123. }
  124. private function getHeader(array $headers, $key)
  125. {
  126. $lowercaseHeader = strtolower($key);
  127. foreach ($headers as $k => $v) {
  128. if (strtolower($k) === $lowercaseHeader) {
  129. return $v;
  130. }
  131. }
  132. return null;
  133. }
  134. }