| 1 | <?php |
||||
| 2 | |||||
| 3 | namespace Soheilrt\AdobeConnectClient\Client\Connection\Curl; |
||||
| 4 | |||||
| 5 | use CURLFile; |
||||
| 6 | use InvalidArgumentException; |
||||
| 7 | use Soheilrt\AdobeConnectClient\Client\Connection\ConnectionInterface; |
||||
| 8 | use Soheilrt\AdobeConnectClient\Client\Connection\ResponseInterface; |
||||
| 9 | use SplFileInfo; |
||||
| 10 | use UnexpectedValueException; |
||||
| 11 | |||||
| 12 | /** |
||||
| 13 | * Connection using cURL. |
||||
| 14 | */ |
||||
| 15 | class Connection implements ConnectionInterface |
||||
| 16 | { |
||||
| 17 | /** |
||||
| 18 | * @var array Associative array of Options |
||||
| 19 | */ |
||||
| 20 | protected $config = []; |
||||
| 21 | |||||
| 22 | /** |
||||
| 23 | * @var string The host URL |
||||
| 24 | */ |
||||
| 25 | protected $host = ''; |
||||
| 26 | |||||
| 27 | /** |
||||
| 28 | * @var array [string => string[]] Simplify headers generation in cURL call |
||||
| 29 | */ |
||||
| 30 | protected $headers = []; |
||||
| 31 | |||||
| 32 | /** |
||||
| 33 | * Create the instance using a host URL and config. |
||||
| 34 | * |
||||
| 35 | * @param string $host The Host URL |
||||
| 36 | * @param array $config An array to config cURL. Use CURLOPT_* as index |
||||
| 37 | * |
||||
| 38 | * @throws InvalidArgumentException if $host is not a valid URL with scheme |
||||
| 39 | */ |
||||
| 40 | public function __construct($host, array $config = []) |
||||
| 41 | { |
||||
| 42 | $this->setHost($host); |
||||
| 43 | $this->setConfig($config); |
||||
| 44 | } |
||||
| 45 | |||||
| 46 | /** |
||||
| 47 | * Set the Host URL. |
||||
| 48 | * |
||||
| 49 | * @param string $host The Host URL |
||||
| 50 | * |
||||
| 51 | * @throws InvalidArgumentException if $host is not a valid URL with scheme |
||||
| 52 | */ |
||||
| 53 | public function setHost($host): void |
||||
| 54 | { |
||||
| 55 | $host = filter_var(trim($host, " ?/\n\t"), FILTER_SANITIZE_URL); |
||||
| 56 | |||||
| 57 | if (!filter_var($host, FILTER_VALIDATE_URL)) { |
||||
| 58 | throw new InvalidArgumentException('Connection Host must be a valid URL with scheme'); |
||||
| 59 | } |
||||
| 60 | $this->host = strpos($host, '/api/xml') === false ? $host . '/api/xml' : $host; |
||||
| 61 | } |
||||
| 62 | |||||
| 63 | /** |
||||
| 64 | * Set the cURL config. |
||||
| 65 | * |
||||
| 66 | * @param array $config Associative array. Items as Option => Value |
||||
| 67 | */ |
||||
| 68 | protected function setConfig(array $config): void |
||||
| 69 | { |
||||
| 70 | //get default configuration value and merge them with user given values in case that user |
||||
| 71 | //if any of given values are missing from below were absent it'll use default config as given below |
||||
| 72 | $defaults = [ |
||||
| 73 | CURLOPT_CONNECTTIMEOUT => 120, |
||||
| 74 | CURLOPT_TIMEOUT => 120, |
||||
| 75 | CURLOPT_MAXREDIRS => 10, |
||||
| 76 | CURLOPT_SSL_VERIFYPEER => true, |
||||
| 77 | CURLOPT_SSL_VERIFYHOST => 2, |
||||
| 78 | ]; |
||||
| 79 | $this->config = $config + $defaults; |
||||
| 80 | |||||
| 81 | // Always need this configurations |
||||
| 82 | $this->config[CURLOPT_RETURNTRANSFER] = true; |
||||
| 83 | $this->config[CURLOPT_FOLLOWLOCATION] = true; |
||||
| 84 | $this->config[CURLOPT_HEADERFUNCTION] = [$this, 'extractHeader']; |
||||
| 85 | } |
||||
| 86 | |||||
| 87 | /** |
||||
| 88 | * {@inheritdoc} |
||||
| 89 | */ |
||||
| 90 | public function get(array $queryParams = []): ResponseInterface |
||||
| 91 | { |
||||
| 92 | $curl_resource = $this->prepareCurl($queryParams); |
||||
| 93 | return $this->makeCurlRequest($curl_resource); |
||||
| 94 | } |
||||
| 95 | |||||
| 96 | /** |
||||
| 97 | * Reset the temporary headers and prepare the cURL. |
||||
| 98 | * |
||||
| 99 | * @param array $queryParams Associative array to add params in URL |
||||
| 100 | * |
||||
| 101 | * @return resource A cURL resource |
||||
| 102 | */ |
||||
| 103 | protected function prepareCurl(array $queryParams = []) |
||||
| 104 | { |
||||
| 105 | $this->headers = []; |
||||
| 106 | |||||
| 107 | $ch = curl_init($this->getFullURL($queryParams)); |
||||
| 108 | curl_setopt_array($ch, $this->config); |
||||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 109 | |||||
| 110 | return $ch; |
||||
|
0 ignored issues
–
show
The expression
return $ch could also return false which is incompatible with the documented return type resource. Did you maybe forget to handle an error condition?
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled. Loading history...
|
|||||
| 111 | } |
||||
| 112 | |||||
| 113 | /** |
||||
| 114 | * Get the full URL with query parameters. |
||||
| 115 | * |
||||
| 116 | * @param array $queryParams Associative array to add params in URL |
||||
| 117 | * |
||||
| 118 | * @return string |
||||
| 119 | */ |
||||
| 120 | protected function getFullURL(array $queryParams): string |
||||
| 121 | { |
||||
| 122 | return empty($queryParams) |
||||
| 123 | ? $this->host |
||||
| 124 | : $this->host . '?' . http_build_query($queryParams, '', '&'); |
||||
| 125 | } |
||||
| 126 | |||||
| 127 | /** |
||||
| 128 | * @param $ch |
||||
| 129 | * |
||||
| 130 | * @return \Soheilrt\AdobeConnectClient\Client\Connection\Curl\Response |
||||
| 131 | */ |
||||
| 132 | protected function makeCurlRequest($ch): Response |
||||
| 133 | { |
||||
| 134 | $body = curl_exec($ch); |
||||
| 135 | $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
||||
| 136 | |||||
| 137 | if ($body === false) { |
||||
| 138 | $exception = new UnexpectedValueException(curl_error($ch), curl_errno($ch)); |
||||
| 139 | curl_close($ch); |
||||
| 140 | |||||
| 141 | throw $exception; |
||||
| 142 | } |
||||
| 143 | curl_close($ch); |
||||
| 144 | |||||
| 145 | return new Response($statusCode, $this->headers, new Stream($body)); |
||||
|
0 ignored issues
–
show
It seems like
$body can also be of type true; however, parameter $content of Soheilrt\AdobeConnectCli...l\Stream::__construct() does only seem to accept string, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 146 | } |
||||
| 147 | |||||
| 148 | /** |
||||
| 149 | * {@inheritdoc} |
||||
| 150 | */ |
||||
| 151 | public function post(array $postParams, array $queryParams = []): ResponseInterface |
||||
| 152 | { |
||||
| 153 | $ch = $this->prepareCurl($queryParams); |
||||
| 154 | curl_setopt($ch, CURLOPT_POST, 1); |
||||
| 155 | curl_setopt($ch, CURLOPT_POSTFIELDS, $this->convertFileParams($postParams)); |
||||
| 156 | return $this->makeCurlRequest($ch); |
||||
| 157 | } |
||||
| 158 | |||||
| 159 | /** |
||||
| 160 | * Convert stream file and SplFileInfo in CURLFile. |
||||
| 161 | * |
||||
| 162 | * @param array $params Associative array of parameters |
||||
| 163 | * |
||||
| 164 | * @return array |
||||
| 165 | */ |
||||
| 166 | protected function convertFileParams(array $params): array |
||||
| 167 | { |
||||
| 168 | foreach ($params as $param => $value) { |
||||
| 169 | $fileInfo = $this->fileInfo($value); |
||||
| 170 | |||||
| 171 | if (empty($fileInfo)) { |
||||
| 172 | continue; |
||||
| 173 | } |
||||
| 174 | $params[$param] = new CURLFile($fileInfo['path'], $fileInfo['mime']); |
||||
| 175 | } |
||||
| 176 | |||||
| 177 | return $params; |
||||
| 178 | } |
||||
| 179 | |||||
| 180 | /** |
||||
| 181 | * Get the filepath and mime-type from a file. |
||||
| 182 | * |
||||
| 183 | * If it's a stream file or \SplFileInfo returns an object with path and mime. |
||||
| 184 | * |
||||
| 185 | * @param resource|SplFileInfo $item A stream file or SplFileInfo object |
||||
| 186 | * |
||||
| 187 | * @return array|null Returns null if it's not a valid stream file or SplFileInfo |
||||
| 188 | */ |
||||
| 189 | protected function fileInfo($item): ?array |
||||
| 190 | { |
||||
| 191 | if (is_resource($item)) { |
||||
| 192 | $streamMeta = stream_get_meta_data($item); |
||||
| 193 | |||||
| 194 | if ($streamMeta['wrapper_type'] !== 'plainfile') { |
||||
| 195 | return null; |
||||
| 196 | } |
||||
| 197 | $path = $streamMeta['uri']; |
||||
| 198 | $mime = mime_content_type($path); |
||||
| 199 | } elseif ($item instanceof SplFileInfo and $item->getType() === 'file') { |
||||
| 200 | $path = $item->getPathname(); |
||||
| 201 | $mime = mime_content_type($path); |
||||
| 202 | } else { |
||||
| 203 | return null; |
||||
| 204 | } |
||||
| 205 | |||||
| 206 | return [ |
||||
| 207 | 'path' => $path, |
||||
| 208 | 'mime' => $mime, |
||||
| 209 | ]; |
||||
| 210 | } |
||||
| 211 | |||||
| 212 | /** |
||||
| 213 | * Extract header line and store it to posterior use. |
||||
| 214 | * |
||||
| 215 | * This method is called by option CURLOPT_HEADERFUNCTION. |
||||
| 216 | * |
||||
| 217 | * @param resource $curlResource |
||||
| 218 | * @param string $headerLine |
||||
| 219 | * |
||||
| 220 | * @return int The size of header line |
||||
| 221 | */ |
||||
| 222 | protected function extractHeader($curlResource, $headerLine): int |
||||
| 223 | { |
||||
| 224 | $headerSize = strlen($headerLine); |
||||
| 225 | $headerLine = trim($headerLine, " \t\n"); |
||||
| 226 | |||||
| 227 | if (empty($headerLine)) { |
||||
| 228 | return $headerSize; |
||||
| 229 | } |
||||
| 230 | |||||
| 231 | $pos = strpos($headerLine, ':'); |
||||
| 232 | |||||
| 233 | if ($pos === false) { |
||||
| 234 | return $headerSize; |
||||
| 235 | } |
||||
| 236 | |||||
| 237 | $header = trim(substr($headerLine, 0, $pos)); |
||||
| 238 | |||||
| 239 | if (!in_array($header, ['Set-Cookie', 'Content-Type'])) { |
||||
| 240 | return $headerSize; |
||||
| 241 | } |
||||
| 242 | $this->headers[$header] = [trim(substr($headerLine, $pos + 1))]; |
||||
| 243 | |||||
| 244 | return $headerSize; |
||||
| 245 | } |
||||
| 246 | } |
||||
| 247 |