From 171e9d50d7acc18fc05d749fa3c2df5a620dc1cc Mon Sep 17 00:00:00 2001
From: James Shields <57457-lostcarpark@users.noreply.drupalcode.org>
Date: Mon, 22 Apr 2024 17:19:27 +0000
Subject: [PATCH] Issue #3441425 by lostcarpark, fjgarlin: PHPStan: "\Drupal
 calls should be avoided in classes"

---
 drush.services.yml                            |  2 +-
 .../ProjectBrowserSource/RandomDataPlugin.php |  3 +-
 .../ProjectBrowserSourceExample.php           | 38 ++++++++++++++++++-
 project_browser.services.yml                  |  2 +-
 src/Commands/UpdateFixtureCommands.php        |  6 ++-
 .../ProjectBrowserSource/DrupalCore.php       | 13 +++++--
 .../ProjectBrowserSource/MockDrupalDotOrg.php | 10 +++--
 src/Plugin/ProjectBrowserSourceBase.php       | 15 +++++++-
 src/ProjectBrowserFixtureHelper.php           |  8 +++-
 .../project_browser_test.services.yml         |  3 +-
 .../src/Datetime/TestTime.php                 | 11 +++++-
 .../src/DrupalOrgClientMiddleware.php         | 14 ++++++-
 .../DrupalDotOrgJsonApi.php                   |  3 +-
 tests/src/Unit/MockDrupalDotOrgTest.php       | 11 +++++-
 .../Unit/ProjectBrowserFixtureHelperTest.php  |  5 ++-
 15 files changed, 120 insertions(+), 24 deletions(-)

diff --git a/drush.services.yml b/drush.services.yml
index b1f73aa97..166b16136 100644
--- a/drush.services.yml
+++ b/drush.services.yml
@@ -3,4 +3,4 @@ services:
     class: Drupal\project_browser\Commands\UpdateFixtureCommands
     tags:
       - { name: drush.command }
-    arguments: ['@logger.factory', '@project_browser.enabled_source', '@event_dispatcher', '@project_browser.fixture_helper']
+    arguments: ['@logger.factory', '@project_browser.enabled_source', '@event_dispatcher', '@project_browser.fixture_helper', '@module_handler']
diff --git a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php b/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
index d7736513a..6f7affb14 100644
--- a/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
+++ b/modules/project_browser_devel/src/Plugin/ProjectBrowserSource/RandomDataPlugin.php
@@ -4,7 +4,6 @@ namespace Drupal\project_browser_devel\Plugin\ProjectBrowserSource;
 
 use Drupal\Component\Utility\Random;
 use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
 use Drupal\project_browser\ProjectBrowser\Project;
 use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
@@ -23,7 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *   description = @Translation("Gets random project and filters information"),
  * )
  */
