From 366ba755e97ce147f1b391e019bc5e789c061f73 Mon Sep 17 00:00:00 2001
From: Eli-T <eli-t@516878.no-reply.drupal.org>
Date: Mon, 24 Oct 2022 21:25:20 +0000
Subject: [PATCH] Issue #3311240: New 2.0.x branch for PHP 8.1 / Drupal 9/10

---
 composer.json                                 |  3 +-
 src/Controller/TypedResourceController.php    | 20 +++-----
 src/Form/JsonApiReferenceConfigForm.php       | 10 ++--
 src/JsonApiClient.php                         | 51 ++++++-------------
 src/JsonApiClientInterface.php                | 10 ++--
 .../FieldType/TypedResourceObjectItem.php     | 22 ++++----
 .../TypedResourceObjectAutocompleteWidget.php | 14 ++---
 7 files changed, 52 insertions(+), 78 deletions(-)

diff --git a/composer.json b/composer.json
index 566e08e..15dd9a6 100644
--- a/composer.json
+++ b/composer.json
@@ -5,6 +5,7 @@
   "keywords": ["Drupal"],
   "minimum-stability": "dev",
   "require": {
-    "drupal/core": "^9 || ^10"
+    "drupal/core": "^9 || ^10",
+    "php": ">=8.1"
   }
 }
