Page MenuHomePhorge

No OneTemporary

Size
10 KB
Referenced Files
None
Subscribers
None
diff --git a/includes/Html/Html.php b/includes/Html/Html.php
index 8612456b8e0..ef32e655bfa 100644
--- a/includes/Html/Html.php
+++ b/includes/Html/Html.php
@@ -443,6 +443,54 @@ class Html {
return $attribs;
}
+ /**
+ * Convert a value for a 'class' attribute in a format accepted by Html::element() and similar
+ * methods to a single string.
+ *
+ * This method may also be used for any other space-separated attribute, such as 'rel'.
+ *
+ * @param array|string $classes
+ * @return string
+ */
+ public static function expandClassList( $classes ): string {
+ // Convert into correct array. Array can contain space-separated
+ // values. Implode/explode to get those into the main array as well.
+ if ( is_array( $classes ) ) {
+ // If input wasn't an array, we can skip this step
+ $arrayValue = [];
+ foreach ( $classes as $k => $v ) {
+ if ( is_string( $v ) ) {
+ // String values should be normal `[ 'foo' ]`
+ // Just append them
+ if ( !isset( $classes[$v] ) ) {
+ // As a special case don't set 'foo' if a
+ // separate 'foo' => true/false exists in the array
+ // keys should be authoritative
+ foreach ( explode( ' ', $v ) as $part ) {
+ // Normalize spacing by fixing up cases where people used
+ // more than 1 space and/or a trailing/leading space
+ if ( $part !== '' && $part !== ' ' ) {
+ $arrayValue[] = $part;
+ }
+ }
+ }
+ } elseif ( $v ) {
+ // If the value is truthy but not a string this is likely
+ // an [ 'foo' => true ], falsy values don't add strings
+ $arrayValue[] = $k;
+ }
+ }
+ } else {
+ $arrayValue = explode( ' ', $classes );
+ // Normalize spacing by fixing up cases where people used
+ // more than 1 space and/or a trailing/leading space
+ $arrayValue = array_diff( $arrayValue, [ '', ' ' ] );
+ }
+
+ // Remove duplicates and create the string
+ return implode( ' ', array_unique( $arrayValue ) );
+ }
+
/**
* Given an associative array of element attributes, generate a string
* to stick after the element name in HTML output. Like [ 'href' =>
@@ -513,43 +561,7 @@ class Html {
// Specific features for attributes that allow a list of space-separated values
if ( isset( $spaceSeparatedListAttributes[$key] ) ) {
// Apply some normalization and remove duplicates
-
- // Convert into correct array. Array can contain space-separated
- // values. Implode/explode to get those into the main array as well.
- if ( is_array( $value ) ) {
- // If input wasn't an array, we can skip this step
- $arrayValue = [];
- foreach ( $value as $k => $v ) {
- if ( is_string( $v ) ) {
- // String values should be normal `[ 'foo' ]`
- // Just append them
- if ( !isset( $value[$v] ) ) {
- // As a special case don't set 'foo' if a
- // separate 'foo' => true/false exists in the array
- // keys should be authoritative
- foreach ( explode( ' ', $v ) as $part ) {
- // Normalize spacing by fixing up cases where people used
- // more than 1 space and/or a trailing/leading space
- if ( $part !== '' && $part !== ' ' ) {
- $arrayValue[] = $part;
- }
- }
- }
- } elseif ( $v ) {
- // If the value is truthy but not a string this is likely
- // an [ 'foo' => true ], falsy values don't add strings
- $arrayValue[] = $k;
- }
- }
- } else {
- $arrayValue = explode( ' ', $value );
- // Normalize spacing by fixing up cases where people used
- // more than 1 space and/or a trailing/leading space
- $arrayValue = array_diff( $arrayValue, [ '', ' ' ] );
- }
-
- // Remove duplicates and create the string
- $value = implode( ' ', array_unique( $arrayValue ) );
+ $value = self::expandClassList( $value );
// Optimization: Skip below boolAttribs check and jump straight
// to its `else` block. The current $spaceSeparatedListAttributes
diff --git a/includes/Output/OutputPage.php b/includes/Output/OutputPage.php
index d0f99772fad..9c1ef3d615f 100644
--- a/includes/Output/OutputPage.php
+++ b/includes/Output/OutputPage.php
@@ -3958,9 +3958,9 @@ class OutputPage extends ContextSource {
}
$bodyAttrs = [];
- // While the implode() is not strictly needed, it's used for backwards compatibility
+ // While the expandClassList() is not strictly needed, it's used for backwards compatibility
// (this used to be built as a string and hooks likely still expect that).
- $bodyAttrs['class'] = implode( ' ', $bodyClasses );
+ $bodyAttrs['class'] = Html::expandClassList( $bodyClasses );
$this->getHookRunner()->onOutputPageBodyAttributes( $this, $sk, $bodyAttrs );
diff --git a/includes/linker/LinkRenderer.php b/includes/linker/LinkRenderer.php
index b61931f1ce5..cf32b779b51 100644
--- a/includes/linker/LinkRenderer.php
+++ b/includes/linker/LinkRenderer.php
@@ -388,14 +388,13 @@ class LinkRenderer {
public function makeExternalLink(
string $url, $text, $title, $linktype = '', $attribs = []
) {
- $class = 'external';
+ $attribs['class'] ??= [];
+ Html::addClass( $attribs['class'], 'external' );
if ( $linktype ) {
- $class .= " $linktype";
+ Html::addClass( $attribs['class'], $linktype );
}
- if ( isset( $attribs['class'] ) && $attribs['class'] ) {
- $class .= " {$attribs['class']}";
- }
- $attribs['class'] = $class;
+ // Stringify attributes for hook compatibility
+ $attribs['class'] = Html::expandClassList( $attribs['class'] );
if ( $text instanceof Message ) {
$text = $text->escaped();
@@ -406,14 +405,10 @@ class LinkRenderer {
}
$newRel = Parser::getExternalLinkRel( $url, $title );
- if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
- $attribs['rel'] = $newRel;
- } elseif ( $newRel !== null ) {
- // Merge the rel attributes.
- $newRels = explode( ' ', $newRel );
- $oldRels = explode( ' ', $attribs['rel'] );
- $combined = array_unique( array_merge( $newRels, $oldRels ) );
- $attribs['rel'] = implode( ' ', $combined );
+ if ( $newRel !== null ) {
+ $attribs['rel'] ??= [];
+ Html::addClass( $attribs['rel'], $newRel );
+ $attribs['rel'] = Html::expandClassList( $attribs['rel'] );
}
$link = '';
$success = $this->hookRunner->onLinkerMakeExternalLink(
diff --git a/includes/skins/components/SkinComponentLink.php b/includes/skins/components/SkinComponentLink.php
index 57d42651bfc..437ec001bff 100644
--- a/includes/skins/components/SkinComponentLink.php
+++ b/includes/skins/components/SkinComponentLink.php
@@ -188,7 +188,7 @@ class SkinComponentLink implements SkinComponent {
$class, $options['link-class']
);
}
- $attrs['class'] = is_array( $class ) ? implode( ' ', $class ) : $class;
+ $attrs['class'] = Html::expandClassList( $class );
foreach ( $attrs as $key => $value ) {
if ( $value === null ) {
continue;
@@ -214,7 +214,7 @@ class SkinComponentLink implements SkinComponent {
'text' => trim( $text ),
];
if ( $classAsProperty ) {
- $data['class'] = $attrs['class'] ?? '';
+ $data['class'] = $attrs['class'];
}
return $data;
}
diff --git a/includes/skins/components/SkinComponentListItem.php b/includes/skins/components/SkinComponentListItem.php
index 58ff49be704..cd127985ff7 100644
--- a/includes/skins/components/SkinComponentListItem.php
+++ b/includes/skins/components/SkinComponentListItem.php
@@ -192,9 +192,7 @@ class SkinComponentListItem implements SkinComponent {
$attrs['title'] = $item['itemtitle'];
}
// Making sure we always have strings as class values
- $classes = is_array( $attrs['class'] ) ?
- implode( ' ', $attrs['class'] ) :
- $attrs['class'] ?? null;
+ $classes = Html::expandClassList( $attrs['class'] );
return [
'tag' => $options['tag'] ?? 'li',
'attrs' => $attrs,
diff --git a/tests/phpunit/includes/Html/HtmlTest.php b/tests/phpunit/includes/Html/HtmlTest.php
index 74e3104d77e..78f11c83efe 100644
--- a/tests/phpunit/includes/Html/HtmlTest.php
+++ b/tests/phpunit/includes/Html/HtmlTest.php
@@ -247,6 +247,10 @@ class HtmlTest extends MediaWikiIntegrationTestCase {
' zero="0"',
[ 'zero' => 0 ]
];
+ yield 'Integration test for space-separated attribs' => [
+ ' class="a b"',
+ [ 'class' => [ 'a', 'b' ] ]
+ ];
}
/**
@@ -273,35 +277,35 @@ class HtmlTest extends MediaWikiIntegrationTestCase {
// $expect, $classes
// string values
yield 'Normalization should strip redundant spaces' => [
- ' class="redundant spaces here"',
+ 'redundant spaces here',
' redundant spaces here '
];
yield 'Normalization should remove duplicates in string-lists' => [
- ' class="foo bar"',
+ 'foo bar',
'foo bar foo bar bar'
];
// array values
yield 'Value with an empty array' => [
- ' class=""',
+ '',
[]
];
yield 'Array with null, empty string and spaces' => [
- ' class=""',
+ '',
[ null, '', ' ', ' ' ]
];
yield 'Normalization should remove duplicates in the array' => [
- ' class="foo bar"',
+ 'foo bar',
[ 'foo', 'bar', 'foo', 'bar', 'bar' ]
];
yield 'Normalization should remove duplicates in string-lists in the array' => [
- ' class="foo bar"',
- [ 'foo bar', 'bar foo', 'foo', 'bar bar' ]
+ 'foo bar baz',
+ [ 'foo bar', 'bar foo', 'foo', 'bar bar', 'baz' ]
];
// Feature added in r96188 - pass attributes values as a PHP array
// only applies to class, rel, and accesskey
yield 'Associative array' => [
- ' class="booltrue one"',
+ 'booltrue one',
[
'booltrue' => true,
'one' => 1,
@@ -319,7 +323,7 @@ class HtmlTest extends MediaWikiIntegrationTestCase {
// We could pass a "class" the values: 'GREEN' and [ 'GREEN' => false ]
// The latter will take precedence
yield 'Duplicate keys' => [
- ' class=""',
+ '',
[
'GREEN',
'GREEN' => false,
@@ -329,16 +333,12 @@ class HtmlTest extends MediaWikiIntegrationTestCase {
}
/**
- * Html::expandAttributes has special features for HTML
- * attributes that use space separated lists and also
- * allows arrays to be used as values.
- *
* @dataProvider provideExpandAttributesClass
*/
public function testExpandAttributesClass( string $expect, $classes ) {
$this->assertEquals(
$expect,
- Html::expandAttributes( [ 'class' => $classes ] )
+ Html::expandClassList( $classes )
);
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jul 5, 5:31 AM (7 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
227481
Default Alt Text
(10 KB)

Event Timeline