diff --git a/core/lib/Drupal/Component/Utility/FilterArray.php b/core/lib/Drupal/Component/Utility/FilterArray.php new file mode 100644 index 0000000000000000000000000000000000000000..172348df11638edc41479ee6edf787f5b32e6bc3 --- /dev/null +++ b/core/lib/Drupal/Component/Utility/FilterArray.php @@ -0,0 +1,31 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Component\Utility; + +/** + * Provides methods to filter arrays. + * + * @ingroup utility + */ +class FilterArray { + + /** + * Removes empty strings from an array. + * + * This method removes all empty strings from the input array. This is + * particularly useful to preserve 0 whilst filtering other falsy values. The + * values are first cast to a string before comparison. + * + * @param array $value + * The array to filter. + * + * @return array + * The filtered array. + */ + public static function removeEmptyStrings(array $value): array { + return array_filter($value, static fn ($item) => (string) $item !== ''); + } + +} diff --git a/core/lib/Drupal/Core/Datetime/Element/Datelist.php b/core/lib/Drupal/Core/Datetime/Element/Datelist.php index 1b918131a8707327bed6305cdfc6563b3f62e75f..c94ecfa58c3f56e7722031a1d00b9ccc405d6910 100644 --- a/core/lib/Drupal/Core/Datetime/Element/Datelist.php +++ b/core/lib/Drupal/Core/Datetime/Element/Datelist.php @@ -2,6 +2,7 @@ namespace Drupal\Core\Datetime\Element; +use Drupal\Component\Utility\FilterArray; use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\Variable; use Drupal\Core\Datetime\DateHelper; @@ -345,7 +346,7 @@ protected static function checkEmptyInputs($input, $parts) { // \Drupal\Core\Datetime\Element\Datelist::valueCallback(). unset($input['object']); // Filters out empty array values, any valid value would have a string length. - $filtered_input = array_filter($input, 'strlen'); + $filtered_input = FilterArray::removeEmptyStrings($input); return array_diff($parts, array_keys($filtered_input)); } diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php index ee648a50f86f98de5478824bfd56e2f4839a4407..9454862ff979225a37f998a25b191a381624f18d 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php @@ -2,6 +2,7 @@ namespace Drupal\Core\TypedData\Plugin\DataType; +use Drupal\Component\Utility\FilterArray; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\TypedData\Attribute\DataType; use Drupal\Core\TypedData\ComplexDataInterface; @@ -92,7 +93,7 @@ public function getString() { $strings[] = $item->getString(); } // Remove any empty strings resulting from empty items. - return implode(', ', array_filter($strings, 'mb_strlen')); + return implode(', ', FilterArray::removeEmptyStrings($strings)); } /** diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php index e08e9312e0c7760d6dc5e6b8569e42e707259d77..16aaa68fdc448711c8a1e842b097d92b6b7569b5 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/Map.php @@ -2,6 +2,7 @@ namespace Drupal\Core\TypedData\Plugin\DataType; +use Drupal\Component\Utility\FilterArray; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\TypedData\Attribute\DataType; use Drupal\Core\TypedData\ComplexDataInterface; @@ -106,7 +107,7 @@ public function getString() { $strings[] = $property->getString(); } // Remove any empty strings resulting from empty items. - return implode(', ', array_filter($strings, 'mb_strlen')); + return implode(', ', FilterArray::removeEmptyStrings($strings)); } /** diff --git a/core/modules/field/src/Plugin/migrate/process/d6/FieldInstanceOptionTranslation.php b/core/modules/field/src/Plugin/migrate/process/d6/FieldInstanceOptionTranslation.php index 6342c027de09f03cad039d6949b55d78f329595c..9ddca549026558f18605f811b4ae86903d2a4b71 100644 --- a/core/modules/field/src/Plugin/migrate/process/d6/FieldInstanceOptionTranslation.php +++ b/core/modules/field/src/Plugin/migrate/process/d6/FieldInstanceOptionTranslation.php @@ -2,6 +2,7 @@ namespace Drupal\field\Plugin\migrate\process\d6; +use Drupal\Component\Utility\FilterArray; use Drupal\migrate\Attribute\MigrateProcess; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; @@ -27,7 +28,7 @@ public function transform($value, MigrateExecutableInterface $migrate_executable if (isset($global_settings['allowed_values'])) { $list = explode("\n", $global_settings['allowed_values']); $list = array_map('trim', $list); - $list = array_filter($list, 'strlen'); + $list = FilterArray::removeEmptyStrings($list); switch ($field_type) { case 'boolean'; $option = preg_replace('/^option_/', '', $row->getSourceProperty('property')); diff --git a/core/modules/field/src/Plugin/migrate/process/d6/FieldOptionTranslation.php b/core/modules/field/src/Plugin/migrate/process/d6/FieldOptionTranslation.php index 6c6cf100fa9307c5232540d46aa99d6db46fca92..94842bda74e03f546437b5957a8437da2dd516b4 100644 --- a/core/modules/field/src/Plugin/migrate/process/d6/FieldOptionTranslation.php +++ b/core/modules/field/src/Plugin/migrate/process/d6/FieldOptionTranslation.php @@ -2,6 +2,7 @@ namespace Drupal\field\Plugin\migrate\process\d6; +use Drupal\Component\Utility\FilterArray; use Drupal\migrate\Attribute\MigrateProcess; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; @@ -29,7 +30,7 @@ public function transform($value, MigrateExecutableInterface $migrate_executable if (isset($global_settings['allowed_values'])) { $list = explode("\n", $global_settings['allowed_values']); $list = array_map('trim', $list); - $list = array_filter($list, 'strlen'); + $list = FilterArray::removeEmptyStrings($list); switch ($field_type) { case 'list_string': case 'list_integer': diff --git a/core/modules/field/src/Plugin/migrate/process/d6/FieldSettings.php b/core/modules/field/src/Plugin/migrate/process/d6/FieldSettings.php index ce247d025d993036cc99833f6a3815b6333bf4cc..ffdb2e6fcb57039a17b013934f70777ca7e9b2b7 100644 --- a/core/modules/field/src/Plugin/migrate/process/d6/FieldSettings.php +++ b/core/modules/field/src/Plugin/migrate/process/d6/FieldSettings.php @@ -2,6 +2,7 @@ namespace Drupal\field\Plugin\migrate\process\d6; +use Drupal\Component\Utility\FilterArray; use Drupal\migrate\Attribute\MigrateProcess; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; @@ -50,7 +51,7 @@ public function getSettings($field_type, $global_settings, $original_field_type if (isset($global_settings['allowed_values'])) { $list = explode("\n", $global_settings['allowed_values']); $list = array_map('trim', $list); - $list = array_filter($list, 'strlen'); + $list = FilterArray::removeEmptyStrings($list); switch ($field_type) { case 'list_string': case 'list_integer': diff --git a/core/modules/sqlite/src/Driver/Database/sqlite/Connection.php b/core/modules/sqlite/src/Driver/Database/sqlite/Connection.php index 8e6d06b6fe2f2fbf55bfc2b489868b429a63458a..53c2fa0c1fe0f45dd57ffb10e5be232d11434234 100644 --- a/core/modules/sqlite/src/Driver/Database/sqlite/Connection.php +++ b/core/modules/sqlite/src/Driver/Database/sqlite/Connection.php @@ -2,6 +2,7 @@ namespace Drupal\sqlite\Driver\Database\sqlite; +use Drupal\Component\Utility\FilterArray; use Drupal\Core\Database\Connection as DatabaseConnection; use Drupal\Core\Database\DatabaseNotFoundException; use Drupal\Core\Database\ExceptionHandler; @@ -246,7 +247,7 @@ public static function sqlFunctionGreatest() { */ public static function sqlFunctionLeast() { // Remove all NULL, FALSE and empty strings values but leaves 0 (zero) values. - $values = array_filter(func_get_args(), 'strlen'); + $values = FilterArray::removeEmptyStrings(func_get_args()); return count($values) < 1 ? NULL : min($values); } diff --git a/core/modules/user/src/Plugin/migrate/process/d6/ProfileFieldOptionTranslation.php b/core/modules/user/src/Plugin/migrate/process/d6/ProfileFieldOptionTranslation.php index fd7aea36bce7b23c3fe993540f6aefc2544a74b0..1cd4efff49b9860222168399a4d9a083fdec37af 100644 --- a/core/modules/user/src/Plugin/migrate/process/d6/ProfileFieldOptionTranslation.php +++ b/core/modules/user/src/Plugin/migrate/process/d6/ProfileFieldOptionTranslation.php @@ -2,6 +2,7 @@ namespace Drupal\user\Plugin\migrate\process\d6; +use Drupal\Component\Utility\FilterArray; use Drupal\migrate\Attribute\MigrateProcess; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; @@ -27,7 +28,7 @@ public function transform($value, MigrateExecutableInterface $migrate_executable $allowed_values = []; $list = explode("\n", $translation); $list = array_map('trim', $list); - $list = array_filter($list, 'strlen'); + $list = FilterArray::removeEmptyStrings($list); if ($field_type === 'list_string') { foreach ($list as $value) { $allowed_values[] = ['label' => $value]; diff --git a/core/modules/views/src/Plugin/views/HandlerBase.php b/core/modules/views/src/Plugin/views/HandlerBase.php index 3c576d13d520f108d76780d110383dbd36dea2b7..583efd832c4c2bb84031734e7d0f3cbb47557773 100644 --- a/core/modules/views/src/Plugin/views/HandlerBase.php +++ b/core/modules/views/src/Plugin/views/HandlerBase.php @@ -2,6 +2,7 @@ namespace Drupal\views\Plugin\views; +use Drupal\Component\Utility\FilterArray; use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\UrlHelper; @@ -758,7 +759,7 @@ public static function breakString($str, $force_int = FALSE) { // Filter any empty matches (Like from '++' in a string) and reset the // array keys. 'strlen' is used as the filter callback so we do not lose // 0 values (would otherwise evaluate == FALSE). - $value = array_values(array_filter($value, 'strlen')); + $value = array_values(FilterArray::removeEmptyStrings($value)); if ($force_int) { $value = array_map('intval', $value); diff --git a/core/tests/Drupal/Tests/Component/Utility/FilterArrayTest.php b/core/tests/Drupal/Tests/Component/Utility/FilterArrayTest.php new file mode 100644 index 0000000000000000000000000000000000000000..289b19bfe824ce9a9ab6f503ac03fdd13a452cd6 --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Utility/FilterArrayTest.php @@ -0,0 +1,62 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\Component\Utility; + +use Drupal\Component\Utility\FilterArray; +use PHPUnit\Framework\TestCase; + +/** + * Test filter array functions. + * + * @group Utility + * + * @coversDefaultClass \Drupal\Component\Utility\FilterArray + */ +class FilterArrayTest extends TestCase { + + /** + * Tests removing empty strings. + * + * @dataProvider providerRemoveEmptyStrings + * @covers ::removeEmptyStrings + */ + public function testRemoveEmptyStrings(array $values, array $expected): void { + $this->assertEquals($expected, array_values(FilterArray::removeEmptyStrings($values))); + } + + /** + * Data provider for testRemoveEmptyStrings(). + * + * @see testRemoveEmptyStrings() + */ + public static function providerRemoveEmptyStrings(): \Generator { + yield 'strings' => [ + ['', ' ', '0', 'true', 'false'], + [' ', '0', 'true', 'false'], + ]; + yield 'integers' => [ + [-1, 0, 1], + [-1, 0, 1], + ]; + yield 'null, true, false' => [ + [NULL, TRUE, FALSE], + [TRUE], + ]; + + $stringable = new class implements \Stringable { + + public function __toString(): string { + return 'foo'; + } + + }; + + yield 'non-scalar' => [ + [new $stringable()], + [new $stringable()], + ]; + } + +}