diff --git a/composer.json b/composer.json index adaec2b..101f762 100644 --- a/composer.json +++ b/composer.json @@ -16,14 +16,14 @@ } ], "require": { - "php": ">=7.3.0", - "guzzlehttp/guzzle": "~6.5.5", - "guzzlehttp/oauth-subscriber": "0.3.*" + "php": ">=8.1.0", + "guzzlehttp/guzzle": ">7.5", + "guzzlehttp/oauth-subscriber": "0.6.*" }, "require-dev": { - "phpunit/phpunit": "~5.0", - "friendsofphp/php-cs-fixer": "~2.12", - "php-coveralls/php-coveralls": "^2.1" + "phpunit/phpunit": ">9.0", + "php-coveralls/php-coveralls": "^2.7", + "friendsofphp/php-cs-fixer": "^3.65" }, "autoload": { "psr-4": { "phpSmug\\": "lib/phpSmug/" } diff --git a/examples/example-external-links.php b/examples/example-external-links.php index 893e9c2..67536ec 100644 --- a/examples/example-external-links.php +++ b/examples/example-external-links.php @@ -67,7 +67,7 @@ // Step 2: Get the User to login to SmugMug and authorise this demo echo '

Click HERE to Authorize This Demo.

'; - // Alternatively, automatically direct your visitor by commenting out the above line in favour of this: + // Alternatively, automatically direct your visitor by commenting out the above line in favour of this: //header("Location:".$client->getAuthorizeURL()); } else { $reqToken = unserialize($_SESSION['SmugGalReqToken']); diff --git a/examples/example-oauth.php b/examples/example-oauth.php index a9a9541..80eb8ea 100644 --- a/examples/example-oauth.php +++ b/examples/example-oauth.php @@ -62,7 +62,8 @@ // Step 2: Get the User to login to SmugMug and authorise this demo echo '

Click HERE to Authorize This Demo.

'; - // Alternatively, automatically direct your visitor by commenting out the above line in favour of this: + + // Alternatively, automatically direct your visitor by commenting out the above line in favour of this: //header("Location:".$client->getAuthorizeURL()); } else { $reqToken = unserialize($_SESSION['SmugGalReqToken']); diff --git a/lib/phpSmug/Client.php b/lib/phpSmug/Client.php index 979ff18..783239b 100644 --- a/lib/phpSmug/Client.php +++ b/lib/phpSmug/Client.php @@ -12,7 +12,7 @@ class Client /** * A few default variables. */ - const VERSION = '4.0.0'; + public const VERSION = '4.0.0'; public $AppName = 'Unknown Application'; public $APIKey; public $OAuthSecret; @@ -149,7 +149,7 @@ public function __call($method, $args) $this->request_options['query'][$key] = $value; } } - break; + break; case 'put': case 'post': case 'patch': @@ -158,10 +158,10 @@ public function __call($method, $args) if ($options) { $this->request_options['json'] = $options; } - break; + break; default: throw new BadMethodCallException('Invalid method: '.$method); - break; + break; } $this->performRequest(strtoupper($method), $url); @@ -337,32 +337,32 @@ private function performRequest($method, $url) private function processResponse($method = null) { switch ($method) { - case 'getRequestToken': - case 'getAccessToken': - parse_str($this->response->getBody(), $token); - $this->setToken($token['oauth_token'], $token['oauth_token_secret']); - // Remove the middleware so it is re-added with the updated credentials on subsequent requests. - $this->stack->remove('oauth_middleware'); - - return $token; - break; - case 'options': - $body = json_decode((string) $this->response->getBody()); - - return $body->Options; - break; - default: - $body = json_decode((string) $this->response->getBody()); - if (isset($body->Response)) { - if (isset($body->Expansions)) { - $body->Response->Expansions = $body->Expansions; - } - - return $body->Response; - } else { - return $body; - } - break; + case 'getRequestToken': + case 'getAccessToken': + parse_str($this->response->getBody(), $token); + $this->setToken($token['oauth_token'], $token['oauth_token_secret']); + // Remove the middleware so it is re-added with the updated credentials on subsequent requests. + $this->stack->remove('oauth_middleware'); + + return $token; + break; + case 'options': + $body = json_decode((string) $this->response->getBody()); + + return $body->Options; + break; + default: + $body = json_decode((string) $this->response->getBody()); + if (isset($body->Response)) { + if (isset($body->Expansions)) { + $body->Response->Expansions = $body->Expansions; + } + + return $body->Response; + } else { + return $body; + } + break; } } diff --git a/phpunit.xml b/phpunit.xml index c44db60..0e6ccdc 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,21 +1,20 @@ - - + - ./test/phpSmug/ + test/phpSmug/ @@ -25,16 +24,9 @@ - - - ./lib/phpSmug/ - - - - - - - + + + src + + diff --git a/test/phpSmug/Tests/ClientSmugMugTest.php b/test/phpSmug/Tests/ClientSmugMugTest.php index 7aaedee..f69e129 100644 --- a/test/phpSmug/Tests/ClientSmugMugTest.php +++ b/test/phpSmug/Tests/ClientSmugMugTest.php @@ -4,13 +4,26 @@ use phpSmug\Client; use GuzzleHttp\Client as GuzzleClient; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\Depends; -class ClientSmugMugTest extends \PHPUnit_Framework_TestCase +class ClientSmugMugTest extends TestCase { - public function setup() + protected const DEFAULT_NICKNAME = 'colinseymour'; + protected const DEFAULT_FOLDER = 'Other'; + protected const APP_NAME = 'phpSmug Unit Testing'; + + protected string $nickname; + protected string $folder; + + public function setUp(): void { + $this->nickname = getenv('NICKNAME') ?? self::DEFAULT_NICKNAME; + $this->folder = getenv('FOLDER') ?? self::DEFAULT_FOLDER; + $options = [ - 'AppName' => 'phpSmug Unit Testing', + 'AppName' => self::APP_NAME, 'OAuthSecret' => getenv('OAUTH_SECRET'), '_verbosity' => 1, '_shorturis' => true, @@ -36,39 +49,36 @@ public function checkEnvVars() } /** - * @test - * * Test unauthenticated GET */ + #[Test] public function shouldGetPublicUserInfo() { $this->checkEnvVars(); - $client = new \phpSmug\Client(getenv('APIKEY'), ['AppName' => 'phpSmug Unit Testing']); - $response = $client->get('user/colinseymour'); + $client = new \phpSmug\Client(getenv('APIKEY'), ['AppName' => self::APP_NAME]); + $response = $client->get('user/' . $this->nickname); $this->assertTrue(is_object($response)); $this->assertEquals('Public', $response->User->ResponseLevel); - $this->assertEquals('colinseymour', $response->User->NickName); + $this->assertEquals($this->nickname, $response->User->NickName); } /** - * @test - * * Test authenticated GET */ + #[Test] public function shouldGetFullUserInfo() { $this->checkEnvVars(); $response = $this->client->get('!authuser'); $this->assertTrue(is_object($response)); $this->assertEquals('Full', $response->User->ResponseLevel); - $this->assertEquals('colinseymour', $response->User->NickName); + $this->assertEquals($this->nickname, $response->User->NickName); } /** - * @test - * * Tests POST by creating a new album */ + #[Test] public function shouldCreateNewAlbum() { $this->checkEnvVars(); @@ -79,7 +89,7 @@ public function shouldCreateNewAlbum() 'Title' => 'New Album from unit testing phpSmug', 'Privacy' => 'Private', ]; - $response = $this->client->post('folder/user/colinseymour/Other!albums', $options); + $response = $this->client->post('folder/user/' . $this->nickname . '/' . $this->folder . '/!albums', $options); $this->assertTrue(is_object($response)); $this->assertEquals($options['NiceName'], $response->Album->NiceName); $this->assertEquals($options['Title'], $response->Album->Title); @@ -89,11 +99,10 @@ public function shouldCreateNewAlbum() } /** - * @test - * @depends shouldCreateNewAlbum - * * Tests PATCH by modifying the previously created album. */ + #[Test] + #[Depends('shouldCreateNewAlbum')] public function shouldModifyNewlyCreatedAlbum($album_uri) { $this->checkEnvVars(); @@ -111,11 +120,10 @@ public function shouldModifyNewlyCreatedAlbum($album_uri) } /** - * @test - * @depends shouldModifyNewlyCreatedAlbum - * * Tests upload() */ + #[Test] + #[Depends('shouldModifyNewlyCreatedAlbum')] public function shouldUploadPictureToNewlyCreatedAlbum($album_uri) { $this->checkEnvVars(); @@ -129,38 +137,39 @@ public function shouldUploadPictureToNewlyCreatedAlbum($album_uri) ]; $response = $this->client->upload($album_uri, 'examples/phpSmug-logo.png', $options); $this->assertTrue(is_object($response)); - $this->assertObjectHasAttribute('Image', $response); + $this->assertObjectHasProperty('Image', $response); return $album_uri; } /** - * @test - * @depends shouldUploadPictureToNewlyCreatedAlbum - * @expectedException \GuzzleHttp\Exception\ClientException - * @expectedExceptionMessage 404 Not Found - * * Tests that we really can't access the private image */ + #[Test] + #[Depends('shouldUploadPictureToNewlyCreatedAlbum')] public function shouldFailToGetPrivateImage($album_uri) { $this->checkEnvVars(); - $thumbnail_url = $this->client->get($album_uri.'!images')->AlbumImage[0]->ThumbnailUrl; + $thumbnail_url = $this->client->get($album_uri . '!images')->AlbumImage[0]->ThumbnailUrl; + $client = new GuzzleClient(); + + $this->expectException(\GuzzleHttp\Exception\ClientException::class); + $this->expectExceptionMessage('404 Not Found'); $client->get($thumbnail_url); } /** - * @test - * @depends shouldUploadPictureToNewlyCreatedAlbum - * * Tests signResource() */ + #[Test] + #[Depends('shouldUploadPictureToNewlyCreatedAlbum')] public function shouldGetPrivateImageWithSignedUrl($album_uri) { $this->checkEnvVars(); $thumbnail_url = $this->client->get($album_uri.'!images')->AlbumImage[0]->ThumbnailUrl; $signed_thumbnail_url = $this->client->signResource($thumbnail_url); + $client = new GuzzleClient(); $client->get($signed_thumbnail_url); @@ -168,11 +177,10 @@ public function shouldGetPrivateImageWithSignedUrl($album_uri) } /** - * @test - * @depends shouldGetPrivateImageWithSignedUrl - * * Tests DELETE */ + #[Test] + #[Depends('shouldGetPrivateImageWithSignedUrl')] public function shouldDeleteNewlyCreatedAlbumWithUploadedPicture($album_uri) { $this->checkEnvVars(); @@ -183,16 +191,15 @@ public function shouldDeleteNewlyCreatedAlbumWithUploadedPicture($album_uri) } /** - * @test - * * Tests OPTIONS */ + #[Test] public function shouldGetInfoAboutMethod() { $this->checkEnvVars(); - $options = $this->client->options('user/colinseymour'); - $this->assertObjectHasAttribute('Output', $options); + $options = $this->client->options('user/' . $this->nickname); + $this->assertObjectHasProperty('Output', $options); $this->assertTrue(is_array($options->Output)); - $this->assertObjectNotHasAttribute('Response', $options); + $this->assertObjectNotHasProperty('Response', $options); } } diff --git a/test/phpSmug/Tests/ClientTest.php b/test/phpSmug/Tests/ClientTest.php index 311c56b..dd42c41 100644 --- a/test/phpSmug/Tests/ClientTest.php +++ b/test/phpSmug/Tests/ClientTest.php @@ -7,14 +7,18 @@ use GuzzleHttp\HandlerStack; use GuzzleHttp\Psr7; use GuzzleHttp\Psr7\Response; +use GuzzleHttp\Psr7\Header; use GuzzleHttp\Middleware; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Attributes\Test; +use PHPUnit\Framework\Attributes\Depends; -class ClientTest extends \PHPUnit_Framework_TestCase +class ClientTest extends TestCase { /** * Setup a few variables for use in later tests. */ - public function setup() + public function setUp(): void { $this->APIKey = 'I-am-not-a-valid-APIKey-but-it-does-not-matter-for-this-test'; $this->user = 'random-user'; @@ -26,9 +30,8 @@ public function setup() $this->fauxAccessTokenResponse = "oauth_token={$this->oauth_token}&oauth_token_secret={$this->oauth_token_secret}"; $this->fauxDeleteResponse = '{"Response":{"Uri":"/api/v2/album/rAnD0m","Locator":"Album","LocatorType":"Object"},"Code":200,"Message":"Ok"}'; } - /** - * @test - */ + + #[Test] public function shouldNotHaveToPassHttpClientToConstructorWithDefaultOptionsSet() { $client = new Client($this->APIKey); @@ -41,19 +44,15 @@ public function shouldNotHaveToPassHttpClientToConstructorWithDefaultOptionsSet( $this->assertEquals(30, $options['timeout']); } - /** - * @test - * @expectedException \phpSmug\Exception\InvalidArgumentException - * @expectedExceptionMessage An API key is required for all SmugMug interactions. - */ + #[Test] public function shouldThrowExceptionIfNoApikey() { + $this->expectException(\phpSmug\Exception\InvalidArgumentException::class); + $this->expectExceptionMessage('An API key is required for all SmugMug interactions.'); $client = new Client(); } - /** - * @test - */ + #[Test] public function shouldHaveOptionsSetInInstance() { $options = [ @@ -72,9 +71,7 @@ public function shouldHaveOptionsSetInInstance() $this->assertEquals(sprintf('Testing phpSmug using phpSmug/%s', $client::VERSION), $client->getDefaultOptions()['headers']['User-Agent']); } - /** - * @test - */ + #[Test] public function shouldSetAndGetOAuthTokens() { $options = ['OAuthSecret' => $this->OAuthSecret]; @@ -87,9 +84,7 @@ public function shouldSetAndGetOAuthTokens() $this->assertEquals($this->oauth_token_secret, $oauth_token_secret); } - /** - * @test - */ + #[Test] public function shouldHaveAPIKeyInQuery() { $client = new Client($this->APIKey); @@ -99,9 +94,7 @@ public function shouldHaveAPIKeyInQuery() $this->assertEquals($this->APIKey, $options['query']['APIKey']); } - /** - * @test - */ + #[Test] public function shouldGetReasonPhrase() { $mock = new MockHandler([ @@ -116,9 +109,7 @@ public function shouldGetReasonPhrase() $this->assertEquals('OK', $client->getReasonPhrase()); } - /** - * @test - */ + #[Test] public function shouldGetHeaders() { $mock = new MockHandler([ @@ -134,9 +125,7 @@ public function shouldGetHeaders() $this->assertArrayHasKey('X-Foo', $client->getHeaders()); } - /** - * @test - */ + #[Test] public function shouldGetStatusCode() { $mock = new MockHandler([ @@ -152,9 +141,7 @@ public function shouldGetStatusCode() $this->assertEquals('200', $client->getStatusCode()); } - /** - * @test - */ + #[Test] public function shouldReturnUntouchedResponse() { $mock = new MockHandler([ @@ -174,9 +161,7 @@ public function shouldReturnUntouchedResponse() $this->assertEquals('OK', $decoded_response->Message); } - /** - * @test - */ + #[Test] public function shouldGetSmugMugMethodOptions() { $mock = new MockHandler([ @@ -189,14 +174,12 @@ public function shouldGetSmugMugMethodOptions() $options = $client->options('user/'.$this->user); - $this->assertObjectHasAttribute('foo', $options); + $this->assertObjectHasProperty('foo', $options); $this->assertEquals('bar', $options->foo); - $this->assertObjectNotHasAttribute('Response', $options); + $this->assertObjectNotHasProperty('Response', $options); } - /** - * @test - */ + #[Test] public function shouldExtractQueryFromURLAndSetQueryInRequest() { $mock = new MockHandler([ @@ -215,9 +198,7 @@ public function shouldExtractQueryFromURLAndSetQueryInRequest() $this->assertEquals(2, $request_options['query']['_verbosity']); } - /** - * @test - */ + #[Test] public function shouldStripOutEmptyQueryArgs() { $mock = new MockHandler([ @@ -232,9 +213,7 @@ public function shouldStripOutEmptyQueryArgs() $this->addToAssertionCount(1); } - /** - * @test - */ + #[Test] public function shouldSetQueryFromOptionsPassedOnRequestAndOverWriteDefaults() { $mock = new MockHandler([ @@ -259,9 +238,7 @@ public function shouldSetQueryFromOptionsPassedOnRequestAndOverWriteDefaults() } } - /** - * @test - */ + #[Test] public function shouldEncodeConfigOption() { $mock = new MockHandler([ @@ -293,9 +270,7 @@ public function shouldEncodeConfigOption() $this->assertEquals(JSON_ERROR_NONE, json_last_error()); } - /** - * @test - */ + #[Test] public function shouldNotDoubleEncodeEncodedConfigOption() { $mock = new MockHandler([ @@ -328,9 +303,7 @@ public function shouldNotDoubleEncodeEncodedConfigOption() $this->assertEquals(JSON_ERROR_NONE, json_last_error()); } - /** - * @test - */ + #[Test] public function shouldReturnReponseObject() { $mock = new MockHandler([ @@ -342,15 +315,13 @@ public function shouldReturnReponseObject() $response = $client->get('user/'.$this->user); - $this->assertObjectHasAttribute('ano', $response); - $this->assertObjectHasAttribute('Expansions', $response); + $this->assertObjectHasProperty('ano', $response); + $this->assertObjectHasProperty('Expansions', $response); $this->assertEquals('bar', $response->ano); $this->assertEquals('bar', $response->Expansions->foo); } - /** - * @test - */ + #[Test] public function shouldSetOAuthParamsInQuery() { $mock = new MockHandler([ @@ -372,9 +343,7 @@ public function shouldSetOAuthParamsInQuery() } } - /** - * @test - */ + #[Test] public function shouldSetCorrectQueryUrl() { $mock = new MockHandler([ @@ -409,9 +378,7 @@ public function shouldSetCorrectQueryUrl() } } - /** - * @test - */ + #[Test] public function shouldSetAndUnSetHeadersEtcForUploadAndAssumeUploadWorkedWithOptionsThatMatchHeaders() { $mock = new MockHandler([ @@ -444,9 +411,7 @@ public function shouldSetAndUnSetHeadersEtcForUploadAndAssumeUploadWorkedWithOpt $this->assertEmpty($request_options['query']); } - /** - * @test - */ + #[Test] public function shouldSetAndUnSetHeadersEtcForUploadAndAssumeUploadWorkedWithOptionsThatDontHaveXSmugInTheirName() { $mock = new MockHandler([ @@ -479,20 +444,16 @@ public function shouldSetAndUnSetHeadersEtcForUploadAndAssumeUploadWorkedWithOpt $this->assertEmpty($request_options['query']); } - /** - * @test - * @expectedException \phpSmug\Exception\InvalidArgumentException - * @expectedExceptionMessage File not found: /path/to/non/existant/file.jpg - */ + #[Test] public function shouldThrowExceptionIfUploadFileNotFound() { + $this->expectException(\phpSmug\Exception\InvalidArgumentException::class); + $this->expectExceptionMessage('File not found: /path/to/non/existant/file.jpg'); $client = new Client($this->APIKey); $client->upload('album/rAnD0m', '/path/to/non/existant/file.jpg'); } - /** - * @test - */ + #[Test] public function shouldSetJsonOptionOnPutAndPatchRequests() { $mock = new MockHandler([ @@ -513,9 +474,7 @@ public function shouldSetJsonOptionOnPutAndPatchRequests() $this->assertEquals($options, $request_options['json']); } - /** - * @test - */ + #[Test] public function shouldSetOAuthParamsInAuthorizationHeader() { $mock = new MockHandler([ @@ -533,7 +492,7 @@ public function shouldSetOAuthParamsInAuthorizationHeader() $client->setToken($this->oauth_token, $this->oauth_token_secret); $client->get('album/rAnD0m'); foreach ($container as $transaction) { - $auth_header = Psr7\parse_header($transaction['request']->getHeader('Authorization')); + $auth_header = Header::parse($transaction['request']->getHeader('Authorization')); // Asserts the header is set and populated $this->assertNotEmpty($auth_header); @@ -554,20 +513,16 @@ public function shouldSetOAuthParamsInAuthorizationHeader() } } - /** - * @test - * @expectedException \phpSmug\Exception\InvalidArgumentException - * @expectedExceptionMessage An OAuthSecret is required for all SmugMug OAuth interactions. - */ + #[Test] public function shouldThrowExcetionIfNoOAuthSecret() { + $this->expectException(\phpSmug\Exception\InvalidArgumentException::class); + $this->expectExceptionMessage('An OAuthSecret is required for all SmugMug OAuth interactions.'); $client = new Client($this->APIKey); $client->setToken($this->oauth_token, $this->oauth_token_secret); } - /** - * @test - */ + #[Test] public function shouldGetRequestToken() { $mock = new MockHandler([ @@ -589,9 +544,7 @@ public function shouldGetRequestToken() $this->assertEquals('true', $request_token['oauth_callback_confirmed']); } - /** - * @test - */ + #[Test] public function shouldGetAccessToken() { $mock = new MockHandler([ @@ -611,9 +564,7 @@ public function shouldGetAccessToken() $this->assertEquals($this->oauth_token_secret, $request_token['oauth_token_secret']); } - /** - * @test - */ + #[Test] public function shouldGenerateAuthorizationUrl() { $options = ['OAuthSecret' => $this->OAuthSecret]; @@ -646,31 +597,25 @@ public function shouldGenerateAuthorizationUrl() $this->assertEquals("https://secure.smugmug.com/services/oauth/1.0a/authorize?oauth_token={$this->oauth_token}&oauth_callback=".urlencode($callback).'&'.\http_build_query($options), $authorize_url); } - /** - * @test - * @expectedException \phpSmug\Exception\InvalidArgumentException - * @expectedExceptionMessage All methods need an argument. - */ + #[Test] public function shouldThrowInvalidArgumentExceptionIfCallMethodWithoutDestination() { + $this->expectException(\phpSmug\Exception\InvalidArgumentException::class); + $this->expectExceptionMessage('All methods need an argument.'); $client = new Client($this->APIKey); $client->get(); } - /** - * @test - * @expectedException \phpSmug\Exception\BadMethodCallException - * @expectedExceptionMessage Invalid method: badmethod - */ + #[Test] public function shouldThrowBadMethodCallException() { + $this->expectException(\phpSmug\Exception\BadMethodCallException::class); + $this->expectExceptionMessage('Invalid method: badmethod'); $client = new Client($this->APIKey); $client->badmethod('album/r4nD0m'); } - /** - * @test - */ + #[Test] public function shouldSignRequestUrlWithOAuthParams() { $client = new Client($this->APIKey, ['OAuthSecret' => $this->OAuthSecret]); @@ -691,9 +636,7 @@ public function shouldSignRequestUrlWithOAuthParams() $this->assertEquals('1.0', $query_parts['oauth_version']); } - /** - * @test - */ + #[Test] public function shouldDeleteAlbum() { $mock = new MockHandler([ diff --git a/test/phpSmug/Tests/PsrComplianceTest.php b/test/phpSmug/Tests/PsrComplianceTest.php index 1ec5efa..42aefa8 100644 --- a/test/phpSmug/Tests/PsrComplianceTest.php +++ b/test/phpSmug/Tests/PsrComplianceTest.php @@ -2,15 +2,15 @@ namespace phpSmug\Tests; +use PHPUnit\Framework\TestCase; +use PHPUnit\Attributes\Test; + /** - * @class * Test properties of our codebase rather than the actual code. */ -class PsrComplianceTest extends \PHPUnit_Framework_TestCase +class PsrComplianceTest extends TestCase { - /** - * @test - */ + #[Test] public function testPsrCompliance() { // If we can't find the command-line tool, we mark the test as skipped