Unserialize(): Passing null to parameter #1 ($data) of type string is deprecated in Drupal\Core\Entity\Sql\SqlContentEntityStorage::loadFromSharedTables()

Created on 28 July 2022, over 2 years ago
Updated 3 February 2023, almost 2 years ago

Problem/Motivation

Solve warning on PHP 8.1

Deprecated function: unserialize(): Passing null to parameter #1 ($data) of type string is deprecated in Drupal\Core\Entity\Sql\SqlContentEntityStorage->loadFromSharedTables() (line 602

Steps to reproduce

Change the project to PHP 8.1 and open the web.

📌 Task
Status

Needs work

Version

10.1

Component
Entity 

Last updated about 12 hours ago

Created by

🇪🇸Spain eduardo morales alberti Spain, 🇪🇺

Live updates comments and jobs are added and updated live.
  • PHP 8.1

    The issue particularly affects sites running on PHP version 8.1.0 or later.

Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • This is happening on taxonomy terms with an empty description. It looks like checking for the value before unserializing is a good solution though I believe null coalescing operator is better than type casting because casting an array to string would output "Array" rather than throwing an error.

  • Status changed to Needs review almost 2 years ago
  • 🇮🇹Italy apaderno Brescia, 🇮🇹
  • Status changed to RTBC almost 2 years ago
  • 🇷🇸Serbia holo96

    #19 solves issue.

    I am wondering if this issue is Normal priority?
    This kind of makes drupal 9.5 & 10 incompatible with >php8.1, at least partially, no one wants warnings all over their website.

    PHP 8.1 is recommended.

    https://www.drupal.org/docs/system-requirements/php-requirements

  • 🇷🇸Serbia holo96

    And just want to add 'map' field type from core is affected, I am not sure if it is used in core...
    But commerce order uses it.

  • Status changed to Needs work almost 2 years ago
  • 🇳🇿New Zealand quietone

    Comment #8 points out that the core is tested on PHP 8.1 and this error is not occurring. In that comment steps to reproduce the problem were asked for and they have not been supplied.

    @akoe, can you expand more on #16 where you said that alexpott's concerns are addressed?

    This needs steps to reproduce so we can duplicate the problem manually and in a test.

    I am setting this back for those steps to be added.

  • 🇷🇸Serbia holo96

    As mentioned in #13:

    Steps to reproduce:
    1. Create new entity type
    2. Install newly added entity type
    3. Create at least one entity (id: 1)
    4. Add map base field to created entity type:

        $fields['data'] = BaseFieldDefinition::create('map')
          ->setLabel(t('Data'))
          ->setDescription(t('A serialized array of additional data.'));
    

    5. load entity
    6. Warning will occur

    I've tried adding:

    ->setInitialValue([])
    

    but it does not help

  • Status changed to Needs review almost 2 years ago
  • Status changed to Needs work almost 2 years ago
  • 🇺🇸United States smustgrave

    This seems like we are fixing a symptom of a possible bigger issue.

    But either way it will need a test case showing the issue.

  • Status changed to Needs review almost 2 years ago
  • 🇷🇸Serbia holo96

    Here is failing test.

    In case #19 patch is applied, test passes.

    I've just updated existing test, I am not sure if we should create new test case for this issue?

  • 🇷🇸Serbia holo96

    Here is combined patch od #27 and #19

  • 🇷🇸Serbia holo96

    So #27 proves recreation of error...
    And it is fixed in #28

  • Status changed to RTBC almost 2 years ago
  • 🇺🇸United States smustgrave

    Will move this one on and see what the committers think.

  • 🇳🇿New Zealand quietone

    Thanks for the steps to reproduce. I used code from the examples module to play with this and wasn't able to reproduce the error.

    I do agree with #26 that this may be fixing the symptom and not the underlying problem. This also should have a better title, one that describes the action that the issue is fixing or improving.

  • 🇷🇸Serbia holo96

    Yes, something is wrong with initial value...
    - In case new entity is added after field installation, value for map field is empty array "a:0:{}" (maybe we can change to that in default value?)
    - Empty string does return false when unserialized but so does null and drupal 8 and 9 worked properly whole lifetime

    So I guess either we use

    $record->{$column_name} ?? '' ('a:0:{}')
    

    Or we fix initial value and create update hook for populating values for backward compatibility.

  • 🇨🇦Canada smulvih2 Canada 🍁

    #28 seems to work for me on 9.5.5

  • 🇺🇦Ukraine rollins

    Patch #28 resolved the issue, thank you DavorHorvacki!

  • Status changed to Needs work over 1 year ago
  • 🇬🇧United Kingdom catch

    I think the root cause of this is probably related to #2346019: Handle initial values when creating a new field storage definition - maybe map item doesn't set an initial value in the field storage definition?

    It could also be #2883851: Add initial values support for configurable and multi-valued fields though which is unresolved.

    If we're able to fix this by setting an initial value, then I think we should. If it's due to #2883851: Add initial values support for configurable and multi-valued fields , then we should add a comment with a @todo to the places adding the null check.

  • 🇩🇪Germany donquixote

    So for us this happens with a custom base field of type 'link', that is set to be optional.

    function mymodule_entity_base_field_info(EntityTypeInterface $entity_type) {
      [..]
      $fields['my_link_field'] = BaseFieldDefinition::create('link')
        ->setLabel(t('My custom link field'))
        ->setRequired(FALSE)
        // I suppose all the below is irrelevant.
        ->setTranslatable(TRUE)
        ->setRevisionable(TRUE)
        ->setSettings([
          'link_type' => LinkItemInterface::LINK_EXTERNAL,
          'title' => DRUPAL_DISABLED,
        ])
        ->setDisplayOptions('form', [
          'type' => 'link_default',
          'weight' => 0,
        ])
        ->setDisplayConfigurable('view', TRUE)
        ->setDisplayConfigurable('form', TRUE);
      [..]
      return $fields;
    }
    

    The base field adds three columns to `node_field_data` and `node_field_revision` tables:
    - my_link_field__uri: varchar, allows NULL
    - my_link_field__title: varchar, allows NULL
    - my_link_field__options: serialized, allows NULL

    Checking the database, I find a bunch of rows where `my_link_field__options` has the value NULL, and other rows where the value is `N;`.
    The `changed` column shows that all those with value NULL are old, and those with value `N;` are newer.

    So this means that, when the base field is added, the value for that column is initialized as NULL.
    But when updating or creating content later, the column is initialized with `N;` (serialized NULL).

    So the steps to reproduce would be:
    - Create a node, while no custom base fields have been declared yet.
    - Declare a custom base field in a module.
    - Create another node.
    - Load each node (separately), compare the output.
    - Also check the database.

    Observed behavior:
    - Column is NULL for the old node, but 'N;' for the new node.
    - Loading the old node produces a warning.

  • 🇩🇪Germany donquixote

    @catch

    If we're able to fix this by setting an initial value, then I think we should.

    I think the initial value idea is incomplete at best.

    If a database column allows NULL, then we should not allow this to cause a problem when loading the entity.
    So we need to do one of two things:
    1. Not allow NULL for serialized columns from optional fields. Only allow 'N;'.
    2. Check for NULL before calling unserialize(). I think this is what the patch in this issue does.

  • 🇷🇸Serbia holo96

    For map field type default value after field is installed is 'a:0:{}' not 'N;', but NULL could work anyway

  • 🇺🇸United States hockey2112

    The patch is #28 worked for me.

  • 🇪🇸Spain saganakat

    The patch is #28 worked for me in drupal 9.5.9

  • 🇺🇸United States MegaKeegMan

    Patch #28 also worked for me, in Drupal 9.5.4

  • 🇷🇸Serbia holo96

    Test only!

    I've added link field to test and separated it to new test file.

    I won't be working on this issue further because I still think #19 is easiest all around solution, backward compatible.

  • 🇭🇺Hungary nagy.balint

    Thanks!

    #28 also works for me under 10.1.4

  • Status changed to Needs review about 1 year ago
  • last update about 1 year ago
    Unable to generate test groups
  • 🇮🇹Italy apaderno Brescia, 🇮🇹
  • Status changed to Needs work about 1 year ago
  • 🇺🇸United States smustgrave

    #44 appears to be a tests only patch that didn’t run. Could we get also a full patch

  • Status changed to Needs review about 1 year ago
  • last update about 1 year ago
    30,362 pass
  • 🇷🇸Serbia holo96

    Here is combined patch.

    I'll move this to Needs review but #36 stays unresolved.

  • Status changed to Needs work about 1 year ago
  • 🇨🇦Canada kiwad

    In case this helps

    I had this specific

    Deprecated function: unserialize(): Passing null to parameter #1 ($data) of type string is deprecated in Drupal\Core\Entity\Sql\SqlContentEntityStorage->mapFromStorageRecords()

    In my case, was related to Profile, maybe some update in the past didnt go through or is missing

    Simply loading/saving the entity changed value of data from false to array and message disappeared

  • 🇪🇸Spain pgrandeg

    #48 works perfect on d10.1.4 and PHP8.1

  • 🇺🇸United States papagrande US West Coast

    For others' reference, a client had a link field that was triggering this error on six nodes. The status page had no errors about the field and the configuration status was clean.

    Resaving the nodes didn't fix the problem. Nor did copying data via SQL from the field table to the revision table. Here's what did:

    1. Resave the field settings.
    2. Resave the field storage settings.
    3. Each node lost the link field data so copy and paste the link data from another server.

    And after resaving the field, the configuration status still had no changes between database and file.

  • 🇵🇪Peru krystalcode

    I focused on mapFromStorageRecords since that was the error that I was encountering. In my case an old profile entity had the data column set to NULL in the database.

    From #48:
    $values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT] = !empty($column['serialize']) ? unserialize($record->{$column_name} ?? '') : $record->{$column_name};

    With this unserialize will return FALSE when passing an empty string. The entity field then ends up having a field item with value FALSE - which is wrong; the field item list should be empty.

    I think the following would be better:

    if (!empty($column['serialize']) && !isset($record->{$column_name})) {
      $values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT] = NULL;
    }
    else {
      $values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT] = !empty($column['serialize']) ? unserialize($record->{$column_name}) : $record->{$column_name};
    }
    
  • 🇺🇸United States SocialNicheGuru

    @krystalcode could you updte patch so we can test.

    In general is there a need to develop tests for this?

  • First commit to issue fork.
  • Status changed to Needs review 7 months ago
  • 🇳🇿New Zealand RoSk0 Wellington

    Started new merge request targeting 11.x with:

    1. patch from #48
    2. suggestion from #54
    3. and links to #2883851: Add initial values support for configurable and multi-valued fields from #36
  • Pipeline finished with Failed
    7 months ago
    Total: 180s
    #173765
  • Pipeline finished with Success
    7 months ago
    Total: 1451s
    #173769
  • 🇺🇸United States amanire

    Good to see progress here! FWIW I just deployed the patch in #48 to a production site and it resolved the deprecation warnings described in https://www.drupal.org/project/profile/issues/3324465 📌 [PHP 8.1] Deprecated function: unserialize(): Passing null to parameter #1 ($data) of type string is deprecated in Drupal\Core\Entity\Sql\SqlContentEntityStorage->mapFromStorageRecords() Closed: duplicate . I was a little surprised at the substantial CPU performance improvement for the site, presumably because these were invalidating the varnish cache.

  • 🇷🇸Serbia holo96

    Updating NULL values to either 'a:0:{}' (map field) or 'N;' (almost any other) on problematic column(s) once also resolves problem.
    No patch needed. You can create update hook for this.

    This works because problem is appearing only when you are adding field to entities with existing entries.

  • Status changed to Needs work 7 months ago
  • 🇺🇸United States smustgrave

    Part of IS appear to be missing. Thanks

  • 🇨🇳China jungle Chongqing, China

    Per @krystalcode's comment in #54, I introduced a utility method to replace the current usages of unserialize() in SqlContentEntityStorage

    +  /**
    +   * Safely unserializes data.
    +   *
    +   * @param string|null $value
    +   *   The serialized string.
    +   *
    +   * @return mixed|null
    +   *   The unserialized data, or null if the data is invalid.
    +   */
    +  public static function safeUnserialize($value) {
    +    // Return null if the value is an empty string or null.
    +    if ($value === '' || $value === null) {
    +      return null;
    +    }
    +
    +    // Attempt to unserialize the value.
    +    $result = @unserialize($value);
    +
    +    // Check if unserialize resulted in false, indicating failure.
    +    // Also ensure the input value wasn't the serialized form of false itself.
    +    if ($result === false && $value !== serialize(false)) {
    +      return null;
    +    }
    +
    +    // Return the unserialized result.
    +    return $result;
    +  }
    +
    

    NW for adding tests.

  • Status changed to Needs review 7 months ago
  • 🇨🇳China jungle Chongqing, China

    With tests added

  • last update 7 months ago
    Patch Failed to Apply
  • Status changed to Needs work 7 months ago
  • 🇺🇸United States smustgrave

    Thanks for working but would need to be an MR to test

    Also was tagged issue summary update which still appears to be needed.

  • 🇨🇳China jungle Chongqing, China

    jungle changed the visibility of the branch 3300404 to hidden.

  • 🇨🇳China jungle Chongqing, China

    jungle changed the visibility of the branch 3300404-deprecated-function-unserialize to hidden.

  • 🇨🇳China jungle Chongqing, China

    jungle changed the visibility of the branch 3300404 to active.

  • 🇨🇳China jungle Chongqing, China

    jungle changed the visibility of the branch 3300404 to active.

  • Pipeline finished with Failed
    7 months ago
    #194080
  • Pipeline finished with Failed
    7 months ago
    Total: 157s
    #194081
  • Pipeline finished with Failed
    7 months ago
    Total: 180s
    #194083
  • Pipeline finished with Success
    7 months ago
    Total: 663s
    #194085
  • Status changed to Needs review 7 months ago
  • 🇨🇳China jungle Chongqing, China
  • Status changed to Needs work 6 months ago
  • 🇺🇸United States smustgrave

    Thanks for a clear issue summary!

    Can we get a change record for the new public function that could be used for others. Sure a contrib module out there maybe could leverage it.

  • 🇬🇧United Kingdom oldspot

    The changes in MR 8092 worked great testing them now on a D10.3.6 install. Thanks.

  • 🇨🇦Canada joseph.olstad

    Patch 63 works well on 10.4.0

  • 🇳🇱Netherlands spokje

    Added a first draft for a CR.

Production build 0.71.5 2024