diff --git a/.github/workflows/linting.yaml b/.github/workflows/linting.yaml index 0edc80b9..88befc4b 100644 --- a/.github/workflows/linting.yaml +++ b/.github/workflows/linting.yaml @@ -23,6 +23,6 @@ jobs: - run: composer install --no-progress - run: composer validate --no-check-all --strict - run: vendor/bin/php-cs-fixer fix --dry-run --verbose --config=.php_cs.dist --using-cache=no --show-progress=none --format=checkstyle | cs2pr - - run: vendor/bin/phpstan analyse src -c phpstan.neon --level=3 --no-progress --error-format=checkstyle | cs2pr + - run: vendor/bin/phpstan analyse src -c phpstan.neon --level=4 --no-progress --error-format=checkstyle | cs2pr - run: vendor/bin/phpstan analyse tests -c tests/phpstan.neon --level=4 --no-progress --error-format=checkstyle | cs2pr - run: composer kimai:code-lint diff --git a/composer.json b/composer.json index c5e6f806..675a9fbd 100644 --- a/composer.json +++ b/composer.json @@ -158,7 +158,7 @@ "kimai:tests-unit": "vendor/bin/phpunit --exclude-group integration tests/", "kimai:tests-integration": "vendor/bin/phpunit --group integration tests/", "kimai:phpstan": [ - "vendor/bin/phpstan analyse src -c phpstan.neon --level=3", + "vendor/bin/phpstan analyse src -c phpstan.neon --level=4", "vendor/bin/phpstan analyse tests -c tests/phpstan.neon --level=4" ], "kimai:codestyle": "vendor/bin/php-cs-fixer fix --dry-run --verbose --show-progress=none", diff --git a/phpstan.neon b/phpstan.neon index fa74bb35..1a2d48d2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,8 +7,8 @@ parameters: tmpDir: %rootDir%/../../../var/cache/phpstan autoload_directories: - %rootDir%/../../../src/Migrations -# symfony: -# container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml' + symfony: + container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml' ignoreErrors: - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::scalarNode\(\).#' - '#Call to an undefined method Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface::integerNode\(\).#' @@ -22,3 +22,5 @@ parameters: - '#Method Symfony\\Contracts\\EventDispatcher\\EventDispatcherInterface::dispatch\(\) invoked with 2 parameters, 1 required.#' excludes_analyse: - %rootDir%/../../../src/Ldap/LdapDriver.php + treatPhpDocTypesAsCertain: false + inferPrivatePropertyTypeFromConstructor: true \ No newline at end of file diff --git a/src/API/TeamController.php b/src/API/TeamController.php index 31aca915..df1531d2 100644 --- a/src/API/TeamController.php +++ b/src/API/TeamController.php @@ -293,7 +293,7 @@ final class TeamController extends BaseApiController throw new NotFoundException('Team not found'); } - /** @var User $user */ + /** @var User|null $user */ $user = $repository->find($userId); if (null === $user) { @@ -356,7 +356,7 @@ final class TeamController extends BaseApiController throw new NotFoundException('Team not found'); } - /** @var User $user */ + /** @var User|null $user */ $user = $repository->find($userId); if (null === $user) { @@ -419,7 +419,7 @@ final class TeamController extends BaseApiController throw new NotFoundException('Team not found'); } - /** @var Customer $customer */ + /** @var Customer|null $customer */ $customer = $repository->find($customerId); if (null === $customer) { @@ -482,7 +482,7 @@ final class TeamController extends BaseApiController throw new NotFoundException('Team not found'); } - /** @var Customer $customer */ + /** @var Customer|null $customer */ $customer = $repository->find($customerId); if (null === $customer) { @@ -541,7 +541,7 @@ final class TeamController extends BaseApiController throw new NotFoundException('Team not found'); } - /** @var Project $project */ + /** @var Project|null $project */ $project = $repository->find($projectId); if (null === $project) { @@ -604,7 +604,7 @@ final class TeamController extends BaseApiController throw new NotFoundException('Team not found'); } - /** @var Project $project */ + /** @var Project|null $project */ $project = $repository->find($projectId); if (null === $project) { diff --git a/src/Command/CreateReleaseCommand.php b/src/Command/CreateReleaseCommand.php index 058af55c..e5b584f0 100644 --- a/src/Command/CreateReleaseCommand.php +++ b/src/Command/CreateReleaseCommand.php @@ -103,13 +103,7 @@ class CreateReleaseCommand extends Command $io->success('Prepare new packages for Kimai ' . $version . ' in ' . $tmpDir); $gitCmd = sprintf(self::CLONE_CMD, $version); - $zip = 'kimai-release-' . $version; - - if ($version === Constants::VERSION && Constants::STATUS !== 'stable') { - $zip .= '_' . Constants::STATUS; - } - - $zip .= '.zip'; + $zip = 'kimai-release-' . $version . '.zip'; $prefix = 'APP_ENV=prod DATABASE_URL=sqlite:///%kernel.project_dir%/var/data/kimai.sqlite'; diff --git a/src/DependencyInjection/Compiler/ExportServiceCompilerPass.php b/src/DependencyInjection/Compiler/ExportServiceCompilerPass.php index b1aa125d..d859fbe6 100644 --- a/src/DependencyInjection/Compiler/ExportServiceCompilerPass.php +++ b/src/DependencyInjection/Compiler/ExportServiceCompilerPass.php @@ -26,11 +26,6 @@ class ExportServiceCompilerPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - // always first check if the primary service is defined - if (!$container->has(ServiceExport::class)) { - return; - } - $definition = $container->findDefinition(ServiceExport::class); $taggedRenderer = $container->findTaggedServiceIds(Kernel::TAG_EXPORT_RENDERER); diff --git a/src/DependencyInjection/Compiler/InvoiceServiceCompilerPass.php b/src/DependencyInjection/Compiler/InvoiceServiceCompilerPass.php index 62f9672b..36c99df5 100644 --- a/src/DependencyInjection/Compiler/InvoiceServiceCompilerPass.php +++ b/src/DependencyInjection/Compiler/InvoiceServiceCompilerPass.php @@ -26,11 +26,6 @@ class InvoiceServiceCompilerPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - // always first check if the primary service is defined - if (!$container->has(ServiceInvoice::class)) { - return; - } - $definition = $container->findDefinition(ServiceInvoice::class); $taggedRenderer = $container->findTaggedServiceIds(Kernel::TAG_INVOICE_RENDERER); diff --git a/src/DependencyInjection/Compiler/WidgetCompilerPass.php b/src/DependencyInjection/Compiler/WidgetCompilerPass.php index 54eba9fd..796c914b 100644 --- a/src/DependencyInjection/Compiler/WidgetCompilerPass.php +++ b/src/DependencyInjection/Compiler/WidgetCompilerPass.php @@ -26,11 +26,6 @@ class WidgetCompilerPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - // always first check if the primary service is defined - if (!$container->has(WidgetRepository::class)) { - return; - } - $definition = $container->findDefinition(WidgetRepository::class); $taggedRenderer = $container->findTaggedServiceIds(Kernel::TAG_WIDGET); diff --git a/src/EventSubscriber/TimezoneSubscriber.php b/src/EventSubscriber/TimezoneSubscriber.php index 2e6395bc..6bb01e7d 100644 --- a/src/EventSubscriber/TimezoneSubscriber.php +++ b/src/EventSubscriber/TimezoneSubscriber.php @@ -42,10 +42,6 @@ class TimezoneSubscriber implements EventSubscriberInterface $user = $this->storage->getToken()->getUser(); - if (null === $user) { - return; - } - if ($user instanceof User) { date_default_timezone_set($user->getTimezone()); } diff --git a/src/EventSubscriber/UserPreferenceSubscriber.php b/src/EventSubscriber/UserPreferenceSubscriber.php index 320a4be6..ba51a19c 100644 --- a/src/EventSubscriber/UserPreferenceSubscriber.php +++ b/src/EventSubscriber/UserPreferenceSubscriber.php @@ -67,7 +67,7 @@ class UserPreferenceSubscriber implements EventSubscriberInterface return $this->formConfig->getUserDefaultTheme(); } - private function getDefaultCurrency(): ?string + private function getDefaultCurrency(): string { return $this->formConfig->getUserDefaultCurrency(); } diff --git a/src/Form/FormTrait.php b/src/Form/FormTrait.php index fa423874..21f4b4af 100644 --- a/src/Form/FormTrait.php +++ b/src/Form/FormTrait.php @@ -123,10 +123,6 @@ trait FormTrait $event->getForm()->add('activity', ActivityType::class, [ 'placeholder' => '', 'query_builder' => function (ActivityRepository $repo) use ($data, $activity) { - if (!empty($activity) && is_string($activity)) { - $activity = $repo->find($activity); - } - return $repo->getQueryBuilderForFormType(new ActivityFormTypeQuery($activity, $data['project'])); }, ]); diff --git a/src/Invoice/InvoiceFormatter.php b/src/Invoice/InvoiceFormatter.php index 38c81139..a8ae7b90 100644 --- a/src/Invoice/InvoiceFormatter.php +++ b/src/Invoice/InvoiceFormatter.php @@ -27,7 +27,7 @@ interface InvoiceFormatter public function getFormattedTime(\DateTime $date); /** - * @param int $amount + * @param int|float $amount * @param string|null $currency * @return mixed */ diff --git a/src/Ldap/LdapUserHydrator.php b/src/Ldap/LdapUserHydrator.php index 2b002ee1..1c5e1b85 100644 --- a/src/Ldap/LdapUserHydrator.php +++ b/src/Ldap/LdapUserHydrator.php @@ -63,11 +63,14 @@ class LdapUserHydrator $this->hydrateUserWithAttributesMap($user, $ldapEntry, $attributeMap); - if (is_array($user->getEmail())) { - $user->setEmail($user->getEmail()[0]); + /** @var string|array|null $email */ + $email = $user->getEmail(); + + if (is_array($email)) { + $user->setEmail($email[0]); } - if (null === $user->getEmail()) { + if (null === $email) { $user->setEmail($user->getUsername()); } diff --git a/src/Repository/Query/TimesheetQuery.php b/src/Repository/Query/TimesheetQuery.php index c84b4e4a..a3176e4a 100644 --- a/src/Repository/Query/TimesheetQuery.php +++ b/src/Repository/Query/TimesheetQuery.php @@ -93,7 +93,7 @@ class TimesheetQuery extends ActivityQuery /** * Limit the data exclusively to the user (eg. users own timesheets). * - * @return User|null + * @return User|int|null */ public function getUser() { diff --git a/src/Saml/Provider/SamlProvider.php b/src/Saml/Provider/SamlProvider.php index 4059e4cd..50355556 100644 --- a/src/Saml/Provider/SamlProvider.php +++ b/src/Saml/Provider/SamlProvider.php @@ -17,7 +17,6 @@ use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProvid use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; -use Symfony\Component\Security\Core\User\ChainUserProvider; use Symfony\Component\Security\Core\User\UserProviderInterface; final class SamlProvider implements AuthenticationProviderInterface @@ -51,9 +50,6 @@ final class SamlProvider implements AuthenticationProviderInterface { $user = null; - /** @var ChainUserProvider $p */ - $p = $this->userProvider; - try { $user = $this->userProvider->loadUserByUsername($token->getUsername()); } catch (UsernameNotFoundException $e) { @@ -73,14 +69,10 @@ final class SamlProvider implements AuthenticationProviderInterface ); } - if ($user) { - $authenticatedToken = $this->tokenFactory->createToken($user, $token->getAttributes(), $user->getRoles()); - $authenticatedToken->setAuthenticated(true); + $authenticatedToken = $this->tokenFactory->createToken($user, $token->getAttributes(), $user->getRoles()); + $authenticatedToken->setAuthenticated(true); - return $authenticatedToken; - } - - throw new AuthenticationException('The authentication failed.'); + return $authenticatedToken; } public function supports(TokenInterface $token) diff --git a/src/Security/DoctrineUserProvider.php b/src/Security/DoctrineUserProvider.php index 852cc468..63a187d8 100644 --- a/src/Security/DoctrineUserProvider.php +++ b/src/Security/DoctrineUserProvider.php @@ -36,7 +36,7 @@ final class DoctrineUserProvider implements UserProviderInterface $user = null; try { - /** @var User $user */ + /** @var User|null $user */ $user = $this->repository->loadUserByUsername($username); } catch (\Exception $ex) { } @@ -57,7 +57,7 @@ final class DoctrineUserProvider implements UserProviderInterface throw new UnsupportedUserException(sprintf('Expected an instance of %s, but got "%s".', User::class, get_class($user))); } - /** @var User $reloadedUser */ + /** @var User|null $reloadedUser */ $reloadedUser = $this->repository->getUserById($user->getId()); if (null === $reloadedUser) { diff --git a/tests/Command/CreateUserCommandTest.php b/tests/Command/CreateUserCommandTest.php index f6534196..812fe797 100644 --- a/tests/Command/CreateUserCommandTest.php +++ b/tests/Command/CreateUserCommandTest.php @@ -11,6 +11,7 @@ namespace App\Tests\Command; use App\Command\CreateUserCommand; use App\Entity\User; +use App\Repository\UserRepository; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Console\Tester\CommandTester; @@ -58,7 +59,9 @@ class CreateUserCommandTest extends KernelTestCase $this->assertStringContainsString('[OK] Success! Created user: MyTestUser', $output); $container = self::$kernel->getContainer(); - $user = $container->get('doctrine')->getRepository(User::class)->loadUserByUsername('MyTestUser'); + /** @var UserRepository $userRepository */ + $userRepository = $container->get('doctrine')->getRepository(User::class); + $user = $userRepository->loadUserByUsername('MyTestUser'); self::assertInstanceOf(User::class, $user); self::assertNotNull($user); } diff --git a/tests/Repository/AbstractRepositoryTest.php b/tests/Repository/AbstractRepositoryTest.php index 290a525d..6ab4c23e 100644 --- a/tests/Repository/AbstractRepositoryTest.php +++ b/tests/Repository/AbstractRepositoryTest.php @@ -10,7 +10,8 @@ namespace App\Tests\Repository; use App\Tests\KernelTestTrait; -use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; +use Doctrine\Persistence\ObjectManager; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; /** @@ -21,7 +22,7 @@ abstract class AbstractRepositoryTest extends KernelTestCase use KernelTestTrait; /** - * @var EntityManager|null + * @var ObjectManager|null */ private $entityManager; @@ -38,7 +39,7 @@ abstract class AbstractRepositoryTest extends KernelTestCase } /** - * @return EntityManager + * @return ObjectManager */ protected function getEntityManager() { @@ -52,7 +53,9 @@ abstract class AbstractRepositoryTest extends KernelTestCase { parent::tearDown(); - $this->entityManager->close(); + if ($this->entityManager instanceof EntityManagerInterface) { + $this->entityManager->close(); + } $this->entityManager = null; // avoid memory leaks } } diff --git a/tests/phpstan.neon b/tests/phpstan.neon index fc982aa0..3dcde60c 100644 --- a/tests/phpstan.neon +++ b/tests/phpstan.neon @@ -5,6 +5,8 @@ includes: parameters: tmpDir: %rootDir%/../../../var/cache/phpstan + symfony: + container_xml_path: '%rootDir%/../../../var/cache/dev/srcApp_KernelDevDebugContainer.xml' ignoreErrors: - '#Access to an undefined property Faker\\Generator::\$stateAbbr.#' - '#Access to an undefined property Faker\\Generator::\$catchPhrase.#'