From 29e324693ea41b67042dc400cfe011242adf15ad Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Thu, 7 May 2020 19:44:17 +0100
Subject: [PATCH] =?UTF-8?q?Issue=20#2988309=20by=20Krzysztof=20Doma=C5=84s?=
 =?UTF-8?q?ki,=20Berdir,=20hchonov:=20Ensure=20that=20all=20field=20types?=
 =?UTF-8?q?=20return=20TRUE=20on=20equals()=20for=20the=20same=20values?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 core/lib/Drupal/Core/Field/FieldItemList.php  |  9 +++++++++
 .../Tests/Core/Field/FieldItemListTest.php    | 19 +++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php
index 4bab6f40ac50..f73872baa36e 100644
--- a/core/lib/Drupal/Core/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -393,6 +393,15 @@ public function equals(FieldItemListInterface $list_to_compare) {
     $callback = function (&$value) use ($non_computed_properties) {
       if (is_array($value)) {
         $value = array_intersect_key($value, $non_computed_properties);
+
+        // Also filter out properties with a NULL value as they might exist in
+        // one field item and not in the other, depending on how the values are
+        // set. Do not filter out empty strings or other false-y values as e.g.
+        // a NULL or FALSE in a boolean field is not the same.
+        $value = array_filter($value, function ($property) {
+          return $property !== NULL;
+        });
+
         ksort($value);
       }
     };
diff --git a/core/tests/Drupal/Tests/Core/Field/FieldItemListTest.php b/core/tests/Drupal/Tests/Core/Field/FieldItemListTest.php
index a5cc7e352dcb..fbd522230316 100644
--- a/core/tests/Drupal/Tests/Core/Field/FieldItemListTest.php
+++ b/core/tests/Drupal/Tests/Core/Field/FieldItemListTest.php
@@ -135,6 +135,25 @@ public function providerTestEquals() {
     // not exist ('3').
     $datasets[] = [TRUE, $field_item_h, $field_item_i];
 
+    /** @var \Drupal\Core\Field\FieldItemBase  $field_item_j */
+    $field_item_j = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE);
+    $field_item_j->setValue(['0' => 1]);
+    /** @var \Drupal\Core\Field\FieldItemBase  $field_item_k */
+    $field_item_k = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE);
+    $field_item_k->setValue(['0' => 1, '1' => NULL]);
+    /** @var \Drupal\Core\Field\FieldItemBase  $field_item_l */
+    $field_item_l = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE);
+    $field_item_l->setValue(['0' => 1, '1' => FALSE]);
+    /** @var \Drupal\Core\Field\FieldItemBase  $field_item_m */
+    $field_item_m = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE);
+    $field_item_m->setValue(['0' => 1, '1' => '']);
+
+    // Tests filter properties with a NULL value. Empty strings or other false-y
+    // values are not filtered.
+    $datasets[] = [TRUE, $field_item_j, $field_item_k];
+    $datasets[] = [FALSE, $field_item_j, $field_item_l];
+    $datasets[] = [FALSE, $field_item_j, $field_item_m];
+
     return $datasets;
   }
 
-- 
GitLab