public static function &get_types() {
        // If it was already done in this request, use cache.
        if (self::$cachedtypes) {
            return self::$cachedtypes;
        }

        // Get defaults.
        $mimetypes = self::get_default_types();

        // Get custom file types.
        $custom = self::get_custom_types();

        // Check value is an array.
        if (!is_array($custom)) {
            debugging('Invalid $CFG->customfiletypes (not array)', DEBUG_DEVELOPER);
            $custom = array();
        }

        foreach ($custom as $customentry) {
            // Each entry is a stdClass object similar to the array values above.
            if (empty($customentry->extension)) {
                debugging('Invalid $CFG->customfiletypes entry (extension field required)',
                        DEBUG_DEVELOPER);
                continue;
            }

            // To delete a standard entry, set 'deleted' to true.
            if (!empty($customentry->deleted)) {
                unset($mimetypes[$customentry->extension]);
                continue;
            }

            // Check required fields.
            if (empty($customentry->type) || empty($customentry->icon)) {
                debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
                        ' (type and icon fields required)', DEBUG_DEVELOPER);
                continue;
            }

            // Build result array.
            $result = array('type' => $customentry->type, 'icon' => $customentry->icon);
            if (!empty($customentry->groups)) {
                if (!is_array($customentry->groups)) {
                    debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
                            ' (groups field not array)', DEBUG_DEVELOPER);
                    continue;
                }
                $result['groups'] = $customentry->groups;
            }
            if (!empty($customentry->string)) {
                if (!is_string($customentry->string)) {
                    debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
                            ' (string field not string)', DEBUG_DEVELOPER);
                    continue;
                }
                $result['string'] = $customentry->string;
            }
            if (!empty($customentry->defaulticon)) {
                if (!is_bool($customentry->defaulticon)) {
                    debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
                            ' (defaulticon field not bool)', DEBUG_DEVELOPER);
                    continue;
                }
                $result['defaulticon'] = $customentry->defaulticon;
            }
            if (!empty($customentry->customdescription)) {
                if (!is_string($customentry->customdescription)) {
                    debugging('Invalid $CFG->customfiletypes entry ' . $customentry->extension .
                            ' (customdescription field not string)', DEBUG_DEVELOPER);
                    continue;
                }
                // As the name suggests, this field is used only for custom entries.
                $result['customdescription'] = $customentry->customdescription;
            }

            // Track whether it is a custom filetype or a modified existing
            // filetype.
            if (array_key_exists($customentry->extension, $mimetypes)) {
                $result['modified'] = true;
            } else {
                $result['custom'] = true;
            }

            // Add result array to list.
            $mimetypes[$customentry->extension] = $result;
        }

        self::$cachedtypes = $mimetypes;
        return self::$cachedtypes;
    }