request($method, $url, $settings); } catch (\Throwable $e) { $response = new Helpers\HTTPHelper($e); } return $response; } /** * Make any type of request asynchronously. * * @param string $url * @param string $method * @param string|array $data * @param array $headers * @param array $cookies * @param array $settings * @return \GuzzleHttp\Promise\PromiseInterface */ public static function async(string $url, string $method = 'GET', $data = null, array $headers = [], array $cookies = [], array $settings = []): \GuzzleHttp\Promise\PromiseInterface { // Create a new Guzzle client, or reuse a cached one if available. $guzzle = self::_createClient(); // Populate settings. $settings = self::_populateSettings($url, $method, 'async', $data, $headers, $cookies, $settings); // Send the request. return $guzzle->requestAsync($method, $url, $settings); } /** * Make multiple concurrent requests. * * @param array $requests[url, method, data, headers, cookies, settings] * @return array */ public static function multiple(array $requests): array { // Create a new Guzzle client, or reuse a cached one if available. $guzzle = self::_createClient(); // Add settings for each request. $promises = []; foreach ($requests as $key => $val) { $settings = self::_populateSettings($val['url'], $val['method'] ?? 'GET', 'multiple', $val['data'] ?? null, $val['headers'] ?? [], $val['cookies'] ?? [], $val['settings'] ?? []); $promise = $guzzle->requestAsync($val['method'] ?? 'GET', $val['url'], $settings); $promises[$key] = $promise; } // Wait for all the requests to complete. $responses = \GuzzleHttp\Promise\Utils::settle($promises)->wait(); $result = []; foreach ($responses as $key => $val) { if ($val['state'] === 'fulfilled') { $result[$key] = $val['value']; } else { $result[$key] = new Helpers\HTTPHelper($val['reason']); } } return $result; } /** * Create a Guzzle client with default options. * * @return \GuzzleHttp\Client */ protected static function _createClient(): \GuzzleHttp\Client { if (!self::$_client) { // Create default settings. $settings = [ 'http_errors' => false, 'timeout' => self::DEFAULT_TIMEOUT, 'verify' => \RX_BASEDIR . 'common/vendor/composer/ca-bundle/res/cacert.pem', ]; // Add proxy settings. $proxy = config('other.proxy') ?: (defined('__PROXY_SERVER__') ? constant('__PROXY_SERVER__') : ''); if ($proxy !== '') { $proxy = parse_url($proxy); $proxy_scheme = preg_match('/^(https|socks)/', $proxy['scheme'] ?? '') ? ($proxy['scheme'] . '://') : 'http://'; $proxy_auth = (!empty($proxy['user']) && !empty($proxy['pass'])) ? ($proxy['user'] . ':' . $proxy['pass'] . '@') : ''; if (!empty($proxy['host']) && !empty($proxy['port'])) { $settings['proxy'] = sprintf('%s%s%s:%s', $proxy_scheme, $proxy_auth, $proxy['host'], $proxy['port']); } } self::$_client = new \GuzzleHttp\Client($settings); } return self::$_client; } /** * Populate a settings array with various options. * * @param string $url * @param string $method * @param string $type * @param string|array $data * @param array $headers * @param array $cookies * @param array $settings * @return array */ protected static function _populateSettings(string $url, string $method = 'GET', string $type = '', $data = null, array $headers = [], array $cookies = [], array $settings = []): array { // Set headers. if ($headers) { $settings['headers'] = $headers; } // Set cookies. if ($cookies && !isset($settings['cookies'])) { $jar = \GuzzleHttp\Cookie\CookieJar::fromArray($cookies, parse_url($url, \PHP_URL_HOST)); $settings['cookies'] = $jar; } // Set the body or POST data. if (is_string($data) && strlen($data) > 0) { $settings['body'] = $data; } elseif (is_array($data) && count($data) > 0) { if (isset($headers['Content-Type']) && preg_match('!^multipart/form-data\b!i', $headers['Content-Type'])) { $settings['multipart'] = []; foreach ($data as $key => $val) { $settings['multipart'][] = ['name' => $key, 'contents' => $val]; } } elseif (isset($headers['Content-Type']) && preg_match('!^application/json\b!i', $headers['Content-Type'])) { $settings['json'] = $data; } elseif ($method !== 'GET') { $settings['form_params'] = $data; } else { $settings['query'] = $data; } } // Set the callback for debugging. if (Debug::isEnabledForCurrentUser() && in_array('slow_remote_requests', config('debug.display_content'))) { $log = array(); $log['url'] = $url; $log['verb'] = $method; $log['type'] = $type; $log['status'] = 0; $log['elapsed_time'] = 0; $log['called_file'] = $log['called_line'] = $log['called_method'] = null; $log['redirect_to'] = null; $log['backtrace'] = []; $bt = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); foreach ($bt as $no => $call) { if($call['file'] !== __FILE__ && $call['file'] !== \RX_BASEDIR . 'classes/file/FileHandler.class.php') { $log['called_file'] = $bt[$no]['file']; $log['called_line'] = $bt[$no]['line']; $next = $no + 1; if (isset($bt[$next])) { $log['called_method'] = ($bt[$next]['class'] ?? '') . ($bt[$next]['type'] ?? '') . ($bt[$next]['function'] ?? ''); $log['backtrace'] = array_slice($bt, $next, 1); } break; } } $settings['on_stats'] = function($stats) use($log) { $log['url'] = strval($stats->getEffectiveUri()); $log['status'] = $stats->hasResponse() ? $stats->getResponse()->getStatusCode() : 0; $log['elapsed_time'] = $stats->getTransferTime(); $log['redirect_to'] = $stats->hasResponse() ? ($stats->getResponse()->getHeaderLine('location') ?: null) : null; Debug::addRemoteRequest($log); }; } else { $settings['on_stats'] = function($stats) { Debug::addTime('remote', $stats->getTransferTime()); }; } return $settings; } }