From 1ddda22231f48387568e956bf670811252c3ef25 Mon Sep 17 00:00:00 2001
From: marioradu <magdamarior@gmail.com>
Date: Mon, 17 Oct 2022 11:11:10 +0300
Subject: [PATCH 1/4] psalm implemented

---
 .github/workflows/static-analysis.yml         |  46 +++++++
 bin/doctrine                                  |   1 +
 bin/doctrine-migrations                       |   8 ++
 .../migrations/Version20221014080316.php      |  57 ++++++++
 psalm.xml                                     |  15 +++
 src/App/src/Common/Pagination.php             |   4 +-
 .../src/Common/UuidOrderedTimeGenerator.php   |  34 ++---
 .../src/Middleware/RememberMeMiddleware.php   |   1 +
 .../src/Resolver/EntityListenerResolver.php   |  12 +-
 src/App/src/Service/RecaptchaService.php      |   4 +-
 src/Contact/src/Form/ContactForm.php          |  19 +--
 src/Plugin/src/ConfigProvider.php             |   2 -
 src/Plugin/src/FormsPlugin.php                |  25 ++--
 src/Plugin/src/TemplatePlugin.php             |   8 +-
 src/Slug/src/SlugCollector.php                |   1 +
 .../Authentication/AuthenticationAdapter.php  |  45 +------
 .../AuthenticationAdapterFactory.php          |   1 +
 src/User/src/Controller/UserController.php    |  17 ++-
 src/User/src/Entity/User.php                  | 126 ++++++++----------
 src/User/src/Entity/UserAvatar.php            |  17 ++-
 src/User/src/Entity/UserDetail.php            |  27 ++--
 src/User/src/Entity/UserIdentity.php          |  33 ++---
 src/User/src/Entity/UserInterface.php         |   7 +
 src/User/src/Entity/UserResetPassword.php     |  38 +++---
 src/User/src/Entity/UserRole.php              |   6 +-
 .../EventListener/UserAvatarEventListener.php |   3 +-
 src/User/src/Form/LoginForm.php               |   8 +-
 src/User/src/Form/ProfileDeleteForm.php       |   7 +-
 src/User/src/Form/ProfileDetailsForm.php      |  14 +-
 src/User/src/Form/ProfilePasswordForm.php     |  10 +-
 src/User/src/Form/RegisterForm.php            |  12 +-
 .../src/Form/RequestResetPasswordForm.php     |   9 +-
 src/User/src/Form/ResetPasswordForm.php       |  14 +-
 src/User/src/Repository/UserRepository.php    |  33 +----
 src/User/src/Service/UserService.php          |  60 +++------
 src/User/src/Service/UserServiceInterface.php |  23 ++--
 36 files changed, 385 insertions(+), 362 deletions(-)
 create mode 100644 .github/workflows/static-analysis.yml
 create mode 100644 bin/doctrine-migrations
 create mode 100644 data/doctrine/migrations/Version20221014080316.php
 create mode 100644 psalm.xml

diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
new file mode 100644
index 00000000..747f12b6
--- /dev/null
+++ b/.github/workflows/static-analysis.yml
@@ -0,0 +1,46 @@
+on:
+  #  - pull_request
+  - push
+
+name: static analysis
+
+jobs:
+  mutation:
+    name: PHP ${{ matrix.php }}-${{ matrix.os }}
+
+    runs-on: ${{ matrix.os }}
+
+    strategy:
+      matrix:
+        os:
+          - ubuntu-latest
+
+        php:
+          - "8.1"
+
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2.3.4
+
+      - name: Install PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: "${{ matrix.php }}"
+          tools: composer:v2, cs2pr
+          coverage: none
+
+      - name: Determine composer cache directory
+        run: echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV
+
+      - name: Cache dependencies installed with composer
+        uses: actions/cache@v2
+        with:
+          path: ${{ env.COMPOSER_CACHE_DIR }}
+          key: php${{ matrix.php }}-composer-${{ hashFiles('**/composer.json') }}
+          restore-keys: |
+            php${{ matrix.php }}-composer-
+      - name: Install dependencies with composer
+        run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
+
+      - name: Static analysis
+        run: vendor/bin/psalm --no-cache --output-format=github --show-info=false --threads=4
diff --git a/bin/doctrine b/bin/doctrine
index e67edec7..33b919c9 100644
--- a/bin/doctrine
+++ b/bin/doctrine
@@ -1,3 +1,4 @@
+#!/usr/bin/env php
 <?php
 
 use Doctrine\ORM\Tools\Console\ConsoleRunner;
