_color_unpack() does not remove leading '#' character from the $hex param causing PHP notice:

PHP7.4:

Passing invalid characters to base_convert(), bindec(), octdec() and hexdec() will now generate a deprecation notice. The result will still be computed as if the invalid characters did not exist. Leading and trailing whitespace, as well as prefixes of type 0x (depending on base) continue to be allowed.

See https://www.php.net/manual/en/migration74.deprecated.php#migration74.dep...

Comments

Taran2L created an issue. See original summary.

avpaderno’s picture

Title: [PHP7.4] [D7] Invalid characters passed for attempted conversion, these have been ignored in _color_unpack() » Invalid characters passed for attempted conversion, these have been ignored in _color_unpack()
taran2l’s picture

Status: Active » Needs review
StatusFileSize
new904 bytes
avpaderno’s picture

Status: Needs review » Needs work

The actual code used from the function is the following one.

  if (strlen($hex) == 4) {
    $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
  }
  $c = hexdec($hex);

Unconditionally removing the leading # is not necessary as, at least in one case, it is already removed. I would rather change the code as follows.

   if (strlen($hex) == 4) {
     $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
   }
   else { 
     $hex = ltrim($hex, '#');
   }

The Color module doesn't seem to validate the value used for $hex, which could also contain '99#ff'. In this case, filtering out any character that isn't acceptable as hexadecimal digit would be better.

function _color_unpack($hex, $normalize = FALSE) {
  $hex = preg_replace("/[^0-9a-fA-F]/", "", $hex);
  if (strlen($hex) == 4) {
    $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
  }
  $c = hexdec($hex);
  for ($i = 16; $i >= 0; $i -= 8) {
    $out[] = ($c >> $i & 0xff) / ($normalize ? 255 : 1);
  }
  return $out;
}
avpaderno’s picture

Status: Needs work » Needs review
StatusFileSize
new562 bytes
taran2l’s picture

@kiamlaluno, I disagree with just removing everything, because Color module does input validation allowing either #XXX or #YYYYYY only:

$form['color'] += color_scheme_form($form, $form_state, $theme);
$form['#validate'][] = 'color_scheme_form_validate';
$form['#submit'][] = 'color_scheme_form_submit';

then

function color_scheme_form_validate($form, &$form_state) {
  // Only accept hexadecimal CSS color strings to avoid XSS upon use.
  foreach ($form_state['values']['palette'] as $key => $color) {
    if (!color_valid_hexadecimal_string($color)) {
      form_set_error('palette][' . $key, t('%name must be a valid hexadecimal CSS color value.', array('%name' => $form['color']['palette'][$key]['#title'])));
    }
  }
}

and finally

function color_valid_hexadecimal_string($color) {
  return preg_match('/^#([a-f0-9]{3}){1,2}$/iD', $color);
}
  1. Stripping everything will be hiding issue with incorrect usage of the Color module
  2. _color_unpack() is an internal function (at least by name) and should not be used directly (or calling code must do the validation)
avpaderno’s picture

StatusFileSize
new509 bytes

If it's sure that $hex will always contain a string matching '/^#([a-f0-9]{3}){1,2}$/iD', then the function code should be the following one.

function _color_unpack($hex, $normalize = FALSE) {
  if (strlen($hex) == 4) {
    $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
  }
  else {
    $hex = substr($hex, 1);
  }
  $c = hexdec($hex);
  for ($i = 16; $i >= 0; $i -= 8) {
    $out[] = ($c >> $i & 0xff) / ($normalize ? 255 : 1);
  }
  return $out;
}
mcdruid’s picture

Issue tags: +PHP 7.4
StatusFileSize
new589 bytes
new635 bytes

This was addressed in D8 in #3086374: Make Drupal 8 & 9 compatible with PHP 7.4

As far as I can see there's no discussion of this code specifically in the issue, but between #29 and #30 the fix went from:

   if (strlen($hex) == 4) {
     $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
   }
+  else {
+    $hex = str_replace('#', '', $hex);
+  }

...to:

-  if (strlen($hex) == 4) {
-    $hex = $hex[1] . $hex[1] . $hex[2] . $hex[2] . $hex[3] . $hex[3];
+  $hex = substr($hex, 1);
+  if (strlen($hex) == 3) {
+    $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];

Leaving us with the code in D8 now:

function _color_unpack($hex, $normalize = FALSE) {                                 
  $hex = substr($hex, 1);                                                          
  if (strlen($hex) == 3) {                                                         
    $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];              
  }                                                                                
  $c = hexdec($hex); 

I don't find this code the most intuitive to read, but it makes sense given what @Taran2L mentioned in #6:

Color module does input validation allowing either #XXX or #YYYYYY only

We should be consistent with D8 wherever possible, so let's backport the same change.

mcdruid’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Pending Drupal 7 commit

As noted in other issues, I'm not sure this method is perfect, but if we compare the exceptions from the no-op patch in #3081386-49: [META] Fully support PHP 7.4 in Drupal 7 with those from the PHP 7.4 test for #8:

$ curl -s https://www.drupal.org/pift-ci-job/1601005 | grep -o 'exception: .* Line [0-9]* of .*:' | perl -pe 's#<.*?>##g' | sort | uniq -c | sort -rn | diff - <(curl -s https://www.drupal.org/pift-ci-job/1602413 | grep -o 'exception: .* Line [0-9]* of .*:' | perl -pe 's#<.*?>##g' | sort | uniq -c | sort -rn)
1c1
<  189257 exception: [Notice] Line 6656 of includes/common.inc:
---
>  189299 exception: [Notice] Line 6656 of includes/common.inc:
3d2
<    1132 exception: [Deprecated function] Line 740 of modules/color/color.module:

...that seems to confirm that the patch fixes the issue on line 740 of color.module:

https://git.drupalcode.org/project/drupal/-/blob/7.x/modules/color/color...

I'm not certain why the number of exceptions from element_children() also goes up, but we're addressing that in #3084935: element_children() and DrupalRequestSanitizer::stripDangerousValues() should not use integer keys as array and that Notice should go away once that issue's done.

As the change here is exactly the same as the one committed to D8, I'm confident enough to mark this RTBC and ready for final review.

fabianx’s picture

Assigned: Unassigned » mcdruid

Ready to be merged - let's get this in. Thanks all!

  • mcdruid committed 398945d on 7.x
    Issue #3084955 by kiamlaluno, mcdruid, Taran2L: Invalid characters...
mcdruid’s picture

Assigned: mcdruid » Unassigned
Status: Reviewed & tested by the community » Fixed
Issue tags: -Pending Drupal 7 commit

Another one done; thanks everyone!

Status: Fixed » Closed (fixed)

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