Problem/Motivation

Since PHP 7.4, doing $item[0] on an integer produces a PHP notices, creates log records, https://3v4l.org/GYsje.

Notice: Trying to access array offset on value of type int in Drupal\Core\Render\Element::property() (line 27 of /usr/local/var/www/drupal/web/core/lib/Drupal/Core/Render/Element.php)

This can happen with Drupal\Core\Render\Element::properties() where it seems common to have elements properties keys as 0, 1, 2 etc.

Steps to reproduce

Make sure to have a sample array with an integer as a key.
Example: Element::properties([0 => 'First']);

Proposed resolution

Check whether a given key is not an integer in Drupal\Core\Render\Element::property().

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Comments

mbovan created an issue. See original summary.

Pooja Ganjage’s picture

StatusFileSize
new1.13 KB

Hi,

I am applying a patch for this issue.

Kindly review the patch once.

Thanks.

Pooja Ganjage’s picture

Status: Active » Needs review
Pooja Ganjage’s picture

StatusFileSize
new1.13 KB

Uploading new patch.

Pooja Ganjage’s picture

StatusFileSize
new530 bytes

Uploading new patch.

Pooja Ganjage’s picture

StatusFileSize
new523 bytes

Uploading new patch.

Pooja Ganjage’s picture

StatusFileSize
new524 bytes
tstoeckler’s picture

Category: Task » Bug report
Status: Needs review » Needs work
Issue tags: +PHP 7.4

Nice catch, this should definitely be fixed.

Thanks for getting this started @Pooja Ganjage! I'm not sure exactly what is going wrong, but somehow you're patches cannot be applied, not sure if there's a way to assist with that.

I think similar to what was done in #3086374: Make Drupal 8 & 9 compatible with PHP 7.4 we should add an is_int() check to the code.

Also, this will need a test. Should be easy to simply add another line to \Drupal\Tests\Core\Render\ElementTest::testChild().

spokje’s picture

@Poonja Ganjage: When I look at the latest 9.1.x-branch I see this code in core/lib/Drupal/Core/Render/Element.php:

  public static function child($key) {
    return !isset($key[0]) || $key[0] != '#';
  }

The code in your patch is looking for this line to replace:

- return (!isset($key === '' || (0 != strpos($key, '#')));

which isn't there, which makes the patch not applicable.

Not sure on what branch you created your patch, but it should be against the latest 9.1.x-branch.

Hope that helps! :)

megha_kundar’s picture

Status: Needs work » Needs review
StatusFileSize
new0 bytes
megha_kundar’s picture

StatusFileSize
new451 bytes

Sorry previous applied patch was zero bytes applying updated patch

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.

aklump’s picture

patch #11 worked for me.

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.

ekes’s picture

StatusFileSize
new1.48 KB

Following @tstoeckler suggestion in [3172166-8] Checking for is_int first. This makes sense for all possible keys passed anyway.

For the tests it is to be expected that an array passed into ::properties() could have mixed keys, so a test added there, and ::property() can just have the one key passed (from anyone doing something like properties() for example).

So attached patch updates the return to check it's not an integer first; and tests both methods. I've run tests against 9.4.x-dev. So I'll leave it at that first; but I think this will apply to 10.0.x-dev too.

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community

Thanks! Looks good to me.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

Nice find and nice test coverage!

