123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- <?php
- namespace Guzzle\Http;
- use Guzzle\Common\Collection;
- use Guzzle\Common\AbstractHasDispatcher;
- use Guzzle\Common\Exception\ExceptionCollection;
- use Guzzle\Common\Exception\InvalidArgumentException;
- use Guzzle\Common\Exception\RuntimeException;
- use Guzzle\Common\Version;
- use Guzzle\Parser\ParserRegistry;
- use Guzzle\Parser\UriTemplate\UriTemplateInterface;
- use Guzzle\Http\Message\RequestInterface;
- use Guzzle\Http\Message\RequestFactory;
- use Guzzle\Http\Message\RequestFactoryInterface;
- use Guzzle\Http\Curl\CurlMultiInterface;
- use Guzzle\Http\Curl\CurlMultiProxy;
- use Guzzle\Http\Curl\CurlHandle;
- use Guzzle\Http\Curl\CurlVersion;
- /**
- * HTTP client
- */
- class Client extends AbstractHasDispatcher implements ClientInterface
- {
- /** @deprecated Use [request.options][params] */
- const REQUEST_PARAMS = 'request.params';
- const REQUEST_OPTIONS = 'request.options';
- const CURL_OPTIONS = 'curl.options';
- const SSL_CERT_AUTHORITY = 'ssl.certificate_authority';
- const DISABLE_REDIRECTS = RedirectPlugin::DISABLE;
- const DEFAULT_SELECT_TIMEOUT = 1.0;
- const MAX_HANDLES = 3;
- /** @var Collection Default HTTP headers to set on each request */
- protected $defaultHeaders;
- /** @var string The user agent string to set on each request */
- protected $userAgent;
- /** @var Collection Parameter object holding configuration data */
- private $config;
- /** @var Url Base URL of the client */
- private $baseUrl;
- /** @var CurlMultiInterface CurlMulti object used internally */
- private $curlMulti;
- /** @var UriTemplateInterface URI template owned by the client */
- private $uriTemplate;
- /** @var RequestFactoryInterface Request factory used by the client */
- protected $requestFactory;
- public static function getAllEvents()
- {
- return array(self::CREATE_REQUEST);
- }
- /**
- * @param string $baseUrl Base URL of the web service
- * @param array|Collection $config Configuration settings
- *
- * @throws RuntimeException if cURL is not installed
- */
- public function __construct($baseUrl = '', $config = null)
- {
- if (!extension_loaded('curl')) {
- // @codeCoverageIgnoreStart
- throw new RuntimeException('The PHP cURL extension must be installed to use Guzzle.');
- // @codeCoverageIgnoreEnd
- }
- $this->setConfig($config ?: new Collection());
- $this->initSsl();
- $this->setBaseUrl($baseUrl);
- $this->defaultHeaders = new Collection();
- $this->setRequestFactory(RequestFactory::getInstance());
- $this->userAgent = $this->getDefaultUserAgent();
- if (!$this->config[self::DISABLE_REDIRECTS]) {
- $this->addSubscriber(new RedirectPlugin());
- }
- }
- final public function setConfig($config)
- {
- if ($config instanceof Collection) {
- $this->config = $config;
- } elseif (is_array($config)) {
- $this->config = new Collection($config);
- } else {
- throw new InvalidArgumentException('Config must be an array or Collection');
- }
- return $this;
- }
- final public function getConfig($key = false)
- {
- return $key ? $this->config[$key] : $this->config;
- }
- /**
- * Set a default request option on the client that will be used as a default for each request
- *
- * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
- * @param mixed $value Value to set
- *
- * @return $this
- */
- public function setDefaultOption($keyOrPath, $value)
- {
- $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
- $this->config->setPath($keyOrPath, $value);
- return $this;
- }
- /**
- * Retrieve a default request option from the client
- *
- * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo)
- *
- * @return mixed|null
- */
- public function getDefaultOption($keyOrPath)
- {
- $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath;
- return $this->config->getPath($keyOrPath);
- }
- final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2)
- {
- $opts = $this->config[self::CURL_OPTIONS] ?: array();
- if ($certificateAuthority === true) {
- // use bundled CA bundle, set secure defaults
- $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem';
- $opts[CURLOPT_SSL_VERIFYPEER] = true;
- $opts[CURLOPT_SSL_VERIFYHOST] = 2;
- } elseif ($certificateAuthority === false) {
- unset($opts[CURLOPT_CAINFO]);
- $opts[CURLOPT_SSL_VERIFYPEER] = false;
- $opts[CURLOPT_SSL_VERIFYHOST] = 0;
- } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) {
- throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean');
- } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) {
- throw new InvalidArgumentException('verifyHost must be 0, 1 or 2');
- } else {
- $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer;
- $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost;
- if (is_file($certificateAuthority)) {
- unset($opts[CURLOPT_CAPATH]);
- $opts[CURLOPT_CAINFO] = $certificateAuthority;
- } elseif (is_dir($certificateAuthority)) {
- unset($opts[CURLOPT_CAINFO]);
- $opts[CURLOPT_CAPATH] = $certificateAuthority;
- } else {
- throw new RuntimeException(
- 'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority
- );
- }
- }
- $this->config->set(self::CURL_OPTIONS, $opts);
- return $this;
- }
- public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array())
- {
- if (!$uri) {
- $url = $this->getBaseUrl();
- } else {
- if (!is_array($uri)) {
- $templateVars = null;
- } else {
- list($uri, $templateVars) = $uri;
- }
- if (strpos($uri, '://')) {
- // Use absolute URLs as-is
- $url = $this->expandTemplate($uri, $templateVars);
- } else {
- $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars));
- }
- }
- // If default headers are provided, then merge them under any explicitly provided headers for the request
- if (count($this->defaultHeaders)) {
- if (!$headers) {
- $headers = $this->defaultHeaders->toArray();
- } elseif (is_array($headers)) {
- $headers += $this->defaultHeaders->toArray();
- } elseif ($headers instanceof Collection) {
- $headers = $headers->toArray() + $this->defaultHeaders->toArray();
- }
- }
- return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options);
- }
- public function getBaseUrl($expand = true)
- {
- return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl;
- }
- public function setBaseUrl($url)
- {
- $this->baseUrl = $url;
- return $this;
- }
- public function setUserAgent($userAgent, $includeDefault = false)
- {
- if ($includeDefault) {
- $userAgent .= ' ' . $this->getDefaultUserAgent();
- }
- $this->userAgent = $userAgent;
- return $this;
- }
- /**
- * Get the default User-Agent string to use with Guzzle
- *
- * @return string
- */
- public function getDefaultUserAgent()
- {
- return 'Guzzle/' . Version::VERSION
- . ' curl/' . CurlVersion::getInstance()->get('version')
- . ' PHP/' . PHP_VERSION;
- }
- public function get($uri = null, $headers = null, $options = array())
- {
- // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded
- return is_array($options)
- ? $this->createRequest('GET', $uri, $headers, null, $options)
- : $this->createRequest('GET', $uri, $headers, $options);
- }
- public function head($uri = null, $headers = null, array $options = array())
- {
- return $this->createRequest('HEAD', $uri, $headers, null, $options);
- }
- public function delete($uri = null, $headers = null, $body = null, array $options = array())
- {
- return $this->createRequest('DELETE', $uri, $headers, $body, $options);
- }
- public function put($uri = null, $headers = null, $body = null, array $options = array())
- {
- return $this->createRequest('PUT', $uri, $headers, $body, $options);
- }
- public function patch($uri = null, $headers = null, $body = null, array $options = array())
- {
- return $this->createRequest('PATCH', $uri, $headers, $body, $options);
- }
- public function post($uri = null, $headers = null, $postBody = null, array $options = array())
- {
- return $this->createRequest('POST', $uri, $headers, $postBody, $options);
- }
- public function options($uri = null, array $options = array())
- {
- return $this->createRequest('OPTIONS', $uri, $options);
- }
- public function send($requests)
- {
- if (!($requests instanceof RequestInterface)) {
- return $this->sendMultiple($requests);
- }
- try {
- /** @var $requests RequestInterface */
- $this->getCurlMulti()->add($requests)->send();
- return $requests->getResponse();
- } catch (ExceptionCollection $e) {
- throw $e->getFirst();
- }
- }
- /**
- * Set a curl multi object to be used internally by the client for transferring requests.
- *
- * @param CurlMultiInterface $curlMulti Multi object
- *
- * @return self
- */
- public function setCurlMulti(CurlMultiInterface $curlMulti)
- {
- $this->curlMulti = $curlMulti;
- return $this;
- }
- /**
- * @return CurlMultiInterface|CurlMultiProxy
- */
- public function getCurlMulti()
- {
- if (!$this->curlMulti) {
- $this->curlMulti = new CurlMultiProxy(
- self::MAX_HANDLES,
- $this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT
- );
- }
- return $this->curlMulti;
- }
- public function setRequestFactory(RequestFactoryInterface $factory)
- {
- $this->requestFactory = $factory;
- return $this;
- }
- /**
- * Set the URI template expander to use with the client
- *
- * @param UriTemplateInterface $uriTemplate URI template expander
- *
- * @return self
- */
- public function setUriTemplate(UriTemplateInterface $uriTemplate)
- {
- $this->uriTemplate = $uriTemplate;
- return $this;
- }
- /**
- * Expand a URI template while merging client config settings into the template variables
- *
- * @param string $template Template to expand
- * @param array $variables Variables to inject
- *
- * @return string
- */
- protected function expandTemplate($template, array $variables = null)
- {
- $expansionVars = $this->getConfig()->toArray();
- if ($variables) {
- $expansionVars = $variables + $expansionVars;
- }
- return $this->getUriTemplate()->expand($template, $expansionVars);
- }
- /**
- * Get the URI template expander used by the client
- *
- * @return UriTemplateInterface
- */
- protected function getUriTemplate()
- {
- if (!$this->uriTemplate) {
- $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template');
- }
- return $this->uriTemplate;
- }
- /**
- * Send multiple requests in parallel
- *
- * @param array $requests Array of RequestInterface objects
- *
- * @return array Returns an array of Response objects
- */
- protected function sendMultiple(array $requests)
- {
- $curlMulti = $this->getCurlMulti();
- foreach ($requests as $request) {
- $curlMulti->add($request);
- }
- $curlMulti->send();
- /** @var $request RequestInterface */
- $result = array();
- foreach ($requests as $request) {
- $result[] = $request->getResponse();
- }
- return $result;
- }
- /**
- * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request.
- *
- * @param RequestInterface $request Request to prepare for the client
- * @param array $options Options to apply to the request
- *
- * @return RequestInterface
- */
- protected function prepareRequest(RequestInterface $request, array $options = array())
- {
- $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher());
- if ($curl = $this->config[self::CURL_OPTIONS]) {
- $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl));
- }
- if ($params = $this->config[self::REQUEST_PARAMS]) {
- Version::warn('request.params is deprecated. Use request.options to add default request options.');
- $request->getParams()->overwriteWith($params);
- }
- if ($this->userAgent && !$request->hasHeader('User-Agent')) {
- $request->setHeader('User-Agent', $this->userAgent);
- }
- if ($defaults = $this->config[self::REQUEST_OPTIONS]) {
- $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS);
- }
- if ($options) {
- $this->requestFactory->applyOptions($request, $options);
- }
- $this->dispatch('client.create_request', array('client' => $this, 'request' => $request));
- return $request;
- }
- /**
- * Initializes SSL settings
- */
- protected function initSsl()
- {
- $authority = $this->config[self::SSL_CERT_AUTHORITY];
- if ($authority === 'system') {
- return;
- }
- if ($authority === null) {
- $authority = true;
- }
- if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') {
- $authority = self::extractPharCacert(__DIR__ . '/Resources/cacert.pem');
- }
- $this->setSslVerification($authority);
- }
- /**
- * @deprecated
- */
- public function getDefaultHeaders()
- {
- Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options');
- return $this->defaultHeaders;
- }
- /**
- * @deprecated
- */
- public function setDefaultHeaders($headers)
- {
- Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options');
- if ($headers instanceof Collection) {
- $this->defaultHeaders = $headers;
- } elseif (is_array($headers)) {
- $this->defaultHeaders = new Collection($headers);
- } else {
- throw new InvalidArgumentException('Headers must be an array or Collection');
- }
- return $this;
- }
- /**
- * @deprecated
- */
- public function preparePharCacert($md5Check = true)
- {
- return sys_get_temp_dir() . '/guzzle-cacert.pem';
- }
- /**
- * Copies the phar cacert from a phar into the temp directory.
- *
- * @param string $pharCacertPath Path to the phar cacert. For example:
- * 'phar://aws.phar/Guzzle/Http/Resources/cacert.pem'
- *
- * @return string Returns the path to the extracted cacert file.
- * @throws \RuntimeException Throws if the phar cacert cannot be found or
- * the file cannot be copied to the temp dir.
- */
- public static function extractPharCacert($pharCacertPath)
- {
- // Copy the cacert.pem file from the phar if it is not in the temp
- // folder.
- $certFile = sys_get_temp_dir() . '/guzzle-cacert.pem';
- if (!file_exists($pharCacertPath)) {
- throw new \RuntimeException("Could not find $pharCacertPath");
- }
- if (!file_exists($certFile) ||
- filesize($certFile) != filesize($pharCacertPath)
- ) {
- if (!copy($pharCacertPath, $certFile)) {
- throw new \RuntimeException(
- "Could not copy {$pharCacertPath} to {$certFile}: "
- . var_export(error_get_last(), true)
- );
- }
- }
- return $certFile;
- }
- }
|