diff --git a/bin/doctrine-migrations b/bin/doctrine-migrations
new file mode 100644
index 00000000..d40cc553
--- /dev/null
+++ b/bin/doctrine-migrations
@@ -0,0 +1,8 @@
+#!/usr/bin/env php
+<?php
+
+declare(strict_types=1);
+
+namespace Doctrine\Migrations;
+
+require __DIR__ . '/../vendor/doctrine/migrations/bin/doctrine-migrations.php';
diff --git a/data/doctrine/migrations/Version20221014080316.php b/data/doctrine/migrations/Version20221014080316.php
new file mode 100644
index 00000000..2b5177fd
--- /dev/null
+++ b/data/doctrine/migrations/Version20221014080316.php
@@ -0,0 +1,57 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Frontend\Migrations;
+
+use Doctrine\DBAL\Schema\Schema;
+use Doctrine\Migrations\AbstractMigration;
+
+/**
+ * Auto-generated Migration: Please modify to your needs!
+ */
+final class Version20221014080316 extends AbstractMigration
+{
+    public function getDescription(): string
+    {
+        return '';
+    }
+
+    public function up(Schema $schema): void
+    {
+        // this up() migration is auto-generated, please modify it to your needs
+        $this->addSql('CREATE TABLE contact_message (uuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', email VARCHAR(150) NOT NULL, name VARCHAR(150) NOT NULL, subject LONGTEXT NOT NULL, message LONGTEXT NOT NULL, platform LONGTEXT NOT NULL, created DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+        $this->addSql('CREATE TABLE user (uuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', identity VARCHAR(191) NOT NULL, password VARCHAR(191) NOT NULL, status ENUM(\'pending\', \'active\'), isDeleted TINYINT(1) NOT NULL, hash VARCHAR(64) NOT NULL, created DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', UNIQUE INDEX UNIQ_8D93D6496A95E9C4 (identity), UNIQUE INDEX UNIQ_8D93D649D1B862B8 (hash), PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+        $this->addSql('CREATE TABLE user_roles (userUuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', roleUuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', INDEX IDX_54FCD59FD73087E9 (userUuid), INDEX IDX_54FCD59F88446210 (roleUuid), PRIMARY KEY(userUuid, roleUuid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+        $this->addSql('CREATE TABLE user_avatar (uuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', name VARCHAR(191) NOT NULL, created DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', userUuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', UNIQUE INDEX UNIQ_73256912D73087E9 (userUuid), PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+        $this->addSql('CREATE TABLE user_detail (uuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', firstName VARCHAR(191) DEFAULT NULL, lastName VARCHAR(191) DEFAULT NULL, created DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', userUuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', UNIQUE INDEX UNIQ_4B5464AED73087E9 (userUuid), PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+        $this->addSql('CREATE TABLE user_remember_me (uuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', rememberMeToken VARCHAR(100) NOT NULL, userAgent VARCHAR(100) NOT NULL, expireDate DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', created DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', userUuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', UNIQUE INDEX UNIQ_D3E96EBD1BBB86A0 (rememberMeToken), UNIQUE INDEX UNIQ_D3E96EBDD73087E9 (userUuid), PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+        $this->addSql('CREATE TABLE user_reset_password (uuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', expires DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', hash VARCHAR(64) NOT NULL, status VARCHAR(20) NOT NULL, created DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', userUuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', UNIQUE INDEX UNIQ_D21DE3BCD1B862B8 (hash), INDEX IDX_D21DE3BCD73087E9 (userUuid), PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+        $this->addSql('CREATE TABLE user_role (uuid BINARY(16) NOT NULL COMMENT \'(DC2Type:uuid_binary_ordered_time)\', name VARCHAR(30) NOT NULL, created DATETIME NOT NULL COMMENT \'(DC2Type:datetime_immutable)\', updated DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetime_immutable)\', UNIQUE INDEX UNIQ_2DE8C6A35E237E06 (name), PRIMARY KEY(uuid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB');
+        $this->addSql('ALTER TABLE user_roles ADD CONSTRAINT FK_54FCD59FD73087E9 FOREIGN KEY (userUuid) REFERENCES user (uuid)');
+        $this->addSql('ALTER TABLE user_roles ADD CONSTRAINT FK_54FCD59F88446210 FOREIGN KEY (roleUuid) REFERENCES user_role (uuid)');
+        $this->addSql('ALTER TABLE user_avatar ADD CONSTRAINT FK_73256912D73087E9 FOREIGN KEY (userUuid) REFERENCES user (uuid)');
+        $this->addSql('ALTER TABLE user_detail ADD CONSTRAINT FK_4B5464AED73087E9 FOREIGN KEY (userUuid) REFERENCES user (uuid)');
+        $this->addSql('ALTER TABLE user_remember_me ADD CONSTRAINT FK_D3E96EBDD73087E9 FOREIGN KEY (userUuid) REFERENCES user (uuid)');
+        $this->addSql('ALTER TABLE user_reset_password ADD CONSTRAINT FK_D21DE3BCD73087E9 FOREIGN KEY (userUuid) REFERENCES user (uuid)');
+    }
+
+    public function down(Schema $schema): void
+    {
+        // this down() migration is auto-generated, please modify it to your needs
+        $this->addSql('ALTER TABLE user_roles DROP FOREIGN KEY FK_54FCD59FD73087E9');
+        $this->addSql('ALTER TABLE user_roles DROP FOREIGN KEY FK_54FCD59F88446210');
+        $this->addSql('ALTER TABLE user_avatar DROP FOREIGN KEY FK_73256912D73087E9');
+        $this->addSql('ALTER TABLE user_detail DROP FOREIGN KEY FK_4B5464AED73087E9');
+        $this->addSql('ALTER TABLE user_remember_me DROP FOREIGN KEY FK_D3E96EBDD73087E9');
+        $this->addSql('ALTER TABLE user_reset_password DROP FOREIGN KEY FK_D21DE3BCD73087E9');
+        $this->addSql('DROP TABLE contact_message');
+        $this->addSql('DROP TABLE user');
+        $this->addSql('DROP TABLE user_roles');
+        $this->addSql('DROP TABLE user_avatar');
+        $this->addSql('DROP TABLE user_detail');
+        $this->addSql('DROP TABLE user_remember_me');
+        $this->addSql('DROP TABLE user_reset_password');
+        $this->addSql('DROP TABLE user_role');
+    }
+}
diff --git a/psalm.xml b/psalm.xml
new file mode 100644
index 00000000..3e4e3d08
--- /dev/null
+++ b/psalm.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<psalm
+    errorLevel="4"
+    resolveFromConfigFile="true"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns="https://getpsalm.org/schema/config"
+    xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
+>
+    <projectFiles>
+        <directory name="src" />
+        <ignoreFiles>
+            <directory name="vendor" />
+        </ignoreFiles>
+    </projectFiles>
+</psalm>
diff --git a/src/App/src/Common/Pagination.php b/src/App/src/Common/Pagination.php
index 9df396b9..7b2843f2 100644
--- a/src/App/src/Common/Pagination.php
+++ b/src/App/src/Common/Pagination.php
@@ -30,8 +30,8 @@ public static function getOffsetAndLimit(array $filters = [])
         }
 
         return [
-            'offset' => (int)$offset,
-            'limit' => (int)$limit
+            'offset' => $offset,
+            'limit' => $limit
         ];
     }
 }
diff --git a/src/App/src/Common/UuidOrderedTimeGenerator.php b/src/App/src/Common/UuidOrderedTimeGenerator.php
index f58728af..780f6fa5 100644
--- a/src/App/src/Common/UuidOrderedTimeGenerator.php
+++ b/src/App/src/Common/UuidOrderedTimeGenerator.php
@@ -4,11 +4,11 @@
 
 namespace Frontend\App\Common;
 
-use Exception;
 use Ramsey\Uuid\Codec\OrderedTimeCodec;
-use Ramsey\Uuid\Uuid;
 use Ramsey\Uuid\UuidFactory;
+use Ramsey\Uuid\UuidFactoryInterface;
 use Ramsey\Uuid\UuidInterface;
+use Ramsey\Uuid\Uuid;
 
 /**
  * Class UuidOrderedTimeGenerator
@@ -16,35 +16,19 @@
  */
 final class UuidOrderedTimeGenerator
 {
-    /** @var UuidFactory $factory */
-    private static $factory;
+    private static UuidFactoryInterface $factory;
 
-    /**
-     * @return UuidInterface
-     */
     public static function generateUuid(): UuidInterface
     {
-        try {
-            return self::getFactory()->uuid1();
-        } catch (Exception $exception) {
-            return null;
-        }
+        return self::getFactory()->uuid1();
     }
 
-    /**
-     * @return UuidFactory
-     */
-    private static function getFactory(): UuidFactory
+    /** @psalm-suppress UndefinedInterfaceMethod */
+    private static function getFactory(): UuidFactoryInterface
     {
-        if (!self::$factory) {
-            self::$factory = clone Uuid::getFactory();
-
-            $codec = new OrderedTimeCodec(
-                self::$factory->getUuidBuilder()
-            );
-
-            self::$factory->setCodec($codec);
-        }
+        self::$factory = clone Uuid::getFactory();
+        $codec = new OrderedTimeCodec(self::$factory->getUuidBuilder());
+        self::$factory->setCodec($codec);
 
         return self::$factory;
     }
diff --git a/src/App/src/Middleware/RememberMeMiddleware.php b/src/App/src/Middleware/RememberMeMiddleware.php
index 4709eb52..389bc18b 100644
--- a/src/App/src/Middleware/RememberMeMiddleware.php
+++ b/src/App/src/Middleware/RememberMeMiddleware.php
@@ -87,6 +87,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
                         $user->getDetail()->getArrayCopy(),
                     );
 
+                    /** @psalm-suppress UndefinedInterfaceMethod */
                     $this->authenticationService->getStorage()->write($identity);
                 }
             }
diff --git a/src/App/src/Resolver/EntityListenerResolver.php b/src/App/src/Resolver/EntityListenerResolver.php
index d4a30f04..c45413ea 100644
--- a/src/App/src/Resolver/EntityListenerResolver.php
+++ b/src/App/src/Resolver/EntityListenerResolver.php
@@ -5,7 +5,9 @@
 namespace Frontend\App\Resolver;
 
 use Doctrine\ORM\Mapping\DefaultEntityListenerResolver;
+use Psr\Container\ContainerExceptionInterface;
 use Psr\Container\ContainerInterface;
+use Psr\Container\NotFoundExceptionInterface;
 
 /**
  * Class EntityListenerResolver
@@ -14,7 +16,7 @@
 class EntityListenerResolver extends DefaultEntityListenerResolver
 {
     /** @var ContainerInterface $container */
-    protected $container;
+    protected ContainerInterface $container;
 
     /**
      * EntityListenerResolver constructor.
@@ -26,10 +28,12 @@ public function __construct(ContainerInterface $container)
     }
 
     /**
-     * @param $className
-     * @return mixed
+     * @param string $className
+     * @return object
+     * @throws ContainerExceptionInterface
+     * @throws NotFoundExceptionInterface
      */
-    public function resolve($className)
+    public function resolve($className): object
     {
         return $this->container->get($className);
     }
diff --git a/src/App/src/Service/RecaptchaService.php b/src/App/src/Service/RecaptchaService.php
index 868c68e8..cae6905b 100644
--- a/src/App/src/Service/RecaptchaService.php
+++ b/src/App/src/Service/RecaptchaService.php
@@ -46,7 +46,7 @@ public function setResponse(string $response): self
      */
     public function isValid(): bool
     {
-        if (! isset($this->response)) {
+        if (empty($this->response)) {
             throw new InvalidArgumentException('Recaptcha response not initialized.');
         }
 
@@ -64,6 +64,8 @@ public function isValid(): bool
         curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
 
         $response = curl_exec($curl);
+
+        /** @psalm-suppress InvalidScalarArgument */
         $response = json_decode($response, true);
         $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
 
diff --git a/src/Contact/src/Form/ContactForm.php b/src/Contact/src/Form/ContactForm.php
index 3d267c64..058e3f33 100644
--- a/src/Contact/src/Form/ContactForm.php
+++ b/src/Contact/src/Form/ContactForm.php
@@ -9,7 +9,7 @@
 use Laminas\Form\Element\Text;
 use Laminas\Form\Element\Textarea;
 use Laminas\Form\Form;
-use Laminas\InputFilter\InputFilter;
+use Laminas\InputFilter\InputFilterInterface;
 
 /**
  * Class ContactForm
@@ -17,8 +17,8 @@
  */
 class ContactForm extends Form
 {
-    /** @var InputFilter $inputFilter */
-    protected $inputFilter;
+    /** @var InputFilterInterface $inputFilter */
+    protected InputFilterInterface $inputFilter;
 
     /**
      * ContactForm constructor.
@@ -84,21 +84,12 @@ public function init()
             ],
             'type' => Textarea::class,
         ]);
-
-//        $this->add([
-//            'name' => 'submit',
-//            'type' => 'submit',
-//            'attributes' => [
-//                'type' => 'submit',
-//                'value' => 'Send message'
-//            ]
-//        ], ['priority' => -105]);
     }
 
     /**
-     * @return null|InputFilter|\Laminas\InputFilter\InputFilterInterface
+     * @return InputFilterInterface
      */
-    public function getInputFilter(): \Laminas\InputFilter\InputFilterInterface
+    public function getInputFilter(): InputFilterInterface
     {
         return $this->inputFilter;
     }
diff --git a/src/Plugin/src/ConfigProvider.php b/src/Plugin/src/ConfigProvider.php
index fad952a0..5ebad757 100644
--- a/src/Plugin/src/ConfigProvider.php
+++ b/src/Plugin/src/ConfigProvider.php
@@ -4,7 +4,6 @@
 
 namespace Frontend\Plugin;
 
-use Frontend\Plugin\Factory\FlashMessengerPluginFactory;
 use Frontend\Plugin\Factory\FormsPluginFactory;
 use Frontend\Plugin\Factory\PluginManagerAwareInitializer;
 use Frontend\Plugin\Factory\PluginManagerFactory;
@@ -26,7 +25,6 @@ public function __invoke(): array
             'dot_controller' => [
                 'plugin_manager' => [
                     'factories' => [
-                        'messenger' => FlashMessengerPluginFactory::class,
                         'forms' => FormsPluginFactory::class,
                     ],
                 ],
diff --git a/src/Plugin/src/FormsPlugin.php b/src/Plugin/src/FormsPlugin.php
index 05e1e7fa..f279d716 100644
--- a/src/Plugin/src/FormsPlugin.php
+++ b/src/Plugin/src/FormsPlugin.php
@@ -14,9 +14,11 @@
 use Dot\FlashMessenger\FlashMessengerInterface;
 use Dot\Form\Factory\FormAbstractServiceFactory;
 use Dot\Form\FormElementManager;
+use Psr\Container\ContainerExceptionInterface;
 use Psr\Container\ContainerInterface;
 use Laminas\Form\Form;
 use Laminas\Form\FormInterface;
+use Psr\Container\NotFoundExceptionInterface;
 
 /**
  * Class FormsPlugin
@@ -24,14 +26,14 @@
  */
 class FormsPlugin implements PluginInterface
 {
-    /** @var  FormElementManager */
-    protected $formElementManager;
+    /** @var FormElementManager $formElementManager */
+    protected FormElementManager $formElementManager;
 
-    /** @var  ContainerInterface */
-    protected $container;
+    /** @var ContainerInterface $container */
+    protected ContainerInterface $container;
 
-    /** @var  FlashMessengerInterface */
-    protected $flashMessenger;
+    /** @var FlashMessengerInterface|null  $flashMessenger*/
+    protected ?FlashMessengerInterface $flashMessenger;
 
     /**
      * FormsPlugin constructor.
@@ -50,8 +52,10 @@ public function __construct(
     }
 
     /**
-     * @param string $name
-     * @return object
+     * @param string|null $name
+     * @return mixed
+     * @throws ContainerExceptionInterface
+     * @throws NotFoundExceptionInterface
      */
     public function __invoke(string $name = null)
     {
@@ -85,7 +89,7 @@ public function __invoke(string $name = null)
     /**
      * @param Form $form
      */
-    public function restoreState(Form $form)
+    public function restoreState(Form $form): void
     {
         if ($this->flashMessenger) {
             $dataKey = $form->getName() . '_data';
@@ -159,6 +163,7 @@ public function getErrors(Form $form): array
 
     /**
      * @param array $formMessages
+     * @psalm-suppress InvalidArrayOffset
      * @return array
      */
     protected function processFormErrors(array $formMessages): array
@@ -167,7 +172,7 @@ protected function processFormErrors(array $formMessages): array
         foreach ($formMessages as $key => $message) {
             if (is_array($message)) {
                 if (!isset($errors[$key])) {
-                    $errors[$key] = array();
+                    $errors[$key] = [];
                 }
 
                 foreach ($message as $k => $m) {
diff --git a/src/Plugin/src/TemplatePlugin.php b/src/Plugin/src/TemplatePlugin.php
index 3aebfe00..1d82cd7e 100644
--- a/src/Plugin/src/TemplatePlugin.php
+++ b/src/Plugin/src/TemplatePlugin.php
@@ -18,8 +18,8 @@
  */
 class TemplatePlugin implements PluginInterface
 {
-    /** @var TemplateRendererInterface */
-    protected $template;
+    /** @var TemplateRendererInterface $template */
+    protected TemplateRendererInterface $template;
 
     /**
      * TemplatePlugin constructor.
@@ -32,8 +32,8 @@ public function __construct(TemplateRendererInterface $template)
 
     /**
      * @param string|null $templateName
-     * @param array|null $params
-     * @return mixed
+     * @param array $params
+     * @return self|string
      */
     public function __invoke(string $templateName = null, array $params = [])
     {
diff --git a/src/Slug/src/SlugCollector.php b/src/Slug/src/SlugCollector.php
index 370ffd7f..4d2ec300 100644
--- a/src/Slug/src/SlugCollector.php
+++ b/src/Slug/src/SlugCollector.php
@@ -282,6 +282,7 @@ public function generateUri(Slug $slug, RouteResult $routeResult, $matchParams =
 
                     if ($part[0] !== self::REMOVABLE_PART) {
                         // Check substitute value with regex
+                        /** @psalm-suppress UndefinedVariable */
                         if (!empty($addOns)) {
                             $substitutions[$part[0]] = $addOns[$p];
                         }
diff --git a/src/User/src/Authentication/AuthenticationAdapter.php b/src/User/src/Authentication/AuthenticationAdapter.php
index e5480d3e..73353f31 100644
--- a/src/User/src/Authentication/AuthenticationAdapter.php
+++ b/src/User/src/Authentication/AuthenticationAdapter.php
@@ -6,20 +6,21 @@
 use Frontend\User\Entity\User;
 use Frontend\User\Entity\UserIdentity;
 use Frontend\User\Entity\UserRole;
+use Laminas\Authentication\Adapter\AbstractAdapter;
 use Laminas\Authentication\Adapter\AdapterInterface;
 use Exception;
 use Laminas\Authentication\Result;
 
-class AuthenticationAdapter implements AdapterInterface
+class AuthenticationAdapter extends AbstractAdapter implements AdapterInterface
 {
     private const METHOD_NOT_EXISTS = "Method %s not found in %s .";
     private const OPTION_VALUE_NOT_PROVIDED = "Option '%s' not provided for '%s' option.";
 
     /** @var string $identity */
-    private string $identity;
+    protected $identity;
 
     /** @var string $credential */
-    private string $credential;
+    protected $credential;
 
     /** @var EntityManager $entityManager */
     private EntityManager $entityManager;
@@ -38,42 +39,6 @@ public function __construct($entityManager, array $config)
         $this->config = $config;
     }
 
-    /**
-     * @param string $identity
-     * @return $this
-     */
-    public function setIdentity(string $identity): self
-    {
-        $this->identity = $identity;
-        return $this;
-    }
-
-    /**
-     * @param string $credential
-     * @return $this
-     */
-    public function setCredential(string $credential): self
-    {
-        $this->credential = $credential;
-        return $this;
-    }
-
-    /**
-     * @return string
-     */
-    private function getIdentity(): string
-    {
-        return $this->identity;
-    }
-
-    /**
-     * @return string
-     */
-    private function getCredential(): string
-    {
-        return $this->credential;
-    }
-
     /**
      * @return Result
      * @throws Exception
@@ -99,7 +64,6 @@ public function authenticate(): Result
             );
         }
 
-        /** @var callable $getCredential */
         $getCredential = "get" . ucfirst($this->config['orm_default']['credential_property']);
 
         /** Check if get credential method exist in the provided identity class */
@@ -117,7 +81,6 @@ public function authenticate(): Result
         /** Check for extra validation options */
         if (! empty($this->config['orm_default']['options'])) {
             foreach ($this->config['orm_default']['options'] as $property => $option) {
-                /** @var callable $methodName */
                 $methodName = "get" . ucfirst($property);
 
                 /** Check if the method exists in the provided identity class */
diff --git a/src/User/src/Authentication/AuthenticationAdapterFactory.php b/src/User/src/Authentication/AuthenticationAdapterFactory.php
index 74a4f7f7..ae2f5be3 100644
--- a/src/User/src/Authentication/AuthenticationAdapterFactory.php
+++ b/src/User/src/Authentication/AuthenticationAdapterFactory.php
@@ -15,6 +15,7 @@ class AuthenticationAdapterFactory
      */
     public function __invoke(ContainerInterface $container): AuthenticationAdapter
     {
+        // TODO Refactor this to use the specific entity repository, not the entity manager
         if (! $container->has(EntityManager::class)) {
             throw new Exception('EntityManager not found.');
         }
diff --git a/src/User/src/Controller/UserController.php b/src/User/src/Controller/UserController.php
index 2711707a..15b7a42e 100644
--- a/src/User/src/Controller/UserController.php
+++ b/src/User/src/Controller/UserController.php
@@ -2,16 +2,19 @@
 
 namespace Frontend\User\Controller;
 
+use Doctrine\ORM\NonUniqueResultException;
+use Doctrine\ORM\OptimisticLockException;
+use Doctrine\ORM\ORMException;
 use Dot\Controller\AbstractActionController;
 use Dot\FlashMessenger\FlashMessenger;
 use Fig\Http\Message\RequestMethodInterface;
 use Frontend\Plugin\FormsPlugin;
+use Frontend\User\Authentication\AuthenticationAdapter;
 use Frontend\User\Entity\User;
 use Frontend\User\Form\LoginForm;
 use Frontend\User\Form\RegisterForm;
 use Frontend\User\Service\UserService;
 use Laminas\Authentication\AuthenticationService;
-use Laminas\Authentication\AuthenticationServiceInterface;
 use Laminas\Diactoros\Response\HtmlResponse;
 use Laminas\Diactoros\Response\RedirectResponse;
 use Mezzio\Router\RouterInterface;
@@ -31,8 +34,8 @@ class UserController extends AbstractActionController
     /** @var UserService $userService */
     protected UserService $userService;
 
-    /** @var AuthenticationServiceInterface $authenticationService */
-    protected AuthenticationServiceInterface $authenticationService;
+    /** @var AuthenticationService $authenticationService */
+    protected AuthenticationService $authenticationService;
 
     /** @var FlashMessenger $messenger */
     protected FlashMessenger $messenger;
@@ -81,14 +84,17 @@ public function __construct(
     }
 
     /**
-     * @throws \Doctrine\ORM\NonUniqueResultException
+     * @return ResponseInterface
+     * @throws NonUniqueResultException
+     * @throws ORMException
+     * @throws OptimisticLockException
      */
     public function loginAction(): ResponseInterface
     {
         if ($this->authenticationService->hasIdentity()) {
             return new RedirectResponse($this->router->generateUri("page"));
         }
-        /** @var LoginForm $form */
+
         $form = new LoginForm();
 
         $shouldRebind = $this->messenger->getData('shouldRebind') ?? true;
@@ -99,6 +105,7 @@ public function loginAction(): ResponseInterface
         if (RequestMethodInterface::METHOD_POST === $this->getRequest()->getMethod()) {
             $form->setData($this->getRequest()->getParsedBody());
             if ($form->isValid()) {
+                /** @var AuthenticationAdapter $adapter */
                 $adapter = $this->authenticationService->getAdapter();
                 $data = $form->getData();
                 $adapter->setIdentity($data['identity'])->setCredential($data['password']);
diff --git a/src/User/src/Entity/User.php b/src/User/src/Entity/User.php
index 78ccbddf..8961cfe7 100644
--- a/src/User/src/Entity/User.php
+++ b/src/User/src/Entity/User.php
@@ -5,12 +5,13 @@
 namespace Frontend\User\Entity;
 
 use Doctrine\Common\Collections\ArrayCollection;
-use Doctrine\ORM\Mapping as ORM;
+use Doctrine\Common\Collections\Collection;
+use Dot\Authorization\Role\RoleInterface;
 use Frontend\App\Common\AbstractEntity;
 use Frontend\App\Common\UuidOrderedTimeGenerator;
+use Doctrine\ORM\Mapping as ORM;
 use Exception;
 
-use function array_map;
 use function bin2hex;
 use function random_bytes;
 
@@ -40,45 +41,38 @@ class User extends AbstractEntity implements UserInterface
 
     /**
      * @ORM\OneToOne(targetEntity="Frontend\User\Entity\UserDetail", cascade={"persist", "remove"}, mappedBy="user")
-     * @var UserDetail $detail
      */
-    protected $detail;
+    protected UserDetail $detail;
 
     /**
      * @ORM\OneToOne(targetEntity="Frontend\User\Entity\UserAvatar", cascade={"persist", "remove"}, mappedBy="user")
-     * @var UserAvatar $avatar
      */
-    protected $avatar;
+    protected ?UserAvatar $avatar;
 
     /**
      * @ORM\Column(name="identity", type="string", length=191, nullable=false, unique=true)
-     * @var string $identity
      */
-    protected $identity;
+    protected string $identity;
 
     /**
      * @ORM\Column(name="password", type="string", length=191, nullable=false)
-     * @var string $password
      */
-    protected $password;
+    protected string $password;
 
     /**
      * @ORM\Column(name="status", type="string", length=20, columnDefinition="ENUM('pending', 'active')")
-     * @var string $status
      */
-    protected $status = self::STATUS_PENDING;
+    protected string $status = self::STATUS_PENDING;
 
     /**
      * @ORM\Column(name="isDeleted", type="boolean")
-     * @var bool $isDeleted
      */
-    protected $isDeleted = self::IS_DELETED_NO;
+    protected bool $isDeleted = self::IS_DELETED_NO;
 
     /**
      * @ORM\Column(name="hash", type="string", length=64, nullable=false, unique=true)
-     * @var string $hash
      */
-    protected $hash;
+    protected string $hash;
 
     /**
      * @ORM\ManyToMany(targetEntity="Frontend\User\Entity\UserRole")
@@ -87,16 +81,14 @@ class User extends AbstractEntity implements UserInterface
      *     joinColumns={@ORM\JoinColumn(name="userUuid", referencedColumnName="uuid")},
      *     inverseJoinColumns={@ORM\JoinColumn(name="roleUuid", referencedColumnName="uuid")}
      * )
-     * @var ArrayCollection $roles
      */
-    protected $roles = [];
+    protected Collection $roles;
 
     /**
      * @ORM\OneToMany(targetEntity="UserResetPassword",
      *     cascade={"persist", "remove"}, mappedBy="user", fetch="EXTRA_LAZY")
-     * @var UserResetPassword[] $resetPassword
      */
-    protected $resetPasswords;
+    protected Collection $resetPasswords;
 
     /**
      * User constructor.
@@ -120,9 +112,9 @@ public function getDetail(): ?UserDetail
 
     /**
      * @param UserDetail $detail
-     * @return UserInterface
+     * @return self
      */
-    public function setDetail(UserDetail $detail): UserInterface
+    public function setDetail(UserDetail $detail): self
     {
         $this->detail = $detail;
 
@@ -139,9 +131,9 @@ public function getAvatar(): ?UserAvatar
 
     /**
      * @param UserAvatar $avatar
-     * @return UserInterface
+     * @return self
      */
-    public function setAvatar(UserAvatar $avatar): UserInterface
+    public function setAvatar(UserAvatar $avatar): self
     {
         $this->avatar = $avatar;
 
@@ -160,9 +152,9 @@ public function getIdentity(): string
 
     /**
      * @param string $identity
-     * @return UserInterface
+     * @return self
      */
-    public function setIdentity(string $identity): UserInterface
+    public function setIdentity(string $identity): self
     {
         $this->identity = $identity;
 
@@ -178,10 +170,10 @@ public function getPassword(): string
     }
 
     /**
-     * @param string|null $password
-     * @return UserInterface
+     * @param string $password
+     * @return self
      */
-    public function setPassword(?string $password): UserInterface
+    public function setPassword(string $password): self
     {
         $this->password = $password;
 
@@ -198,9 +190,9 @@ public function getStatus(): string
 
     /**
      * @param string $status
-     * @return UserInterface
+     * @return self
      */
-    public function setStatus(string $status): UserInterface
+    public function setStatus(string $status): self
     {
         $this->status = $status;
 
@@ -217,9 +209,9 @@ public function getIsDeleted(): bool
 
     /**
      * @param bool $isDeleted
-     * @return $this
+     * @return self
      */
-    public function setIsDeleted(bool $isDeleted)
+    public function setIsDeleted(bool $isDeleted): self
     {
         $this->isDeleted = $isDeleted;
 
@@ -235,10 +227,10 @@ public function getHash(): string
     }
 
     /**
-     * @param string|null $hash
-     * @return $this
+     * @param string $hash
+     * @return self
      */
-    public function setHash(string $hash = null)
+    public function setHash(string $hash): self
     {
         $this->hash = $hash;
 
@@ -254,10 +246,10 @@ public function getRoles()
     }
 
     /**
-     * @param UserRole $role
-     * @return UserInterface
+     * @param RoleInterface $role
+     * @return self
      */
-    public function addRole(UserRole $role): UserInterface
+    public function addRole(RoleInterface $role): self
     {
         if (!$this->roles->contains($role)) {
             $this->roles->add($role);
@@ -267,10 +259,10 @@ public function addRole(UserRole $role): UserInterface
     }
 
     /**
-     * @param UserRole $role
-     * @return UserInterface
+     * @param RoleInterface $role
+     * @return self
      */
-    public function removeRole(UserRole $role): UserInterface
+    public function removeRole(RoleInterface $role): self
     {
         if (!$this->roles->contains($role)) {
             $this->roles->removeElement($role);
@@ -280,10 +272,9 @@ public function removeRole(UserRole $role): UserInterface
     }
 
     /**
-     * @return $this
-     * @throws Exception
+     * @return self
      */
-    public function renewHash()
+    public function renewHash(): self
     {
         $this->hash = self::generateHash();
 
@@ -292,7 +283,6 @@ public function renewHash()
 
     /**
      * @return string
-     * @throws Exception
      */
     public static function generateHash(): string
     {
@@ -308,15 +298,15 @@ public static function generateHash(): string
     /**
      * @return bool
      */
-    public function isActive()
+    public function isActive(): bool
     {
         return $this->status === self::STATUS_ACTIVE;
     }
 
     /**
-     * @return $this
+     * @return self
      */
-    public function markAsDeleted()
+    public function markAsDeleted(): self
     {
         $this->isDeleted = self::IS_DELETED_YES;
 
@@ -326,38 +316,37 @@ public function markAsDeleted()
     /**
      * @return string
      */
-    public function getName()
+    public function getName(): string
     {
         return $this->getDetail()->getFirstName() . ' ' . $this->getDetail()->getLastName();
     }
 
     /**
-     * @return User
+     * @return self
      */
-    public function activate()
+    public function activate(): self
     {
         return $this->setStatus(self::STATUS_ACTIVE);
     }
 
     /**
-     * @return $this
-     * @throws Exception
+     * @return self
      */
-    public function resetRoles()
+    public function resetRoles(): self
     {
-        foreach ($this->roles->getIterator()->getArrayCopy() as $role) {
+        $this->roles->map(function (RoleInterface $role) {
             $this->removeRole($role);
-        }
+        });
+
         $this->roles = new ArrayCollection();
 
         return $this;
     }
 
     /**
-     * @return $this
-     * @throws Exception
+     * @return self
      */
-    public function createResetPassword()
+    public function createResetPassword(): self
     {
         $resetPassword = new UserResetPassword();
         $resetPassword->setHash(self::generateHash());
@@ -370,16 +359,19 @@ public function createResetPassword()
 
     /**
      * @param UserResetPassword $resetPassword
+     * @return self
      */
-    public function addResetPassword(UserResetPassword $resetPassword)
+    public function addResetPassword(UserResetPassword $resetPassword): self
     {
         $this->resetPasswords->add($resetPassword);
+
+        return $this;
     }
 
     /**
      * @return ArrayCollection
      */
-    public function getResetPasswords()
+    public function getResetPasswords(): ArrayCollection
     {
         return $this->resetPasswords;
     }
@@ -388,16 +380,16 @@ public function getResetPasswords()
      * @param UserResetPassword $resetPassword
      * @return bool
      */
-    public function hasResetPassword(UserResetPassword $resetPassword)
+    public function hasResetPassword(UserResetPassword $resetPassword): bool
     {
         return $this->resetPasswords->contains($resetPassword);
     }
 
     /**
      * @param UserResetPassword $resetPassword
-     * @return $this
+     * @return self
      */
-    public function removeResetPassword(UserResetPassword $resetPassword)
+    public function removeResetPassword(UserResetPassword $resetPassword): self
     {
         $this->resetPasswords->removeElement($resetPassword);
 
@@ -406,9 +398,9 @@ public function removeResetPassword(UserResetPassword $resetPassword)
 
     /**
      * @param array $resetPasswords
-     * @return $this
+     * @return self
      */
-    public function setResetPasswords(array $resetPasswords)
+    public function setResetPasswords(array $resetPasswords): self
     {
         foreach ($resetPasswords as $resetPassword) {
             $this->resetPasswords->add($resetPassword);
diff --git a/src/User/src/Entity/UserAvatar.php b/src/User/src/Entity/UserAvatar.php
index 069d6c6d..eeb5ffbe 100644
--- a/src/User/src/Entity/UserAvatar.php
+++ b/src/User/src/Entity/UserAvatar.php
@@ -20,20 +20,18 @@ class UserAvatar extends AbstractEntity
     /**
      * @ORM\OneToOne(targetEntity="Frontend\User\Entity\User", inversedBy="avatar")
      * @ORM\JoinColumn(name="userUuid", referencedColumnName="uuid", nullable=false)
-     * @var UserInterface $user
      */
-    protected $user;
+    protected UserInterface $user;
 
     /**
      * @ORM\Column(name="name", type="string", length=191)
-     * @var $name
      */
-    protected $name;
+    protected string $name;
 
     /**
      * @var string $url
      */
-    protected $url;
+    protected string $url;
 
     /**
      * UserAvatar constructor.
@@ -53,7 +51,7 @@ public function getUser(): UserInterface
 
     /**
      * @param UserInterface $user
-     * @return UserAvatar
+     * @return self
      */
     public function setUser(UserInterface $user): self
     {
@@ -63,16 +61,16 @@ public function setUser(UserInterface $user): self
     }
 
     /**
-     * @return mixed
+     * @return string
      */
-    public function getName()
+    public function getName(): string
     {
         return $this->name;
     }
 
     /**
      * @param $name
-     * @return UserAvatar
+     * @return self
      */
     public function setName($name): self
     {
@@ -91,6 +89,7 @@ public function getUrl(): string
 
     /**
      * @param string $url
+     * @return void
      */
     public function setUrl(string $url): void
     {
diff --git a/src/User/src/Entity/UserDetail.php b/src/User/src/Entity/UserDetail.php
index 7adf3e62..a40b94f0 100644
--- a/src/User/src/Entity/UserDetail.php
+++ b/src/User/src/Entity/UserDetail.php
@@ -19,21 +19,18 @@ class UserDetail extends AbstractEntity
     /**
      * @ORM\OneToOne(targetEntity="Frontend\User\Entity\User", inversedBy="detail")
      * @ORM\JoinColumn(name="userUuid", referencedColumnName="uuid", nullable=false)
-     * @var UserInterface $user
      */
-    protected $user;
+    protected UserInterface $user;
 
     /**
      * @ORM\Column(name="firstName", type="string", length=191, nullable=true)
-     * @var $firstName
      */
-    protected $firstName;
+    protected string $firstName;
 
     /**
      * @ORM\Column(name="lastName", type="string", length=191, nullable=true)
-     * @var $lastName
      */
-    protected $lastName;
+    protected string $lastName;
 
     /**
      * UserDetail constructor.
@@ -44,7 +41,7 @@ public function __construct()
     }
 
     /**
-     * @return User
+     * @return UserInterface
      */
     public function getUser(): UserInterface
     {
@@ -53,9 +50,9 @@ public function getUser(): UserInterface
 
     /**
      * @param User $user
-     * @return $this
+     * @return self
      */
-    public function setUser(User $user)
+    public function setUser(User $user): self
     {
         $this->user = $user;
 
@@ -63,16 +60,16 @@ public function setUser(User $user)
     }
 
     /**
-     * @return mixed
+     * @return string|null
      */
-    public function getFirstName()
+    public function getFirstName(): ?string
     {
         return $this->firstName;
     }
 
     /**
      * @param $firstName
-     * @return UserDetail
+     * @return self
      */
     public function setFirstName($firstName): self
     {
@@ -82,16 +79,16 @@ public function setFirstName($firstName): self
     }
 
     /**
-     * @return mixed
+     * @return string|null
      */
-    public function getLastName()
+    public function getLastName(): ?string
     {
         return $this->lastName;
     }
 
     /**
      * @param $lastName
-     * @return UserDetail
+     * @return self
      */
     public function setLastName($lastName): self
     {
diff --git a/src/User/src/Entity/UserIdentity.php b/src/User/src/Entity/UserIdentity.php
index 53341c67..c0e14ac0 100644
--- a/src/User/src/Entity/UserIdentity.php
+++ b/src/User/src/Entity/UserIdentity.php
@@ -13,16 +13,16 @@
 class UserIdentity implements UserInterface
 {
     /** @var string $identity */
-    protected $identity;
+    protected string $identity;
 
     /** @var array $roles */
-    protected $roles;
+    protected array $roles;
 
     /** @var array $details */
-    protected $details;
+    protected array $details;
 
     /** @var string $uuid */
-    protected $uuid;
+    protected string $uuid;
 
     /**
      * UserIdentity constructor.
@@ -44,15 +44,25 @@ public function __construct(
     }
 
     /**
-     * Get the unique user identity (id, username, email address or ...)
+     * @return string
+     */
+    public function getUuid(): string
+    {
+        return $this->uuid;
+    }
+
+    /**
+     * @return string
      */
     public function getIdentity(): string
     {
         return $this->identity;
     }
 
+
     /**
      * @return iterable
+     * @psalm-suppress LessSpecificImplementedReturnType
      */
     public function getRoles(): iterable
     {
@@ -61,8 +71,8 @@ public function getRoles(): iterable
 
     /**
      * @param string $name
-     * @param null $default
-     * @return mixed|null
+     * @param null|mixed $default
+     * @return mixed
      */
     public function getDetail(string $name, $default = null)
     {
@@ -71,17 +81,10 @@ public function getDetail(string $name, $default = null)
 
     /**
      * @return array
+     * @psalm-return array<string, mixed>
      */
     public function getDetails(): array
     {
         return $this->details;
     }
-
-    /**
-     * @return User
-     */
-    public function getUuid(): string
-    {
-        return $this->uuid;
-    }
 }
diff --git a/src/User/src/Entity/UserInterface.php b/src/User/src/Entity/UserInterface.php
index c9a80279..873f64d3 100644
--- a/src/User/src/Entity/UserInterface.php
+++ b/src/User/src/Entity/UserInterface.php
@@ -4,12 +4,19 @@
 
 namespace Frontend\User\Entity;
 
+use Ramsey\Uuid\UuidInterface;
+
 /**
  * Interface UserInterface
  * @package Frontend\User\Entity
  */
 interface UserInterface
 {
+    /**
+     * @return UuidInterface
+     */
+    public function getUuid(): UuidInterface;
+
     /**
      * @return UserDetail|null
      */
diff --git a/src/User/src/Entity/UserResetPassword.php b/src/User/src/Entity/UserResetPassword.php
index 55d34036..654a0c9b 100644
--- a/src/User/src/Entity/UserResetPassword.php
+++ b/src/User/src/Entity/UserResetPassword.php
@@ -30,27 +30,23 @@ class UserResetPassword extends AbstractEntity
     /**
      * @ORM\ManyToOne(targetEntity="User", cascade={"persist", "remove"}, inversedBy="resetPasswords")
      * @ORM\JoinColumn(name="userUuid", referencedColumnName="uuid", nullable=false)
-     * @var User $user
      */
-    protected $user;
+    protected User $user;
 
     /**
      * @ORM\Column(name="expires", type="datetime_immutable", nullable=false)
-     * @var DateTimeImmutable
      */
-    protected $expires;
+    protected DateTimeImmutable $expires;
 
     /**
      * @ORM\Column(name="hash", type="string", length=64, nullable=false, unique=true)
-     * @var $hash
      */
-    protected $hash;
+    protected string $hash;
 
     /**
      * @ORM\Column(name="status", type="string", length=20, nullable=false)
-     * @var string $status
      */
-    protected $status = self::STATUS_REQUESTED;
+    protected string $status = self::STATUS_REQUESTED;
 
     /**
      * UserResetPassword constructor.
@@ -74,9 +70,9 @@ public function getUser(): User
 
     /**
      * @param User $user
-     * @return $this
+     * @return self
      */
-    public function setUser(User $user)
+    public function setUser(User $user): self
     {
         $this->user = $user;
 
@@ -93,9 +89,9 @@ public function getExpires(): DateTimeImmutable
 
     /**
      * @param DateTimeImmutable $expires
-     * @return $this
+     * @return self
      */
-    public function setExpires(DateTimeImmutable $expires)
+    public function setExpires(DateTimeImmutable $expires): self
     {
         $this->expires = $expires;
 
@@ -103,18 +99,18 @@ public function setExpires(DateTimeImmutable $expires)
     }
 
     /**
-     * @return mixed
+     * @return string
      */
-    public function getHash()
+    public function getHash(): string
     {
         return $this->hash;
     }
 
     /**
      * @param $hash
-     * @return $this
+     * @return self
      */
-    public function setHash($hash)
+    public function setHash($hash): self
     {
         $this->hash = $hash;
 
@@ -131,9 +127,9 @@ public function getStatus(): string
 
     /**
      * @param string $status
-     * @return $this
+     * @return self
      */
-    public function setStatus(string $status)
+    public function setStatus(string $status): self
     {
         $this->status = $status;
 
@@ -147,7 +143,7 @@ public function setStatus(string $status)
     /**
      * @return bool
      */
-    public function isCompleted()
+    public function isCompleted(): bool
     {
         return $this->getStatus() === self::STATUS_COMPLETED;
     }
@@ -166,9 +162,9 @@ public function isValid(): bool
     }
 
     /**
-     * @return $this
+     * @return self
      */
-    public function markAsCompleted()
+    public function markAsCompleted(): self
     {
         $this->status = self::STATUS_COMPLETED;
 
diff --git a/src/User/src/Entity/UserRole.php b/src/User/src/Entity/UserRole.php
index 79d34f7d..0f92b454 100644
--- a/src/User/src/Entity/UserRole.php
+++ b/src/User/src/Entity/UserRole.php
@@ -5,6 +5,7 @@
 namespace Frontend\User\Entity;
 
 use Doctrine\ORM\Mapping as ORM;
+use Dot\Authorization\Role\RoleInterface;
 use Frontend\App\Common\AbstractEntity;
 
 /**
@@ -14,7 +15,7 @@
  * @ORM\HasLifecycleCallbacks()
  * @package Frontend\User\Entity
  */
-class UserRole extends AbstractEntity
+class UserRole extends AbstractEntity implements RoleInterface
 {
     public const ROLE_ADMIN = 'admin';
     public const ROLE_USER = 'user';
@@ -27,9 +28,8 @@ class UserRole extends AbstractEntity
 
     /**
      * @ORM\Column(name="name", type="string", length=30, nullable=false, unique=true)
-     * @var string $name
      */
-    protected $name;
+    protected string $name;
 
     /**
      * UserRole constructor.
diff --git a/src/User/src/EventListener/UserAvatarEventListener.php b/src/User/src/EventListener/UserAvatarEventListener.php
index 7bf6e835..01e20e09 100644
--- a/src/User/src/EventListener/UserAvatarEventListener.php
+++ b/src/User/src/EventListener/UserAvatarEventListener.php
@@ -16,8 +16,7 @@
  */
 class UserAvatarEventListener
 {
-    /** @var array $config */
-    protected $config;
+    protected array $config;
 
     /**
      * UserAvatarEventListener constructor.
diff --git a/src/User/src/Form/LoginForm.php b/src/User/src/Form/LoginForm.php
index 3b1ac0b0..bab69be2 100644
--- a/src/User/src/Form/LoginForm.php
+++ b/src/User/src/Form/LoginForm.php
@@ -9,7 +9,7 @@
 use Laminas\Form\Element\Password;
 use Laminas\Form\Element\Submit;
 use Laminas\Form\Form;
-use Laminas\InputFilter\InputFilter;
+use Laminas\InputFilter\InputFilterInterface;
 
 /**
  * Class LoginForm
@@ -17,7 +17,7 @@
  */
 class LoginForm extends Form
 {
-    /** @var InputFilter $inputFilter */
+    /** @var InputFilterInterface $inputFilter */
     protected $inputFilter;
 
     /**
@@ -82,9 +82,9 @@ public function init()
     }
 
     /**
-     * @return null|InputFilter|\Laminas\InputFilter\InputFilterInterface
+     * @return InputFilterInterface
      */
-    public function getInputFilter(): \Laminas\InputFilter\InputFilterInterface
+    public function getInputFilter(): InputFilterInterface
     {
         return $this->inputFilter;
     }
diff --git a/src/User/src/Form/ProfileDeleteForm.php b/src/User/src/Form/ProfileDeleteForm.php
index 62ec2b3a..a75c8132 100644
--- a/src/User/src/Form/ProfileDeleteForm.php
+++ b/src/User/src/Form/ProfileDeleteForm.php
@@ -9,6 +9,7 @@
 use Laminas\Form\Element\Submit;
 use Laminas\Form\Form;
 use Laminas\InputFilter\InputFilter;
+use Laminas\InputFilter\InputFilterInterface;
 
 /**
  * Class ProfileDeleteForm
@@ -16,7 +17,7 @@
  */
 class ProfileDeleteForm extends Form
 {
-    /** @var InputFilter $inputFilter */
+    /** @var InputFilterInterface $inputFilter */
     protected $inputFilter;
 
     /**
@@ -65,9 +66,9 @@ public function init()
     }
 
     /**
-     * @return null|InputFilter|\Laminas\InputFilter\InputFilterInterface
+     * @return InputFilterInterface
      */
-    public function getInputFilter(): \Laminas\InputFilter\InputFilterInterface
+    public function getInputFilter(): InputFilterInterface
     {
         return $this->inputFilter;
     }
diff --git a/src/User/src/Form/ProfileDetailsForm.php b/src/User/src/Form/ProfileDetailsForm.php
index 8b068fdb..e10095f0 100644
--- a/src/User/src/Form/ProfileDetailsForm.php
+++ b/src/User/src/Form/ProfileDetailsForm.php
@@ -5,15 +5,11 @@
 namespace Frontend\User\Form;
 
 use Frontend\User\Fieldset\UserDetailFieldset;
-use Frontend\User\InputFilter\RegisterInputFilter;
 use Frontend\User\InputFilter\UserDetailInputFilter;
-use Laminas\Form\Element\Collection;
-use Laminas\Form\Element\Email;
-use Laminas\Form\Element\Password;
 use Laminas\Form\Element\Submit;
-use Laminas\Form\Element\Text;
 use Laminas\Form\Form;
 use Laminas\InputFilter\InputFilter;
+use Laminas\InputFilter\InputFilterInterface;
 
 /**
  * Class ProfileDetailsForm
@@ -21,8 +17,8 @@
  */
 class ProfileDetailsForm extends Form
 {
-    /** @var InputFilter $inputFilter */
-    protected $inputFilter;
+    /** @var InputFilterInterface $inputFilter */
+    protected InputFilterInterface $inputFilter;
 
     /**
      * ProfileDetailsForm constructor.
@@ -60,9 +56,9 @@ public function init()
     }
 
     /**
-     * @return null|InputFilter|\Laminas\InputFilter\InputFilterInterface
+     * @return InputFilterInterface
      */
-    public function getInputFilter(): \Laminas\InputFilter\InputFilterInterface
+    public function getInputFilter(): InputFilterInterface
     {
         return $this->inputFilter;
     }
diff --git a/src/User/src/Form/ProfilePasswordForm.php b/src/User/src/Form/ProfilePasswordForm.php
index 3d6c72bf..9e872f57 100644
--- a/src/User/src/Form/ProfilePasswordForm.php
+++ b/src/User/src/Form/ProfilePasswordForm.php
@@ -7,8 +7,8 @@
 use Frontend\User\InputFilter\ProfilePasswordInputFilter;
 use Laminas\Form\Element\Password;
 use Laminas\Form\Element\Submit;
+use Laminas\InputFilter\InputFilterInterface;
 use Laminas\Form\Form;
-use Laminas\InputFilter\InputFilter;
 
 /**
  * Class ProfilePasswordForm
@@ -16,8 +16,8 @@
  */
 class ProfilePasswordForm extends Form
 {
-    /** @var InputFilter $inputFilter */
-    protected $inputFilter;
+    /** @var InputFilterInterface $inputFilter */
+    protected InputFilterInterface $inputFilter;
 
     /**
      * ProfilePasswordForm constructor.
@@ -71,9 +71,9 @@ public function init()
     }
 
     /**
-     * @return null|InputFilter|\Laminas\InputFilter\InputFilterInterface
+     * @return InputFilterInterface
      */
-    public function getInputFilter(): \Laminas\InputFilter\InputFilterInterface
+    public function getInputFilter(): InputFilterInterface
     {
         return $this->inputFilter;
     }
diff --git a/src/User/src/Form/RegisterForm.php b/src/User/src/Form/RegisterForm.php
index 0599d798..48074e39 100644
--- a/src/User/src/Form/RegisterForm.php
+++ b/src/User/src/Form/RegisterForm.php
@@ -7,13 +7,11 @@
 use Frontend\User\Fieldset\UserDetailFieldset;
 use Frontend\User\InputFilter\RegisterInputFilter;
 use Frontend\User\InputFilter\UserDetailInputFilter;
-use Laminas\Form\Element\Collection;
 use Laminas\Form\Element\Email;
 use Laminas\Form\Element\Password;
 use Laminas\Form\Element\Submit;
-use Laminas\Form\Element\Text;
+use Laminas\InputFilter\InputFilterInterface;
 use Laminas\Form\Form;
-use Laminas\InputFilter\InputFilter;
 
 /**
  * Class RegisterForm
@@ -21,8 +19,8 @@
  */
 class RegisterForm extends Form
 {
-    /** @var InputFilter $inputFilter */
-    protected $inputFilter;
+    /** @var InputFilterInterface $inputFilter */
+    protected InputFilterInterface $inputFilter;
 
     /**
      * RegisterForm constructor.
@@ -94,9 +92,9 @@ public function init()
     }
 
     /**
-     * @return null|InputFilter|\Laminas\InputFilter\InputFilterInterface
+     * @return InputFilterInterface
      */
-    public function getInputFilter(): \Laminas\InputFilter\InputFilterInterface
+    public function getInputFilter(): InputFilterInterface
     {
         return $this->inputFilter;
     }
diff --git a/src/User/src/Form/RequestResetPasswordForm.php b/src/User/src/Form/RequestResetPasswordForm.php
index 5a94e088..6d597afa 100644
--- a/src/User/src/Form/RequestResetPasswordForm.php
+++ b/src/User/src/Form/RequestResetPasswordForm.php
@@ -9,6 +9,7 @@
 use Laminas\Form\Element\Submit;
 use Laminas\Form\Form;
 use Laminas\InputFilter\InputFilter;
+use Laminas\InputFilter\InputFilterInterface;
 
 /**
  * Class RequestResetPasswordForm
@@ -16,11 +17,11 @@
  */
 class RequestResetPasswordForm extends Form
 {
-    /** @var InputFilter $inputFilter */
+    /** @var InputFilterInterface $inputFilter */
     protected $inputFilter;
 
     /**
-     * LoginForm constructor.
+     * RequestResetPasswordForm constructor.
      * @param null $name
      * @param array $options
      */
@@ -60,9 +61,9 @@ public function init()
     }
 
     /**
-     * @return null|InputFilter|\Laminas\InputFilter\InputFilterInterface
+     * @return InputFilterInterface
      */
-    public function getInputFilter(): \Laminas\InputFilter\InputFilterInterface
+    public function getInputFilter(): InputFilterInterface
     {
         return $this->inputFilter;
     }
diff --git a/src/User/src/Form/ResetPasswordForm.php b/src/User/src/Form/ResetPasswordForm.php
index 37b427a6..ca491813 100644
--- a/src/User/src/Form/ResetPasswordForm.php
+++ b/src/User/src/Form/ResetPasswordForm.php
@@ -4,17 +4,11 @@
 
 namespace Frontend\User\Form;
 
-use Frontend\User\Fieldset\UserDetailFieldset;
-use Frontend\User\InputFilter\RegisterInputFilter;
 use Frontend\User\InputFilter\ResetPasswordInputFilter;
-use Frontend\User\InputFilter\UserDetailInputFilter;
-use Laminas\Form\Element\Collection;
-use Laminas\Form\Element\Email;
 use Laminas\Form\Element\Password;
 use Laminas\Form\Element\Submit;
-use Laminas\Form\Element\Text;
 use Laminas\Form\Form;
-use Laminas\InputFilter\InputFilter;
+use Laminas\InputFilter\InputFilterInterface;
 
 /**
  * Class ResetPasswordForm
@@ -22,7 +16,7 @@
  */
 class ResetPasswordForm extends Form
 {
-    /** @var InputFilter $inputFilter */
+    /** @var InputFilterInterface $inputFilter */
     protected $inputFilter;
 
     /**
@@ -77,9 +71,9 @@ public function init()
     }
 
     /**
-     * @return null|InputFilter|\Laminas\InputFilter\InputFilterInterface
+     * @return InputFilterInterface
      */
-    public function getInputFilter(): \Laminas\InputFilter\InputFilterInterface
+    public function getInputFilter(): InputFilterInterface
     {
         return $this->inputFilter;
     }
diff --git a/src/User/src/Repository/UserRepository.php b/src/User/src/Repository/UserRepository.php
index da20ed74..e688692e 100644
--- a/src/User/src/Repository/UserRepository.php
+++ b/src/User/src/Repository/UserRepository.php
@@ -12,6 +12,7 @@
 use Doctrine\ORM\NonUniqueResultException;
 use Doctrine\ORM\ORMException;
 use Doctrine\ORM\OptimisticLockException;
+use DateTimeImmutable;
 use Exception;
 
 /**
@@ -57,8 +58,6 @@ public function findByIdentity(string $identity): UserInterface
 
     /**
      * @param User $user
-     * @throws ORMException
-     * @throws OptimisticLockException
      */
     public function saveUser(User $user)
     {
@@ -90,20 +89,6 @@ public function exists(string $email = '', ?string $uuid = '')
         }
     }
 
-    /**
-     * @param string $email
-     * @return mixed
-     * @throws NonUniqueResultException
-     */
-    public function getUserByEmail(string $email)
-    {
-        $qb = $this->getEntityManager()->createQueryBuilder();
-        $qb->select('user')
-            ->from(User::class, 'user')
-            ->where('user.identity = :identity')->setParameter('identity', $email);
-        return $qb->getQuery()->useQueryCache(true)->getOneOrNullResult();
-    }
-
     /**
      * @param string $hash
      * @return User|null
@@ -125,10 +110,8 @@ public function findByResetPasswordHash(string $hash): ?User
     /**
      * @param UserRememberMe $rememberUser
      * @return void
-     * @throws ORMException
-     * @throws OptimisticLockException
      */
-    public function saveRememberUser(UserRememberMe $rememberUser)
+    public function saveRememberUser(UserRememberMe $rememberUser): void
     {
         $em = $this->getEntityManager();
         $rememberUser->touch();
@@ -156,7 +139,7 @@ public function getRememberUser($token): ?UserRememberMe
     /**
      * @param User $user
      * @param string $userAgent
-     * @return int|mixed|string|null
+     * @return mixed
      * @throws NonUniqueResultException
      */
     public function findRememberMeUser(User $user, string $userAgent)
@@ -174,10 +157,10 @@ public function findRememberMeUser(User $user, string $userAgent)
     }
 
     /**
-     * @param \DateTimeImmutable $currentDate
-     * @return void
+     * @param DateTimeImmutable $currentDate
+     * @return mixed
      */
-    public function deleteExpiredCookies(\DateTimeImmutable $currentDate)
+    public function deleteExpiredCookies(DateTimeImmutable $currentDate)
     {
         $qb = $this->getEntityManager()->createQueryBuilder();
         $qb->delete(UserRememberMe::class, 'user_remember_me')
@@ -190,10 +173,8 @@ public function deleteExpiredCookies(\DateTimeImmutable $currentDate)
     /**
      * @param UserRememberMe $userRememberMe
      * @return void
-     * @throws ORMException
-     * @throws OptimisticLockException
      */
-    public function removeUserRememberMe(UserRememberMe $userRememberMe)
+    public function removeUserRememberMe(UserRememberMe $userRememberMe): void
     {
         $this->getEntityManager()->remove($userRememberMe);
         $this->getEntityManager()->flush();
diff --git a/src/User/src/Service/UserService.php b/src/User/src/Service/UserService.php
index 688fa9e9..9ec7ed48 100644
--- a/src/User/src/Service/UserService.php
+++ b/src/User/src/Service/UserService.php
@@ -5,6 +5,8 @@
 namespace Frontend\User\Service;
 
 use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\NonUniqueResultException;
+use Doctrine\ORM\OptimisticLockException;
 use Doctrine\ORM\ORMException;
 use Dot\AnnotatedServices\Annotation\Inject;
 use Dot\AnnotatedServices\Annotation\Service;
@@ -100,7 +102,7 @@ public function __construct(
     /**
      * @param string $uuid
      * @return User|null
-     * @throws \Doctrine\ORM\NonUniqueResultException
+     * @throws NonUniqueResultException
      */
     public function findByUuid(string $uuid)
     {
@@ -110,7 +112,7 @@ public function findByUuid(string $uuid)
     /**
      * @param string $identity
      * @return UserInterface
-     * @throws \Doctrine\ORM\NonUniqueResultException
+     * @throws NonUniqueResultException
      */
     public function findByIdentity(string $identity): UserInterface
     {
@@ -121,8 +123,8 @@ public function findByIdentity(string $identity): UserInterface
      * @param array $data
      * @return UserInterface
      * @throws \Exception
-     * @throws \Doctrine\ORM\ORMException
-     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws ORMException
+     * @throws OptimisticLockException
      */
     public function createUser(array $data): UserInterface
     {
@@ -173,8 +175,8 @@ public function createUser(array $data): UserInterface
      * @return User
      * @throws ORMException
      * @throws \Doctrine\ORM\NoResultException
-     * @throws \Doctrine\ORM\NonUniqueResultException
-     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws NonUniqueResultException
+     * @throws OptimisticLockException
      */
     public function updateUser(User $user, array $data = [])
     {
@@ -265,7 +267,7 @@ protected function createAvatar(User $user, UploadedFile $uploadedFile)
         }
         $fileName = sprintf(
             'avatar-%s.%s',
-            UuidOrderedTimeGenerator::generateUuid(),
+            UuidOrderedTimeGenerator::generateUuid()->toString(),
             self::EXTENSIONS[$uploadedFile->getClientMediaType()]
         );
         $avatar->setName($fileName);
@@ -302,7 +304,7 @@ public function deleteAvatarFile(string $path)
      * @param string|null $uuid
      * @return bool
      * @throws \Doctrine\ORM\NoResultException
-     * @throws \Doctrine\ORM\NonUniqueResultException
+     * @throws NonUniqueResultException
      */
     public function exists(string $email = '', ?string $uuid = '')
     {
@@ -324,7 +326,7 @@ public function getUsers(): array
      * @return bool
      * @throws MailException
      */
-    public function sendActivationMail(User $user)
+    public function sendActivationMail(User $user): bool
     {
         if ($user->isActive()) {
             return false;
@@ -362,44 +364,22 @@ public function findOneBy(array $params = []): ?User
     /**
      * @param User $user
      * @return User
-     * @throws \Doctrine\ORM\ORMException
-     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws ORMException
+     * @throws OptimisticLockException
      */
-    public function activateUser(User $user)
+    public function activateUser(User $user): User
     {
         $this->userRepository->saveUser($user->activate());
 
         return $user;
     }
 
-    /**
-     * @param string $email
-     * @return array
-     * @throws \Doctrine\ORM\NonUniqueResultException
-     */
-    public function getRoleNamesByEmail(string $email)
-    {
-        $roleList = [];
-
-        /** @var User $user */
-        $user = $this->userRepository->getUserByEmail($email);
-
-        if (!empty($user)) {
-            /** @var UserRole $role */
-            foreach ($user->getRoles() as $role) {
-                $roleList[] = $role->getName();
-            }
-        }
-
-        return $roleList;
-    }
-
     /**
      * @param User $user
      * @return bool
      * @throws MailException
      */
-    public function sendResetPasswordRequestedMail(User $user)
+    public function sendResetPasswordRequestedMail(User $user): bool
     {
         $this->mailService->setBody(
             $this->templateRenderer->render('user::reset-password-requested', [
@@ -419,7 +399,7 @@ public function sendResetPasswordRequestedMail(User $user)
      * @param string|null $hash
      * @return User|null
      * @throws \Doctrine\ORM\NoResultException
-     * @throws \Doctrine\ORM\NonUniqueResultException
+     * @throws NonUniqueResultException
      */
     public function findByResetPasswordHash(?string $hash): ?User
     {
@@ -464,8 +444,8 @@ public function getRepository(): UserRepository
      * @param string $userAgent
      * @return void
      * @throws ORMException
-     * @throws \Doctrine\ORM\NonUniqueResultException
-     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws NonUniqueResultException
+     * @throws OptimisticLockException
      */
     public function addRememberMeToken(User $user, string $userAgent)
     {
@@ -508,8 +488,8 @@ public function addRememberMeToken(User $user, string $userAgent)
      * @return void
      * @throws ORMException
      * @throws \Doctrine\ORM\NoResultException
-     * @throws \Doctrine\ORM\NonUniqueResultException
-     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws NonUniqueResultException
+     * @throws OptimisticLockException
      */
     public function deleteRememberMeCookie()
     {
diff --git a/src/User/src/Service/UserServiceInterface.php b/src/User/src/Service/UserServiceInterface.php
index a40bedd1..46c1f12f 100644
--- a/src/User/src/Service/UserServiceInterface.php
+++ b/src/User/src/Service/UserServiceInterface.php
@@ -2,6 +2,7 @@
 
 namespace Frontend\User\Service;
 
+use Doctrine\ORM\OptimisticLockException;
 use Doctrine\ORM\ORMException;
 use Dot\Mail\Exception\MailException;
 use Frontend\User\Entity\User;
@@ -14,12 +15,13 @@
  */
 interface UserServiceInterface
 {
+    // TODO refactor this interface, it should only have CRUD methods.
     /**
      * @param array $data
      * @return UserInterface
      * @throws \Exception
-     * @throws \Doctrine\ORM\ORMException
-     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws ORMException
+     * @throws OptimisticLockException
      */
     public function createUser(array $data): UserInterface;
 
@@ -28,7 +30,7 @@ public function createUser(array $data): UserInterface;
      * @return bool
      * @throws MailException
      */
-    public function sendActivationMail(User $user);
+    public function sendActivationMail(User $user): bool;
 
     /**
      * @param array $params
@@ -39,18 +41,11 @@ public function findOneBy(array $params = []): ?User;
     /**
      * @param User $user
      * @return User
-     * @throws \Doctrine\ORM\ORMException
-     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws ORMException
+     * @throws OptimisticLockException
      */
     public function activateUser(User $user);
 
-    /**
-     * @param string $email
-     * @return array
-     * @throws \Doctrine\ORM\NonUniqueResultException
-     */
-    public function getRoleNamesByEmail(string $email);
-
     /**
      * @param string $uuid
      * @return User|null
@@ -68,7 +63,7 @@ public function getRepository(): UserRepository;
      * @return void
      * @throws ORMException
      * @throws \Doctrine\ORM\NonUniqueResultException
-     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws OptimisticLockException
      */
     public function addRememberMeToken(User $user, string $userAgent);
 
@@ -77,7 +72,7 @@ public function addRememberMeToken(User $user, string $userAgent);
      * @throws ORMException
      * @throws \Doctrine\ORM\NoResultException
      * @throws \Doctrine\ORM\NonUniqueResultException
-     * @throws \Doctrine\ORM\OptimisticLockException
+     * @throws OptimisticLockException
      */
     public function deleteRememberMeCookie();
 }

From 600b67755824a38df39ee298a46a754364b984ed Mon Sep 17 00:00:00 2001
From: marioradu <magdamarior@gmail.com>
Date: Mon, 17 Oct 2022 11:13:53 +0300
Subject: [PATCH 2/4] added psalm

---
 composer.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index aa50842b..0f30955a 100644
--- a/composer.json
+++ b/composer.json
@@ -75,7 +75,8 @@
         "phpunit/phpunit": "^9.5.21",
         "roave/security-advisories": "dev-master",
         "squizlabs/php_codesniffer": "^3.7.1",
-        "filp/whoops": "^2.14.5"
+        "filp/whoops": "^2.14.5",
+        "vimeo/psalm": "^4.29"
     },
     "autoload": {
         "psr-4": {

From 6970551b083042379ba03441df160b45f38eed12 Mon Sep 17 00:00:00 2001
From: marioradu <magdamarior@gmail.com>
Date: Mon, 17 Oct 2022 11:18:17 +0300
Subject: [PATCH 3/4] revert to php 7.4

---
 .github/workflows/static-analysis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
index 747f12b6..8fa0c48a 100644
--- a/.github/workflows/static-analysis.yml
+++ b/.github/workflows/static-analysis.yml
@@ -16,7 +16,7 @@ jobs:
           - ubuntu-latest
 
         php:
-          - "8.1"
+          - "7.4"
 
     steps:
       - name: Checkout

From 148736d9a8731223fb8c61e6caff9e48d6b1a4c1 Mon Sep 17 00:00:00 2001
From: marioradu <magdamarior@gmail.com>
Date: Wed, 19 Oct 2022 10:52:20 +0300
Subject: [PATCH 4/4] small changes

---
 src/User/src/Controller/AccountController.php |  4 ---
 src/User/src/Service/UserService.php          | 26 +++++++------------
 2 files changed, 10 insertions(+), 20 deletions(-)

diff --git a/src/User/src/Controller/AccountController.php b/src/User/src/Controller/AccountController.php
index 50ed92e9..9ab8c4e1 100644
--- a/src/User/src/Controller/AccountController.php
+++ b/src/User/src/Controller/AccountController.php
@@ -11,7 +11,6 @@
 use Frontend\Plugin\FormsPlugin;
 use Frontend\User\Entity\User;
 use Frontend\User\Entity\UserIdentity;
-use Frontend\User\Entity\UserInterface;
 use Frontend\User\Entity\UserResetPassword;
 use Frontend\User\Form\ProfileDeleteForm;
 use Frontend\User\Form\ProfileDetailsForm;
@@ -23,13 +22,11 @@
 use Laminas\Authentication\AuthenticationService;
 use Laminas\Authentication\AuthenticationServiceInterface;
 use Laminas\Diactoros\Response\HtmlResponse;
-use Laminas\Diactoros\Response\JsonResponse;
 use Laminas\Diactoros\Response\RedirectResponse;
 use Mezzio\Router\RouterInterface;
 use Mezzio\Template\TemplateRendererInterface;
 use Dot\AnnotatedServices\Annotation\Inject;
 use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\UploadedFileInterface;
 use Exception;
 
 class AccountController extends AbstractActionController
@@ -314,7 +311,6 @@ public function avatarAction(): ResponseInterface
             $this->template->render('user::profile', [
                 'action' => 'avatar',
                 'content' => $this->template->render('profile::avatar', [
-                    'userUploadsBaseUrl' => 'http://localhost:8080/uploads/user/',
                     'user' => $user,
                     'form' => $form
                 ]),
diff --git a/src/User/src/Service/UserService.php b/src/User/src/Service/UserService.php
index 9ec7ed48..fb0d3c47 100644
--- a/src/User/src/Service/UserService.php
+++ b/src/User/src/Service/UserService.php
@@ -12,9 +12,9 @@
 use Dot\AnnotatedServices\Annotation\Service;
 use Dot\Mail\Exception\MailException;
 use Dot\Mail\Service\MailService;
+use Exception;
 use Frontend\App\Common\Message;
 use Frontend\App\Common\UuidOrderedTimeGenerator;
-use Frontend\Contact\Repository\MessageRepository;
 use Frontend\User\Entity\UserRememberMe;
 use Frontend\User\Entity\User;
 use Frontend\User\Entity\UserAvatar;
@@ -122,9 +122,8 @@ public function findByIdentity(string $identity): UserInterface
     /**
      * @param array $data
      * @return UserInterface
-     * @throws \Exception
+     * @throws Exception
      * @throws ORMException
-     * @throws OptimisticLockException
      */
     public function createUser(array $data): UserInterface
     {
@@ -148,7 +147,7 @@ public function createUser(array $data): UserInterface
             foreach ($data['roles'] as $roleName) {
                 $role = $this->userRoleRepository->findByName($roleName);
                 if (!$role instanceof UserRole) {
-                    throw new \Exception('Role not found: ' . $roleName);
+                    throw new Exception('Role not found: ' . $roleName);
                 }
                 $user->addRole($role);
             }
@@ -160,7 +159,7 @@ public function createUser(array $data): UserInterface
         }
 
         if (empty($user->getRoles())) {
-            throw new \Exception(Message::RESTRICTION_ROLES);
+            throw new Exception(Message::RESTRICTION_ROLES);
         }
 
         $this->userRepository->saveUser($user);
@@ -172,13 +171,10 @@ public function createUser(array $data): UserInterface
     /**
      * @param User $user
      * @param array $data
-     * @return User
+     * @return UserInterface
      * @throws ORMException
-     * @throws \Doctrine\ORM\NoResultException
-     * @throws NonUniqueResultException
-     * @throws OptimisticLockException
      */
-    public function updateUser(User $user, array $data = [])
+    public function updateUser(User $user, array $data = []): UserInterface
     {
         if (isset($data['email']) && !is_null($data['email'])) {
             if ($this->exists($data['email'], $user->getUuid()->toString())) {
@@ -237,7 +233,7 @@ public function updateUser(User $user, array $data = [])
             }
         }
         if (empty($user->getRoles())) {
-            throw new \Exception(Message::RESTRICTION_ROLES);
+            throw new Exception(Message::RESTRICTION_ROLES);
         }
 
         $this->userRepository->saveUser($user);
@@ -250,7 +246,7 @@ public function updateUser(User $user, array $data = [])
      * @param UploadedFile $uploadedFile
      * @return UserAvatar
      */
-    protected function createAvatar(User $user, UploadedFile $uploadedFile)
+    protected function createAvatar(User $user, UploadedFile $uploadedFile): UserAvatar
     {
         $path = $this->config['uploads']['user']['path'] . DIRECTORY_SEPARATOR;
         $path .= $user->getUuid()->toString() . DIRECTORY_SEPARATOR;
@@ -286,7 +282,7 @@ protected function createAvatar(User $user, UploadedFile $uploadedFile)
      * @param string $path
      * @return bool
      */
-    public function deleteAvatarFile(string $path)
+    public function deleteAvatarFile(string $path): bool
     {
         if (empty($path)) {
             return false;
@@ -303,10 +299,8 @@ public function deleteAvatarFile(string $path)
      * @param string $email
      * @param string|null $uuid
      * @return bool
-     * @throws \Doctrine\ORM\NoResultException
-     * @throws NonUniqueResultException
      */
-    public function exists(string $email = '', ?string $uuid = '')
+    public function exists(string $email = '', ?string $uuid = ''): bool
     {
         return !is_null(
             $this->userRepository->exists($email, $uuid)
