diff --git a/composer.json b/composer.json index 2f29b45..9a6a3b9 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "phpunit/phpunit": "^8.3 || ^9.3", "orchestra/testbench": ">=4.0", "squizlabs/php_codesniffer": "^3.5", - "pestphp/pest": "^1.0" + "pestphp/pest": "^1.0", + "phpstan/phpstan": "^1.10" }, "autoload": { "psr-4": { @@ -33,6 +34,7 @@ }, "scripts": { "test": "./vendor/bin/pest", + "phpstan": "./vendor/bin/phpstan", "coverage": "XDEBUG_MODE=coverage ./vendor/bin/pest --coverage", "phpsniff": "vendor/bin/phpcs --standard=\"PSR12\" ./src", "phpfix": "vendor/bin/phpcbf --standard=\"PSR12\" ./src" diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..bc92f87 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + level: 8 + paths: + - src + checkGenericClassInNonGenericObjectType: false diff --git a/src/Api/AbstractResource.php b/src/Api/AbstractResource.php index 079a3f3..afd31b1 100644 --- a/src/Api/AbstractResource.php +++ b/src/Api/AbstractResource.php @@ -2,10 +2,8 @@ namespace Olssonm\Swish\Api; -use GuzzleHttp\Client; -use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\ClientInterface; use GuzzleHttp\Psr7\Request as Psr7Request; -use GuzzleHttp\Psr7\Response; use Olssonm\Swish\Exceptions\ClientException; use Olssonm\Swish\Exceptions\ServerException; use Olssonm\Swish\Exceptions\ValidationException; @@ -14,40 +12,53 @@ abstract class AbstractResource { - protected Client $client; + protected ClientInterface $client; - public function __construct(Client $client) + public function __construct(ClientInterface $client) { $this->client = $client; } /** * Retrieve resource + * + * @param $transaction + * @return mixed */ - abstract public function get($transaction); + abstract public function get($transaction); // @phpstan-ignore-line /** * Create resource + * + * @param $transaction + * @return mixed */ - abstract public function create($transaction); + abstract public function create($transaction); // @phpstan-ignore-line /** * Cancel transaction + * + * @param $transaction + * @return mixed */ - abstract public function cancel($transaction); + abstract public function cancel($transaction); // @phpstan-ignore-line /** * Main API caller * * @param string $verb * @param string $uri - * @param array $headers + * @param array $headers * @param string|null $payload - * @return Response + * @return ResponseInterface * @throws ClientException|ServerException|ValidationException */ - protected function request(string $verb, string $uri, array $headers = [], $payload = null): Response - { + protected function request( + string $verb, + string $uri, + array $headers = [], + string|null $payload = null + ): ResponseInterface { $request = new Psr7Request( $verb, $uri, @@ -118,6 +129,9 @@ protected function triggerException( $response->getReasonPhrase() ); + /** + * @var \Exception + */ throw new $class( $message, $request, diff --git a/src/Api/Payments.php b/src/Api/Payments.php index b224288..f44bc40 100644 --- a/src/Api/Payments.php +++ b/src/Api/Payments.php @@ -32,12 +32,20 @@ public function get($payment): Payment */ public function create($payment): PaymentResult { - $response = $this->request('PUT', sprintf('v2/paymentrequests/%s', $payment->id), [], json_encode($payment)); + $response = $this->request( + 'PUT', + sprintf('v2/paymentrequests/%s', $payment->id), + [], + (string) json_encode($payment) + ); + + $location = $response->getHeaderLine('Location'); + $token = $response->getHeaderLine('PaymentRequestToken'); return new PaymentResult([ 'id' => Id::parse($response), - 'location' => $response->getHeaderLine('Location') ?? null, - 'paymentRequestToken' => $response->getHeaderLine('PaymentRequestToken') ?? null, + 'location' => strlen($location) > 0 ? $location : null, + 'paymentRequestToken' => strlen($token) > 0 ? $token : null, ]); } @@ -51,7 +59,7 @@ public function cancel($payment): Payment { $response = $this->request('PATCH', sprintf('v1/paymentrequests/%s', $payment->id), [ 'Content-Type' => 'application/json-patch+json', - ], json_encode([[ + ], (string) json_encode([[ 'op' => 'replace', 'path' => '/status', 'value' => 'cancelled', diff --git a/src/Api/Refunds.php b/src/Api/Refunds.php index cb0d586..962d3b5 100644 --- a/src/Api/Refunds.php +++ b/src/Api/Refunds.php @@ -32,11 +32,13 @@ public function get($refund): Refund */ public function create($refund): RefundResult { - $response = $this->request('PUT', sprintf('v2/refunds/%s', $refund->id), [], json_encode($refund)); + $response = $this->request('PUT', sprintf('v2/refunds/%s', $refund->id), [], (string) json_encode($refund)); + + $location = $response->getHeaderLine('Location'); return new RefundResult([ 'id' => Id::parse($response), - 'location' => $response->getHeaderLine('Location') ?? null, + 'location' => strlen($location) > 0 ? $location : null, ]); } @@ -44,7 +46,7 @@ public function create($refund): RefundResult * Cancel a refund. * * @param Refund $transaction - * @throws BadMethodCallException + * @throws \BadMethodCallException */ public function cancel($transaction): void { diff --git a/src/Callback.php b/src/Callback.php index af815f8..37d1e95 100644 --- a/src/Callback.php +++ b/src/Callback.php @@ -16,7 +16,7 @@ class Callback public static function parse($content = null) { if (is_null($content)) { - $content = file_get_contents('php://input'); + $content = (string) file_get_contents('php://input'); } try { diff --git a/src/Certificate.php b/src/Certificate.php index 2ed9d9d..6c11d26 100644 --- a/src/Certificate.php +++ b/src/Certificate.php @@ -4,11 +4,11 @@ class Certificate { - private $client; + private ?string $client; - private $passphrase; + private ?string $passphrase; - private $root; + private bool|string $root; /** * Certificate constructor @@ -24,7 +24,10 @@ public function __construct(?string $clientPath, ?string $passphrase = null, boo $this->root = $rootPath; } - public function getClientCertificate(): ?array + /** + * @return array + */ + public function getClientCertificate(): array { return [ $this->client, diff --git a/src/Client.php b/src/Client.php index 0716ef7..930040b 100644 --- a/src/Client.php +++ b/src/Client.php @@ -19,13 +19,16 @@ class Client { protected string $endpoint; + /** + * @var array + */ protected array $history = []; public const PRODUCTION_ENDPOINT = 'https://cpc.getswish.net/swish-cpcapi/api/'; public const TEST_ENDPOINT = 'https://mss.cpc.getswish.net/swish-cpcapi/api/'; - protected GuzzleHttpClient $client; + protected ClientInterface $client; public function __construct( Certificate $certificate = null, @@ -53,8 +56,8 @@ public function setup( CURLOPT_TIMEOUT => 0, CURLOPT_CONNECTTIMEOUT => 20, ], - 'verify' => $certificate->getRootCertificate(), - 'cert' => $certificate->getClientCertificate(), + 'verify' => $certificate?->getRootCertificate(), + 'cert' => $certificate?->getClientCertificate(), 'base_uri' => $endpoint, 'http_errors' => false, ]); @@ -63,14 +66,17 @@ public function setup( /** * Return the clients call-history * - * @return array + * @return array */ - public function getHistory() + public function getHistory(): array { return $this->history; } - public function __call($method, $args) + /** + * @param array $args + */ + public function __call(string $method, array $args): mixed { if ( !is_object($args[0]) || @@ -89,6 +95,7 @@ public function __call($method, $args) break; } + // @phpstan-ignore-next-line return call_user_func_array([$class, $method], $args); } } diff --git a/src/Exceptions/InvalidUuidException.php b/src/Exceptions/InvalidUuidException.php index 228c217..97e6c33 100644 --- a/src/Exceptions/InvalidUuidException.php +++ b/src/Exceptions/InvalidUuidException.php @@ -4,7 +4,7 @@ class InvalidUuidException extends \InvalidArgumentException { - public function __construct($message = null, $code = 0) + public function __construct(string $message = null, int $code = 0) { if (!$message) { throw new $this('Invalid UUID ' . get_class($this), $code); diff --git a/src/Exceptions/ValidationException.php b/src/Exceptions/ValidationException.php index 58c0589..a36903b 100644 --- a/src/Exceptions/ValidationException.php +++ b/src/Exceptions/ValidationException.php @@ -9,8 +9,16 @@ class ValidationException extends RequestException { + /** + * @var array + */ private array $errors = []; + /** + * Undocumented function + * + * @param array $handlerContext + */ public function __construct( string $message, RequestInterface $request, @@ -19,7 +27,7 @@ public function __construct( array $handlerContext = [] ) { - $data = json_decode((string) $response->getBody()->getContents()); + $data = json_decode((string) $response?->getBody()->getContents()); if (is_array($data)) { foreach ($data as $error) { @@ -36,9 +44,11 @@ public function __construct( parent::__construct($message, $request, $response, $previous, $handlerContext); } + /** + * @return array + */ public function getErrors(): array { - return $this->errors; } } diff --git a/src/Payment.php b/src/Payment.php index eaa9a26..487867f 100644 --- a/src/Payment.php +++ b/src/Payment.php @@ -23,7 +23,10 @@ */ class Payment extends Resource { - public function __construct($attributes = []) + /** + * @param array $attributes + */ + public function __construct(array $attributes = []) { parent::__construct($attributes); $this->id = $this->id ?? Uuid::make(); diff --git a/src/Providers/SwishServiceProvider.php b/src/Providers/SwishServiceProvider.php index 0d28a91..284c4bb 100644 --- a/src/Providers/SwishServiceProvider.php +++ b/src/Providers/SwishServiceProvider.php @@ -61,7 +61,7 @@ public function register(): void /** * Get the services provided by the provider. * - * @return array + * @return array * @codeCoverageIgnore */ public function provides() diff --git a/src/Refund.php b/src/Refund.php index f24cf35..65737a1 100644 --- a/src/Refund.php +++ b/src/Refund.php @@ -5,24 +5,27 @@ use Olssonm\Swish\Util\Uuid; /** - * @property string id - * @property string originalPaymentReference - * @property string payerPaymentReference - * @property string callbackUrl - * @property string payerAlias - * @property string payeeAlias - * @property string amount - * @property string currency - * @property string message - * @property string status - * @property string dateCreated - * @property string datePaid - * @property string errorCode - * @property string errorMessage - * @property string additionalInformation + * @property string $id + * @property string $originalPaymentReference + * @property string $payerPaymentReference + * @property string $callbackUrl + * @property string $payerAlias + * @property string $payeeAlias + * @property string $amount + * @property string $currency + * @property string $message + * @property string $status + * @property string $dateCreated + * @property string $datePaid + * @property string $errorCode + * @property string $errorMessage + * @property string $additionalInformation */ class Refund extends Resource { + /** + * @param array $attributes + */ public function __construct(array $attributes = []) { parent::__construct($attributes); diff --git a/src/Resource.php b/src/Resource.php index 73fa883..84e22f6 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -7,14 +7,20 @@ class Resource implements \ArrayAccess, \Countable, \JsonSerializable { + /** + * @var array + */ protected array $attributes; + /** + * @param array $attributes + */ public function __construct(array $attributes = []) { $this->attributes = $attributes; } - public function __set($key, $value) + public function __set(string $key, mixed $value) { if (in_array($key, ['id', 'instructionUUID'])) { if (!Uuid::validate($value)) { @@ -25,53 +31,59 @@ public function __set($key, $value) $this->attributes[$key] = $value; } - public function __get($key) + public function __get(string $key): mixed { return $this->attributes[$key]; } - public function __isset($key) + public function __isset(string $key): bool { return isset($this->attributes[$key]); } #[\ReturnTypeWillChange] - public function offsetSet($key, $value) + public function offsetSet($key, $value): void { $this->{$key} = $value; } #[\ReturnTypeWillChange] - public function offsetExists($key) + public function offsetExists($key): bool { return \array_key_exists($key, $this->attributes); } #[\ReturnTypeWillChange] - public function offsetUnset($key) + public function offsetUnset($key): void { unset($this->attributes[$key]); } #[\ReturnTypeWillChange] - public function offsetGet($key) + public function offsetGet($key): mixed { return \array_key_exists($key, $this->attributes) ? $this->attributes[$key] : null; } #[\ReturnTypeWillChange] - public function count() + public function count(): int { return \count($this->attributes); } + /** + * @return array + */ #[\ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): array { return $this->toArray(); } - public function toArray() + /** + * @return array + */ + public function toArray(): array { return $this->attributes; } diff --git a/src/Util/Id.php b/src/Util/Id.php index e9e11db..b66f0ca 100644 --- a/src/Util/Id.php +++ b/src/Util/Id.php @@ -2,18 +2,19 @@ namespace Olssonm\Swish\Util; -use GuzzleHttp\Psr7\Response; +use Psr\Http\Message\ResponseInterface; class Id { /** * Parse the ID from the response's Location-header. * - * @param Response $response + * @param ResponseInterface $response * @return null|string */ - public static function parse(Response $response): string + public static function parse(ResponseInterface $response): ?string { - return pathinfo(parse_url($response->getHeaderLine('Location'), PHP_URL_PATH), PATHINFO_BASENAME); + $url = parse_url($response->getHeaderLine('Location'), PHP_URL_PATH); + return is_string($url) ? pathinfo($url, PATHINFO_BASENAME) : null; } }