Problem/Motivation

Postponed on #3493718: Convert system_requirements() into OOP hooks and install time class

In IRC the other day I was chatting with @realityloop who was getting some notices on admin pages because a module installed on his site was incorrectly returning from hook_requirements.
The module didn't provide a title in the return, and this was causing a large number of warnings, because by default we assume this is present in a uasort call.

Proposed resolution

Replace the return value of hook_requirements with value objects. This will enforce the structure of the return in a way that an array cannot.
Implement \ArrayAccess for BC sake

Remaining tasks

Remaining tasks:

  • Decide if we should just enforce required properties by using constructor args
  • Ensure sorting works
  • Replace usages of requirements arrays

User interface changes

API changes

Addition of a new value object

Data model changes

Issue fork drupal-2909472

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

larowlan created an issue. See original summary.

kim.pepper’s picture

Assigned: Unassigned » kim.pepper

I need something to do on a 24 hr Sydney to London flight. ;-(

kim.pepper’s picture

Status: Active » Needs review
StatusFileSize
new5.06 KB

Initial implementation for review.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Extension/Requirement/Requirement.php
@@ -0,0 +1,158 @@
+  const SEVERITY_INFO = -1;
+
+  const SEVERITY_OK = 0;
+
+  const SEVERITY_WARNING = 1;
+
+  const SEVERITY_ERROR = 2;

I'm wondering whether we should have one class per severity aka. RequirementInfo, RequirementOk, RequirementWarning and SeverityError? It feels like this would cause an ever better to read requirement.

larowlan’s picture

I'm wondering whether we should have one class per severity aka. RequirementInfo, RequirementOk, RequirementWarning and SeverityError

Yeah and would be consistent with AccessResults - agree

larowlan’s picture

  1. +++ b/core/lib/Drupal/Core/Extension/Requirement/Requirement.php
    @@ -0,0 +1,158 @@
    +    $this->definition = $values;
    

    We should be setting defaults here to avoid the issue outlined in the OP.

     $this->definition = $values + [
    'title' => '',
    'description' => '',
    //...
    
    
  2. +++ b/core/lib/Drupal/Core/Extension/Requirement/Requirement.php
    @@ -0,0 +1,158 @@
    +  public function setTitle($title) {
    ...
    +  public function setValue($value) {
    ...
    +  public function setSeverity($severity) {
    

    We should add getters so we can eventually use that over the \ArrayAccess

  3. +++ b/core/lib/Drupal/Core/Extension/Requirement/Requirement.php
    @@ -0,0 +1,158 @@
    +   * @return static
    +   *   The object itself for chaining.
    ...
    +   * @return static
    +   *   The object itself for chaining.
    

    These should just be return $this with no comment

larowlan’s picture

+++ b/core/lib/Drupal/Core/Extension/Requirement/Requirement.php
@@ -0,0 +1,158 @@
+  protected $definition = [];

Same as mentioned in #2908735: [meta] Add value objects to represent hook_schema we should use named properties for title, description, value, severity and then save $definition for stuff outside that - this will optimize the memory footprint

kim.pepper’s picture

StatusFileSize
new11.56 KB
new7.43 KB

Thanks for the review. I've switched to using properties and having a separate class for each severity level.

sam152’s picture

  1. +++ b/core/lib/Drupal/Core/Extension/Requirement/BaseRequirement.php
    @@ -0,0 +1,209 @@
    +  const PHASE_INSTALL = 'install';
    ...
    +  const PHASE_UPDATE = 'update';
    ...
    +  const PHASE_RUNTIME = 'runtime';
    ...
    +  const SEVERITY_INFO = -1;
    ...
    +  const SEVERITY_OK = 0;
    ...
    +  const SEVERITY_WARNING = 1;
    ...
    +  const SEVERITY_ERROR = 2;
    

    Comments.

  2. +++ b/core/lib/Drupal/Core/Extension/Requirement/BaseRequirement.php
    @@ -0,0 +1,209 @@
    +  public function offsetExists($offset) {
    

    inheritdoc?

  3. +++ b/core/lib/Drupal/Core/Extension/Requirement/BaseRequirement.php
    @@ -0,0 +1,209 @@
    +    if (in_array($offset, [
    +      'description',
    +      'title',
    +      'value',
    +      'severity',
    +    ], TRUE)) {
    +      return TRUE;
    +    }
    +    return FALSE;
    

    Just return in_array...

  4. +++ b/core/lib/Drupal/Core/Extension/Requirement/BaseRequirement.php
    @@ -0,0 +1,209 @@
    +      case 'severity':
    

    Shouldn't be able to set this if it's based on the class, but is this needed for BC?

  5. +++ b/core/lib/Drupal/Core/Extension/Requirement/BaseRequirementOk.php
    @@ -0,0 +1,17 @@
    +class BaseRequirementOk extends BaseRequirement {
    

    No "Base" in class name?

kim.pepper’s picture

StatusFileSize
new4.98 KB
new9.04 KB
  1. Fixed
  2. Fixed
  3. Fixed
  4. I removed the setter, but there might be a broader discussion on how we support BC with this as in #2908735: [meta] Add value objects to represent hook_schema
  5. Fixed

After discussing with @Sam152, I added an interface for Requirement too.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Extension/Requirement/RequirementInterface.php
@@ -0,0 +1,107 @@
+  /**
+   * The OK severity.
+   */
+  const SEVERITY_OK = 0;
+
+  /**
+   * The info severity.
+   */
+  const SEVERITY_INFO = -1;
+
+  /**
+   * The error severity.
+   */
+  const SEVERITY_ERROR = 2;
+
+  /**
+   * The warning severity.
+   */
+  const SEVERITY_WARNING = 1;

Should we move this constant on the individual classes?

kim.pepper’s picture

Should we move this constant on the individual classes?

Wouldn't it make more sense to allow you to compare different severity levels? In SystemManager we are doing:

<?php
public function getMaxSeverity(&$requirements) {
    $severity = static::REQUIREMENT_OK;
    foreach ($requirements as $requirement) {
      if (isset($requirement['severity'])) {
        $severity = max($severity, $requirement['severity']);
      }
    }
    return $severity;
  }
?>

Doing a comparison of classes to see which had the max severity would be cumbersome.

larowlan’s picture

Issue tags: +DrupalSouth 2017
dawehner’s picture

Wouldn't it make more sense to allow you to compare different severity levels? In SystemManager we are doing:

Maybe we could provide some nice helper functions for that?

kim.pepper’s picture

StatusFileSize
new13.13 KB
new4.09 KB

Sounds like a plan. Here's a patch with the above and a helper.

Status: Needs review » Needs work

The last submitted patch, 15: 2909472-15.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

dawehner’s picture

+++ b/core/lib/Drupal/Core/Extension/Requirement/RequirementHelper.php
@@ -0,0 +1,39 @@
+    return array_reduce($requirements, [__CLASS__, 'max'], RequirementInterface::SEVERITY_OK);

Nice usage of array_reduce!

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new1.19 KB
new12.81 KB

Thanks! I've inlined the max method to an anonymous function.

The previous fail was a random one related to Layout Builder.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

idebr’s picture

The data model for a Requirement in this issue does not match the return values being implemented in #2505499: Explicitly support render arrays in hook_requirements(), specifically the supported value of description.

The related issue does indicate there is a need for a value object that outlines the correct data model.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

kristen pol’s picture

Status: Needs review » Needs work
Issue tags: +DrupalSouth

Surprisingly, this patch still applies :)

KristenBackupMBP:drupal-9.3.x-dev admin$ patch -p1 < 2909472-18.patch 
patching file core/lib/Drupal/Core/Extension/Requirement/BaseRequirement.php
patching file core/lib/Drupal/Core/Extension/Requirement/RequirementError.php
patching file core/lib/Drupal/Core/Extension/Requirement/RequirementHelper.php
patching file core/lib/Drupal/Core/Extension/Requirement/RequirementInfo.php
patching file core/lib/Drupal/Core/Extension/Requirement/RequirementInterface.php
patching file core/lib/Drupal/Core/Extension/Requirement/RequirementOk.php
patching file core/lib/Drupal/Core/Extension/Requirement/RequirementWarning.php
patching file core/modules/system/src/SystemManager.php
Hunk #1 succeeded at 127 (offset -3 lines).
patching file core/tests/Drupal/Tests/Core/Extension/Requirement/RequirementHelperTest.php
patching file core/tests/Drupal/Tests/Core/Extension/Requirement/RequirementTest.php

Reviewed the code and there are a few items:

  1. +++ b/core/lib/Drupal/Core/Extension/Requirement/RequirementHelper.php
    @@ -0,0 +1,26 @@
    +<?php
    +
    +
    +namespace Drupal\Core\Extension\Requirement;
    

    Extra line.

  2. +++ b/core/modules/system/src/SystemManager.php
    @@ -130,8 +130,11 @@ public function listRequirements() {
    +   * @deprecated Will be removed in Drupal 9.0.0
    +   * @see \Drupal\Core\Extension\Requirement\RequirementHelper::getMaxSeverity()
    

    Deprecation version needs to change. Example;

    @deprecated in drupal:9.4.0 and is removed from drupal:10.0.0.
    
  3. +++ b/core/tests/Drupal/Tests/Core/Extension/Requirement/RequirementHelperTest.php
    @@ -0,0 +1,73 @@
    +    $info = RequirementInfo::create()
    +      ->setValue("Foo");
    +    $warning = RequirementWarning::create()
    +      ->setValue("Baz");
    +    $error = RequirementError::create()
    +      ->setValue("Baz");
    +    $ok = RequirementOk::create()
    +      ->setValue("Bar");
    

    Nitpicks:
    1. Use different values for warning vs error.
    2. Use single quotes.

Also, please review feedback from #21.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andypost’s picture

Related issues: +#2909480: Move REQUIREMENT_* constants out of install.inc file
+++ b/core/lib/Drupal/Core/Extension/Requirement/RequirementInterface.php
@@ -0,0 +1,107 @@
+  const SEVERITY_OK = 0;
...
+  const SEVERITY_INFO = -1;
...
+  const SEVERITY_ERROR = 2;
...
+  const SEVERITY_WARNING = 1;

Instead of constants it could be enum or even removed in favour of methods (like accessResult doing).
But this weight used only for frontend to group sort requirements - that usage is missing in replacement

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

voleger made their first commit to this issue’s fork.

kim.pepper’s picture

kim.pepper’s picture

kim.pepper’s picture

Issue summary: View changes
Status: Needs work » Needs review

Converted to a single Requirement class with enums for RequirementSeverity and RequirementPhase.

Remaining tasks:

  • Decide if we should just enforce required properties by using constructor args
  • Ensure sorting works
  • Replace usages of requirements arrays
kim.pepper’s picture

Hiding old patches

smustgrave’s picture

Status: Needs review » Needs work

Seems to have some test failures.

kim.pepper’s picture

I think this issue is quite large, having to convert all of cores hook requirements to value objects as well as handle deprecations.

We should split out the smaller task of creating enums for RequirementSeverity and RequirementPhase #3410938: Create enums for RequirementSeverity and deprecate drupal_requirements_severity() constants

berdir’s picture

#3409372: [meta] Replace hook_requirements() with OOP classes is two thirds done.

If we can get this also into 11.2 then this might considerably simplify the BC layer. New hook use value objects, old hooks use arrays, we just need to merge it together in the places where core calls both the old and the new hooks?

dww’s picture

Title: Add value objects to represent the return of hook_requirements » [PP-1] Add value objects to represent the return of hook_requirements
Status: Needs work » Postponed
Issue tags: +11.2.0 release priority
Related issues: +#3500632: Convert hook_requirements() that do not interact with install time

Agreed this would be great to do in 11.2.0 so the new hooks only use the new API. Tagging as such. But this should now be postponed on #3410938: Create enums for RequirementSeverity and deprecate drupal_requirements_severity() constants, no?

kim.pepper’s picture

Assigned: kim.pepper » Unassigned
voleger’s picture

Title: [PP-1] Add value objects to represent the return of hook_requirements » Add value objects to represent the return of hook_requirements
Status: Postponed » Needs work
kim.pepper’s picture

Status: Needs work » Needs review

Rebased on 11.x

nicxvan’s picture

Status: Needs review » Needs work

Several failures, this definitely won't make it in for 11.2 I think.

Personally I would want to postpone this in the system_requirements conversion. That issue was green and just need to take the new enum into account.

I'll link it later when I fix that issue. #3493718: Convert system_requirements() into OOP hooks and install time class

nicxvan’s picture

Component: base system » extension system
kim.pepper’s picture

Title: Add value objects to represent the return of hook_requirements » [PP-1] Add value objects to represent the return of hook_requirements
Status: Needs work » Postponed

Damn. I wish I saw your comment before I started converting system_requirements() 😓

kim.pepper’s picture

Issue summary: View changes

I pushed up my work in progress in case it's useful later.

kim.pepper’s picture

larowlan’s picture

larowlan’s picture

larowlan’s picture

Title: [PP-1] Add value objects to represent the return of hook_requirements » Add value objects to represent the return of hook_requirements
Status: Postponed » Active

Blocker is in

kim.pepper’s picture

Assigned: Unassigned » kim.pepper

Working on this

kim.pepper’s picture

Assigned: kim.pepper » Unassigned
Status: Active » Needs review

Rebased on 11.x and moved everything over from system_requirements() to \Drupal\system\Install\Requirements\SystemRequirements::checkRequirements()

nicxvan’s picture

Took a first pass review.

We also want to handle when update requirements / phase and install time for modules and profiles during profile install amc module install.

nicxvan’s picture

Is this meant to be scoped to only runtime?

Most of the changes seem like that but some of the changes should run during update and install.

I'm also unsure why we're not getting failures from update and install being converted but no equivalent mapper like you put in runtime requirements during the status report.

nicxvan’s picture

Status: Needs review » Needs work

A bunch of minor suggestions and a few substantial questions.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.