-class RandomDataPlugin extends ProjectBrowserSourceBase implements ContainerFactoryPluginInterface {
+class RandomDataPlugin extends ProjectBrowserSourceBase {
 
   /**
    * Utility to create random data.
diff --git a/modules/project_browser_source_example/src/Plugin/ProjectBrowserSource/ProjectBrowserSourceExample.php b/modules/project_browser_source_example/src/Plugin/ProjectBrowserSource/ProjectBrowserSourceExample.php
index 9148c6126..75b1be393 100644
--- a/modules/project_browser_source_example/src/Plugin/ProjectBrowserSource/ProjectBrowserSourceExample.php
+++ b/modules/project_browser_source_example/src/Plugin/ProjectBrowserSource/ProjectBrowserSourceExample.php
@@ -5,6 +5,8 @@ namespace Drupal\project_browser_source_example\Plugin\ProjectBrowserSource;
 use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
 use Drupal\project_browser\ProjectBrowser\Project;
 use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * Project Browser Source Plugin example code.
@@ -17,6 +19,39 @@ use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
  */
 class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
 
+  /**
+   * Constructor for example plugin.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
+   *   The request from the browser.
+   */
+  public function __construct(
+    array $configuration,
+    $plugin_id,
+    $plugin_definition,
+    protected readonly RequestStack $requestStack,
+  ) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('request_stack'),
+    );
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -35,6 +70,7 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
     // own REST endpoint, GraphQL, a Google Spreadsheet, anything! Refer to
     // other plugins if you want more examples. The $query should be taken into
     // account when filtering, but adapted to the way that you obtain the data.
+    $request = $this->requestStack->getCurrentRequest();
     $projects_from_source = [
       // The source data can use any keys, we will adapt it later.
       [
@@ -44,7 +80,7 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
         'short_description' => 'Quick summary to show in the cards.',
         'long_description' => 'Extended project information to show in the detail page',
         'author' => 'Jane Doe',
-        'logo' => \Drupal::request()->getSchemeAndHttpHost() . '/core/misc/logo/drupal-logo.svg',
+        'logo' => $request->getSchemeAndHttpHost() . '/core/misc/logo/drupal-logo.svg',
         'created_at' => strtotime('1 year ago'),
         'updated_at' => strtotime('1 month ago'),
         'categories' => ['cat_1:Category 1'],
diff --git a/project_browser.services.yml b/project_browser.services.yml
index 8c52996a5..8bc2ee932 100644
--- a/project_browser.services.yml
+++ b/project_browser.services.yml
@@ -15,7 +15,7 @@ services:
       - { name: 'event_subscriber' }
   project_browser.fixture_helper:
     class: Drupal\project_browser\ProjectBrowserFixtureHelper
-    arguments: ['@database', '@state', '@http_client']
+    arguments: ['@database', '@state', '@http_client', '@module_handler']
   cache.project_browser:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
diff --git a/src/Commands/UpdateFixtureCommands.php b/src/Commands/UpdateFixtureCommands.php
index 8b08e1d7c..325f1debc 100644
--- a/src/Commands/UpdateFixtureCommands.php
+++ b/src/Commands/UpdateFixtureCommands.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\project_browser\Commands;
 
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\project_browser\EnabledSourceHandler;
@@ -39,12 +40,15 @@ class UpdateFixtureCommands extends DrushCommands {
    *   The event dispatcher.
    * @param \Drupal\project_browser\ProjectBrowserFixtureHelper $fixtureHelper
    *   The project browser fixture helper.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
+   *   The module handler.
    */
   public function __construct(
     private readonly LoggerChannelFactoryInterface $loggerChannelFactory,
     private readonly EnabledSourceHandler $enabledSource,
     private readonly EventDispatcherInterface $eventDispatcher,
     private readonly ProjectBrowserFixtureHelper $fixtureHelper,
+    private readonly ModuleHandlerInterface $moduleHandler,
   ) {
     parent::__construct();
   }
@@ -84,7 +88,7 @@ class UpdateFixtureCommands extends DrushCommands {
       $this->logger()->notice($this->t('Begin Fixture Generation'));
       $sandbox = [];
 
-      $module_path = \Drupal::service('module_handler')->getModule('project_browser')->getPath();
+      $module_path = $this->moduleHandler->getModule('project_browser')->getPath();
       while (empty($sandbox) || $sandbox['#finished'] !== TRUE) {
         $progress_message = $this->fixtureHelper->hackyFixtureMaker($sandbox);
 
diff --git a/src/Plugin/ProjectBrowserSource/DrupalCore.php b/src/Plugin/ProjectBrowserSource/DrupalCore.php
index 697544b72..84bb22516 100644
--- a/src/Plugin/ProjectBrowserSource/DrupalCore.php
+++ b/src/Plugin/ProjectBrowserSource/DrupalCore.php
@@ -5,12 +5,12 @@ namespace Drupal\project_browser\Plugin\ProjectBrowserSource;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\Extension;
 use Drupal\Core\Extension\ModuleExtensionList;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Site\Settings;
 use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
 use Drupal\project_browser\ProjectBrowser\Project;
 use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * The source plugin to get Drupal core projects list.
@@ -21,7 +21,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *   description = @Translation("Gets core projects and filters information"),
  * )
  */
-class DrupalCore extends ProjectBrowserSourceBase implements ContainerFactoryPluginInterface {
+class DrupalCore extends ProjectBrowserSourceBase {
 
   /**
    * All core modules are covered under security policy.
@@ -53,6 +53,8 @@ class DrupalCore extends ProjectBrowserSourceBase implements ContainerFactoryPlu
    *   The plugin_id for the plugin instance.
    * @param mixed $plugin_definition
    *   The plugin implementation definition.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
+   *   The request from the browser.
    * @param \Drupal\Core\Cache\CacheBackendInterface $cacheBin
    *   The cache back end interface.
    * @param \Drupal\Core\Extension\ModuleExtensionList $moduleExtensionList
@@ -62,6 +64,7 @@ class DrupalCore extends ProjectBrowserSourceBase implements ContainerFactoryPlu
     array $configuration,
     $plugin_id,
     $plugin_definition,
+    private readonly RequestStack $requestStack,
     private readonly CacheBackendInterface $cacheBin,
     private readonly ModuleExtensionList $moduleExtensionList,
   ) {
@@ -76,8 +79,9 @@ class DrupalCore extends ProjectBrowserSourceBase implements ContainerFactoryPlu
       $configuration,
       $plugin_id,
       $plugin_definition,
+      $container->get('request_stack'),
       $container->get('cache.project_browser'),
-      $container->get('extension.list.module')
+      $container->get('extension.list.module'),
     );
   }
 
@@ -169,6 +173,7 @@ class DrupalCore extends ProjectBrowserSourceBase implements ContainerFactoryPlu
       return $stored_projects->data;
     }
 
+    $request = $this->requestStack->getCurrentRequest();
     $returned_list = [];
     foreach ($this->getCoreModules() as $module_name => $module) {
       // Dummy data is used for the fields that are unavailable for core
@@ -177,7 +182,7 @@ class DrupalCore extends ProjectBrowserSourceBase implements ContainerFactoryPlu
         id: 0,
         logo: [
           'file' => [
-            'uri' => \Drupal::request()->getSchemeAndHttpHost() . '/core/misc/logo/drupal-logo.svg',
+            'uri' => $request->getSchemeAndHttpHost() . '/core/misc/logo/drupal-logo.svg',
             'resource' => 'image',
           ],
           'alt' => '',
diff --git a/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php b/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php
index 129b73301..da367b9b1 100644
--- a/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php
+++ b/src/Plugin/ProjectBrowserSource/MockDrupalDotOrg.php
@@ -6,7 +6,7 @@ use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Database\Connection;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\State\StateInterface;
 use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
 use Drupal\project_browser\ProjectBrowser\Project;
@@ -31,7 +31,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  *   description = @Translation("Gets project and filters information from a mock API"),
  * )
  */
-class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFactoryPluginInterface {
+class MockDrupalDotOrg extends ProjectBrowserSourceBase {
 
   /**
    * This is what the Mock understands as "Covered" modules.
@@ -73,6 +73,8 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
    *   The session state.
    * @param \Drupal\Core\Cache\CacheBackendInterface $cacheBin
    *   The back end cache interface.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
+   *   The module handler.
    */
   public function __construct(
     array $configuration,
@@ -83,6 +85,7 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
     private readonly ClientInterface $httpClient,
     private readonly StateInterface $state,
     private readonly CacheBackendInterface $cacheBin,
+    private readonly ModuleHandlerInterface $moduleHandler,
    ) {
     parent::__construct($configuration, $plugin_id, $plugin_definition);
   }
@@ -100,6 +103,7 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
       $container->get('http_client'),
       $container->get('state'),
       $container->get('cache.project_browser'),
+      $container->get('module_handler'),
     );
   }
 
@@ -325,7 +329,7 @@ class MockDrupalDotOrg extends ProjectBrowserSourceBase implements ContainerFact
    *   The category ID and name, keyed by ID.
    */
   protected function getCategoryData(): array {
-    $module_path = \Drupal::service('module_handler')->getModule('project_browser')->getPath();
+    $module_path = $this->moduleHandler->getModule('project_browser')->getPath();
     $category_list = Json::decode(file_get_contents($module_path . '/fixtures/category_list.json')) ?? [];
     $categories = [];
     foreach ($category_list as $category) {
diff --git a/src/Plugin/ProjectBrowserSourceBase.php b/src/Plugin/ProjectBrowserSourceBase.php
index e5205dc00..6fe42612b 100644
--- a/src/Plugin/ProjectBrowserSourceBase.php
+++ b/src/Plugin/ProjectBrowserSourceBase.php
@@ -2,8 +2,10 @@
 
 namespace Drupal\project_browser\Plugin;
 
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Plugin\PluginBase;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Defines an abstract base class for a Project Browser source.
@@ -12,7 +14,7 @@ use Drupal\Core\StringTranslation\StringTranslationTrait;
  * @see \Drupal\project_browser\Plugin\ProjectBrowserSourceManager
  * @see plugin_api
  */
-abstract class ProjectBrowserSourceBase extends PluginBase implements ProjectBrowserSourceInterface {
+abstract class ProjectBrowserSourceBase extends PluginBase implements ProjectBrowserSourceInterface, ContainerFactoryPluginInterface {
 
   use StringTranslationTrait;
 
@@ -72,6 +74,17 @@ abstract class ProjectBrowserSourceBase extends PluginBase implements ProjectBro
    */
   const ALL_VALUES_ID = 'all';
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+    );
+  }
+
   /**
    * Returns the available security options that plugins will parse.
    *
diff --git a/src/ProjectBrowserFixtureHelper.php b/src/ProjectBrowserFixtureHelper.php
index 93dabeccd..a7534c9e2 100644
--- a/src/ProjectBrowserFixtureHelper.php
+++ b/src/ProjectBrowserFixtureHelper.php
@@ -7,6 +7,7 @@ use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\State\StateInterface;
 use GuzzleHttp\ClientInterface;
 use GuzzleHttp\Exception\RequestException;
@@ -28,11 +29,14 @@ class ProjectBrowserFixtureHelper {
    *   The state interface.
    * @param \GuzzleHttp\ClientInterface $httpClient
    *   Client for fetching external data.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
+   *   The module handler.
    */
   public function __construct(
     private readonly Connection $connection,
     private readonly StateInterface $state,
     private readonly ClientInterface $httpClient,
+    private readonly ModuleHandlerInterface $moduleHandler,
   ) {}
 
   /**
@@ -383,7 +387,7 @@ class ProjectBrowserFixtureHelper {
    */
   protected function requestProjectReleases(string $project): array {
     $url = "https://updates.drupal.org/release-history/$project/current";
-    $response = \Drupal::httpClient()->request('GET', $url);
+    $response = $this->httpClient->request('GET', $url);
     if ($response->getStatusCode() !== 200) {
       throw new \RuntimeException("Request to $url failed, returned {$response->getStatusCode()} with reason: {$response->getReasonPhrase()}");
     }
@@ -518,7 +522,7 @@ class ProjectBrowserFixtureHelper {
     }
 
     if ($sandbox['#finished'] === TRUE) {
-      $module_path = \Drupal::service('module_handler')->getModule('project_browser')->getPath();
+      $module_path = $this->moduleHandler->getModule('project_browser')->getPath();
       file_put_contents($module_path . '/fixtures/project_data.json', '[]');
       file_put_contents($module_path . '/fixtures/categories.json', '[]');
 
diff --git a/tests/modules/project_browser_test/project_browser_test.services.yml b/tests/modules/project_browser_test/project_browser_test.services.yml
index e157cf9c8..b473fc730 100644
--- a/tests/modules/project_browser_test/project_browser_test.services.yml
+++ b/tests/modules/project_browser_test/project_browser_test.services.yml
@@ -2,8 +2,9 @@ services:
   project_browser_test.time:
     class: Drupal\project_browser_test\Datetime\TestTime
     decorates: datetime.time
-    arguments: ['@project_browser_test.time.inner','@request_stack']
+    arguments: ['@project_browser_test.time.inner', '@request_stack', '@state']
   project_browser_test.drupalorg_client_middleware:
     class: Drupal\project_browser_test\DrupalOrgClientMiddleware
     tags:
       - { name: http_client_middleware }
+    arguments: ['@module_handler']
diff --git a/tests/modules/project_browser_test/src/Datetime/TestTime.php b/tests/modules/project_browser_test/src/Datetime/TestTime.php
index 320f18fe1..f1edbe15a 100644
--- a/tests/modules/project_browser_test/src/Datetime/TestTime.php
+++ b/tests/modules/project_browser_test/src/Datetime/TestTime.php
@@ -3,6 +3,7 @@
 namespace Drupal\project_browser_test\Datetime;
 
 use Drupal\Component\Datetime\Time;
+use Drupal\Core\State\StateInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
@@ -24,8 +25,14 @@ class TestTime extends Time {
    *   The time service.
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
    *   The RequestStack object.
+   * @param \Drupal\Core\State\StateInterface $state
+   *   The state storage service.
    */
-  public function __construct(Time $time, RequestStack $request_stack) {
+  public function __construct(
+    Time $time,
+    RequestStack $request_stack,
+    protected readonly StateInterface $state,
+  ) {
     $this->decoratorTime = $time;
     parent::__construct($request_stack);
   }
@@ -34,7 +41,7 @@ class TestTime extends Time {
    * {@inheritdoc}
    */
   public function getRequestTime(): int {
-    if ($faked_date = \Drupal::state()->get('project_browser_test.fake_date_time')) {
+    if ($faked_date = $this->state->get('project_browser_test.fake_date_time')) {
       return \DateTime::createFromFormat('U', $faked_date)->getTimestamp();
     }
     return $this->decoratorTime->getRequestTime();
diff --git a/tests/modules/project_browser_test/src/DrupalOrgClientMiddleware.php b/tests/modules/project_browser_test/src/DrupalOrgClientMiddleware.php
index f293376c3..06f23ca17 100644
--- a/tests/modules/project_browser_test/src/DrupalOrgClientMiddleware.php
+++ b/tests/modules/project_browser_test/src/DrupalOrgClientMiddleware.php
@@ -3,6 +3,7 @@
 namespace Drupal\project_browser_test;
 
 use Drupal\Component\Serialization\Json;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\project_browser_test\Plugin\ProjectBrowserSource\DrupalDotOrgJsonApi;
 use GuzzleHttp\Promise\FulfilledPromise;
 use GuzzleHttp\Psr7\Response;
@@ -17,6 +18,17 @@ use Psr\Http\Message\RequestInterface;
  */
 class DrupalOrgClientMiddleware {
 
+  /**
+   * Constructor for settings form.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
+   *   The module handler.
+   */
+  public function __construct(
+    private readonly ModuleHandlerInterface $moduleHandler,
+  ) {
+  }
+
   /**
    * Invoked method that returns a promise.
    */
@@ -91,7 +103,7 @@ class DrupalOrgClientMiddleware {
           ];
 
           if (isset($path_to_fixture[$relevant_path])) {
-            $module_path = \Drupal::service('module_handler')->getModule('project_browser')->getPath();
+            $module_path = $this->moduleHandler->getModule('project_browser')->getPath();
             $data = file_get_contents($module_path . '/tests/fixtures/drupalorg_jsonapi/' . $path_to_fixture[$relevant_path]);
             $json_response = new Response(200, [], $data);
             return new FulfilledPromise($json_response);
diff --git a/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php b/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php
index 661aaeef6..de56aaed9 100644
--- a/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php
+++ b/tests/modules/project_browser_test/src/Plugin/ProjectBrowserSource/DrupalDotOrgJsonApi.php
@@ -4,7 +4,6 @@ namespace Drupal\project_browser_test\Plugin\ProjectBrowserSource;
 
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\Html;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
 use Drupal\project_browser\ProjectBrowser\Project;
@@ -24,7 +23,7 @@ use Symfony\Component\HttpFoundation\Response;
  *   description = @Translation("Gets project and related information from JSON:API endpoint"),
  * )
  */
-class DrupalDotOrgJsonApi extends ProjectBrowserSourceBase implements ContainerFactoryPluginInterface {
+class DrupalDotOrgJsonApi extends ProjectBrowserSourceBase {
 
   use StringTranslationTrait;
 
diff --git a/tests/src/Unit/MockDrupalDotOrgTest.php b/tests/src/Unit/MockDrupalDotOrgTest.php
index edad7c559..6d1896bd0 100644
--- a/tests/src/Unit/MockDrupalDotOrgTest.php
+++ b/tests/src/Unit/MockDrupalDotOrgTest.php
@@ -4,6 +4,7 @@ namespace Drupal\Tests\project_browser\Unit;
 
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\project_browser\Plugin\ProjectBrowserSource\MockDrupalDotOrg;
 use Drupal\Tests\UnitTestCase;
 use GuzzleHttp\ClientInterface;
@@ -51,6 +52,13 @@ class MockDrupalDotOrgTest extends UnitTestCase {
    */
   protected $state;
 
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected ModuleHandlerInterface $moduleHandler;
+
   /**
    * ProjectBrowser cache bin.
    *
@@ -69,11 +77,12 @@ class MockDrupalDotOrgTest extends UnitTestCase {
     $this->httpClient = $this->createMock(ClientInterface::class);
     $this->cacheBin = $this->createMock(CacheBackendInterface::class);
     $this->state = $this->createMock('\Drupal\Core\State\StateInterface');
+    $this->moduleHandler = $this->createMock(ModuleHandlerInterface::class);
 
     $configuration = [];
     $plugin_id = $this->randomMachineName();
     $plugin_definition = [];
-    $this->plugin = new MockDrupalDotOrg($configuration, $plugin_id, $plugin_definition, $this->logger, $this->database, $this->httpClient, $this->state, $this->cacheBin);
+    $this->plugin = new MockDrupalDotOrg($configuration, $plugin_id, $plugin_definition, $this->logger, $this->database, $this->httpClient, $this->state, $this->cacheBin, $this->moduleHandler);
   }
 
   /**
diff --git a/tests/src/Unit/ProjectBrowserFixtureHelperTest.php b/tests/src/Unit/ProjectBrowserFixtureHelperTest.php
index 677603676..c8f8c04a7 100644
--- a/tests/src/Unit/ProjectBrowserFixtureHelperTest.php
+++ b/tests/src/Unit/ProjectBrowserFixtureHelperTest.php
@@ -5,6 +5,7 @@ namespace Drupal\Tests\project_browser\Unit;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Database\Query\Insert;
 use Drupal\Core\Database\Query\Truncate;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\State\StateInterface;
 use Drupal\project_browser\ProjectBrowserFixtureHelper;
 use Drupal\Tests\UnitTestCase;
@@ -138,7 +139,9 @@ EOS,
 
     $http_client = $this->prophesize(ClientInterface::class);
 
-    $class = new ProjectBrowserFixtureHelper($connection->reveal(), $state->reveal(), $http_client->reveal());
+    $module_handler = $this->prophesize(ModuleHandlerInterface::class);
+
+    $class = new ProjectBrowserFixtureHelper($connection->reveal(), $state->reveal(), $http_client->reveal(), $module_handler->reveal());
     $class->populateFromFixture(vfsStream::url('root/fixtures/projects.json'), vfsStream::url('root/fixtures/categories.json'));
   }
 
-- 
GitLab