get('/users'); * $response2 = $client->post('/users', ['body' => ['name' => 'Heera']]); * $response3 = $client->put('/users/1', ['body' => ['name' => 'Updated']]); * $response4 = $client->patch('/users/1', [ * 'body' => ['email' => 'updated@example.com']] * ); * $response5 = $client->delete('/users/1'); * $response6 = $client->head('/users'); * $response7 = $client->options('/users'); * * // Using statically (default base URL is empty): * $response8 = Client::get('https://example.com/users'); * $response9 = Client::post('https://example.com/users', [ * 'body' => ['name' => 'Static']] * ); */ class Client { /** * Base URl for the request. * * @var string */ protected $baseUrl = ''; /** * Cookies to send with the request. * * @var array */ protected $cookies = []; /** * Headers to send with the request. * * @var array */ protected $headers = []; /** * Options to set for the request. * * @var array */ protected $options = []; /** * Request body|Data|params to set in the request. * * @var array */ protected $body = []; /** * Request query params to pass with the url. * * @var array */ protected $query = []; /** * Stores args temporarily for then(). * * @var null|array */ private $args = null; /** * Create a new HTTP client. * * @param string $baseUrl * @param array $args */ public function __construct($baseUrl = '', $args = []) { $this->baseUrl = rtrim($baseUrl, '/'); $this->cookies = $args['cookies'] ?? []; $this->headers = $args['headers'] ?? []; $this->options = $args['options'] ?? []; } /** * Create a new HTTP client. * * @param string $baseUrl * @param array $args */ public static function make($baseUrl = '', $args = []) { $args['cookies'] = $args['cookies'] ?? []; $args['headers'] = $args['headers'] ?? []; $args['options'] = $args['options'] ?? []; return new static($baseUrl, $args); } /** * Sets one or more options. * * @return self */ public function withOption($key, $value = null) { $options = is_array($key) ? $key : [$key => $value]; foreach ($options as $key => $value) { $this->options[$key] = $value; } return $this; } /** * Sets the blocking option to false (non-blocking). * * @return self */ public function async() { return $this->withOption('blocking', false); } /** * Sets the sslverify option. * * @return self */ public function secure($verify = true) { return $this->withOption('sslverify', $verify); } /** * Sets one or more headers. * * @return self */ public function withHeader($key, $value = null) { $headers = is_array($key) ? $key : [$key => $value]; foreach ($headers as $key => $value) { $this->headers[$key] = $value; } return $this; } /** * Sets one or more cookies. * * @return self */ public function withCookie($key, $value = null) { $cookies = is_array($key) ? $key : [$key => $value]; foreach ($cookies as $key => $value) { $this->cookies[$key] = $value; } return $this; } /** * Sets one or more request body param. * * @return self */ public function withData($key, $value = null) { $data = is_array($key) ? $key : [$key => $value]; foreach ($data as $key => $value) { $this->body[$key] = $value; } return $this; } /** * Sets one or more request body param. * * @return self */ public function withBody($key, $value = null) { return $this->withData($key, $value); } /** * Sets one or more request body param. * * @return self */ public function withParam($key, $value = null) { return $this->withData($key, $value); } /** * Sets one or more request body param. * * @return self */ public function withQuery($key, $value = null) { $data = is_array($key) ? $key : [$key => $value]; foreach ($data as $key => $value) { $this->query[$key] = $value; } return $this; } /** * Build the request arguments. * * @param array $params * @param string $method * @return array */ protected function buildRequestArgs($params, $method) { $defaultParams = [ 'body' => [], 'cookies' => [], 'headers' => [], ]; $params = wp_parse_args($params[0] ?? [], $defaultParams); $options = array_merge($this->options, $params['options'] ?? []); $params = [ 'method' => strtoupper($method), 'body' => array_merge($this->body, $params['body']), 'cookies' => array_merge($this->cookies, $params['cookies']), 'headers' => array_merge($this->headers, $params['headers']), ]; foreach($options as $key => $value) { $params[$key] = $value; } return $params; } /** * Send the request. * * @param string $url * @param array $args * @return \FluentBooking\Framework\Http\Response */ protected function request($url, $args = []) { $parsedUrl = parse_url($url); $delimiter = isset($parsedUrl['query']) ? '&' : '?'; $url .= $this->query ? $delimiter . http_build_query($this->query) : ''; $response = wp_remote_request($url, $args); if (is_wp_error($response)) { throw new Exception($response->get_error_message(), 500); } $this->cookies = array_merge( $this->cookies, wp_remote_retrieve_cookies($response) ); return $this->makeResponse($response); } /** * Download a remote file. * * @param string $url * @return \FluentBooking\Framework\Http\Request\File * @throws \Exception */ public function downloadFile($url) { if (!function_exists('download_url')) { require_once ABSPATH . 'wp-admin/includes/file.php'; } $parsed = parse_url($url); if (!isset($parsed['scheme'])) { $url = trim($this->baseUrl, '/') . '/' . trim($url, '/'); } if (is_wp_error($file = download_url($url))) { throw new Exception($file->get_error_message(), 500); } add_action('shutdown', function () use ($file) { @unlink($file); }); return new File( $file, basename($url), mime_content_type($file) ?: 'application/octet-stream', filesize($file), UPLOAD_ERR_OK ); } /** * Upload a file to a remote server. * * @param string $url * @param string $path * @param array $fields * @param string $name * @return \FluentBooking\Framework\Http\Response */ public function uploadFile($url, $path, $fields = [], $name = 'file') { $path = $path instanceof File ? $path->getPathname() : $path; if (!file_exists($path)) { throw new Exception('File does not exist.', 500); } $boundary = wp_generate_password(24, false); $headers = [ 'Accept' => '*/*', 'Content-Type' => 'multipart/form-data; boundary=' . $boundary, ]; $fileName = basename($path); $content = file_get_contents($path); $mime = mime_content_type($path); $body = ''; foreach ($fields as $key => $value) { $body .= "--" . $boundary . "\r\n"; $body .= 'Content-Disposition: form-data; name="' . $key . '"' . "\r\n\r\n"; $body .= $value . "\r\n"; } $body .= "--" . $boundary . "\r\n"; $body .= 'Content-Disposition: form-data; name="'.$name.'"; filename="' . $fileName . '"' . "\r\n"; $body .= 'Content-Type: ' . $mime . "\r\n\r\n"; $body .= $content . "\r\n"; $body .= "--" . $boundary . "--\r\n"; $response = wp_remote_post($url, [ 'headers' => $headers, 'body' => $body, 'timeout' => 60, ]); return $this->makeResponse($response); } /** * Build a response object from an anonymous class. * * @param array $response * @return \FluentBooking\Framework\Http\Response */ protected function makeResponse($response) { return new Response($response); } protected function checkIfValidHttpMethod($method) { $validHttpMethods = [ 'get', 'post', 'put', 'delete', 'patch', 'options', 'head' ]; if (!in_array(strtolower($method), $validHttpMethods)) { throw new BadMethodCallException("Method $method does not exist."); } } /** * Handles the dynamic calls. * * @param string $method * @param array $args * @return mixed */ public function __call($method, $args) { if ($method === 'download') { return $this->downloadFile(...$args); } // Handles dynamic method calls like: // get, post and so on. $url = array_shift($args); $parsed = parse_url($url); if (!isset($parsed['scheme'])) { $url = trim($this->baseUrl, '/') . '/' . trim($url, '/'); } $this->checkIfValidHttpMethod($method); return $this->request( $url, $this->buildRequestArgs($args, $method) ); } /** * Handle the static dynamic calls. * * @param string $method * @param array $args * @return self */ public static function __callStatic($method, $args) { return static::make()->$method(...$args); } }