diff --git a/src/Controller/TypedResourceController.php b/src/Controller/TypedResourceController.php
index 3f764dd..45ab35b 100644
--- a/src/Controller/TypedResourceController.php
+++ b/src/Controller/TypedResourceController.php
@@ -9,33 +9,27 @@ use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
- * Class TypedResourceController.
+ * Class to provide a controller for autocomplete look-ups.
  *
  * @package Drupal\jsonapi_reference\Controller
  */
 class TypedResourceController extends ControllerBase {
 
-  /**
-   * Client to talk to our JSON:API entrypoint.
-   *
-   * @var \Drupal\jsonapi_reference\JsonApiClientInterface
-   */
-  protected $client;
-
   /**
    * TypedResourceController constructor.
    *
    * @param \Drupal\jsonapi_reference\JsonApiClientInterface $client
    *   Client to talk to our JSON:API entrypoint.
    */
-  public function __construct(JsonApiClientInterface $client) {
-    $this->client = $client;
+  public function __construct(
+    protected readonly JsonApiClientInterface $client,
+  ) {
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function create(ContainerInterface $container) {
+  public static function create(ContainerInterface $container): TypedResourceController {
     return new static($container->get('jsonapi_reference.jsonapi_client'));
   }
 
@@ -51,11 +45,11 @@ class TypedResourceController extends ControllerBase {
    *
    * @return \Symfony\Component\HttpFoundation\JsonResponse
    *   JsonResponse containing array of search results.
-   *   Each element in the array conatins a value and label showing the matching
+   *   Each element in the array contains a value and label showing the matching
    *   values of the autocomplete attribute, and the corresponding objects
    *   JSON:API ID.
    */
-  public function autocomplete(Request $request, $resource_object_type, $autocomplete_attribute) {
+  public function autocomplete(Request $request, string $resource_object_type, string $autocomplete_attribute): JsonResponse {
     $response = [];
     $query = $request->query->get('q');
     $search_results = $this->client->search($resource_object_type, $autocomplete_attribute, $query);
diff --git a/src/Form/JsonApiReferenceConfigForm.php b/src/Form/JsonApiReferenceConfigForm.php
index ae0f3b6..75bc402 100644
--- a/src/Form/JsonApiReferenceConfigForm.php
+++ b/src/Form/JsonApiReferenceConfigForm.php
@@ -6,14 +6,14 @@ use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Form\FormStateInterface;
 
 /**
- * Class JsonApiReferenceConfigForm.
+ * Class to provide a form for configuring the module.
  */
 class JsonApiReferenceConfigForm extends ConfigFormBase {
 
   /**
    * {@inheritdoc}
    */
-  protected function getEditableConfigNames() {
+  protected function getEditableConfigNames(): array {
     return [
       'jsonapi_reference.settings',
     ];
@@ -22,14 +22,14 @@ class JsonApiReferenceConfigForm extends ConfigFormBase {
   /**
    * {@inheritdoc}
    */
-  public function getFormId() {
+  public function getFormId(): string {
     return 'json_api_reference_config_form';
   }
 
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state) {
+  public function buildForm(array $form, FormStateInterface $form_state): array {
     $config = $this->config('jsonapi_reference.settings');
     $form['entrypoint'] = [
       '#type' => 'textfield',
@@ -61,7 +61,7 @@ class JsonApiReferenceConfigForm extends ConfigFormBase {
   /**
    * {@inheritdoc}
    */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
+  public function submitForm(array &$form, FormStateInterface $form_state): void {
     parent::submitForm($form, $form_state);
 
     $config = $this->config('jsonapi_reference.settings');
diff --git a/src/JsonApiClient.php b/src/JsonApiClient.php
index 7b8d6df..a2a0aa1 100644
--- a/src/JsonApiClient.php
+++ b/src/JsonApiClient.php
@@ -9,46 +9,26 @@ use GuzzleHttp\Exception\GuzzleException;
 use GuzzleHttp\RequestOptions;
 
 /**
- * Class JsonApiClient.
+ * Client for accessing data through JSON:API.
  *
  * @package Drupal\jsonapi_reference
  */
 class JsonApiClient implements JsonApiClientInterface {
 
-  /**
-   * Client for querying the JSON:API entrypoint.
-   *
-   * @var \GuzzleHttp\ClientInterface
-   */
-  protected $httpClient;
-
   /**
    * Entrypoint to call to retrieve JSONAPI objects.
-   *
-   * @var string
    */
-  protected $entrypoint;
+  protected string $entrypoint;
 
   /**
    * Username to use when authenticating to the entrypoint.
-   *
-   * @var string
    */
-  protected $username;
+  protected string $username;
 
   /**
    * Password to use when authenticating to the entrypoint.
-   *
-   * @var string
    */
-  protected $password;
-
-  /**
-   * Logger channel.
-   *
-   * @var \Drupal\Core\Logger\LoggerChannelInterface
-   */
-  protected $loggerChannel;
+  protected string $password;
 
   /**
    * JsonApiClient constructor.
@@ -60,18 +40,19 @@ class JsonApiClient implements JsonApiClientInterface {
    * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
    *   Config factory.
    */
-  public function __construct(ClientInterface $httpClient, LoggerChannelInterface $loggerChannel, ConfigFactoryInterface $configFactory) {
-    $this->httpClient = $httpClient;
+  public function __construct(
+    protected readonly ClientInterface $httpClient,
+    protected readonly LoggerChannelInterface $loggerChannel,
+    ConfigFactoryInterface $configFactory) {
     $this->entrypoint = $configFactory->get('jsonapi_reference.settings')->get('endpoint');
     $this->username = $configFactory->get('jsonapi_reference.settings')->get('username');
     $this->password = $configFactory->get('jsonapi_reference.settings')->get('password');
-    $this->loggerChannel = $loggerChannel;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function listResourceObjectTypes() {
+  public function listResourceObjectTypes(): array {
     return array_keys($this->getResourceObjectTypes());
   }
 
@@ -83,7 +64,7 @@ class JsonApiClient implements JsonApiClientInterface {
    *   Each object contains a href element pointing to the JSON:API entrypoint
    *   for that specific type.
    */
-  protected function getResourceObjectTypes() {
+  protected function getResourceObjectTypes(): array {
     try {
       $response = $this->httpClient->request('get', $this->entrypoint, $this->getRequestOptions());
     }
@@ -102,7 +83,7 @@ class JsonApiClient implements JsonApiClientInterface {
   /**
    * {@inheritdoc}
    */
-  public function search($resource_object_type, $search_attribute, $query) {
+  public function search($resource_object_type, $search_attribute, $query): array {
     $path = $this->getPathForResourceObjectType($resource_object_type);
     if (!$path) {
       $this->loggerChannel->error('No path for resource object type @type found at JSON:API entrypoint @entrypoint.', [
@@ -149,7 +130,7 @@ class JsonApiClient implements JsonApiClientInterface {
   /**
    * {@inheritdoc}
    */
-  public function searchById($resource_object_type, $id) {
+  public function searchById(string $resource_object_type, string $id): object|NULL {
     $path = $this->getPathForResourceObjectType($resource_object_type);
     if (!$path) {
       return NULL;
@@ -163,10 +144,8 @@ class JsonApiClient implements JsonApiClientInterface {
       $this->loggerChannel->error($e->getMessage());
       return NULL;
     }
-    $response = json_decode($response->getBody()->getContents());
-
-    return $response;
 
+    return json_decode($response->getBody()->getContents());
   }
 
   /**
@@ -175,7 +154,7 @@ class JsonApiClient implements JsonApiClientInterface {
    * @return array
    *   Array of request options, including headers and auth.
    */
-  protected function getRequestOptions() {
+  protected function getRequestOptions(): array {
     $options = [
       RequestOptions::HEADERS => [
         'Content-Type' => 'application/json',
@@ -202,7 +181,7 @@ class JsonApiClient implements JsonApiClientInterface {
    *   URI for the given object type.
    *   Empty string if none found.
    */
-  protected function getPathForResourceObjectType($resource_object_type) {
+  protected function getPathForResourceObjectType(string $resource_object_type): string {
     $types = $this->getResourceObjectTypes();
 
     if (!array_key_exists($resource_object_type, $types)) {
diff --git a/src/JsonApiClientInterface.php b/src/JsonApiClientInterface.php
index fb97fcd..8e2e98e 100644
--- a/src/JsonApiClientInterface.php
+++ b/src/JsonApiClientInterface.php
@@ -3,7 +3,7 @@
 namespace Drupal\jsonapi_reference;
 
 /**
- * Interface JsonApiClientInterface.
+ * Interface for JSON:API clients.
  *
  * @package Drupal\jsonapi_reference
  */
@@ -22,7 +22,7 @@ interface JsonApiClientInterface {
    *     .
    *   ];
    */
-  public function listResourceObjectTypes();
+  public function listResourceObjectTypes(): array;
 
   /**
    * Search the entrypoint for a resource object of a given type.
@@ -39,13 +39,13 @@ interface JsonApiClientInterface {
    *   $search_attribute containing $query, and the values are the IDs of the
    *   corresponding resource objects.
    */
-  public function search($resource_object_type, $search_attribute, $query);
+  public function search(string $resource_object_type, string $search_attribute, string $query): array;
 
   /**
    * Looks up a resource object of a given type by ID.
    *
    * @param string $resource_object_type
-   *   Object type, eg node--article.
+   *   Object type, e.g. node--article.
    * @param string $id
    *   GUID of object we are searching for.
    *
@@ -58,6 +58,6 @@ interface JsonApiClientInterface {
    *   properties.
    *   Returns NULL if no object found.
    */
-  public function searchById($resource_object_type, $id);
+  public function searchById(string $resource_object_type, string $id): object|NULL;
 
 }
diff --git a/src/Plugin/Field/FieldType/TypedResourceObjectItem.php b/src/Plugin/Field/FieldType/TypedResourceObjectItem.php
index c9855a9..03a3c26 100644
--- a/src/Plugin/Field/FieldType/TypedResourceObjectItem.php
+++ b/src/Plugin/Field/FieldType/TypedResourceObjectItem.php
@@ -26,7 +26,7 @@ class TypedResourceObjectItem extends FieldItemBase {
   /**
    * {@inheritdoc}
    */
-  public static function defaultStorageSettings() {
+  public static function defaultStorageSettings(): array {
     return [
       'resource_object_type' => '',
     ] + parent::defaultStorageSettings();
@@ -35,7 +35,7 @@ class TypedResourceObjectItem extends FieldItemBase {
   /**
    * {@inheritdoc}
    */
-  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
+  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition): array {
     // Prevent early t() calls by using the TranslatableMarkup.
     $properties['value'] = DataDefinition::create('string')
       ->setLabel(new TranslatableMarkup('ID'))
@@ -48,8 +48,8 @@ class TypedResourceObjectItem extends FieldItemBase {
   /**
    * {@inheritdoc}
    */
-  public static function schema(FieldStorageDefinitionInterface $field_definition) {
-    $schema = [
+  public static function schema(FieldStorageDefinitionInterface $field_definition): array {
+    return [
       'columns' => [
         'value' => [
           'type' => 'varchar_ascii',
@@ -58,14 +58,12 @@ class TypedResourceObjectItem extends FieldItemBase {
         ],
       ],
     ];
-
-    return $schema;
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getConstraints() {
+  public function getConstraints(): array {
     $constraints = parent::getConstraints();
     $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
     // @todo: do we know these will all be GUIDs? What about a non-Drupal
@@ -78,7 +76,7 @@ class TypedResourceObjectItem extends FieldItemBase {
   /**
    * {@inheritdoc}
    */
-  public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
+  public static function generateSampleValue(FieldDefinitionInterface $field_definition): array {
     $values['value'] = \Drupal::service('uuid')->generate();
     return $values;
   }
@@ -86,10 +84,10 @@ class TypedResourceObjectItem extends FieldItemBase {
   /**
    * {@inheritdoc}
    */
-  public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
+  public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data): array {
     $elements = [];
 
-    /* @var $client \Drupal\jsonapi_reference\JsonApiClientInterface */
+    /** @var \Drupal\jsonapi_reference\JsonApiClientInterface $client */
     $client = \Drupal::service('jsonapi_reference.jsonapi_client');
     $types = $client->listResourceObjectTypes();
 
@@ -108,8 +106,10 @@ class TypedResourceObjectItem extends FieldItemBase {
 
   /**
    * {@inheritdoc}
+   *
+   * @throws \Drupal\Core\TypedData\Exception\MissingDataException
    */
-  public function isEmpty() {
+  public function isEmpty(): bool {
     $value = $this->get('value')->getValue();
     return $value === NULL || $value === '';
   }
diff --git a/src/Plugin/Field/FieldWidget/TypedResourceObjectAutocompleteWidget.php b/src/Plugin/Field/FieldWidget/TypedResourceObjectAutocompleteWidget.php
index 3cb1d47..be9a592 100644
--- a/src/Plugin/Field/FieldWidget/TypedResourceObjectAutocompleteWidget.php
+++ b/src/Plugin/Field/FieldWidget/TypedResourceObjectAutocompleteWidget.php
@@ -23,7 +23,7 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase {
   /**
    * {@inheritdoc}
    */
-  public static function defaultSettings() {
+  public static function defaultSettings(): array {
     return [
       'autocomplete_attribute' => 'title',
     ] + parent::defaultSettings();
@@ -32,7 +32,7 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase {
   /**
    * {@inheritdoc}
    */
-  public function settingsForm(array $form, FormStateInterface $form_state) {
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
     $element['autocomplete_attribute'] = [
       '#type' => 'textfield',
       '#title' => $this->t('Autocomplete attribute'),
@@ -47,7 +47,7 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase {
   /**
    * {@inheritdoc}
    */
-  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state): array {
     $field = $items->getFieldDefinition();
     $field_label = $field->getLabel();
     $field_settings = $field->getSettings();
@@ -87,11 +87,11 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase {
    * @return mixed
    *   The value to assign to the element.
    */
-  public static function valueCallback(array $element, $input, FormStateInterface $form_state) {
+  public static function valueCallback(array $element, mixed $input, FormStateInterface $form_state): mixed {
     $value = '';
 
-    // If we're rebuilding the form due to AJAX (eg adding another item to the
-    // field), just keep the input as is.
+    // If we're rebuilding the form due to AJAX (for example, adding another
+    // item to the field), just keep the input as is.
     if ($form_state->isRebuilding() && $input) {
       return $input;
     }
@@ -116,7 +116,7 @@ class TypedResourceObjectAutocompleteWidget extends WidgetBase {
     // Otherwise we need to convert the other way - for the given id..
     if ($element['#default_value']) {
       // ..look it up via jsonapi and format with the title.
-      /* @var $client \Drupal\jsonapi_reference\JsonApiClientInterface */
+      /** @var \Drupal\jsonapi_reference\JsonApiClientInterface $client */
       $client = \Drupal::service('jsonapi_reference.jsonapi_client');
       $jsonapi_object = $client->searchById($element['#resource_object_type'], $element['#default_value']);
       if ($jsonapi_object) {
-- 
GitLab