public function get_components_features($tags = '') {
        global $CFG;

        // If we already have a list created then just return that, as it's up-to-date.
        // If tags are passed then it's a new filter of features we need.
        if (!empty($this->features) && empty($tags)) {
            return $this->features;
        }

        // Gets all the components with features.
        $features = array();
        $featurespaths = array();
        $components = $this->get_components_with_tests();

        if ($components) {
            foreach ($components as $componentname => $path) {
                $path = $this->clean_path($path) . self::get_behat_tests_path();
                if (empty($featurespaths[$path]) && file_exists($path)) {
                    list($key, $featurepath) = $this->get_clean_feature_key_and_path($path);
                    $featurespaths[$key] = $featurepath;
                }
            }
            foreach ($featurespaths as $path) {
                $additional = glob("$path/*.feature");

                $additionalfeatures = array();
                foreach ($additional as $featurepath) {
                    list($key, $path) = $this->get_clean_feature_key_and_path($featurepath);
                    $additionalfeatures[$key] = $path;
                }

                $features = array_merge($features, $additionalfeatures);
            }
        }

        // Optionally include features from additional directories.
        if (!empty($CFG->behat_additionalfeatures)) {
            $additional = array_map("realpath", $CFG->behat_additionalfeatures);
            $additionalfeatures = array();
            foreach ($additional as $featurepath) {
                list($key, $path) = $this->get_clean_feature_key_and_path($featurepath);
                $additionalfeatures[$key] = $path;
            }
            $features = array_merge($features, $additionalfeatures);
        }

        // Sanitize feature key.
        $cleanfeatures = array();
        foreach ($features as $featurepath) {
            list($key, $path) = $this->get_clean_feature_key_and_path($featurepath);
            $cleanfeatures[$key] = $path;
        }

        // Sort feature list.
        ksort($cleanfeatures);

        $this->features = $cleanfeatures;

        // If tags are passed then filter features which has sepecified tags.
        if (!empty($tags)) {
            $cleanfeatures = $this->filtered_features_with_tags($cleanfeatures, $tags);
        }

        return $cleanfeatures;
    }