original_string: 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;
      }
code: 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;
      }
code_tokens: 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 ; }
docstring: Return list of features.
  
  @param string $tags tags.
  @return array
docstring_tokens: Return list of features .