Fix PHP 8 / Drupal 10 test failures

Created on 26 February 2023, over 1 year ago

The PHP 8.1 / Drupal 10 tests for this module have been failing since 23 March 2022. See https://www.drupal.org/node/2043793/qa β†’

Initially, DrupalCI was crashing with no information and no console output, which made it pretty much impossible to figure out.

But as of 18 Nov 2022 the test output has come back so we can look into this problem and see what exactly is going on.

I traced one problem to core issue πŸ“Œ Fix 'Access to an undefined property' PHPStan L0 errors - public properties Fixed .

In PHP 8.1, all class properties must be declared - code can no longer just dynamically create a class property by dereferencing it. So for example, this used to be valid code:

class Test {
}
$t = new Test();
$t->myvalue =3;

But now, in PHP 8.1, this will fail because "myvalue" isn't declared as a property on Test.

Views Aggregator doesn't do this kind of thing - it's a very non-OO type of thing to do, but it was a "feature" of PHP that was heavily used in the early days by Drupal modules that wanted to use objects. One of those modules was Views, which later moved into Drupal core along with this now-obsolete usage of dynamic properties.

In the issue referenced above, dynamic properties were explicitly declared in core in order to eliminate these errors. And when they were defined, they were typed.

The reason we are seeing test failures is because the Table plugin defined by Views Aggregator uses one of these formerly-dynamic properties. Namely, FieldPluginBase::last_render, which is now declared and typed as holding string|MarkupInterface|NULL

The problem comes because Views Aggregator stores an array value in this variable, which causes PHPUnit to fail. This failure may be seen in the DrupalCI test output at https://dispatcher.drupalci.org/job/drupal_contrib/664939/artifact/jenki...

Note that because PHPUnit stops executing a test when it encounters a failure, this may not be the only problem. But if the attached patch solves this one, we will be able to see the next problem (if there is a next problem).

πŸ“Œ Task
Status

Needs review

Version