+++ b/core/lib/Drupal/Core/Render/Element.php
@@ -24,7 +24,7 @@ class Element {
-    return $key[0] == '#';
+    return !is_int($key) && $key[0] == '#';

How about is_string($key) && $key[0] == '#' - it has less logic - the lack of the ! - and is more inline with the $key documentation. Array keys have to a string or an integer. Even floats don't work.

ekes’s picture

Status: Needs work » Needs review
StatusFileSize
new1.48 KB
new414 bytes

> Array keys have to a string or an integer. Even floats don't work.

Just read those casting rules. Sure there's some potential fun there - but for this it's straightforward.

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community

Thanks for the review @alexpott and the quick turnaround @ekes. Marking RTBC assuming the bot passes.

alexpott’s picture

Version: 9.4.x-dev » 9.3.x-dev
Status: Reviewed & tested by the community » Fixed

Committed and pushed 60a42060171 to 10.0.x and ed0195c2bf2 to 9.4.x and 089376cf5ed to 9.3.x. Thanks!

  • alexpott committed 60a4206 on 10.0.x
    Issue #3172166 by Pooja Ganjage, ekes, Megha_kundar, tstoeckler, mbovan...

  • alexpott committed ed0195c on 9.4.x
    Issue #3172166 by Pooja Ganjage, ekes, Megha_kundar, tstoeckler, mbovan...

  • alexpott committed 089376c on 9.3.x
    Issue #3172166 by Pooja Ganjage, ekes, Megha_kundar, tstoeckler, mbovan...
xjm’s picture

Status: Fixed » Needs review
StatusFileSize
new1.48 KB

I believe this issue has introduced a regression on PHP 7.3:
https://www.drupal.org/pift-ci-job/2305316

Here's a patch to test the revert.

  • xjm committed b596245 on 9.3.x
    Revert "Issue #3172166 by Pooja Ganjage, ekes, Megha_kundar, tstoeckler...

  • xjm committed f004d8a on 9.4.x
    Revert "Issue #3172166 by Pooja Ganjage, ekes, Megha_kundar, tstoeckler...

  • xjm committed cf5f35c on 10.0.x
    Revert "Issue #3172166 by Pooja Ganjage, ekes, Megha_kundar, tstoeckler...
xjm’s picture

Status: Needs review » Needs work

That fixes PHP 7.3 tests, so I've reverted it from all three branches. So, we'll need to come up with a way to make the tests pass on PHP 7.3 in Drupal 9.3 and 9.4 while still fixing the existing notice on PHP 7.4. Be sure to run tests against environments for all three PHP versions before commit. (That's always a good idea anyway for any issue where we're fixing something PHP-version-specific.) Thanks!

xjm’s picture

...I guess I probably didn't need to revert it on 10.0.x since it requires PHP 8, but maybe there's a better way to update the test anyway. Up to whoever picks this back up. Thanks!

ekes’s picture

StatusFileSize
new1.43 KB
new519 bytes

Added a 7.3 test to #3172166-19: Element::properties() produces notices if given an array with integer keys but assuming that fails it will be because of $this->assertNotContains(0, $properties); which must be some feature of assertNotContains with a (int) 0 and PHP 7.3. The code being tested runs the same from 5.4 onward https://3v4l.org/fn61K

As the $this->assertCount(2, $properties); was also added, and the code checks for properties then containing the two expected keys, checking that the other two are not there is extraneous.

So assuming above fails, this one should pass. I'll run it against 7.3. 7.4 and 8.

alexpott’s picture

+++ b/core/tests/Drupal/Tests/Core/Render/ElementTest.php
@@ -29,13 +30,14 @@ public function testProperties() {
+    $this->assertCount(2, $properties);
     $this->assertContains('#property1', $properties);
     $this->assertContains('#property2', $properties);
-    $this->assertNotContains('property3', $properties);

Let's replace all this with

    $this->assertSame(['#property1', '#property2'], $properties);

It's simpler and works on all PHPs.

ekes’s picture

StatusFileSize
new1.46 KB
new536 bytes
alexpott’s picture

Status: Needs work » Needs review

I also think we should file a follow up to do:

   public static function child($key) {
-    return !isset($key[0]) || $key[0] != '#';
+    return !static::property($key);
   }

The Element::child() must be the inverse of Element::property() - so let's make this true in code.

Also in Element::children() we have if (is_int($key) || $key === '' || $key[0] !== '#') { I guess we're inlining this here for performance - which makes sense (up to a point - this stuff is called thousands of times so often the measuring makes it seem way way slower than it actually is). But regardless - we can still make this more efficient by doing if (!(is_string($key) && $key[0] !== '#')) {. We could include changing this logic in the same followup - the follow could be called something like "Align the property / child determination in Element class". Note the performance arguments for Element::child() don't really hold based on core usage.

alexpott’s picture

#33 looks great.

ekes’s picture

Tests now passing on both for PHP 7.3 through 8. Follow-up posted #3262736: Align the property / child determination in Element class.

tstoeckler’s picture

Status: Needs review » Reviewed & tested by the community

Awesome, thanks! Back to RTBC then

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed e14d16c5894 to 10.0.x and 1538662f475 to 9.4.x and e71fcde4d2c to 9.3.x. Thanks!

  • alexpott committed e14d16c on 10.0.x
    Issue #3172166 by Pooja Ganjage, ekes, Megha_kundar, xjm, alexpott,...

  • alexpott committed 1538662 on 9.4.x
    Issue #3172166 by Pooja Ganjage, ekes, Megha_kundar, xjm, alexpott,...

  • alexpott committed e71fcde on 9.3.x
    Issue #3172166 by Pooja Ganjage, ekes, Megha_kundar, xjm, alexpott,...

Status: Fixed » Closed (fixed)

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