public static function get_instances_fields_data(array $fields, array $instanceids, bool $adddefaults = true) : array {
        global $DB;

        // Create the results array where instances and fields order is the same as in the input arrays.
        $result = array_fill_keys($instanceids, array_fill_keys(array_keys($fields), null));

        if (empty($instanceids) || empty($fields)) {
            return $result;
        }

        // Retrieve all existing data.
        list($sqlfields, $params) = $DB->get_in_or_equal(array_keys($fields), SQL_PARAMS_NAMED, 'fld');
        list($sqlinstances, $iparams) = $DB->get_in_or_equal($instanceids, SQL_PARAMS_NAMED, 'ins');
        $sql = "SELECT d.*
                  FROM {customfield_field} f
                  JOIN {customfield_data} d ON (f.id = d.fieldid AND d.instanceid {$sqlinstances})
                 WHERE f.id {$sqlfields}";
        $fieldsdata = $DB->get_recordset_sql($sql, $params + $iparams);
        foreach ($fieldsdata as $data) {
            $result[$data->instanceid][$data->fieldid] = data_controller::create(0, $data, $fields[$data->fieldid]);
        }
        $fieldsdata->close();

        if ($adddefaults) {
            // Add default data where it was not retrieved.
            foreach ($instanceids as $instanceid) {
                foreach ($fields as $fieldid => $field) {
                    if ($result[$instanceid][$fieldid] === null) {
                        $result[$instanceid][$fieldid] =
                            data_controller::create(0, (object)['instanceid' => $instanceid], $field);
                    }
                }
            }
        } else {
            // Remove null-placeholders for data that was not retrieved.
            foreach ($instanceids as $instanceid) {
                $result[$instanceid] = array_filter($result[$instanceid]);
            }
        }

        return $result;
    }