Quantcast
Channel: Issues for Drupal core
Viewing all articles
Browse latest Browse all 295019

Deprecated function: strnatcasecmp(): Passing null to parameter #1 ($string1) of type string is deprecated in LayoutPluginManager

$
0
0

Problem/Motivation

Deprecated function: strnatcasecmp(): Passing null to parameter #2 ($string2) of type string is deprecated in Drupal\Core\Layout\LayoutPluginManager->Drupal\Core\Layout\{closure}() (line 204 of core/lib/Drupal/Core/Layout/LayoutPluginManager.php).

appears on pages, where layouts are being listed in a select which uses $this->layoutPluginManager->getLayoutOptions() to get its options.

Example:

$form['field_layouts']['field_layout'] = [
      '#type' => 'select',
      '#title' => $this->t('Select a layout'),
      '#options' => $this->layoutPluginManager->getLayoutOptions(),
      '#default_value' => $layout_plugin->getPluginId(),
      '#ajax' => [
        'callback' => '::settingsAjax',
        'wrapper' => 'field-layout-settings-wrapper',
        'trigger_as' => ['name' => 'field_layout_change'],
      ],
    ];

(FieldLayoutEntityDisplayFormTrait.php)

layout_builder's layout_builder_blank aka BlankLayout neither has a label nor a category defined in its annotation:

/**
 * Provides a layout plugin that produces no output.
 *
 * @see \Drupal\layout_builder\Field\LayoutSectionItemList::removeSection()
 * @see \Drupal\layout_builder\SectionListTrait::addBlankSection()
 * @see \Drupal\layout_builder\SectionListTrait::hasBlankSection()
 *
 * @internal
 *   This layout plugin is intended for internal use by Layout Builder only.
 *
 * @Layout(
 *   id = "layout_builder_blank",
 * )
 */
class BlankLayout extends LayoutDefault {

it should be sorted out by

/**
 * Implements hook_plugin_filter_TYPE_alter().
 */
function layout_builder_plugin_filter_layout_alter(array &$definitions, array $extra, $consumer) {
  // Hide the blank layout plugin from listings.
  unset($definitions['layout_builder_blank']);
}

(layout_builder.module)

but that hook never gets called!

So the layout_builder_blank layout is being passed through without label or category, but these methods rely on these values to be present. They are being called indirectly by $this->layoutPluginManager->getLayoutOptions()

  /**
   * {@inheritdoc}
   *
   * @return \Drupal\Core\Layout\LayoutDefinition[]
   */
  public function getSortedDefinitions(array $definitions = NULL, $label_key = 'label') {
    // Sort the plugins first by category, then by label.
    $definitions = $definitions ?? $this->getDefinitions();
    uasort($definitions, function (LayoutDefinition $a, LayoutDefinition $b) {
      if ($a->getCategory() != $b->getCategory()) {
        return strnatcasecmp($a->getCategory(), $b->getCategory());
      }
      return strnatcasecmp($a->getLabel(), $b->getLabel());
    });
    return $definitions;
  }

  /**
   * {@inheritdoc}
   *
   * @return \Drupal\Core\Layout\LayoutDefinition[][]
   */
  public function getGroupedDefinitions(array $definitions = NULL, $label_key = 'label') {
    $definitions = $this->getSortedDefinitions($definitions ?? $this->getDefinitions(), $label_key);
    $grouped_definitions = [];
    foreach ($definitions as $id => $definition) {
      $grouped_definitions[(string) $definition->getCategory()][$id] = $definition;
    }
    return $grouped_definitions;
  }

  /**
   * {@inheritdoc}
   */
  public function getLayoutOptions() {
    $layout_options = [];
    foreach ($this->getGroupedDefinitions() as $category => $layout_definitions) {
      foreach ($layout_definitions as $name => $layout_definition) {
        $layout_options[$category][$name] = $layout_definition->getLabel();
      }
    }
    return $layout_options;
  }

This is why the error appears.

This deprecation warning didn't appear in older versions of PHP, so the bug wasn't visible.

TL;DR:

/**
 * Implements hook_plugin_filter_TYPE_alter().
 */
function layout_builder_plugin_filter_layout_alter(array &$definitions, array $extra, $consumer) {
  // Hide the blank layout plugin from listings.
  unset($definitions['layout_builder_blank']);
}

(layout_builder.module)
is not called, so the layout_builder_blank layout, which neither has a label nor a description is not being sorted out, which leads to this error!

Steps to reproduce

  1. Install Drupal with the Umami demo profile.
  2. Enable the field_layout module.
  3. Log in as an administrator.
  4. Visit /en/admin/structure/types/manage/article/form-display.

Alternatively, install Drupal with the Standard profile, then enable the field_layout and layout_builder modules.

Further analysis

It seems as like the LayoutPluginManager (DI-injected by $container->get('plugin.manager.core.layout'),) is entirely missing FilteredPluginManagerTrait, so that its alterations are never being called:

/**
   * Implements \Drupal\Core\Plugin\FilteredPluginManagerInterface::getFilteredDefinitions().
   */
  public function getFilteredDefinitions($consumer, $contexts = NULL, array $extra = []) {
    if (!is_null($contexts)) {
      $definitions = $this->getDefinitionsForContexts($contexts);
    }
    else {
      $definitions = $this->getDefinitions();
    }

    $type = $this->getType();
    $hooks = [];
    $hooks[] = "plugin_filter_{$type}";
    $hooks[] = "plugin_filter_{$type}__{$consumer}";
    $this->moduleHandler()->alter($hooks, $definitions, $extra, $consumer);
    $this->themeManager()->alter($hooks, $definitions, $extra, $consumer);
    return $definitions;
  }

meanwhile, they are being called in Layout builder (e.g. admin/structure/types/manage/page/display/default/layout) when adding a section.
So maybe layout_builder is doing something different or special?
If layout_builder is not installed, everything is fine!

Proposed resolution

Add missing annotations to Drupal\layout_builder\Plugin\Layout\BlankLayout so that getCategory() and getLabel() return string|\Drupal\Core\StringTranslation\TranslatableMarkup as required, instead of NULL.

Call getFilteredDefinitions() in LayoutPluginManager::getLayoutOptions().

Other approaches:

  1. Add an optional parameter to LayoutPluginManager::getLayoutOptions() and pass it to getFilteredDefinitions(), using $this->getType() as a fallback.
  2. Make $consumer optional in getFilteredDefinitions(), and invoke just the first hook if it is not provided.

Remaining tasks

Decide whether to implement one of the other approaches in this issue or in a follow-up.

User interface changes

None, except for not showing the PHP error.

API changes

To be determined.

Data model changes

None

Release notes snippet

N/A


Viewing all articles
Browse latest Browse all 295019

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>