diff --git a/README.md b/README.md index b3b8e2660..3555b52b3 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,9 @@ Model Usage You can also use the library in an object oriented manner. ```php -$client = new \Gitlab\Client('http://git.yourdomain.com/api/v3/'); // change here -$client->authenticate('your_gitlab_token_here', \Gitlab\Client::AUTH_URL_TOKEN); // change here +$guzzle = new \Gitlab\HttpClient\Adapter\Guzzle4(); +$client = new \Gitlab\Client('http://git.yourdomain.com/api/v3/', $guzzle); // change here +$client->authenticate('your_gitlab_token_here', \Gitlab\HttpClient\HttpClientInterface::AUTH_URL_TOKEN); // change here ``` Creating a new project diff --git a/composer.json b/composer.json index 5002c8af5..2ae050323 100644 --- a/composer.json +++ b/composer.json @@ -21,10 +21,12 @@ } ], "require": { - "php": ">=5.3.2", - "ext-curl": "*", - "kriswallsmith/buzz": ">=0.7" + "php": ">=5.3.2" }, + "suggest": { + "kriswallsmith/buzz": "To use Buzz as the HTTP adapter (~0.7)", + "guzzlehttp/guzzle": "To use Guzzle as the HTTP adapter (~3.7, ~4.1)" + }, "autoload": { "psr-0": { "Gitlab\\": "lib/" } } diff --git a/lib/Gitlab/Api/AbstractApi.php b/lib/Gitlab/Api/AbstractApi.php index 63632a3f9..2645e915a 100644 --- a/lib/Gitlab/Api/AbstractApi.php +++ b/lib/Gitlab/Api/AbstractApi.php @@ -40,7 +40,7 @@ public function configure() */ protected function get($path, array $parameters = array(), $requestHeaders = array()) { - $response = $this->client->getHttpClient()->get($path, $parameters, $requestHeaders); + $response = $this->client->get($path, $parameters, $requestHeaders); return $response->getContent(); } @@ -50,7 +50,7 @@ protected function get($path, array $parameters = array(), $requestHeaders = arr */ protected function post($path, array $parameters = array(), $requestHeaders = array()) { - $response = $this->client->getHttpClient()->post($path, $parameters, $requestHeaders); + $response = $this->client->post($path, $parameters, $requestHeaders); return $response->getContent(); } @@ -60,7 +60,7 @@ protected function post($path, array $parameters = array(), $requestHeaders = ar */ protected function patch($path, array $parameters = array(), $requestHeaders = array()) { - $response = $this->client->getHttpClient()->patch($path, $parameters, $requestHeaders); + $response = $this->client->patch($path, $parameters, $requestHeaders); return $response->getContent(); } @@ -70,7 +70,7 @@ protected function patch($path, array $parameters = array(), $requestHeaders = a */ protected function put($path, array $parameters = array(), $requestHeaders = array()) { - $response = $this->client->getHttpClient()->put($path, $parameters, $requestHeaders); + $response = $this->client->put($path, $parameters, $requestHeaders); return $response->getContent(); } @@ -80,7 +80,7 @@ protected function put($path, array $parameters = array(), $requestHeaders = arr */ protected function delete($path, array $parameters = array(), $requestHeaders = array()) { - $response = $this->client->getHttpClient()->delete($path, $parameters, $requestHeaders); + $response = $this->client->delete($path, $parameters, $requestHeaders); return $response->getContent(); } diff --git a/lib/Gitlab/Client.php b/lib/Gitlab/Client.php index 373da4820..e6e87b143 100644 --- a/lib/Gitlab/Client.php +++ b/lib/Gitlab/Client.php @@ -2,32 +2,16 @@ namespace Gitlab; -use Buzz\Client\Curl; -use Buzz\Client\ClientInterface; - use Gitlab\Api\ApiInterface; use Gitlab\Exception\InvalidArgumentException; +use Gitlab\HttpClient\Adapter\AdapterInterface; use Gitlab\HttpClient\HttpClient; -use Gitlab\HttpClient\HttpClientInterface; -use Gitlab\HttpClient\Listener\AuthListener; /** * Simple API wrapper for Gitlab */ class Client { - /** - * Constant for authentication method. Indicates the default, but deprecated - * login with username and token in URL. - */ - const AUTH_URL_TOKEN = 'url_token'; - - /** - * Constant for authentication method. Indicates the new login method with - * with username and token via HTTP Authentication. - */ - const AUTH_HTTP_TOKEN = 'http_token'; - /** * @var array */ @@ -36,10 +20,8 @@ class Client 'timeout' => 60 ); - private $base_url = null; - /** - * The Buzz instance used to communicate with Gitlab + * The HTTP client instance used to communicate with Gitlab * * @var HttpClient */ @@ -48,17 +30,12 @@ class Client /** * Instantiate a new Gitlab client * - * @param string $baseUrl - * @param null|ClientInterface $httpClient Buzz client + * @param string $baseUrl + * @param AdapterInterface $adapter */ - public function __construct($baseUrl, ClientInterface $httpClient = null) + public function __construct($baseUrl, AdapterInterface $adapter) { - $httpClient = $httpClient ?: new Curl(); - $httpClient->setTimeout($this->options['timeout']); - $httpClient->setVerifyPeer(false); - - $this->base_url = $baseUrl; - $this->httpClient = new HttpClient($this->base_url, $this->options, $httpClient); + $this->httpClient = new HttpClient($baseUrl, $adapter); } /** @@ -131,38 +108,32 @@ public function api($name) */ public function authenticate($token, $authMethod = null, $sudo = null) { - $this->httpClient->addListener( - new AuthListener( - $authMethod, - $token, - $sudo - ) - ); + $this->httpClient->authenticate($token, $authMethod, $sudo); } - /** - * @return HttpClient - */ - public function getHttpClient() + public function get($path, array $parameters = array(), array $headers = array()) { - return $this->httpClient; + return $this->httpClient->get($path, $parameters, $headers); } - /** - * @param HttpClientInterface $httpClient - */ - public function setHttpClient(HttpClientInterface $httpClient) + public function post($path, array $parameters = array(), array $headers = array()) { - $this->httpClient = $httpClient; + return $this->httpClient->post($path, $parameters, $headers); } - public function setBaseUrl($url) + public function patch($path, array $parameters = array(), array $headers = array()) { - $this->base_url = $url; + return $this->httpClient->patch($path, $parameters, $headers); } - public function getBaseUrl() + + public function delete($path, array $parameters = array(), array $headers = array()) + { + return $this->httpClient->delete($path, $parameters, $headers); + } + + public function put($path, array $parameters = array(), array $headers = array()) { - return $this->base_url; + return $this->httpClient->put($path, $parameters, $headers); } /** diff --git a/lib/Gitlab/HttpClient/Adapter/AdapterInterface.php b/lib/Gitlab/HttpClient/Adapter/AdapterInterface.php new file mode 100644 index 000000000..9dc1b9c79 --- /dev/null +++ b/lib/Gitlab/HttpClient/Adapter/AdapterInterface.php @@ -0,0 +1,9 @@ +client = $client ?: new Curl(); + + $this->addListener(new ErrorListener()); + } + + /** + * @param int $timeout + * + * @return $this + */ + public function setTimeout($timeout) + { + $this->client->setTimeout($timeout); + + return $this; + } + + /** + * @param bool $verify + * + * @return $this + */ + public function verifyPeer($verify) + { + $this->client->setVerifyPeer($verify); + + return $this; + } + + /** + * @param string $path + * @param array $parameters + * @param string $httpMethod + * @param array $headers + * + * @throws ErrorException + * @throws RuntimeException + * + * @return BuzzResponse + */ + public function request($path, array $parameters = array(), $httpMethod = 'GET', array $headers = array()) + { + $request = new Request($httpMethod); + $request->setHeaders($headers); + $request->fromUrl($path); + $request->setContent(http_build_query($parameters)); + + $hasListeners = 0 < count($this->listeners); + if ($hasListeners) { + foreach ($this->listeners as $listener) { + $listener->preSend($request); + } + } + + $response = new BuzzResponse(); + + try { + $this->client->send($request, $response); + } catch (\LogicException $e) { + throw new ErrorException($e->getMessage(), $e->getCode(), $e); + } catch (\RuntimeException $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + + if ($hasListeners) { + foreach ($this->listeners as $listener) { + $listener->postSend($request, $response); + } + } + + return $response; + } + + public function authenticate($token, $authMethod, $sudo = null) + { + $this->addListener( + new AuthListener( + $authMethod, + $token, + $sudo + ) + ); + } + + public function addListener(ListenerInterface $listener) + { + $this->listeners[get_class($listener)] = $listener; + } +} diff --git a/lib/Gitlab/HttpClient/Adapter/Guzzle3Adapter.php b/lib/Gitlab/HttpClient/Adapter/Guzzle3Adapter.php new file mode 100644 index 000000000..d5275bb06 --- /dev/null +++ b/lib/Gitlab/HttpClient/Adapter/Guzzle3Adapter.php @@ -0,0 +1,56 @@ +client = $client ?: new Client(); + } + + /** + * @param string $path + * @param array $parameters + * @param string $httpMethod + * @param array $headers + * + * @throws ErrorException + * @throws RuntimeException + * + * @return Guzzle3Response + */ + public function request($path, array $parameters = array(), $httpMethod = 'GET', array $headers = array()) + { + $request = $this->client->createRequest($httpMethod, $path, $headers, http_build_query($parameters)); + + try { + $response = $this->client->send($request); + } catch (\LogicException $e) { + throw new ErrorException($e->getMessage(), $e->getCode(), $e); + } catch (\RuntimeException $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + + return new Guzzle3Response($response); + } + + public function authenticate($token, $authMethod, $sudo = null) + { + $this->client->addSubscriber(new AuthSubscriber($authMethod, $token, $sudo)); + } +} diff --git a/lib/Gitlab/HttpClient/Adapter/Guzzle4Adapter.php b/lib/Gitlab/HttpClient/Adapter/Guzzle4Adapter.php new file mode 100644 index 000000000..60ce8a907 --- /dev/null +++ b/lib/Gitlab/HttpClient/Adapter/Guzzle4Adapter.php @@ -0,0 +1,56 @@ +client = $client ?: new Client(); + } + + /** + * @param string $path + * @param array $parameters + * @param string $httpMethod + * @param array $headers + * + * @throws ErrorException + * @throws RuntimeException + * + * @return Guzzle4Response + */ + public function request($path, array $parameters = array(), $httpMethod = 'GET', array $headers = array()) + { + $request = $this->client->createRequest($httpMethod, $path, $headers, http_build_query($parameters)); + + try { + $response = $this->client->send($request); + } catch (\LogicException $e) { + throw new ErrorException($e->getMessage(), $e->getCode(), $e); + } catch (\RuntimeException $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + + return new Guzzle4Response($response); + } + + public function authenticate($token, $authMethod, $sudo = null) + { + $this->client->getEmitter()->attach(new AuthSubscriber($authMethod, $token, $sudo)); + } +} diff --git a/lib/Gitlab/HttpClient/HttpClient.php b/lib/Gitlab/HttpClient/HttpClient.php index c0a33bcb0..deb649fcc 100644 --- a/lib/Gitlab/HttpClient/HttpClient.php +++ b/lib/Gitlab/HttpClient/HttpClient.php @@ -2,14 +2,7 @@ namespace Gitlab\HttpClient; -use Buzz\Client\ClientInterface; -use Buzz\Listener\ListenerInterface; - -use Gitlab\Exception\ErrorException; -use Gitlab\Exception\RuntimeException; -use Gitlab\HttpClient\Listener\ErrorListener; -use Gitlab\HttpClient\Message\Request; -use Gitlab\HttpClient\Message\Response; +use Gitlab\HttpClient\Adapter\AdapterInterface; /** * Performs requests on Gitlab API. API documentation should be self-explanatory. @@ -19,48 +12,30 @@ class HttpClient implements HttpClientInterface { /** - * @var array + * @var string */ - protected $options = array( - 'user_agent' => 'php-gitlab-api (http://github.com/m4tthumphrey/php-gitlab-api)', - 'timeout' => 10, - ); - - protected $base_url = null; + protected $baseUrl; - /** - * @var array - */ - protected $listeners = array(); /** * @var array */ protected $headers = array(); - private $lastResponse; - private $lastRequest; - /** - * @param array $options - * @param ClientInterface $client + * @var AdapterInterface */ - public function __construct($baseUrl, array $options, ClientInterface $client) - { - $this->base_url = $baseUrl; - $this->options = array_merge($this->options, $options); - $this->client = $client; - - $this->addListener(new ErrorListener($this->options)); - - $this->clearHeaders(); - } + protected $adapter; /** - * {@inheritDoc} + * @param string $baseUrl + * @param AdapterInterface $adapter */ - public function setOption($name, $value) + public function __construct($baseUrl, AdapterInterface $adapter) { - $this->options[$name] = $value; + $this->baseUrl = $baseUrl; + $this->adapter = $adapter; + + $this->clearHeaders(); } /** @@ -79,21 +54,13 @@ public function clearHeaders() $this->headers = array(); } - /** - * @param ListenerInterface $listener - */ - public function addListener(ListenerInterface $listener) - { - $this->listeners[get_class($listener)] = $listener; - } - /** * {@inheritDoc} */ public function get($path, array $parameters = array(), array $headers = array()) { if (0 < count($parameters)) { - $path .= (false === strpos($path, '?') ? '?' : '&').http_build_query($parameters, '', '&'); + $path .= (false === strpos($path, '?') ? '?' : '&') . http_build_query($parameters, '', '&'); } return $this->request($path, array(), 'GET', $headers); @@ -136,69 +103,14 @@ public function put($path, array $parameters = array(), array $headers = array() */ public function request($path, array $parameters = array(), $httpMethod = 'GET', array $headers = array()) { - $path = trim($this->base_url.$path, '/'); - - $request = $this->createRequest($httpMethod, $path); - $request->addHeaders($headers); - $request->setContent(http_build_query($parameters)); - - $hasListeners = 0 < count($this->listeners); - if ($hasListeners) { - foreach ($this->listeners as $listener) { - $listener->preSend($request); - } - } - - $response = new Response(); - - try { - $this->client->send($request, $response); - } catch (\LogicException $e) { - throw new ErrorException($e->getMessage()); - } catch (\RuntimeException $e) { - throw new RuntimeException($e->getMessage()); - } - - $this->lastRequest = $request; - $this->lastResponse = $response; - - if ($hasListeners) { - foreach ($this->listeners as $listener) { - $listener->postSend($request, $response); - } - } - - return $response; - } - - /** - * @return Request - */ - public function getLastRequest() - { - return $this->lastRequest; - } + $path = '/' . trim($path, '/') . '/'; + $headers = array_merge($this->headers, $headers); - /** - * @return Response - */ - public function getLastResponse() - { - return $this->lastResponse; + return $this->adapter->request($this->baseUrl . $path, $parameters, $httpMethod, $headers); } - /** - * @param string $httpMethod - * @param string $url - * - * @return Request - */ - private function createRequest($httpMethod, $url) + public function authenticate($token, $authMethod = null, $sudo = null) { - $request = new Request($httpMethod); - $request->setHeaders($this->headers); - $request->fromUrl($url); - - return $request; + $this->adapter->authenticate($token, $authMethod, $sudo); } } diff --git a/lib/Gitlab/HttpClient/HttpClientInterface.php b/lib/Gitlab/HttpClient/HttpClientInterface.php index 75699a2f9..9c43b3b17 100644 --- a/lib/Gitlab/HttpClient/HttpClientInterface.php +++ b/lib/Gitlab/HttpClient/HttpClientInterface.php @@ -1,8 +1,7 @@ @@ -49,28 +47,25 @@ public function __construct($method, $token, $sudo = null) */ public function preSend(RequestInterface $request) { - // Skip by default - if (null === $this->method) { - return; - } - switch ($this->method) { - case Client::AUTH_HTTP_TOKEN: - $request->addHeader('PRIVATE-TOKEN: '.$this->token); + case HttpClientInterface::AUTH_HTTP_TOKEN: + $request->addHeader('PRIVATE-TOKEN:' . $this->token); if (!is_null($this->sudo)) { - $request->addHeader('SUDO: '.$this->sudo); + $request->addHeader('SUDO:' . $this->sudo); } break; - case Client::AUTH_URL_TOKEN: - $url = $request->getUrl(); - $query=array('private_token' => $this->token); + case HttpClientInterface::AUTH_URL_TOKEN: + $url = $request->getUrl(); + $query = array('private_token' => $this->token); + if (!is_null($this->sudo)) { $query['sudo'] = $this->sudo; } + $url .= (false === strpos($url, '?') ? '?' : '&').utf8_encode(http_build_query($query, '', '&')); - $request->fromUrl(new Url($url)); + $request->setUrl($url); break; } } diff --git a/lib/Gitlab/HttpClient/Listener/ErrorListener.php b/lib/Gitlab/HttpClient/Listener/ErrorListener.php index eb47abf75..9238ebb98 100644 --- a/lib/Gitlab/HttpClient/Listener/ErrorListener.php +++ b/lib/Gitlab/HttpClient/Listener/ErrorListener.php @@ -13,19 +13,6 @@ */ class ErrorListener implements ListenerInterface { - /** - * @var array - */ - private $options; - - /** - * @param array $options - */ - public function __construct(array $options) - { - $this->options = $options; - } - /** * {@inheritDoc} */ @@ -38,7 +25,7 @@ public function preSend(RequestInterface $request) */ public function postSend(RequestInterface $request, MessageInterface $response) { - /** @var $response \Gitlab\HttpClient\Message\Response */ + /** @var $response \Gitlab\HttpClient\Message\BuzzResponse */ if ($response->isClientError() || $response->isServerError()) { $content = $response->getContent(); if (is_array($content) && isset($content['message'])) { diff --git a/lib/Gitlab/HttpClient/Message/Response.php b/lib/Gitlab/HttpClient/Message/BuzzResponse.php similarity index 63% rename from lib/Gitlab/HttpClient/Message/Response.php rename to lib/Gitlab/HttpClient/Message/BuzzResponse.php index 49228f32f..c40cb00fe 100644 --- a/lib/Gitlab/HttpClient/Message/Response.php +++ b/lib/Gitlab/HttpClient/Message/BuzzResponse.php @@ -4,7 +4,7 @@ use Buzz\Message\Response as BaseResponse; -class Response extends BaseResponse +class BuzzResponse extends BaseResponse implements ResponseInterface { /** * {@inheritDoc} @@ -12,16 +12,15 @@ class Response extends BaseResponse public function getContent() { $response = parent::getContent(); + if ($this->getHeader("Content-Type") === "application/json") { $content = json_decode($response, true); - - if (JSON_ERROR_NONE !== json_last_error()) { - return $response; + + if (JSON_ERROR_NONE === json_last_error()) { + return $content; } - - return $content; - } else { - return $response; } + + return $response; } } diff --git a/lib/Gitlab/HttpClient/Message/Guzzle3Response.php b/lib/Gitlab/HttpClient/Message/Guzzle3Response.php new file mode 100644 index 000000000..7d0287520 --- /dev/null +++ b/lib/Gitlab/HttpClient/Message/Guzzle3Response.php @@ -0,0 +1,33 @@ +response = $response; + } + + /** + * {@inheritDoc} + */ + public function getContent() + { + $response = $this->response->getBody(true); + + if ($this->response->getHeader("Content-Type")->hasValue("application/json")) { + $content = json_decode($response, true); + + if (JSON_ERROR_NONE === json_last_error()) { + return $content; + } + } + + return $response; + } +} diff --git a/lib/Gitlab/HttpClient/Message/Guzzle4Response.php b/lib/Gitlab/HttpClient/Message/Guzzle4Response.php new file mode 100644 index 000000000..008fcb443 --- /dev/null +++ b/lib/Gitlab/HttpClient/Message/Guzzle4Response.php @@ -0,0 +1,33 @@ +response = $response; + } + + /** + * {@inheritDoc} + */ + public function getContent() + { + $response = $this->response->getBody(true); + + if ($this->response->getHeader("Content-Type") === "application/json") { + $content = json_decode($response, true); + + if (JSON_ERROR_NONE === json_last_error()) { + return $content; + } + } + + return $response; + } +} diff --git a/lib/Gitlab/HttpClient/Message/ResponseInterface.php b/lib/Gitlab/HttpClient/Message/ResponseInterface.php new file mode 100644 index 000000000..1761feee1 --- /dev/null +++ b/lib/Gitlab/HttpClient/Message/ResponseInterface.php @@ -0,0 +1,8 @@ +method = $method; + $this->token = $token; + $this->sudo = $sudo; + } + + public static function getSubscribedEvents() + { + return array('request.before_send' => 'beforeRequest'); + } + + public function getEvents() + { + return array('before' => array('beforeRequest')); + } + + public function beforeRequest($event) + { + if ($event instanceof Event) { + /** + * @var Request + */ + $request = $event['request']; + } elseif ($event instanceof BeforeEvent) { + $request = $event->getRequest(); + } else { + throw new InvalidArgumentException(); + } + + switch ($this->method) { + case HttpClientInterface::AUTH_HTTP_TOKEN: + $request->addHeader('PRIVATE-TOKEN', $this->token); + if (!is_null($this->sudo)) { + $request->addHeader('SUDO', $this->sudo); + } + break; + + case HttpClientInterface::AUTH_URL_TOKEN: + $url = $request->getUrl(); + $query = array('private_token' => $this->token); + + if (!is_null($this->sudo)) { + $query['sudo'] = $this->sudo; + } + + $url .= (false === strpos($url, '?') ? '?' : '&').utf8_encode(http_build_query($query, '', '&')); + + $request->setUrl($url); + break; + } + } +} diff --git a/test.php b/test.php new file mode 100644 index 000000000..e69de29bb