diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php index 7ea9b4c..a410610 100644 --- a/core/lib/Drupal/Core/Template/Attribute.php +++ b/core/lib/Drupal/Core/Template/Attribute.php @@ -150,13 +150,9 @@ public function addClass() { if (isset($this->storage['class']) && $this->storage['class'] instanceOf AttributeArray) { // Merge the values passed in from the class value array. $classes = array_merge($this->storage['class']->value(), $classes); - // Filter out any duplicate values. - $classes = array_unique($classes); $this->storage['class']->exchangeArray($classes); } else { - // Filter out any duplicate values. - $classes = array_unique($classes); $this->offsetSet('class', $classes); } } @@ -185,7 +181,7 @@ public function removeClass() { } // Remove the values passed in from the value array. - $classes = array_diff($this->storage['class']->value(), $classes); + $classes = array_values(array_diff($this->storage['class']->value(), $classes)); $this->storage['class']->exchangeArray($classes); } return $this; diff --git a/core/lib/Drupal/Core/Template/AttributeArray.php b/core/lib/Drupal/Core/Template/AttributeArray.php index dbe8089..24e9c9d 100644 --- a/core/lib/Drupal/Core/Template/AttributeArray.php +++ b/core/lib/Drupal/Core/Template/AttributeArray.php @@ -30,6 +30,14 @@ class AttributeArray extends AttributeValueBase implements \ArrayAccess, \IteratorAggregate { /** + * Ensures empty array as a result of array_filter will not print '$name=""'. + * + * @see \Drupal\Core\Template\AttributeArray::__toString() + * @see \Drupal\Core\Template\AttributeValueBase::render() + */ + const PRINT_IF_EMPTY_STRING = FALSE; + + /** * Implements ArrayAccess::offsetGet(). */ public function offsetGet($offset) { @@ -67,7 +75,7 @@ public function offsetExists($offset) { */ public function __toString() { // Filter out any empty values before printing. - $this->value = array_filter($this->value); + $this->value = array_unique(array_filter($this->value)); return String::checkPlain(implode(' ', $this->value)); } diff --git a/core/lib/Drupal/Core/Template/AttributeValueBase.php b/core/lib/Drupal/Core/Template/AttributeValueBase.php index 73e6bee..3daf7c5 100644 --- a/core/lib/Drupal/Core/Template/AttributeValueBase.php +++ b/core/lib/Drupal/Core/Template/AttributeValueBase.php @@ -17,6 +17,13 @@ abstract class AttributeValueBase { /** + * Renders '$name=""' if $value is an empty string. + * + * @see \Drupal\Core\Template\AttributeValueBase::render() + */ + const PRINT_IF_EMPTY_STRING = TRUE; + + /** * The value itself. * * @var mixed @@ -48,8 +55,9 @@ public function __construct($name, $value) { * The string representation of the attribute. */ public function render() { - if (isset($this->value)) { - return String::checkPlain($this->name) . '="' . $this . '"'; + $value = (string) $this; + if (isset($this->value) && static::PRINT_IF_EMPTY_STRING || !empty($value)) { + return String::checkPlain($this->name) . '="' . $value . '"'; } } diff --git a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php index 9f2cb50..a02f67b 100644 --- a/core/tests/Drupal/Tests/Core/Template/AttributeTest.php +++ b/core/tests/Drupal/Tests/Core/Template/AttributeTest.php @@ -69,6 +69,25 @@ public function testAddClasses() { $attribute->addClass(); $this->assertEmpty($attribute['class']); + // Test various permutations of adding values to empty Attribute objects. + foreach (array(NULL, FALSE, '', []) as $value) { + // Single value. + $attribute->addClass($value); + $this->assertEmpty((string) $attribute); + + // Multiple values. + $attribute->addClass($value, $value); + $this->assertEmpty((string) $attribute); + + // Single value in array. + $attribute->addClass([$value]); + $this->assertEmpty((string) $attribute); + + // Single value in arrays. + $attribute->addClass([$value], [$value]); + $this->assertEmpty((string) $attribute); + } + // Add one class on empty attribute. $attribute->addClass('banana'); $this->assertArrayEquals(array('banana'), $attribute['class']->value()); @@ -87,7 +106,7 @@ public function testAddClasses() { // Add an array of duplicate classes. $attribute->addClass(array('red', 'green', 'blue'), array('aa', 'aa', 'banana'), 'yy'); - $this->assertArrayEquals(array('banana', 'aa', 'xx', 'yy', 'red', 'green', 'blue'), $attribute['class']->value()); + $this->assertEquals('banana aa xx yy red green blue', (string) $attribute['class']); } /** @@ -95,7 +114,8 @@ public function testAddClasses() { * @covers ::removeClass() */ public function testRemoveClasses() { - $classes = array('example-class', 'aa', 'xx', 'yy', 'red', 'green', 'blue'); + // Add duplicate class to ensure that both duplicates are removed. + $classes = array('example-class', 'aa', 'xx', 'yy', 'red', 'green', 'blue', 'red'); $attribute = new Attribute(array('class' => $classes)); // Remove one class. @@ -109,6 +129,14 @@ public function testRemoveClasses() { // Remove an array of classes. $attribute->removeClass(array('red', 'green', 'blue')); $this->assertNotContains(array('red', 'green', 'blue'), $attribute['class']->value()); + + // Remove a class that does not exist. + $attribute->removeClass('gg'); + $this->assertNotContains(array('gg'), $attribute['class']->value()); + $this->assertArrayEquals(array('aa'), $attribute['class']->value()); + + $attribute->removeClass('aa'); + $this->assertEmpty((string) $attribute); } /**