2.0

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @tr
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia
  • Status changed to Needs work over 1 year ago
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    Clearly that wasn't the correct fix.

    Looking deeper, it seems that the views_aggregator_group_and_compress() function returns something different than all the other aggregator functions. The other functions return an array with a 'column' and 'group' keys, each of which contain a string. While the views_aggregator_group_and_compress() function returns an array that doesn't have either of these keys. I'm not sure exactly what these aggregator functions are supposed to return because the hook that defines the functions isn't documented fully, so there seems to be a lot of latitude in what can be returned. There also seems to be a problem with views_aggregator_range(), which sometimes returns an array in the 'column' key instead of a string.

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    Because this affects the latest Drupal core version and PHP version, it causes the GitLab CI tests (which by default only run on the current versions) to fail.

    Bumping priority.

  • πŸ‡ΊπŸ‡ΈUnited States ivanbueno

    This patch solved the issue for me locally.

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    Well like I said in #4, "the views_aggregator_group_and_compress() function returns something different than all the other aggregator functions". Ignoring the output of an aggregator function if it isn't the assumed type, like the patch does, is not the answer. That doesn't fix anything, it just hides the error message. The proper way to fix this going forward is to turn the aggregator functions into plugins so that they can share an interface that ensures they can be called interchangeably and so that code using those functions knows exactly what will be returned by those functions. Most uses of isset(), IMO, are due to bad architecture shaped by the numerous shortcomings of PHP as a language. Sprinkling in more isset() just to make error messages go away doesn't actually make the code better and doesn't actually fix improperly written functions like views_aggregator_group_and_compress(). Now that PHP 8 finally has some 20th Century innovations like return type hinting, we should use those to make the code better.

  • Open in Jenkins β†’ Open on Drupal.org β†’
    Core: 10.1.x + Environment: PHP 8.1 & MySQL 5.7
    last update 12 months ago
    11 pass
  • Status changed to Needs review 12 months ago
  • Open in Jenkins β†’ Open on Drupal.org β†’
    Core: 9.5.5 + Environment: PHP 7.3 & MySQL 5.7
    last update 12 months ago
    11 pass
  • πŸ‡§πŸ‡ͺBelgium msnassar

    It might be related. After updating to D10.1, we encounter similar issue:
    TypeError: Cannot assign array to property Drupal\views\Plugin\views\field\FieldPluginBase::$last_render of type Drupal\Component\Render\MarkupInterface|string|null in Drupal\views_aggregator\Plugin\views\style\Table->executeAggregationFunctions() (line 540 of modules/contrib/views_aggregator/src/Plugin/views/style/Table.php).

    After some debugging, I noticed that the function views_aggregator_tally declares the variable $values = ['column' => []];. However, at the end, the 'column' element will never get a value! In this case, I wonder why the $values has the 'column' element? I also removed $separator_column = empty($separator_column) ? '<br/>' : $separator_column; because it is not being used by the function.

    Regarding the PHP version, we are using PHP 8.1, which has no issues with dynamic properties. The dynamic properties are deprecated in PHP 8.2

  • Open in Jenkins β†’ Open on Drupal.org β†’
    Core: 10.1.x + Environment: PHP 8.1 & MySQL 5.7
    last update 12 months ago
    7 pass, 2 fail
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    All of the views_aggregator functions return an array with keys 'column' and/or keys for each group. Each of those array elements is also an array, which contain the values for column aggregation and group aggregation respectively.

    This is pretty much undocumented, although views_aggregator_views_aggregation_functions_info() in views_aggregator_functions.inc, and in views_aggregator.api.php explains this somewhat. As well as the comments in src/Plugin/views/style/Table.php

    In the Table plugin, the function results are taken out of the 'column' array element or the individual group array elements, as needed.

    So because views_aggregator_tally() is declared in views_aggregator_views_aggregation_functions_info() to provide both column and group aggregation, both of those keys are expected in the array returned by that function. So I suspect what you are trying to do is do column aggregation with views_aggregator_tally(), but that function does not seem to have code to do column aggregation. The fix then should also include changing the function declaration in views_aggregator_views_aggregation_functions_info() to reflect this. But it seems views_aggregator_tally() was INTENDED to do column aggregation, so I would like to find out if the code for column integration actually existed in a previous version and got lost, or whether it was never actually implemented.

    And this is part of what I was getting at in #8 - without some type of structure like an interface declaring the return types for these functions, there is no mechanism to ensure that they return the right things - that is really the MINIMUM we should be doing. Replacing the return array containing magic undocumented keys with a structured object, or even having different aggregator functions for column and group aggregation is an important thing to do to keep this module viable going forward. And rather than having an _info() hook to discover aggregator functions, these should all be plugins so they don't all have to be declared an loaded on every page in one massive include file but instead can be discovered and loaded from their own individual (and small) files only when and if they are needed.

    I think we can fix this issue with views_aggregator_tally() and views_aggregator_group_and_compress() without making these major changes, but we're going to keep running into problems like this until the whole structure of how we define and use these functions is improved and modernized.

    Regarding the PHP version, we are using PHP 8.1, which has no issues with dynamic properties. The dynamic properties are deprecated in PHP 8.2

    Yes, sorry my initial post is a little inexact. The problem first showed up because core made changes to remove dynamic properties in preparation for PHP 8.2. Core did this by declaring the properties and giving them types. The test failures showed up in this module, and in our PHP 8.1 tests (was not able to test against 8.2 back then), because these core changes were only made in PHP 8 compatible code - for example the union type for last_render is a PHP 8 construct and when core added the property declaration for last_render and used a union type that cause the test failures in our PHP 8.1/D10 tests but not any other tests because D9 core didn't make the same changes and because D10 never supported less than PHP 8.1.

  • Status changed to Needs work 11 months ago
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia
  • Open in Jenkins β†’ Open on Drupal.org β†’
    Core: 10.2.x + Environment: PHP 8.1 & MariaDB 10.3.22
    last update 9 months ago
    7 pass, 2 fail
  • ivnish Kazakhstan

    patch #9 works and helps me

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    tr β†’ changed the visibility of the branch 3344504-fix-php-8 to hidden.

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    tr β†’ changed the visibility of the branch 2.1.x to hidden.

  • Merge request !15Issue #3344504 by tr: Fix test failures β†’ (Merged) created by tr
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    Over the past few days I've done a lot of work on typing (See πŸ“Œ Parameter typing and return type hints Active ) and turned on strict_types in the tests (See πŸ“Œ Add declare(strict_types=1) to all tests Active ).

    At this point I feel I can start to drill down into the tests and fix the remaining typing issues one by one to get to the root of the two test failures. I will be making a lot of small commits here before this is ready for review.

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    Oh that worked. Now let me take my "experiment" out and move it to the right place.

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia
  • ivnish Kazakhstan

    I tested patch in my project and got the error

    TypeError: Drupal\views_aggregator\Plugin\views\style\Table::setAggregatedGroupValues(): Argument #3 ($group_aggregation_results) must be of type int, string given, called in /var/www/web/modules/contrib/views_aggregator/src/Plugin/views/style/Table.php on line 395 in Drupal\views_aggregator\Plugin\views\style\Table->setAggregatedGroupValues() (line 1153 of /var/www/web/modules/contrib/views_aggregator/src/Plugin/views/style/Table.php).

  • ivnish Kazakhstan

    Needs to add

    $group_aggregation_results = (int) $this->options['group_aggregation']['group_aggregation_results'];

    for strict types

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    How did you get that error? I should add a test for that section of code, because it clearly isn't being tested currently.

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    Yes, I can see it is intended to hold an integer value.

    But I need to know what you were doing when you got that error so I can replicate that error with a test case.

    The schema is wrong. There are at least two other type definitions in the schema that are wrong too, so I would like to write a test case that will check all the options values.

    When the schema is corrected, that option will hold an integer value and a cast will not be needed. The solution is to fix the schema.

  • ivnish Kazakhstan

    I just have a views with such settings:

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    And you see the error when you save the view? Or when you visit the URL of the view? Where do you see the error, in your server logs or your Drupal logs?

  • ivnish Kazakhstan

    I visit the URL of the view. The site has WSOD and I see the error in Drupal dblog

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    OK, that's what I need to know. Thank you.

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    I pushed a fix to the MR - can you please try this and see if it solved the problem for you?

    You will probably have to re-save the table settings on your view for this change to take effect. Edit the view, click on the "Settings" link next to "Table with aggregation options", then press the "Apply" button, then "Save" the view.

  • ivnish Kazakhstan

    Yes, patch + resave views solved my WSOD

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    Thanks for looking at that. I'm going to handle the schema fixes in a different issue - πŸ“Œ Turn on strict config schema checking Active - because it requires an update function and an update test. I want to get the above fixes in so that the tests are all running before I start messing with new tests.

    The update function in that issue will automatically modify existing views, so no one will have to manually save the view with the new settings.

  • Pipeline finished with Skipped
    about 1 month ago
    #307695
    • tr β†’ committed dbda1486 on 2.1.x
      Issue #3344504 by tr, ivnish: Fix test failures
      
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    Committed to 2.1.x.

    I'm leaving this open to backport to 2.0.x to fix the same failures that occur there.
    I'm only going to backport a small portion of this - I'm not going to try to backport all of the typing changes, but just backport the two key sections identified by fixing the typing issues. Namely the return value changes to the tally and range functions.

  • Merge request !18Resolve #3344504 "2.0 test failures" β†’ (Merged) created by tr
  • Pipeline finished with Skipped
    about 1 month ago
    #307704
    • tr β†’ committed fa4ab177 on 2.0.x
      Issue #3344504 by tr, ivnish: Fix test failures
      
  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia
  • ivnish Kazakhstan

    I tested again. Branch 2.1.x, last commit.

    I returned my views and I got WSOD again. Re-save views doesn't help now. Error the same as #21

  • ivnish Kazakhstan

    Do you forget to update schema for 2.1.x branch?

    group_aggregation_results is string in schema https://git.drupalcode.org/project/views_aggregator/-/blob/2.1.x/config/...

    But function needs int https://git.drupalcode.org/project/views_aggregator/-/blob/2.1.x/src/Plu...

  • πŸ‡ΊπŸ‡ΈUnited States tr Cascadia

    No, I didn't forget. I said in #32 that I was going to do the schema changes in that other issue. I'm writing the test for that now.

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024