diff --git a/src/Bookboon.php b/src/Bookboon.php index d6bcb62..da716e1 100644 --- a/src/Bookboon.php +++ b/src/Bookboon.php @@ -41,30 +41,42 @@ public function __construct(Client $client) } /** - * @param $appId - * @param $appSecret + * @param string $appId + * @param string $appSecret * @param array $scopes * @param array $headers - * @param null $appUserId - * @param null $redirectUri + * @param string|null $appUserId + * @param string|null $redirectUri * @param Cache|null $cache * @return Bookboon + * @throws Exception\UsageException */ - public static function create($appId, $appSecret, array $scopes, array $headers = [], $appUserId = null, $redirectUri = null, Cache $cache = null) - { + public static function create( + $appId, + $appSecret, + array $scopes, + array $headers = [], + $appUserId = null, + $redirectUri = null, + Cache $cache = null + ) { $headersObject = new Headers(); foreach ($headers as $key => $value) { $headersObject->set($key, $value); } - return new Bookboon(new OauthClient($appId, $appSecret, $headersObject, $scopes, $cache, $redirectUri, $appUserId)); + return new Bookboon( + new OauthClient($appId, $appSecret, $headersObject, $scopes, $cache, $redirectUri, $appUserId) + ); } + /** - * @param $url + * @param string $url * @param array $variables - * @param $httpMethod - * @param bool $shouldCache + * @param string $httpMethod + * @param boolean $shouldCache * @return BookboonResponse + * @throws Exception\UsageException */ public function rawRequest($url, array $variables = [], $httpMethod = Client::HTTP_GET, $shouldCache = true) { diff --git a/src/Client/BasicAuthClient.php b/src/Client/BasicAuthClient.php index 0d38d08..fad0100 100644 --- a/src/Client/BasicAuthClient.php +++ b/src/Client/BasicAuthClient.php @@ -15,7 +15,7 @@ class BasicAuthClient implements Client { use ClientTrait, ResponseTrait, RequestTrait; - const C_VERSION = '2.0'; + const C_VERSION = '2.1'; protected static $CURL_REQUESTS; @@ -28,7 +28,18 @@ class BasicAuthClient implements Client CURLOPT_SSL_VERIFYHOST => 2, ); - public function __construct($apiId, $apiSecret, Headers $headers, Cache $cache = null) + protected $_apiUri; + + /** + * BasicAuthClient constructor. + * @param string $apiId + * @param string $apiSecret + * @param Headers $headers + * @param Cache|null $cache + * @param string|null $apiUri + * @throws UsageException + */ + public function __construct($apiId, $apiSecret, Headers $headers, Cache $cache = null, $apiUri = null) { if (empty($apiId) || empty($apiSecret)) { throw new UsageException("Key and secret are required"); @@ -38,23 +49,32 @@ public function __construct($apiId, $apiSecret, Headers $headers, Cache $cache = $this->setApiSecret($apiSecret); $this->setCache($cache); $this->setHeaders($headers); + + $this->_apiUri = $this->parseUriOrDefault($apiUri); } /** * Makes the actual query call to the remote api. * - * @param string $url The url relative to the address + * @param string $uri The url relative to the address * @param string $type Bookboon::HTTP_GET or Bookboon::HTTP_POST * @param array $variables array of post variables (key => value) - * @oaram string $contentType - * * @param string $contentType * @return BookboonResponse + * @throws ApiAuthenticationException * @throws ApiGeneralException * @throws ApiTimeoutException + * @throws \Bookboon\Api\Exception\ApiNotFoundException + * @throws \Bookboon\Api\Exception\ApiSyntaxException + * @oaram string $contentType + * */ - protected function executeQuery($url, $type = self::HTTP_GET, $variables = array(), $contentType = self::CONTENT_TYPE_FORM) - { + protected function executeQuery( + $uri, + $type = self::HTTP_GET, + $variables = array(), + $contentType = self::CONTENT_TYPE_FORM + ) { $http = curl_init(); $headers = $this->getHeaders()->getAll(); @@ -70,7 +90,7 @@ protected function executeQuery($url, $type = self::HTTP_GET, $variables = array } curl_setopt($http, CURLOPT_USERAGENT, $this->getUserAgentString()); - curl_setopt($http, CURLOPT_URL, Client::API_PROTOCOL . "://$url"); + curl_setopt($http, CURLOPT_URL, $uri); curl_setopt($http, CURLOPT_USERPWD, $this->getApiId() . ':' . $this->getApiSecret()); curl_setopt($http, CURLOPT_HTTPHEADER, $headers); @@ -97,7 +117,7 @@ protected function executeQuery($url, $type = self::HTTP_GET, $variables = array substr($response, $headersSize), substr($response, 0, $headersSize), $httpStatus, - $url + $uri ); return new BookboonResponse($responseArray, $headers); @@ -153,8 +173,8 @@ public function requestAccessToken(array $options = array(), $type = OauthGrants } /** - * @param $variables - * @param $contentType + * @param array $variables + * @param string $contentType * @return string */ protected function encodeByContentType(array $variables, $contentType) @@ -164,7 +184,7 @@ protected function encodeByContentType(array $variables, $contentType) /** - * @param $appUserId + * @param string $appUserId * @throws UsageException */ public function setAct($appUserId) @@ -202,10 +222,9 @@ public function generateState() } /** - * @param $stateParameter - * @param $stateSession - * @return bool - * @throws ApiInvalidStateException + * @param string $stateParameter + * @param string $stateSession + * @return boolean * @throws UsageException */ public function isCorrectState($stateParameter, $stateSession) @@ -213,8 +232,19 @@ public function isCorrectState($stateParameter, $stateSession) throw new UsageException("Not Supported"); } + /** + * @return string + */ protected function getComponentVersion() { return self::C_VERSION; } + + /** + * @return string + */ + protected function getBaseApiUri() + { + return $this->_apiUri; + } } \ No newline at end of file diff --git a/src/Client/BookboonResponse.php b/src/Client/BookboonResponse.php index 6d065b5..0864bc8 100644 --- a/src/Client/BookboonResponse.php +++ b/src/Client/BookboonResponse.php @@ -9,27 +9,27 @@ class BookboonResponse /** * @var array */ - protected $returnArray; + protected $responseArray; /** * @var array */ - protected $headers; + protected $responseHeaders; /** - * @var array + * @var EntityStore */ protected $entityStore; /** * BookboonResponse constructor. - * @param $returnArray - * @param $headers + * @param array $responseArray + * @param array $responseHeaders */ - public function __construct($returnArray, $headers) + public function __construct(array $responseArray, array $responseHeaders) { - $this->returnArray = $returnArray; - $this->headers = $headers; + $this->responseArray = $responseArray; + $this->responseHeaders = $responseHeaders; } /** @@ -37,7 +37,8 @@ public function __construct($returnArray, $headers) */ public function getHeaders() { - return $this->headers; + + return $this->responseHeaders; } /** @@ -45,11 +46,11 @@ public function getHeaders() */ public function getReturnArray() { - return $this->returnArray; + return $this->responseArray; } /** - * @return array + * @return EntityStore */ public function getEntityStore() { @@ -57,7 +58,7 @@ public function getEntityStore() } /** - * @param $entityStore + * @param EntityStore $entityStore */ public function setEntityStore(EntityStore $entityStore) { diff --git a/src/Client/Client.php b/src/Client/Client.php index b8da812..fdf10ae 100644 --- a/src/Client/Client.php +++ b/src/Client/Client.php @@ -19,9 +19,10 @@ interface Client const CONTENT_TYPE_FORM = 'application/x-www-form-urlencoded'; const API_PROTOCOL = 'https'; - const API_URL = 'bookboon.com/api'; + const API_HOST = 'bookboon.com'; + const API_PATH = '/api'; - const VERSION = 'Bookboon-PHP/3.0'; + const VERSION = 'Bookboon-PHP/3.1'; /** * Prepares the call to the api and if enabled tries cache provider first for GET calls. diff --git a/src/Client/Oauth/BookboonProvider.php b/src/Client/Oauth/BookboonProvider.php index 8de4c78..0b80c6a 100644 --- a/src/Client/Oauth/BookboonProvider.php +++ b/src/Client/Oauth/BookboonProvider.php @@ -23,7 +23,7 @@ class BookboonProvider extends AbstractProvider public function __construct(array $options = [], array $collaborators = []) { - if (isset($options['baseUri'])) { + if (isset($options['baseUri']) && $options['baseUri'] != "") { $parts = explode('://', $options['baseUri']); $this->protocol = $parts[0]; $this->host = $parts[1]; diff --git a/src/Client/OauthClient.php b/src/Client/OauthClient.php index ed51860..5207023 100644 --- a/src/Client/OauthClient.php +++ b/src/Client/OauthClient.php @@ -23,7 +23,9 @@ class OauthClient implements Client { use ClientTrait, ResponseTrait, RequestTrait; - const C_VERSION = '2.0'; + const C_VERSION = '2.1'; + + protected $_apiUri; /** @var AccessToken */ private $accessToken; @@ -41,12 +43,23 @@ class OauthClient implements Client * @param Headers $headers * @param array $scopes * @param Cache $cache - * @param $redirectUri - * @param $appUserId + * @param string $redirectUri + * @param string $appUserId + * @param string|null $authServiceUri + * @param string|null $apiUri * @throws UsageException */ - public function __construct($apiId, $apiSecret, Headers $headers, array $scopes, Cache $cache = null, $redirectUri = null, $appUserId = null) - { + public function __construct( + $apiId, + $apiSecret, + Headers $headers, + array $scopes, + Cache $cache = null, + $redirectUri = null, + $appUserId = null, + $authServiceUri = null, + $apiUri = null + ) { if (empty($apiId)) { throw new UsageException("Client id is required"); } @@ -55,24 +68,31 @@ public function __construct($apiId, $apiSecret, Headers $headers, array $scopes, 'clientId' => $apiId, 'clientSecret' => $apiSecret, 'scope' => $scopes, - 'redirectUri' => $redirectUri + 'redirectUri' => $redirectUri, + 'baseUri' => $authServiceUri ]); $this->setCache($cache); $this->setHeaders($headers); $this->setAct($appUserId); + + $this->_apiUri = $this->parseUriOrDefault($apiUri); } /** - * @param $url + * @param string $uri * @param string $type * @param array $variables * @param string $contentType - * @return mixed + * @return BookboonResponse * @throws ApiAccessTokenExpired * @throws ApiAuthenticationException + * @throws \Bookboon\Api\Exception\ApiGeneralException + * @throws \Bookboon\Api\Exception\ApiNotFoundException + * @throws \Bookboon\Api\Exception\ApiSyntaxException + * @throws \GuzzleHttp\Exception\GuzzleException */ - protected function executeQuery($url, $type = Client::HTTP_GET, $variables = array(), $contentType = Client::CONTENT_TYPE_JSON) + protected function executeQuery($uri, $type = Client::HTTP_GET, $variables = array(), $contentType = Client::CONTENT_TYPE_JSON) { if (!($this->getAccessToken() instanceof AccessToken)) { throw new ApiAuthenticationException("Not authenticated"); @@ -83,8 +103,6 @@ protected function executeQuery($url, $type = Client::HTTP_GET, $variables = arr 'headers' => $this->headers->getHeadersArray() ]; $options['headers']['User-Agent'] = $this->getUserAgentString(); - - $url = Client::API_PROTOCOL . '://' . $url; if (count($variables) > 0 && $type == Client::HTTP_POST) { $postType = $contentType == Client::CONTENT_TYPE_JSON ? 'json' : 'form_params'; @@ -94,7 +112,7 @@ protected function executeQuery($url, $type = Client::HTTP_GET, $variables = arr try { $request = $this->provider->getAuthenticatedRequest( $type, - $url, + $uri, $this->getAccessToken() ); @@ -103,7 +121,6 @@ protected function executeQuery($url, $type = Client::HTTP_GET, $variables = arr } } - catch (IdentityProviderException $e) { throw new ApiAuthenticationException("Identity not found"); } @@ -121,7 +138,7 @@ protected function executeQuery($url, $type = Client::HTTP_GET, $variables = arr $response->getBody()->getContents(), $response->getHeaders(), $response->getStatusCode(), - $url + $uri ); return new BookboonResponse($responseArray, $response->getHeaders()); @@ -194,7 +211,7 @@ public function generateState() /** * @param AccessToken $accessToken - * @return mixed|void + * @return void * @throws ApiAccessTokenExpired */ public function setAccessToken(AccessToken $accessToken) @@ -209,9 +226,8 @@ public function setAccessToken(AccessToken $accessToken) /** * @param $stateParameter * @param $stateSession - * @return bool + * @return boolean * @throws ApiInvalidStateException - * @throws UsageException */ public function isCorrectState($stateParameter, $stateSession) { @@ -268,13 +284,29 @@ public function getAccessToken() return $this->accessToken; } + /** + * @param $request + * @param $data + * @return void + */ protected function reportDeveloperInfo($request, $data) { // TODO: Implement reportDeveloperInfo() method. } + /** + * @return string + */ protected function getComponentVersion() { return self::C_VERSION; } + + /** + * @return string + */ + protected function getBaseApiUri() + { + return $this->_apiUri; + } } \ No newline at end of file diff --git a/src/Client/RequestTrait.php b/src/Client/RequestTrait.php index 9756b99..9c472a6 100644 --- a/src/Client/RequestTrait.php +++ b/src/Client/RequestTrait.php @@ -8,13 +8,18 @@ trait RequestTrait { /** - * @param $url + * @param string $uri * @param string $type * @param array $variables * @param string $contentType * @return mixed */ - abstract protected function executeQuery($url, $type = Client::HTTP_GET, $variables = array(), $contentType = 'application/x-www-form-urlencoded'); + abstract protected function executeQuery( + $uri, + $type = Client::HTTP_GET, + $variables = array(), + $contentType = Client::CONTENT_TYPE_FORM + ); /** * @return Cache|null @@ -48,7 +53,11 @@ abstract protected function reportDeveloperInfo($request, $data); */ public function makeRequest($relativeUrl, array $variables = array(), $httpMethod = Client::HTTP_GET, $shouldCache = true, $contentType = Client::CONTENT_TYPE_JSON) { - $queryUrl = Client::API_URL . $relativeUrl; + if (strpos($relativeUrl, '/') !== 0) { + throw new UsageException('Location must begin with forward slash'); + } + + $queryUrl = $this->getBaseApiUri() . $relativeUrl; $postVariables = array(); if ($httpMethod == Client::HTTP_GET && count($variables) !== 0) { @@ -59,9 +68,6 @@ public function makeRequest($relativeUrl, array $variables = array(), $httpMetho $postVariables = $variables; } - if (substr($relativeUrl, 0, 1) !== '/') { - throw new UsageException('Location must begin with forward slash'); - } if ($this->getCache() != null && $this->getCache()->isCachable($queryUrl, $httpMethod) && $shouldCache) { $result = $this->getFromCache($queryUrl); @@ -109,4 +115,36 @@ protected function getFromCache($queryUrl) return $result; } + /** + * @param string $uri + * @return string + * @throws UsageException + */ + protected function parseUriOrDefault($uri) + { + $protocol = Client::API_PROTOCOL; + $host = Client::API_HOST; + $path = Client::API_PATH; + + if (!empty($uri)) { + $parts = explode('://', $uri); + $protocol = $parts[0]; + $host = $parts[1]; + if (strpos($host, '/') !== false) { + throw new UsageException('URI must not contain forward slashes'); + } + } + + if ($protocol != 'http' && $protocol != 'https') { + throw new UsageException('Invalid protocol specified in URI'); + } + + return "${protocol}://${host}${path}"; + } + + /** + * @return string + */ + abstract protected function getBaseApiUri(); + } \ No newline at end of file diff --git a/tests/Client/RequestTraitTest.php b/tests/Client/RequestTraitTest.php index 2e34c3e..60cd327 100644 --- a/tests/Client/RequestTraitTest.php +++ b/tests/Client/RequestTraitTest.php @@ -20,43 +20,47 @@ public function callingBack() { public function testPlainGet() { $mock = $this->getMockForTrait('\Bookboon\Api\Client\RequestTrait'); + $mock->method("getBaseApiUri")->willReturn(Client::API_HOST . Client::API_PATH); $mock->method('executeQuery') ->will($this->returnCallback(array($this, 'callingBack'))); $mock->makeRequest('/plain_get'); - $this->assertEquals(Client::API_URL . '/plain_get', $this->returnValues[0]); + $this->assertEquals(Client::API_HOST . Client::API_PATH . '/plain_get', $this->returnValues[0]); } public function testGetWithQueryString() { $mock = $this->getMockForTrait('\Bookboon\Api\Client\RequestTrait'); + $mock->method("getBaseApiUri")->willReturn(Client::API_HOST . Client::API_PATH); $mock->method('executeQuery') ->will($this->returnCallback(array($this, 'callingBack'))); $mock->makeRequest('/get_query_string', array("query2" => "test1")); - $this->assertEquals(Client::API_URL . '/get_query_string?query2=test1', $this->returnValues[0]); + $this->assertEquals(Client::API_HOST . Client::API_PATH . '/get_query_string?query2=test1', $this->returnValues[0]); } public function testPlainPost() { $mock = $this->getMockForTrait('\Bookboon\Api\Client\RequestTrait'); + $mock->method("getBaseApiUri")->willReturn(Client::API_HOST . Client::API_PATH); $mock->method('executeQuery') ->will($this->returnCallback(array($this, 'callingBack'))); $mock->makeRequest('/plain_post', array(), Client::HTTP_POST); - $this->assertEquals(Client::API_URL . '/plain_post', $this->returnValues[0]); + $this->assertEquals(Client::API_HOST . Client::API_PATH . '/plain_post', $this->returnValues[0]); $this->assertEquals(Client::HTTP_POST, $this->returnValues[1]); } public function testPlainPostWithValues() { $mock = $this->getMockForTrait('\Bookboon\Api\Client\RequestTrait'); + $mock->method("getBaseApiUri")->willReturn(Client::API_HOST . Client::API_PATH); $mock->method('executeQuery') ->will($this->returnCallback(array($this, 'callingBack'))); @@ -74,6 +78,7 @@ private function getCacheMock() public function testMakeRequestNotCached() { $mock = $this->getMockForTrait('\Bookboon\Api\Client\RequestTrait'); + $mock->method("getBaseApiUri")->willReturn(Client::API_HOST . Client::API_PATH); $cacheMock = $this->getCacheMock(); $cacheMock->method("get")->willReturn(false); @@ -89,7 +94,7 @@ public function testMakeRequestNotCached() $result = $mock->makeRequest('/plain_get'); $this->assertNull($result); - $this->assertEquals(Client::API_URL . '/plain_get', $this->returnValues[0]); + $this->assertEquals(Client::API_HOST . Client::API_PATH . '/plain_get', $this->returnValues[0]); } public function testMakeRequestCached()