FormData.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  10. * @link http://cakephp.org CakePHP(tm) Project
  11. * @since 3.0.0
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Cake\Network\Http;
  15. use Cake\Network\Http\FormData\Part;
  16. use Countable;
  17. /**
  18. * Provides an interface for building
  19. * multipart/form-encoded message bodies.
  20. *
  21. * Used by Http\Client to upload POST/PUT data
  22. * and files.
  23. *
  24. */
  25. class FormData implements Countable
  26. {
  27. /**
  28. * Boundary marker.
  29. *
  30. * @var string
  31. */
  32. protected $_boundary;
  33. /**
  34. * The parts in the form data.
  35. *
  36. * @var array
  37. */
  38. protected $_parts = [];
  39. /**
  40. * Get the boundary marker
  41. *
  42. * @return string
  43. */
  44. public function boundary()
  45. {
  46. if ($this->_boundary) {
  47. return $this->_boundary;
  48. }
  49. $this->_boundary = md5(uniqid(time()));
  50. return $this->_boundary;
  51. }
  52. /**
  53. * Method for creating new instances of Part
  54. *
  55. * @param string $name The name of the part.
  56. * @param string $value The value to add.
  57. * @return \Cake\Network\Http\FormData\Part
  58. */
  59. public function newPart($name, $value)
  60. {
  61. return new Part($name, $value);
  62. }
  63. /**
  64. * Add a new part to the data.
  65. *
  66. * The value for a part can be a string, array, int,
  67. * float, filehandle, or object implementing __toString()
  68. *
  69. * If the $value is an array, multiple parts will be added.
  70. * Files will be read from their current position and saved in memory.
  71. *
  72. * @param string $name The name of the part.
  73. * @param mixed $value The value for the part.
  74. * @return $this
  75. */
  76. public function add($name, $value)
  77. {
  78. if (is_array($value)) {
  79. $this->addRecursive($name, $value);
  80. } elseif (is_resource($value)) {
  81. $this->_parts[] = $this->addFile($name, $value);
  82. } elseif (is_string($value) && strlen($value) && $value[0] === '@') {
  83. trigger_error(
  84. 'Using the @ syntax for file uploads is not safe and is deprecated. ' .
  85. 'Instead you should use file handles.',
  86. E_USER_DEPRECATED
  87. );
  88. $this->_parts[] = $this->addFile($name, $value);
  89. } else {
  90. $this->_parts[] = $this->newPart($name, $value);
  91. }
  92. return $this;
  93. }
  94. /**
  95. * Add multiple parts at once.
  96. *
  97. * Iterates the parameter and adds all the key/values.
  98. *
  99. * @param array $data Array of data to add.
  100. * @return $this
  101. */
  102. public function addMany(array $data)
  103. {
  104. foreach ($data as $name => $value) {
  105. $this->add($name, $value);
  106. }
  107. return $this;
  108. }
  109. /**
  110. * Add either a file reference (string starting with @)
  111. * or a file handle.
  112. *
  113. * @param string $name The name to use.
  114. * @param mixed $value Either a string filename, or a filehandle.
  115. * @return \Cake\Network\Http\FormData\Part
  116. */
  117. public function addFile($name, $value)
  118. {
  119. $filename = false;
  120. $contentType = 'application/octet-stream';
  121. if (is_resource($value)) {
  122. $content = stream_get_contents($value);
  123. if (stream_is_local($value)) {
  124. $finfo = new \finfo(FILEINFO_MIME);
  125. $metadata = stream_get_meta_data($value);
  126. $contentType = $finfo->file($metadata['uri']);
  127. $filename = basename($metadata['uri']);
  128. }
  129. } else {
  130. $finfo = new \finfo(FILEINFO_MIME);
  131. $value = substr($value, 1);
  132. $filename = basename($value);
  133. $content = file_get_contents($value);
  134. $contentType = $finfo->file($value);
  135. }
  136. $part = $this->newPart($name, $content);
  137. $part->type($contentType);
  138. if ($filename) {
  139. $part->filename($filename);
  140. }
  141. return $part;
  142. }
  143. /**
  144. * Recursively add data.
  145. *
  146. * @param string $name The name to use.
  147. * @param mixed $value The value to add.
  148. * @return void
  149. */
  150. public function addRecursive($name, $value)
  151. {
  152. foreach ($value as $key => $value) {
  153. $key = $name . '[' . $key . ']';
  154. $this->add($key, $value);
  155. }
  156. }
  157. /**
  158. * Returns the count of parts inside this object.
  159. *
  160. * @return int
  161. */
  162. public function count()
  163. {
  164. return count($this->_parts);
  165. }
  166. /**
  167. * Converts the FormData and its parts into a string suitable
  168. * for use in an HTTP request.
  169. *
  170. * @return string
  171. */
  172. public function __toString()
  173. {
  174. $boundary = $this->boundary();
  175. $out = '';
  176. foreach ($this->_parts as $part) {
  177. $out .= "--$boundary\r\n";
  178. $out .= (string)$part;
  179. $out .= "\r\n";
  180. }
  181. $out .= "--$boundary--\r\n\r\n";
  182. return $out;
  183. }
  184. }