Commit 9fd632c9 authored by webchick's avatar webchick

Issue #2448765 by nlisgo, Damien Tournoud, vlad.n, rteijeiro, Berdir, Fabianx,...

Issue #2448765 by nlisgo, Damien Tournoud, vlad.n, rteijeiro, Berdir, Fabianx, dawehner: Element::children sort order undefined and slower than it could be - This makes tests fail in PHP7
parent 3bd051fb
......@@ -77,15 +77,23 @@ public static function children(array &$elements, $sort = FALSE) {
$sort = isset($elements['#sorted']) ? !$elements['#sorted'] : $sort;
// Filter out properties from the element, leaving only children.
$children = array();
$count = count($elements);
$child_weights = array();
$i = 0;
$sortable = FALSE;
foreach ($elements as $key => $value) {
if ($key === '' || $key[0] !== '#') {
if (is_array($value)) {
$children[$key] = $value;
if (isset($value['#weight'])) {
$weight = $value['#weight'];
$sortable = TRUE;
}
else {
$weight = 0;
}
// Supports weight with up to three digit precision and conserve
// the insertion order.
$child_weights[$key] = floor($weight * 1000) + $i / $count;
}
// Only trigger an error if the value is not null.
// @see http://drupal.org/node/1283892
......@@ -93,21 +101,24 @@ public static function children(array &$elements, $sort = FALSE) {
trigger_error(SafeMarkup::format('"@key" is an invalid render array key', array('@key' => $key)), E_USER_ERROR);
}
}
$i++;
}
// Sort the children if necessary.
if ($sort && $sortable) {
uasort($children, 'Drupal\Component\Utility\SortArray::sortByWeightProperty');
asort($child_weights);
// Put the sorted children back into $elements in the correct order, to
// preserve sorting if the same element is passed through
// \Drupal\Core\Render\Element::children() twice.
foreach ($children as $key => $child) {
foreach ($child_weights as $key => $weight) {
$value = $elements[$key];
unset($elements[$key]);
$elements[$key] = $child;
$elements[$key] = $value;
}
$elements['#sorted'] = TRUE;
}
return array_keys($children);
return array_keys($child_weights);
}
/**
......
......@@ -337,19 +337,19 @@ function testTwoPagers() {
$this->assertRaw('Comment 1 on field comment');
$this->assertRaw('Comment 1 on field comment_2');
// Navigate to next page of field 1.
$this->clickLinkWithXPath('//a[@rel="next"]');
$this->clickLinkWithXPath('//h3/a[normalize-space(text())=:label]/ancestor::section[1]//a[@rel="next"]', array(':label' => 'Comment 1 on field comment'));
// Check only one pager updated.
$this->assertRaw('Comment 2 on field comment');
$this->assertRaw('Comment 1 on field comment_2');
// Return to page 1.
$this->drupalGet('node/' . $node->id());
// Navigate to next page of field 2.
$this->clickLinkWithXPath('//a[@rel="next"]', 1);
$this->clickLinkWithXPath('//h3/a[normalize-space(text())=:label]/ancestor::section[1]//a[@rel="next"]', array(':label' => 'Comment 1 on field comment_2'));
// Check only one pager updated.
$this->assertRaw('Comment 1 on field comment');
$this->assertRaw('Comment 2 on field comment_2');
// Navigate to next page of field 1.
$this->clickLinkWithXPath('//a[@rel="next"]');
$this->clickLinkWithXPath('//h3/a[normalize-space(text())=:label]/ancestor::section[1]//a[@rel="next"]', array(':label' => 'Comment 1 on field comment'));
// Check only one pager updated.
$this->assertRaw('Comment 2 on field comment');
$this->assertRaw('Comment 2 on field comment_2');
......@@ -365,6 +365,10 @@ function testTwoPagers() {
*
* @param string $xpath
* Xpath query that targets an anchor tag, or set of anchor tags.
* @param array $arguments
* An array of arguments with keys in the form ':name' matching the
* placeholders in the query. The values may be either strings or numeric
* values.
* @param int $index
* Link position counting from zero.
*
......@@ -373,9 +377,9 @@ function testTwoPagers() {
*
* @see WebTestBase::clickLink()
*/
protected function clickLinkWithXPath($xpath, $index = 0) {
protected function clickLinkWithXPath($xpath, $arguments = array(), $index = 0) {
$url_before = $this->getUrl();
$urls = $this->xpath($xpath);
$urls = $this->xpath($xpath, $arguments);
if (isset($urls[$index])) {
$url_target = $this->getAbsoluteUrl($urls[$index]['href']);
$this->pass(SafeMarkup::format('Clicked link %label (@url_target) from @url_before', array('%label' => $xpath, '@url_target' => $url_target, '@url_before' => $url_before)), 'Browser');
......
......@@ -218,7 +218,8 @@ public function testDefaultSearchPageOrdering() {
$this->drupalGet('search');
$elements = $this->xpath('//*[contains(@class, :class)]//a', array(':class' => 'tabs primary'));
$this->assertIdentical((string) $elements[0]['href'], \Drupal::url('search.view_node_search'));
$this->assertIdentical((string) $elements[1]['href'], \Drupal::url('search.view_user_search'));
$this->assertIdentical((string) $elements[1]['href'], \Drupal::url('search.view_dummy_search_type'));
$this->assertIdentical((string) $elements[2]['href'], \Drupal::url('search.view_user_search'));
}
/**
......
......@@ -54,8 +54,8 @@ public function testPluginLocalTask() {
$this->drupalGet(Url::fromRoute('menu_test.local_task_test_tasks_view'));
$this->assertLocalTasks([
['menu_test.local_task_test_tasks_view', []],
['menu_test.local_task_test_tasks_settings', []],
['menu_test.local_task_test_tasks_edit', []],
['menu_test.local_task_test_tasks_settings', []],
]);
// Ensure the view tab is active.
......
......@@ -89,6 +89,18 @@ public function testChildren() {
$expected = array('child2', 'child1', 'child3');
$this->assertSame($expected, Element::children($element_no_weight, TRUE));
// The order of children with same weight should be preserved.
$element_mixed_weight = array(
'child5' => array('#weight' => 10),
'child3' => array('#weight' => -10),
'child1' => array(),
'child4' => array('#weight' => 10),
'child2' => array(),
);
$expected = array('child3', 'child1', 'child2', 'child5', 'child4');
$this->assertSame($expected, Element::children($element_mixed_weight, TRUE));
}
/**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment