diff --git a/.travis.yml b/.travis.yml index d5d448c..302ea0f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,7 @@ php: - 7.0 - 7.1 -before_script: - - composer self-update 1.2.1 +install: - composer install --no-interaction -v script: diff --git a/composer.json b/composer.json index 17fecf1..949535b 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.6.0", "psr/log": "1.0.*", - "psr/http-message": "1.0.*", + "guzzlehttp/psr7": "1.4.*", "php-http/client-implementation": "@stable" }, "autoload": { @@ -40,7 +40,6 @@ } }, "suggest": { - "php-http/guzzle5-adapter": "To enable use of Guzzle 5 http client (http://docs.php-http.org/en/latest/clients/guzzle5-adapter.html)", "php-http/guzzle6-adapter": "To enable use of Guzzle 6 http client (http://docs.php-http.org/en/latest/clients/guzzle6-adapter.html)", "php-http/react-adapter": "To enable React http client (http://docs.php-http.org/en/latest/clients/react-adapter.html)", "php-http/buzz-adapter": "To enable use of Buzz http client (http://docs.php-http.org/en/latest/clients/buzz-adapter.html)", diff --git a/src/AbstractClient.php b/src/AbstractClient.php index e4e143c..d66de03 100644 --- a/src/AbstractClient.php +++ b/src/AbstractClient.php @@ -16,7 +16,6 @@ limitations under the License. */ -use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; @@ -87,14 +86,14 @@ protected function requireOK(array $r) } /** - * @param RequestInterface $r + * @param Request $r * @return array( * @type int duration in microseconds * @type \Psr\Http\Message\ResponseInterface|null http response * @type \DCarbone\PHPConsulAPI\Error|null any seen errors * ) */ - protected function doRequest(RequestInterface $r) + protected function doRequest(Request $r) { $rt = microtime(true); $response = null; @@ -103,7 +102,7 @@ protected function doRequest(RequestInterface $r) { // If we actually have a client defined... if (isset($this->c->HttpClient)) - $response = $this->c->HttpClient->sendRequest($r); + $response = $this->c->HttpClient->sendRequest($r->toPsrRequest()); // Otherwise, throw error to be caught below else throw new \RuntimeException('Unable to execute query as no HttpClient has been defined.'); diff --git a/src/Request.php b/src/Request.php index f0208db..e999627 100644 --- a/src/Request.php +++ b/src/Request.php @@ -16,18 +16,15 @@ limitations under the License. */ -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\StreamInterface; -use Psr\Http\Message\UriInterface; + +use GuzzleHttp\Psr7\Request as Psr7Request; +use GuzzleHttp\Psr7\Stream as Psr7Stream; /** * Class Request * @package DCarbone\PHPConsulAPI\Http */ -class Request implements RequestInterface -{ - // PHPConsulAPI properties - +class Request { /** @var \DCarbone\PHPConsulAPI\Config */ private $config; /** @var \DCarbone\PHPConsulAPI\Params */ @@ -35,25 +32,17 @@ class Request implements RequestInterface /** @var string */ private $path; - /** @var array */ - private $_normalizedHeaders = ['accept' => 'Accept', 'content-type' => 'Content-Type']; - /** @var StreamInterface|null */ - private $_compiledBody = null; - - // PSR-7 properties below + /** @var \DCarbone\PHPConsulAPI\Uri */ + private $uri; /** @var string */ - private $protocolVersion = '1.1'; + private $method = 'POST'; + /** @var array */ private $headers = ['Accept' => ['application/json'], 'Content-Type' => ['application/json']]; - /** @var \Psr\Http\Message\StreamInterface */ + + /** @var null|resource */ private $body = null; - /** @var string */ - private $requestTarget = null; - /** @var string */ - private $method = 'POST'; - /** @var \Psr\Http\Message\UriInterface */ - private $uri = null; /** * Request constructor. @@ -62,319 +51,144 @@ class Request implements RequestInterface * @param Config $config * @param string $body */ - public function __construct($method, $path, Config $config, $body = null) - { + public function __construct($method, $path, Config $config, $body = null) { $this->config = $config; $this->params = new Params(); $this->method = strtoupper($method); $this->path = $path; - if ('' !== ($dc = $config->getDatacenter())) + if ('' !== ($dc = $config->getDatacenter())) { $this->params['dc'] = $dc; + } - if (0 !== ($wait = $config->getWaitTime())) + if (0 !== ($wait = $config->getWaitTime())) { $this->params['wait'] = $wait; + } - if ('' !== ($token = $config->getToken())) - { - if ($config->isTokenInHeader()) + if ('' !== ($token = $config->getToken())) { + if ($config->isTokenInHeader()) { $this->headers['X-Consul-Token'] = $token; - else + } else { $this->params['token'] = $token; + } } - $this->body = $body; + if (null !== $body) { + $str = ''; + switch (gettype($body)) { + case 'object': + case 'array': + $str = json_encode($body); + break; + + case 'integer': + case 'double': + $str = (string)$body; + break; + + case 'string': + $str = $body; + break; + + case 'boolean': + $str = $body ? 'true' : 'false'; + break; + } + $this->body = fopen('php://memory', 'w+'); + fwrite($this->body, $str); + } } - public function __clone() - { - $this->_compiledBody = null; + /** + * Attempt to close body stream, if set. + */ + public function __destruct() { + if (isset($this->body) && 'resource' === gettype($this->body)) { + @fclose($this->body); + } } /** * @param \DCarbone\PHPConsulAPI\QueryOptions|null $queryOptions */ - public function setQueryOptions(QueryOptions $queryOptions = null) - { - if (null === $queryOptions) + public function setQueryOptions(QueryOptions $queryOptions = null) { + if (null === $queryOptions) { return; - - if ('' !== ($dc = $queryOptions->getDatacenter())) + } + + if ('' !== ($dc = $queryOptions->getDatacenter())) { $this->params['dc'] = $dc; + } - if ($queryOptions->getAllowStale()) + if ($queryOptions->getAllowStale()) { $this->params['stale'] = ''; + } - if ($queryOptions->getRequireConsistent()) + if ($queryOptions->getRequireConsistent()) { $this->params['consistent'] = ''; + } - if (0 !== ($waitIndex = $queryOptions->getWaitIndex())) + if (0 !== ($waitIndex = $queryOptions->getWaitIndex())) { $this->params['index'] = $waitIndex; - - if (0 !== ($waitTime = $queryOptions->getWaitTime())) + } + + if (0 !== ($waitTime = $queryOptions->getWaitTime())) { $this->params['wait'] = $waitTime; + } - if ('' !== ($token = $queryOptions->getToken())) - { - if ($this->config->isTokenInHeader()) + if ('' !== ($token = $queryOptions->getToken())) { + if ($this->config->isTokenInHeader()) { $this->headers['X-Consul-Token'] = $token; - else + } else { $this->params['token'] = $token; + } } - if ('' !== ($near = $queryOptions->getNear())) + if ('' !== ($near = $queryOptions->getNear())) { $this->params['near'] = $near; + } } /** * @param \DCarbone\PHPConsulAPI\WriteOptions|null $writeOptions */ - public function setWriteOptions(WriteOptions $writeOptions = null) - { - if (null === $writeOptions) + public function setWriteOptions(WriteOptions $writeOptions = null) { + if (null === $writeOptions) { return; + } - if ('' !== ($dc = $writeOptions->getDatacenter())) + if ('' !== ($dc = $writeOptions->getDatacenter())) { $this->params['dc'] = $dc; - - if ('' !== ($token = $writeOptions->getToken())) - $this->params['token'] = $token; - } - - /** - * @inheritDoc - */ - public function getProtocolVersion() - { - return $this->protocolVersion; - } - - /** - * @inheritDoc - */ - public function withProtocolVersion($version) - { - $clone = clone $this; - $clone->protocolVersion = $version; - return $clone; - } - - /** - * @inheritDoc - */ - public function getHeaders() - { - return $this->headers; - } - - /** - * @inheritDoc - */ - public function hasHeader($name) - { - return isset($this->_normalizedHeaders[strtolower($name)]); - } - - /** - * @inheritDoc - */ - public function getHeader($name) - { - $lower = strtolower($name); - if (!isset($this->_normalizedHeaders[$lower])) - return []; - - return $this->headers[$this->_normalizedHeaders[$lower]]; - } - - /** - * @inheritDoc - */ - public function getHeaderLine($name) - { - $lower = strtolower($name); - if (!isset($this->_normalizedHeaders[$name])) - return ''; - - return implode(',', $this->headers[$this->_normalizedHeaders[$lower]]); - } - - /** - * @inheritDoc - */ - public function withHeader($name, $value) - { - $type = gettype($value); - if ('string' !== $type && 'array' !== $type) - throw new \InvalidArgumentException(sprintf('$value must be array or string, %s seen.', gettype($value))); - - $lower = strtolower($name); - - $clone = clone $this; - $clone->_normalizedHeaders[$lower] = $name; - - if ('string' === $type) - $clone->headers[$name] = [$value]; - else - $clone->headers[$name] = $value; - - return $clone; - } - - /** - * @inheritDoc - */ - public function withAddedHeader($name, $value) - { - $type = gettype($value); - if ('string' !== $type && 'array' !== $type) - throw new \InvalidArgumentException('$value must be array or string, %s seen.', gettype($value)); - - $lower = strtolower($name); - - if (isset($this->_normalizedHeaders[$lower])) - $headerValues = $this->headers[$this->_normalizedHeaders[$lower]]; - else - $headerValues = []; - - if ('string' === $type) - $headerValues[] = $value; - else - $headerValues = array_merge($headerValues, $value); - - $clone = clone $this; - - $clone->_normalizedHeaders[$lower] = $name; - $clone->headers[$name] = $headerValues; - - return $clone; - } - - /** - * @inheritDoc - */ - public function withoutHeader($name) - { - $clone = clone $this; - - $lower = strtolower($name); - if (isset($clone->_normalizedHeaders[$lower])) - unset($clone->headers[$clone->_normalizedHeaders[$lower]], $clone->_normalizedHeaders[$lower]); - - return $clone; - } - - /** - * @inheritDoc - */ - public function getBody() - { - if (isset($this->body) && !isset($this->_compiledBody)) - $this->_compiledBody = new RequestBody($this->body); - - return $this->_compiledBody; - } - - /** - * @inheritDoc - */ - public function withBody(StreamInterface $body) - { - $clone = clone $this; - $clone->body = $body; - return $clone; - } - - /** - * @inheritDoc - */ - public function getRequestTarget() - { - if (null === $this->requestTarget) - { - $uri = $this->getUri(); - $p = $uri->getPath(); - $q = $uri->getQuery(); - - if ('' === $p) - $t = '/'; - else - $t = $p; - - if ('' !== $q) - $t = sprintf('%s?%s', $t, $q); - - $this->requestTarget = $t; } - return $this->requestTarget; - } - - /** - * @inheritDoc - */ - public function withRequestTarget($requestTarget) - { - $clone = clone $this; - $clone->requestTarget = trim($requestTarget); - return $clone; - } - - /** - * @inheritDoc - */ - public function getMethod() - { - return $this->method; - } - - /** - * @inheritDoc - */ - public function withMethod($method) - { - static $allowable = ['GET', 'PUT', 'POST', 'DELETE']; - $upper = strtoupper($method); - if (in_array($upper, $allowable, true)) - { - $clone = clone $this; - $clone->method = $upper; - return $clone; + if ('' !== ($token = $writeOptions->getToken())) { + $this->params['token'] = $token; } - - throw new \InvalidArgumentException( - '"%s" is not an allowable request method. Allowable: ["%s"]', - $upper, - implode('", "', $allowable) - ); } /** - * @inheritDoc + * @return \DCarbone\PHPConsulAPI\Uri */ - public function getUri() - { - if (null === $this->uri) + public function getUri() { + if (!isset($this->uri)) { $this->uri = new Uri($this->path, $this->config, $this->params); + } return $this->uri; } /** - * @inheritDoc + * Constructs a Psr7 compliant request for use in a Psr7 client. + * + * @return \Psr\Http\Message\RequestInterface */ - public function withUri(UriInterface $uri, $preserveHost = false) - { - if ($uri === $this->uri) - return $this; - - $clone = clone $this; - $clone->uri = $uri; - - if ($preserveHost) - $clone->uri = $this->uri->withHost($this->uri->getHost()); - - return $clone; + public function toPsrRequest() { + return new Psr7Request( + $this->method, + $this->getUri(), + $this->headers, + isset($this->body) ? new Psr7Stream($this->body) : null + ); } } diff --git a/src/RequestBody.php b/src/RequestBody.php deleted file mode 100644 index 9b1365e..0000000 --- a/src/RequestBody.php +++ /dev/null @@ -1,276 +0,0 @@ -stream = fopen('php://memory', 'w+'); - fwrite($this->stream, $str); - - $this->size = mb_strlen($str, $encoding); - - $this->meta = stream_get_meta_data($this->stream); - - $this->_state = self::STATE_OPEN; - } - - /** - * @inheritDoc - */ - public function __toString() - { - if (null === $this->stream) - return ''; - - - $this->rewind(); - $str = ''; - while(!$this->eof() && $data = $this->read(8192)) - { - $str .= $data; - } - - return $str; - } - - /** - * @inheritDoc - */ - public function close() - { - if (self::STATE_OPEN === $this->_state) - { - fclose($this->stream); - $this->stream = null; - $this->size = 0; - $this->_state = self::STATE_CLOSED; - } - } - - /** - * @inheritDoc - */ - public function detach() - { - if (self::STATE_OPEN === $this->_state) - $stream = $this->stream; - else - $stream = null; - - $this->stream = null; - $this->size = 0; - $this->_state = self::STATE_DETACHED; - return $stream; - } - - /** - * @inheritDoc - */ - public function getSize() - { - if (self::STATE_OPEN !== $this->_state) - return null; - - return $this->size; - } - - /** - * @inheritDoc - */ - public function tell() - { - if (self::STATE_OPEN === $this->_state) - return ftell($this->stream); - - throw new \RuntimeException(); - } - - /** - * @inheritDoc - */ - public function eof() - { - if (null === $this->stream) - return true; - - return feof($this->stream); - } - - /** - * @inheritDoc - */ - public function isSeekable() - { - return self::STATE_OPEN === $this->_state; - } - - /** - * @inheritDoc - */ - public function seek($offset, $whence = SEEK_SET) - { - if ($this->isSeekable()) - fseek($this->stream, $offset, $whence); - else - throw $this->createStateException(__METHOD__); - } - - /** - * @inheritDoc - */ - public function rewind() - { - if ($this->isSeekable()) - rewind($this->stream); - else - throw $this->createStateException(__METHOD__); - } - - /** - * @inheritDoc - */ - public function isWritable() - { - return false; - } - - /** - * @inheritDoc - */ - public function write($string) - { - throw new \RuntimeException('PHPConsulAPI request bodies are immutable.'); - } - - /** - * @inheritDoc - */ - public function isReadable() - { - return self::STATE_OPEN === $this->_state; - } - - /** - * @inheritDoc - */ - public function read($length) - { - if (self::STATE_OPEN === $this->_state) - return (string)fread($this->stream, $length); - - throw $this->createStateException(__METHOD__); - } - - /** - * @inheritDoc - */ - public function getContents() - { - if (self::STATE_OPEN === $this->_state) - { - $str = ''; - while(!$this->eof() && $data = $this->read(8192)) - { - $str .= $data; - } - - return $str; - } - - throw $this->createStateException(__METHOD__); - } - - /** - * @inheritDoc - */ - public function getMetadata($key = null) - { - if (self::STATE_OPEN === $this->_state) - { - if (null === $key) - return $this->meta; - - if (isset($this->meta[$key])) - return $this->meta[$key]; - - return null; - } - - return null; - } - - /** - * @param string $action - * @return \RuntimeException - */ - private function createStateException($action) - { - if (self::STATE_DETACHED === $this->_state) - return new \RuntimeException(sprintf('Cannot "%s", request body is in a detached state', $action)); - - return new \RuntimeException('Cannot "%s", request body is closed', $action); - } -} diff --git a/src/Uri.php b/src/Uri.php index a4d7085..5071ea5 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -22,8 +22,7 @@ * Class Uri * @package DCarbone\PHPConsulAPI\Http */ -class Uri implements UriInterface -{ +class Uri implements UriInterface { /** @var string */ private $scheme = ''; /** @var string */ @@ -48,20 +47,16 @@ class Uri implements UriInterface * @param Config $config * @param Params $params */ - public function __construct($path, Config $config, Params $params) - { + public function __construct($path, Config $config, Params $params) { $this->scheme = $config->Scheme; $this->userInfo = $config->HttpAuth; $this->path = $path; $a = $config->Address; - if (false === ($pos = strpos($a, ':'))) - { + if (false === ($pos = strpos($a, ':'))) { $this->host = $a; - } - else - { + } else { $this->host = substr($a, 0, $pos); $this->port = (int)substr($a, $pos + 1); } @@ -69,35 +64,34 @@ public function __construct($path, Config $config, Params $params) $this->query = (string)$params; } - public function __clone() - { + public function __clone() { $this->_compiled = null; } /** * @inheritDoc */ - public function getScheme() - { + public function getScheme() { return $this->scheme; } /** * @inheritDoc */ - public function getAuthority() - { + public function getAuthority() { $ui = $this->getUserInfo(); $host = $this->getHost(); $port = $this->getPort(); - if ('' === $ui) + if ('' === $ui) { $uri = $host; - else + } else { $uri = sprintf('%s@%s', $ui, $host); + } - if (null === $port || 0 === $port) + if (null === $port || 0 === $port) { return $uri; + } return sprintf('%s:%d', $uri, $port); } @@ -105,59 +99,51 @@ public function getAuthority() /** * @inheritDoc */ - public function getUserInfo() - { + public function getUserInfo() { return (string)$this->userInfo; } /** * @inheritDoc */ - public function getHost() - { + public function getHost() { return $this->host; } /** * @inheritDoc */ - public function getPort() - { + public function getPort() { return $this->port; } /** * @inheritDoc */ - public function getPath() - { + public function getPath() { return $this->path; } /** * @inheritDoc */ - public function getQuery() - { + public function getQuery() { return $this->query; } /** * @inheritDoc */ - public function getFragment() - { + public function getFragment() { return $this->fragment; } /** * @inheritDoc */ - public function withScheme($scheme) - { + public function withScheme($scheme) { $scheme = strtolower($scheme); - if ('http' === $scheme || 'https' === $scheme) - { + if ('http' === $scheme || 'https' === $scheme) { $clone = clone $this; $clone->scheme = $scheme; return $clone; @@ -169,8 +155,7 @@ public function withScheme($scheme) /** * @inheritDoc */ - public function withUserInfo($user, $password = null) - { + public function withUserInfo($user, $password = null) { $clone = clone $this; $clone->userInfo = new HttpAuth($user, $password); return $clone; @@ -179,10 +164,8 @@ public function withUserInfo($user, $password = null) /** * @inheritDoc */ - public function withHost($host) - { - if (null === $host) - { + public function withHost($host) { + if (null === $host) { $clone = clone $this; $clone->host = ''; return $clone; @@ -197,10 +180,8 @@ public function withHost($host) /** * @inheritDoc */ - public function withPort($port) - { - if (null !== $port && (!is_int($port) || 0 >= $port || 65535 < $port)) - { + public function withPort($port) { + if (null !== $port && (!is_int($port) || 0 >= $port || 65535 < $port)) { throw new \InvalidArgumentException( sprintf('Port must be integer greater than 0 and less than 65535, saw "%s"', is_int($port) ? (string)$port : gettype($port)) @@ -215,17 +196,14 @@ public function withPort($port) /** * @inheritDoc */ - public function withPath($path) - { - if (is_string($path)) - { + public function withPath($path) { + if (is_string($path)) { $clone = clone $this; $clone->path = ltrim($path, "/"); return $clone; } - if (null === $path) - { + if (null === $path) { $clone = clone $this; $clone->path = ''; return $clone; @@ -237,18 +215,15 @@ public function withPath($path) /** * @inheritDoc */ - public function withQuery($query) - { + public function withQuery($query) { // TODO: Some validation...? - if (null === $query) - { + if (null === $query) { $clone = clone $this; $clone->query = ''; return $clone; } - if (is_string($query)) - { + if (is_string($query)) { $clone = clone $this; $clone->query = $query; return $clone; @@ -260,8 +235,7 @@ public function withQuery($query) /** * @inheritDoc */ - public function withFragment($fragment) - { + public function withFragment($fragment) { $clone = clone $this; $clone->fragment = trim((string)$fragment, " \t\r\n\0\x0B#"); return $clone; @@ -270,47 +244,47 @@ public function withFragment($fragment) /** * @inheritDoc */ - public function __toString() - { - if (null === $this->_compiled) - { + public function __toString() { + if (null === $this->_compiled) { $s = $this->getScheme(); $a = $this->getAuthority(); $p = $this->getPath(); $q = $this->getQuery(); $f = $this->getFragment(); - if ('' === $s) + if ('' === $s) { $uri = ''; - else + } else { $uri = sprintf('%s:', $s); + } - if ('' !== $a) + if ('' !== $a) { $uri = sprintf('%s//%s', $uri, $a); + } - if ('' !== $p) - { - if (0 === strpos($p, '/')) - { - if ('' === $a) + if ('' !== $p) { + if (0 === strpos($p, '/')) { + if ('' === $a) { $uri = sprintf('%s/%s', $uri, $p); - else + } else { $uri = sprintf('%s%s', $uri, $p); - } - else - { - if ('' === $a) + } + } else { + if ('' === $a) { $uri = sprintf('%s/%s', $uri, ltrim($p, "/")); - else + } else { $uri = sprintf('%s/%s', $uri, $p); + } } } - if ('' !== $q) + if ('' !== $q) { $uri = sprintf('%s?%s', $uri, $q); + } - if ('' !== $f) + if ('' !== $f) { $uri = sprintf('%s#%s', $uri, $f); + } $this->_compiled = $uri; }