1<?php 2/** 3 * Copyright 2017 Facebook, Inc. 4 * 5 * You are hereby granted a non-exclusive, worldwide, royalty-free license to 6 * use, copy, modify, and distribute this software in source code or binary 7 * form for use in connection with the web services and APIs provided by 8 * Facebook. 9 * 10 * As with any software that integrates with the Facebook platform, your use 11 * of this software is subject to the Facebook Developer Principles and 12 * Policies [http://developers.facebook.com/policy/]. This copyright notice 13 * shall be included in all copies or substantial portions of the software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 * 23 */ 24namespace Facebook\Http; 25 26use Facebook\FileUpload\FacebookFile; 27 28/** 29 * Class RequestBodyMultipartt 30 * 31 * Some things copied from Guzzle 32 * 33 * @package Facebook 34 * 35 * @see https://github.com/guzzle/guzzle/blob/master/src/Post/MultipartBody.php 36 */ 37class RequestBodyMultipart implements RequestBodyInterface 38{ 39 /** 40 * @var string The boundary. 41 */ 42 private $boundary; 43 44 /** 45 * @var array The parameters to send with this request. 46 */ 47 private $params; 48 49 /** 50 * @var array The files to send with this request. 51 */ 52 private $files = []; 53 54 /** 55 * @param array $params The parameters to send with this request. 56 * @param array $files The files to send with this request. 57 * @param string $boundary Provide a specific boundary. 58 */ 59 public function __construct(array $params = [], array $files = [], $boundary = null) 60 { 61 $this->params = $params; 62 $this->files = $files; 63 $this->boundary = $boundary ?: uniqid(); 64 } 65 66 /** 67 * @inheritdoc 68 */ 69 public function getBody() 70 { 71 $body = ''; 72 73 // Compile normal params 74 $params = $this->getNestedParams($this->params); 75 foreach ($params as $k => $v) { 76 $body .= $this->getParamString($k, $v); 77 } 78 79 // Compile files 80 foreach ($this->files as $k => $v) { 81 $body .= $this->getFileString($k, $v); 82 } 83 84 // Peace out 85 $body .= "--{$this->boundary}--\r\n"; 86 87 return $body; 88 } 89 90 /** 91 * Get the boundary 92 * 93 * @return string 94 */ 95 public function getBoundary() 96 { 97 return $this->boundary; 98 } 99 100 /** 101 * Get the string needed to transfer a file. 102 * 103 * @param string $name 104 * @param FacebookFile $file 105 * 106 * @return string 107 */ 108 private function getFileString($name, FacebookFile $file) 109 { 110 return sprintf( 111 "--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"%s\r\n\r\n%s\r\n", 112 $this->boundary, 113 $name, 114 $file->getFileName(), 115 $this->getFileHeaders($file), 116 $file->getContents() 117 ); 118 } 119 120 /** 121 * Get the string needed to transfer a POST field. 122 * 123 * @param string $name 124 * @param string $value 125 * 126 * @return string 127 */ 128 private function getParamString($name, $value) 129 { 130 return sprintf( 131 "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", 132 $this->boundary, 133 $name, 134 $value 135 ); 136 } 137 138 /** 139 * Returns the params as an array of nested params. 140 * 141 * @param array $params 142 * 143 * @return array 144 */ 145 private function getNestedParams(array $params) 146 { 147 $query = http_build_query($params, null, '&'); 148 $params = explode('&', $query); 149 $result = []; 150 151 foreach ($params as $param) { 152 list($key, $value) = explode('=', $param, 2); 153 $result[urldecode($key)] = urldecode($value); 154 } 155 156 return $result; 157 } 158 159 /** 160 * Get the headers needed before transferring the content of a POST file. 161 * 162 * @param FacebookFile $file 163 * 164 * @return string 165 */ 166 protected function getFileHeaders(FacebookFile $file) 167 { 168 return "\r\nContent-Type: {$file->getMimetype()}"; 169 } 170} 171