Verified Commit ab3d896f authored by Elias Häußler's avatar Elias Häußler 🐛
Browse files

[TASK] Include PHPStan and fix issues reported by PHPStan

parent 37ae91a1
Pipeline #774 passed with stages
in 2 minutes and 20 seconds
......@@ -17,6 +17,7 @@ workflow:
stages:
- lint
- sca
- test
lint:php:
......@@ -30,6 +31,21 @@ lint:php:
when: never
- when: on_success
sca:php:
stage: sca
before_script:
- composer install --no-progress
- mkdir -p .build
script:
- composer sca -- --error-format gitlab > .build/phpstan.json
artifacts:
reports:
codequality: .build/phpstan.json
rules:
- if: '$CI_PIPELINE_SOURCE == "pipeline"'
when: never
- when: on_success
test:
image: webdevops/php:${PHP_VERSION}
stage: test
......
......@@ -33,6 +33,7 @@
"composer/composer": "^1.0 || ^2.0",
"friendsofphp/php-cs-fixer": "^2.17",
"php-http/mock-client": "^1.4",
"phpstan/phpstan": "^0.12.80",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
"rpkamp/mailhog-client": "^0.3 || ^0.4 || ^0.5"
},
......@@ -51,6 +52,7 @@
},
"scripts": {
"lint": "php-cs-fixer fix",
"sca": "phpstan analyse -c phpstan.neon",
"test": [
"@test:docker:start",
"@test:run",
......
parameters:
level: 6
paths:
- src
- tests
excludePaths:
- tests/Build/*
......@@ -48,16 +48,19 @@ class Plugin implements PluginInterface, EventSubscriberInterface
$this->reporter = new Reporter($composer);
}
public function deactivate(Composer $composer, IOInterface $io)
public function deactivate(Composer $composer, IOInterface $io): void
{
// Nothing to do here. Just go ahead :)
}
public function uninstall(Composer $composer, IOInterface $io)
public function uninstall(Composer $composer, IOInterface $io): void
{
// Nothing to do here. Just go ahead :)
}
/**
* @return array<string, mixed>
*/
public static function getSubscribedEvents(): array
{
return [
......
......@@ -66,7 +66,7 @@ class Reporter
private $registeredServices;
/**
* @var array
* @var array<string, mixed>
*/
private $configuration;
......@@ -96,6 +96,7 @@ class Reporter
/** @var ServiceInterface $registeredService */
foreach ($this->registeredServices as $registeredService) {
if (!in_array(ServiceInterface::class, class_implements($registeredService), true)) {
/* @phpstan-ignore-next-line */
throw new \InvalidArgumentException(sprintf('Service "%s" must implement "%s".', $registeredService, ServiceInterface::class), 1600814017);
}
if ($registeredService::isEnabled($this->configuration)) {
......@@ -119,6 +120,9 @@ class Reporter
$this->options = $options;
}
/**
* @param string[] $registeredServices
*/
public function setRegisteredServices(array $registeredServices): self
{
$this->registeredServices = $registeredServices;
......@@ -126,6 +130,9 @@ class Reporter
return $this;
}
/**
* @return string[]
*/
private function getDefaultServices(): array
{
return [
......@@ -146,6 +153,9 @@ class Reporter
);
}
/**
* @return array<string, mixed>
*/
private function resolveConfiguration(): array
{
return $this->composer->getPackage()->getExtra()['update-check'] ?? [];
......
......@@ -50,6 +50,9 @@ abstract class AbstractService implements ServiceInterface
*/
protected $options;
/**
* @param array<string, mixed> $configuration
*/
public static function isEnabled(array $configuration): bool
{
$identifier = static::getIdentifier();
......
......@@ -45,7 +45,7 @@ class Email extends AbstractService
private $transport;
/**
* @var array
* @var string[]
*/
private $receivers;
......@@ -54,6 +54,9 @@ class Email extends AbstractService
*/
private $sender;
/**
* @param string[] $receivers
*/
public function __construct(string $dsn, array $receivers, string $sender)
{
$this->transport = Transport::fromDsn($dsn);
......@@ -201,6 +204,9 @@ class Email extends AbstractService
return $this->transport;
}
/**
* @return string[]
*/
public function getReceivers(): array
{
return $this->receivers;
......
......@@ -125,6 +125,8 @@ class GitLab extends AbstractService
/**
* @param OutdatedPackage[] $outdatedPackages
*
* @return array<string, string>
*/
private function getPackagesPayload(array $outdatedPackages): array
{
......
......@@ -36,10 +36,13 @@ use EliasHaeussler\ComposerUpdateCheck\Package\UpdateCheckResult;
interface ServiceInterface
{
/**
* @return static
* @param array<string, mixed> $configuration
*/
public static function fromConfiguration(array $configuration): self;
/**
* @param array<string, mixed> $configuration
*/
public static function isEnabled(array $configuration): bool;
public function report(UpdateCheckResult $result): bool;
......
......@@ -108,6 +108,8 @@ class Slack extends AbstractService
/**
* @param OutdatedPackage[] $outdatedPackages
*
* @return array[]
*/
private function renderBlocks(array $outdatedPackages): array
{
......
......@@ -112,6 +112,8 @@ class Teams extends AbstractService
/**
* @param OutdatedPackage[] $outdatedPackages
*
* @return array[]
*/
private function renderSections(array $outdatedPackages): array
{
......
......@@ -48,6 +48,9 @@ trait RemoteServiceTrait
private $client;
/**
* @param mixed[] $payload
* @param array<string, mixed> $headers
*
* @throws ClientExceptionInterface
*/
protected function sendRequest(array $payload, array $headers = []): ResponseInterface
......
......@@ -31,6 +31,12 @@ namespace EliasHaeussler\ComposerUpdateReporter;
*/
class Util
{
/**
* @param array<string, mixed> $array1
* @param array<string, mixed> $array2
*
* @return array<string, mixed>
*/
public static function arrayDiffRecursive(array $array1, array $array2): array
{
$difference = [];
......
......@@ -51,12 +51,18 @@ trait ClientMockTrait
return new Psr18Client(new MockHttpClient($callback));
}
/**
* @param array<string, mixed> $expectedPayloadSubset
*/
protected function assertPayloadOfLastRequestContainsSubset(array $expectedPayloadSubset): void
{
$payload = $this->getPayloadOfLastRequest();
self::assertSame([], Util::arrayDiffRecursive($expectedPayloadSubset, $payload));
}
/**
* @return array<string, mixed>
*/
protected function getPayloadOfLastRequest(): array
{
$lastResponse = $this->mockedResponse;
......
......@@ -37,10 +37,29 @@ use EliasHaeussler\ComposerUpdateReporter\Service\ServiceInterface;
*/
class DummyService extends AbstractService
{
/**
* @var bool|null
*/
public static $enabled;
/**
* @var bool
*/
public static $successful = true;
/**
* @var bool
*/
public static $reportWasExecuted = false;
/**
* @var OutputBehavior|null
*/
public static $customBehavior;
/**
* @var Options|null
*/
public static $customOptions;
public static function fromConfiguration(array $configuration): ServiceInterface
......@@ -92,6 +111,7 @@ class DummyService extends AbstractService
public function unsetBehavior(): void
{
/* @phpstan-ignore-next-line */
$this->behavior = null;
}
......
......@@ -57,9 +57,9 @@ class AbstractServiceTest extends AbstractTestCase
* @test
* @dataProvider isEnabledReturnsStateOfAvailabilityDataProvider
*
* @param $environmentVariable
* @param array<string, mixed> $configuration
*/
public function isEnabledReturnsStateOfAvailability(array $configuration, $environmentVariable, bool $expected): void
public function isEnabledReturnsStateOfAvailability(array $configuration, ?string $environmentVariable, bool $expected): void
{
$this->modifyEnvironmentVariable('DUMMY_ENABLE', $environmentVariable);
......@@ -118,6 +118,9 @@ class AbstractServiceTest extends AbstractTestCase
static::assertStringContainsString('Dummy report was successful', $this->getIO()->getOutput());
}
/**
* @return array<string, array>
*/
public function isEnabledReturnsStateOfAvailabilityDataProvider(): array
{
return [
......
......@@ -33,6 +33,7 @@ use rpkamp\Mailhog\MailhogClient;
use rpkamp\Mailhog\Message\Contact;
use Symfony\Component\HttpClient\HttplugClient;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
/**
* EmailTest.
......@@ -45,10 +46,29 @@ class EmailTest extends AbstractTestCase
use OutputBehaviorTrait;
use TestEnvironmentTrait;
/**
* @var string
*/
protected static $mailhogHost;
/**
* @var int
*/
protected static $mailhogSmtpPort;
/**
* @var int
*/
protected static $mailhogApiPort;
/**
* @var string
*/
protected static $mailhogSmtp;
/**
* @var string
*/
protected static $mailhogApi;
/**
......@@ -124,6 +144,8 @@ class EmailTest extends AbstractTestCase
/**
* @test
* @dataProvider fromConfigurationThrowsExceptionIfEmailDsnIsNotSetDataProvider
*
* @param array<string, mixed> $configuration
*/
public function fromConfigurationThrowsExceptionIfEmailDsnIsNotSet(array $configuration): void
{
......@@ -190,18 +212,17 @@ class EmailTest extends AbstractTestCase
];
/** @var Email $subject */
$subject = Email::fromConfiguration($configuration);
/** @var EsmtpTransport $transport */
$transport = $subject->getTransport();
/** @var SocketStream $stream */
$stream = $transport->getStream();
static::assertInstanceOf(Email::class, $subject);
static::assertInstanceOf(EsmtpTransport::class, $transport);
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
static::assertSame('', $transport->getUsername());
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
static::assertSame('', $transport->getPassword());
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
static::assertSame(static::$mailhogHost, $transport->getStream()->getHost());
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
static::assertSame(static::$mailhogSmtpPort, $transport->getStream()->getPort());
static::assertSame(static::$mailhogHost, $stream->getHost());
static::assertSame(static::$mailhogSmtpPort, $stream->getPort());
static::assertSame(['foo@foo.com', 'foo@another-foo.com'], $subject->getReceivers());
static::assertSame('baz@baz.com', $subject->getSender());
}
......@@ -217,18 +238,17 @@ class EmailTest extends AbstractTestCase
/** @var Email $subject */
$subject = Email::fromConfiguration([]);
/** @var EsmtpTransport $transport */
$transport = $subject->getTransport();
/** @var SocketStream $stream */
$stream = $transport->getStream();
static::assertInstanceOf(Email::class, $subject);
static::assertInstanceOf(EsmtpTransport::class, $transport);
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
static::assertSame('', $transport->getUsername());
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
static::assertSame('', $transport->getPassword());
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
static::assertSame(static::$mailhogHost, $transport->getStream()->getHost());
/** @noinspection PhpPossiblePolymorphicInvocationInspection */
static::assertSame(static::$mailhogSmtpPort, $transport->getStream()->getPort());
static::assertSame(static::$mailhogHost, $stream->getHost());
static::assertSame(static::$mailhogSmtpPort, $stream->getPort());
static::assertSame(['foo@foo.com', 'foo@another-foo.com'], $subject->getReceivers());
static::assertSame('baz@baz.com', $subject->getSender());
}
......@@ -270,6 +290,9 @@ class EmailTest extends AbstractTestCase
static::assertSame($expected, $message->body);
}
/**
* @return array<string, array>
*/
public function fromConfigurationThrowsExceptionIfEmailDsnIsNotSetDataProvider(): array
{
return [
......@@ -292,6 +315,9 @@ class EmailTest extends AbstractTestCase
];
}
/**
* @return array<string, array>
*/
public function reportSendsUpdateReportSuccessfullyDataProvider(): array
{
return [
......
......@@ -92,6 +92,8 @@ class GitLabTest extends AbstractTestCase
/**
* @test
* @dataProvider fromConfigurationThrowsExceptionIfGitLabUrlIsNotSetDataProvider
*
* @param array<string, mixed> $configuration
*/
public function fromConfigurationThrowsExceptionIfGitLabUrlIsNotSet(array $configuration): void
{
......@@ -182,6 +184,9 @@ class GitLabTest extends AbstractTestCase
$this->assertPayloadOfLastRequestContainsSubset($expectedPayloadSubset);
}
/**
* @return array<string, array>
*/
public function fromConfigurationThrowsExceptionIfGitLabUrlIsNotSetDataProvider(): array
{
return [
......@@ -203,6 +208,9 @@ class GitLabTest extends AbstractTestCase
];
}
/**
* @return array<string, array>
*/
public function reportSendsUpdateReportSuccessfullyDataProvider(): array
{
return [
......
......@@ -92,6 +92,8 @@ class MattermostTest extends AbstractTestCase
/**
* @test
* @dataProvider fromConfigurationThrowsExceptionIfMattermostUrlIsNotSetDataProvider
*
* @param array<string, mixed> $configuration
*/
public function fromConfigurationThrowsExceptionIfMattermostUrlIsNotSet(array $configuration): void
{
......@@ -197,6 +199,9 @@ class MattermostTest extends AbstractTestCase
static::assertStringContainsString($expected, $text);
}
/**
* @return array<string, array>
*/
public function fromConfigurationThrowsExceptionIfMattermostUrlIsNotSetDataProvider(): array
{
return [
......@@ -218,6 +223,9 @@ class MattermostTest extends AbstractTestCase
];
}
/**
* @return array<string, array>
*/
public function reportSendsUpdateReportSuccessfullyDataProvider(): array
{
return [
......
......@@ -81,6 +81,8 @@ class SlackTest extends AbstractTestCase
/**
* @test
* @dataProvider fromConfigurationThrowsExceptionIfSlackUrlIsNotSetDataProvider
*
* @param array<string, mixed> $configuration
*/
public function fromConfigurationThrowsExceptionIfSlackUrlIsNotSet(array $configuration): void
{
......@@ -249,6 +251,9 @@ class SlackTest extends AbstractTestCase
$this->assertPayloadOfLastRequestContainsSubset($expectedPayloadSubset);
}
/**
* @return array<string, array>
*/
public function fromConfigurationThrowsExceptionIfSlackUrlIsNotSetDataProvider(): array
{
return [
......@@ -270,6 +275,9 @@ class SlackTest extends AbstractTestCase
];
}
/**
* @return array<string, array>
*/
public function reportSendsUpdateReportSuccessfullyDataProvider(): array
{
return [
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment