diff --git a/drush/features.drush.inc b/drush/features.drush.inc
index c36e60f..cbb0e23 100644
--- a/drush/features.drush.inc
+++ b/drush/features.drush.inc
@@ -183,8 +183,13 @@ function drush_features_status() {
 
 /**
  * Drush command callback for features-list-packages.
+ *
+ * @param string $package_name
+ *   (optional) The package name.
+ *
+ * @return array|bool
  */
-function drush_features_list_packages($package = '') {
+function drush_features_list_packages($package_name = '') {
   _drush_features_options();
   /** @var \Drupal\features\FeaturesManagerInterface $manager */
   $manager = \Drupal::service('features.manager');
@@ -194,20 +199,20 @@ function drush_features_list_packages($package = '') {
   $result = array();
 
   // If no package was specified, list all packages.
-  if (empty($package)) {
+  if (empty($package_name)) {
     drush_hide_output_fields(array('object'));
-    foreach ($packages as $item) {
-      $overrides = $manager->detectOverrides($item);
-      $state = $item['state'];
-      if (!empty($overrides) && ($item['status'] != FeaturesManagerInterface::STATUS_NO_EXPORT)) {
+    foreach ($packages as $package) {
+      $overrides = $manager->detectOverrides($package);
+      $state = $package->getState();
+      if (!empty($overrides) && ($package->getStatus() != FeaturesManagerInterface::STATUS_NO_EXPORT)) {
         $state = FeaturesManagerInterface::STATE_OVERRIDDEN;
       }
 
-      $result[$item['machine_name']] = array(
-        'name' => $item['name'],
-        'machine_name' => $item['machine_name'],
-        'status' => $manager->statusLabel($item['status']),
-        'version' => $item['version'],
+      $result[$package->getMachineName()] = array(
+        'name' => $package->getName(),
+        'machine_name' => $package->getMachineName(),
+        'status' => $manager->statusLabel($package->getStatus()),
+        'version' => $package->getVersion(),
         'state' => ($state != FeaturesManagerInterface::STATE_DEFAULT)
           ? $manager->stateLabel($state)
           : '',
@@ -217,8 +222,8 @@ function drush_features_list_packages($package = '') {
   }
   // If a valid package was listed, list its configuration.
   else {
-    foreach ($packages as $item) {
-      if ($item['machine_name'] == $package) {
+    foreach ($packages as $package) {
+      if ($package->getMachineName() == $package_name) {
         drush_hide_output_fields(array(
           'machine_name',
           'name',
@@ -226,7 +231,7 @@ function drush_features_list_packages($package = '') {
           'version',
           'state',
         ));
-        foreach ($item['config'] as $item_name) {
+        foreach ($package->getConfig() as $item_name) {
           $result[$item_name] = array(
             'object' => $item_name,
           );
@@ -238,7 +243,7 @@ function drush_features_list_packages($package = '') {
   }
 
   // If no matching package found, return an error.
-  drush_log(dt('Package "@package" not found.', array('@package' => $package)), 'warning');
+  drush_log(dt('Package "@package" not found.', array('@package' => $package_name)), 'warning');
   return FALSE;
 }
 
@@ -646,7 +651,6 @@ function drush_features_import() {
 function _drush_features_load_feature($module, $any = FALSE) {
   /** @var \Drupal\features\FeaturesManagerInterface $manager */
   $manager = \Drupal::service('features.manager');
-  $packages = $manager->getPackages();
   $feature = $manager->getPackage($module);
   if ($any && !isset($feature)) {
     // See if this is a non-features module.
@@ -656,8 +660,8 @@ function _drush_features_load_feature($module, $any = FALSE) {
       $extension = $modules[$module];
       $feature = $manager->initPackageFromExtension($extension);
       $config = $manager->listExtensionConfig($extension);
-      $feature['config'] = $config;
-      $feature['status'] = FeaturesManagerInterface::STATUS_ENABLED;
+      $feature->setConfig($config);
+      $feature->setStatus(FeaturesManagerInterface::STATUS_ENABLED);
     }
   }
   return $feature;
@@ -865,7 +869,7 @@ function _drush_features_get_component_map() {
     }
     if (!empty($item->getPackage())) {
       $package = $packages[$item->getPackage()];
-      $result[$type][$short_name][] = $package['machine_name'];
+      $result[$type][$short_name][] = $package->getMachineName();
     }
   }
 
diff --git a/modules/features_ui/src/Controller/FeaturesUIController.php b/modules/features_ui/src/Controller/FeaturesUIController.php
index 4d08d88..cc1ac66 100644
--- a/modules/features_ui/src/Controller/FeaturesUIController.php
+++ b/modules/features_ui/src/Controller/FeaturesUIController.php
@@ -120,7 +120,7 @@ class FeaturesUIController implements ContainerInjectionInterface {
             $allow = TRUE;
             if (!$allow_conflicts && $config_collection[$dependent_item_name]->getPackage()) {
               if ($packages[$config_collection[$dependent_item_name]->getPackage()]) {
-                $allow = ($packages[$config_collection[$dependent_item_name]->getPackage()]['status'] == FeaturesManagerInterface::STATUS_NO_EXPORT)
+                $allow = ($packages[$config_collection[$dependent_item_name]->getPackage()]->getStatus() == FeaturesManagerInterface::STATUS_NO_EXPORT)
                   || ($config_collection[$item_name]->getPackage() == $config_collection[$dependent_item_name]->getPackage());
               }
             }
diff --git a/modules/features_ui/src/Form/FeaturesDiffForm.php b/modules/features_ui/src/Form/FeaturesDiffForm.php
index a43e2f5..a55703b 100644
--- a/modules/features_ui/src/Form/FeaturesDiffForm.php
+++ b/modules/features_ui/src/Form/FeaturesDiffForm.php
@@ -13,6 +13,7 @@ use Drupal\features\FeaturesAssignerInterface;
 use Drupal\features\FeaturesManagerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\features\Package;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Component\Diff\DiffFormatter;
 use Drupal\config_update\ConfigRevertInterface;
@@ -111,7 +112,7 @@ class FeaturesDiffForm extends FormBase {
       return array();
     }
     elseif (!empty($featurename)) {
-      $machine_name = $packages[$featurename]['machine_name'];
+      $machine_name = $packages[$featurename]->getMachineName();
       $packages = array($packages[$featurename]);
     }
     else {
@@ -128,17 +129,17 @@ class FeaturesDiffForm extends FormBase {
 
     $options = array();
     foreach ($packages as $package) {
-      if ($package['status'] != FeaturesManagerInterface::STATUS_NO_EXPORT) {
+      if ($package->getStatus() != FeaturesManagerInterface::STATUS_NO_EXPORT) {
         $missing = $this->featuresManager->reorderMissing($this->featuresManager->detectMissing($package));
         $overrides = $this->featuresManager->detectOverrides($package, TRUE);
         if (!empty($overrides) || !empty($missing)) {
           $options += array(
-            $package['machine_name'] => array(
+            $package->getMachineName() => array(
               'row' => array(
                 'data' => array(
                   '#type' => 'html_tag',
                   '#tag' => 'h2',
-                  '#value' => SafeMarkup::checkPlain($package['name']),
+                  '#value' => SafeMarkup::checkPlain($package->getName()),
                 ),
               ),
               '#attributes' => array(
@@ -203,8 +204,8 @@ class FeaturesDiffForm extends FormBase {
   /**
    * Returns a form element for the given overrides.
    *
-   * @param array $package
-   *   A package array.
+   * @param \Drupal\features\Package $package
+   *   A package.
    * @param array $overrides
    *   An array of overrides.
    * @param array $missing
@@ -213,7 +214,7 @@ class FeaturesDiffForm extends FormBase {
    * @return array
    *   A form element.
    */
-  protected function diffOutput($package, $overrides, $missing = array()) {
+  protected function diffOutput(Package $package, $overrides, $missing = array()) {
     $element = array();
     $config = $this->featuresManager->getConfigCollection();
     $components = array_merge($missing, $overrides);
@@ -263,7 +264,7 @@ class FeaturesDiffForm extends FormBase {
           ),
         ),
         '#attributes' => array(
-          'class' => 'diff-' . $package['machine_name'],
+          'class' => 'diff-' . $package->getMachineName(),
         ),
       );
     }
diff --git a/modules/features_ui/src/Form/FeaturesEditForm.php b/modules/features_ui/src/Form/FeaturesEditForm.php
index 21caf40..2c340a7 100644
--- a/modules/features_ui/src/Form/FeaturesEditForm.php
+++ b/modules/features_ui/src/Form/FeaturesEditForm.php
@@ -14,6 +14,7 @@ use Drupal\features\FeaturesGeneratorInterface;
 use Drupal\features\FeaturesManagerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\features\Package;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -43,9 +44,9 @@ class FeaturesEditForm extends FormBase {
   protected $generator;
 
   /**
-   * Current package array being edited.
+   * Current package being edited.
    *
-   * @var array
+   * @var \Drupal\features\Package
    */
   protected $package;
 
@@ -188,7 +189,7 @@ class FeaturesEditForm extends FormBase {
       '#title' => t('Name'),
       '#description' => t('Example: Image gallery') . ' (' . t('Do not begin name with numbers.') . ')',
       '#type' => 'textfield',
-      '#default_value' => $this->package['name'],
+      '#default_value' => $this->package->getName(),
     );
     if (!$bundle->isDefault()) {
       $form['info']['name']['#description'] .= '<br/>' .
@@ -200,7 +201,7 @@ class FeaturesEditForm extends FormBase {
       '#title' => t('Machine-readable name'),
       '#description' => t('Example: image_gallery') . ' ' . t('May only contain lowercase letters, numbers and underscores.'),
       '#required' => TRUE,
-      '#default_value' => $bundle->getShortName($this->package['machine_name']),
+      '#default_value' => $bundle->getShortName($this->package->getMachineName()),
       '#machine_name' => array(
         'source' => array('info', 'name'),
         'exists' => array($this, 'featureExists'),
@@ -216,7 +217,7 @@ class FeaturesEditForm extends FormBase {
       '#description' => t('Provide a short description of what users should expect when they enable your feature.'),
       '#type' => 'textarea',
       '#rows' => 3,
-      '#default_value' => $this->package['description'],
+      '#default_value' => $this->package->getDescription(),
     );
 
     $form['info']['package'] = array(
@@ -235,7 +236,7 @@ class FeaturesEditForm extends FormBase {
       '#description' => t('Examples: 8.x-1.0, 8.x-1.0-beta1'),
       '#type' => 'textfield',
       '#required' => FALSE,
-      '#default_value' => $this->package['version'],
+      '#default_value' => $this->package->getVersion(),
       '#size' => 30,
     );
 
@@ -306,12 +307,12 @@ class FeaturesEditForm extends FormBase {
     $bundle_name = $form_state->getValue('package');
     $bundle = $this->assigner->getBundle($bundle_name);
     if (isset($bundle) && isset($old_bundle)) {
-      $short_name = $old_bundle->getShortName($this->package['machine_name']);
+      $short_name = $old_bundle->getShortName($this->package->getMachineName());
       if ($bundle->isDefault()) {
         $short_name = $old_bundle->getFullName($short_name);
       }
-      $this->package['machine_name'] = $bundle->getFullName($short_name);
-      $form['info']['machine_name']['#value'] = $bundle->getShortName($this->package['machine_name']);
+      $this->package->setMachineName($bundle->getFullName($short_name));
+      $form['info']['machine_name']['#value'] = $bundle->getShortName($this->package->getMachineName());
     }
     return $form['info'];
   }
@@ -465,7 +466,7 @@ class FeaturesEditForm extends FormBase {
    *   Optional form_state information for user selections. Can be updated to
    *   reflect new selection status.
    *
-   * @return array
+   * @return \Drupal\features\Package
    *   New export array to be exported
    *   array['components'][$component_name] = $component_info
    *     $component_info['options'][$section] is list of available options
@@ -498,11 +499,11 @@ class FeaturesEditForm extends FormBase {
   protected function getComponentList(FormStateInterface $form_state) {
     $config = $this->featuresManager->getConfigCollection();
 
-    $package_name = $this->package['machine_name'];
+    $package_name = $this->package->getMachineName();
     // Auto-detect dependencies for included config.
-    $package_config = !empty($this->package['config']) ? $this->package['config'] : array();
-    if (!empty($this->package['config_orig'])) {
-      $package_config = array_unique(array_merge($package_config, $this->package['config_orig']));
+    $package_config = $this->package->getConfig();
+    if (!empty($this->package->getConfigOrig())) {
+      $package_config = array_unique(array_merge($package_config, $this->package->getConfigOrig()));
     }
     if (!empty($package_config)) {
       $this->featuresManager->assignConfigDependents($package_config, $package_name);
@@ -517,37 +518,35 @@ class FeaturesEditForm extends FormBase {
     $this->conflicts = array();
     foreach ($config as $item_name => $item) {
       if (($item->getPackage() != $package_name) &&
-        !empty($packages[$item->getPackage()]) && ($packages[$item->getPackage()]['status'] != FeaturesManagerInterface::STATUS_NO_EXPORT)) {
+        !empty($packages[$item->getPackage()]) && ($packages[$item->getPackage()]->getStatus() != FeaturesManagerInterface::STATUS_NO_EXPORT)) {
         $this->conflicts[$item->getType()][$item->getShortName()] = $item->getLabel();
       }
       if ($this->allowConflicts
         || !isset($this->conflicts[$item->getType()][$item->getShortName()])
-        || (!empty($this->package['config_orig']) && in_array($item_name, $this->package['config_orig']))) {
+        || ($this->package->getConfigOrig() && in_array($item_name, $this->package->getConfigOrig()))) {
         $components[$item->getType()][$item->getShortName()] = $item->getLabel();
       }
     }
 
     // Make a map of the config data already exported to the Feature.
     $exported_features_info = array();
-    if (!empty($this->package['config_orig'])) {
-      foreach ($this->package['config_orig'] as $item_name) {
-        // Make sure the extension provided item exists in the active
-        // configuration storage.
-        if (isset($config[$item_name])) {
-          $item = $config[$item_name];
-          // Remove any conflicts if those are not being allowed.
+    foreach ($this->package->getConfigOrig() as $item_name) {
+      // Make sure the extension provided item exists in the active
+      // configuration storage.
+      if (isset($config[$item_name])) {
+        $item = $config[$item_name];
+      // Remove any conflicts if those are not being allowed.
           // if ($this->allowConflicts || !isset($this->conflicts[$item['type']][$item['name_short']])) {
-          $exported_features_info[$item->getType()][$item->getShortName()] = $item->getLabel();
-          // }
-        }
+        $exported_features_info[$item->getType()][$item->getShortName()] = $item->getLabel();
+        // }
       }
     }
-    $exported_features_info['dependencies'] = !empty($this->package['info']['dependencies']) ? $this->package['info']['dependencies'] : array();
+    $exported_features_info['dependencies'] = $this->package->getDependencyInfo();
 
     // Make a map of any config specifically excluded and/or required.
     foreach (array('excluded', 'required') as $constraint) {
       $this->{$constraint} = array();
-      $info = !empty($this->package['info']['features'][$constraint]) ? $this->package['info']['features'][$constraint] : array();
+      $info = !empty($this->package->getFeaturesInfo()[$constraint]) ? $this->package->getFeaturesInfo()[$constraint] : array();
       foreach ($info as $item_name) {
         $item = $config[$item_name];
         $this->{$constraint}[$item->getType()][$item->getShortName()] = $item->getLabel();
@@ -556,11 +555,11 @@ class FeaturesEditForm extends FormBase {
 
     // Make a map of the config data to be exported within the Feature.
     $new_features_info = array();
-    foreach ($this->package['config'] as $item_name) {
+    foreach ($this->package->getConfig() as $item_name) {
       $item = $config[$item_name];
       $new_features_info[$item->getType()][$item->getShortName()] = $item->getLabel();
     }
-    $new_features_info['dependencies'] = !empty($this->package['dependencies']) ? $this->package['dependencies'] : array();
+    $new_features_info['dependencies'] = $this->package->getDependencies();
 
     // Assemble the combined component list.
     $config_new = array();
@@ -603,7 +602,7 @@ class FeaturesEditForm extends FormBase {
     }
 
     // Generate new populated feature.
-    $export = $this->package;
+    $export['package'] = $this->package;
     $export['config_new'] = $config_new;
 
     // Now fill the $export with categorized sections of component options
@@ -707,9 +706,10 @@ class FeaturesEditForm extends FormBase {
               }
               // Remove excluded item from export.
               if ($component == 'dependencies') {
-                unset($export['dependencies'][$key]);
+                $export['package']->removeDependency($key);
               }
               else {
+                $export['package']->removeConfig($key);
                 unset($export['config'][$config_name]);
               }
             }
@@ -797,17 +797,18 @@ class FeaturesEditForm extends FormBase {
     $bundle = $this->assigner->getBundle($this->bundle);
     $this->assigner->assignConfigPackages();
 
-    $this->package['name'] = $form_state->getValue('name');
-    $this->package['machine_name'] = $bundle->getFullName($form_state->getValue('machine_name'));
-    $this->package['description'] = $form_state->getValue('description');
-    $this->package['version'] = $form_state->getValue('version');
-    $this->package['bundle'] = $bundle->getMachineName();
+    $this->package = new Package($bundle->getFullName($form_state->getValue('machine_name')), [
+      'name' => $form_state->getValue('name'),
+      'description' => $form_state->getValue('description'),
+      'version' => $form_state->getValue('version'),
+      'bundle' => $bundle->getMachineName(),
+    ]);
     // Save it first just to create it in case it's a new package.
     $this->featuresManager->setPackage($this->package);
 
-    $this->package['config'] = $this->updatePackageConfig($form_state);
-    $this->package['excluded'] = $this->updateExcluded();
-    $this->package['required'] = $this->updateRequired();
+    $this->package->setConfig($this->updatePackageConfig($form_state));
+    $this->package->setExcluded($this->updateExcluded());
+    $this->package->setRequired($this->updateRequired());
     // Now save it with the selected config data.
     $this->featuresManager->setPackage($this->package);
 
@@ -819,9 +820,9 @@ class FeaturesEditForm extends FormBase {
     }
 
     // Set default redirect, but allow generators to change it later.
-    $form_state->setRedirect('features.edit', array('featurename' => $this->package['machine_name']));
+    $form_state->setRedirect('features.edit', array('featurename' => $this->package->getMachineName()));
     if (!empty($method_id)) {
-      $packages = array($this->package['machine_name']);
+      $packages = array($this->package->getMachineName());
       $this->generator->generatePackages($method_id, $packages, $bundle);
       $this->generator->applyExportFormSubmit($method_id, $form, $form_state);
     }
diff --git a/modules/features_ui/src/Form/FeaturesExportForm.php b/modules/features_ui/src/Form/FeaturesExportForm.php
index 53447ea..fc33756 100644
--- a/modules/features_ui/src/Form/FeaturesExportForm.php
+++ b/modules/features_ui/src/Form/FeaturesExportForm.php
@@ -16,6 +16,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
+use Drupal\features\Package;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\Url;
 
@@ -228,7 +229,7 @@ class FeaturesExportForm extends FormBase {
   /**
    * Builds the portion of the form showing a listing of features.
    *
-   * @param array $packages
+   * @param \Drupal\features\Package[] $packages
    *   The packages.
    *
    * @return array
@@ -248,11 +249,11 @@ class FeaturesExportForm extends FormBase {
     $options = array();
     $first = TRUE;
     foreach ($packages as $package) {
-      if ($first && $package['status'] == FeaturesManagerInterface::STATUS_NO_EXPORT) {
+      if ($first && $package->getStatus() == FeaturesManagerInterface::STATUS_NO_EXPORT) {
         // Don't offer new non-profile packages that are empty.
-        if ($package['status'] === FeaturesManagerInterface::STATUS_NO_EXPORT &&
-          !$this->assigner->getBundle()->isProfilePackage($package['machine_name']) &&
-          empty($package['config'])) {
+        if ($package->getStatus() === FeaturesManagerInterface::STATUS_NO_EXPORT &&
+          !$this->assigner->getBundle()->isProfilePackage($package->getMachineName()) &&
+          empty($package->getConfig())) {
           continue;
         }
         $first = FALSE;
@@ -264,7 +265,7 @@ class FeaturesExportForm extends FormBase {
           ),
         );
       }
-      $options[$package['machine_name']] = $this->buildPackageDetail($package);
+      $options[$package->getMachineName()] = $this->buildPackageDetail($package);
     }
 
     $element = array(
@@ -282,36 +283,36 @@ class FeaturesExportForm extends FormBase {
   /**
    * Builds the details of a package.
    *
-   * @param array $package
-   *   The package name.
+   * @param \Drupal\features\Package $package
+   *   The package.
    *
    * @return array
    *   A render array of a form element.
    */
-  protected function buildPackageDetail(array $package) {
+  protected function buildPackageDetail(Package $package) {
     $config_collection = $this->featuresManager->getConfigCollection();
 
-    $url = Url::fromRoute('features.edit', array('featurename' => $package['machine_name']));
+    $url = Url::fromRoute('features.edit', array('featurename' => $package->getMachineName()));
 
     $element['name'] = array(
-      'data' => \Drupal::l($package['name'], $url),
+      'data' => \Drupal::l($package->getName(), $url),
       'class' => array('feature-name'),
     );
-    $machine_name = $package['machine_name'];
+    $machine_name = $package->getMachineName();
     // Except for the 'unpackaged' pseudo-package, display the full name, since
     // that's what will be generated.
     if ($machine_name !== 'unpackaged') {
-      $machine_name = $this->assigner->getBundle($package['bundle'])->getFullName($machine_name);
+      $machine_name = $this->assigner->getBundle($package->getBundle())->getFullName($machine_name);
     }
     $element['machine_name'] = $machine_name;
     $element['status'] = array(
-      'data' => $this->featuresManager->statusLabel($package['status']),
+      'data' => $this->featuresManager->statusLabel($package->getStatus()),
       'class' => array('column-nowrap'),
     );
     // Use 'data' instead of plain string value so a blank version doesn't
     // remove column from table.
     $element['version'] = array(
-      'data' => SafeMarkup::checkPlain($package['version']),
+      'data' => SafeMarkup::checkPlain($package->getVersion()),
       'class' => array('column-nowrap'),
     );
     $overrides = $this->featuresManager->detectOverrides($package);
@@ -319,13 +320,13 @@ class FeaturesExportForm extends FormBase {
     $conflicts = array();
     $missing = array();
 
-    if ($package['status'] == FeaturesManagerInterface::STATUS_NO_EXPORT) {
+    if ($package->getStatus() == FeaturesManagerInterface::STATUS_NO_EXPORT) {
       $overrides = array();
       $new_config = array();
     }
     // Bundle package configuration by type.
     $package_config = array();
-    foreach ($package['config'] as $item_name) {
+    foreach ($package->getConfig() as $item_name) {
       $item = $config_collection[$item_name];
       $package_config[$item->getType()][] = array(
         'name' => SafeMarkup::checkPlain($item_name),
@@ -335,58 +336,54 @@ class FeaturesExportForm extends FormBase {
       );
     }
     // Conflict config from other modules.
-    if (!empty($package['config_orig'])) {
-      foreach ($package['config_orig'] as $item_name) {
-        if (!isset($config_collection[$item_name])) {
-          $missing[] = $item_name;
-          $package_config['missing'][] = array(
-            'name' => SafeMarkup::checkPlain($item_name),
-            'label' => SafeMarkup::checkPlain($item_name),
-            'class' => 'features-conflict',
-          );
-        }
-        elseif (!in_array($item_name, $package['config'])) {
-          $item = $config_collection[$item_name];
-          $conflicts[] = $item_name;
-          $package_config[$item->getType()][] = array(
-            'name' => SafeMarkup::checkPlain($item_name),
-            'label' => SafeMarkup::checkPlain($item->getLabel()),
-            'class' => 'features-conflict',
-          );
-        }
+    foreach ($package->getConfigOrig() as $item_name) {
+      if (!isset($config_collection[$item_name])) {
+        $missing[] = $item_name;
+        $package_config['missing'][] = array(
+          'name' => SafeMarkup::checkPlain($item_name),
+          'label' => SafeMarkup::checkPlain($item_name),
+          'class' => 'features-conflict',
+        );
+      }
+      elseif (!in_array($item_name, $package->getConfig())) {
+        $item = $config_collection[$item_name];
+        $conflicts[] = $item_name;
+        $package_config[$item->getType()][] = array(
+          'name' => SafeMarkup::checkPlain($item_name),
+          'label' => SafeMarkup::checkPlain($item->getLabel()),
+          'class' => 'features-conflict',
+        );
       }
     }
     // Add dependencies.
     $package_config['dependencies'] = array();
-    if (!empty($package['dependencies'])) {
-      foreach ($package['dependencies'] as $dependency) {
-        $package_config['dependencies'][] = array(
-          'name' => $dependency,
-          'label' => $this->moduleHandler->getName($dependency),
-          'class' => '',
-        );
-      }
+    foreach ($package->getDependencies() as $dependency) {
+      $package_config['dependencies'][] = array(
+        'name' => $dependency,
+        'label' => $this->moduleHandler->getName($dependency),
+        'class' => '',
+      );
     }
 
     $class = '';
     $label = '';
     if (!empty($conflicts)) {
-      $url = Url::fromRoute('features.edit', array('featurename' => $package['machine_name']));
+      $url = Url::fromRoute('features.edit', array('featurename' => $package->getMachineName()));
       $class = 'features-conflict';
       $label = t('Conflicts');
     }
     elseif (!empty($overrides)) {
-      $url = Url::fromRoute('features.diff', array('featurename' => $package['machine_name']));
+      $url = Url::fromRoute('features.diff', array('featurename' => $package->getMachineName()));
       $class = 'features-override';
       $label = $this->featuresManager->stateLabel(FeaturesManagerInterface::STATE_OVERRIDDEN);
     }
     elseif (!empty($new_config)) {
-      $url = Url::fromRoute('features.diff', array('featurename' => $package['machine_name']));
+      $url = Url::fromRoute('features.diff', array('featurename' => $package->getMachineName()));
       $class = 'features-detected';
       $label = t('New detected');
     }
     elseif (!empty($missing)) {
-      $url = Url::fromRoute('features.edit', array('featurename' => $package['machine_name']));
+      $url = Url::fromRoute('features.edit', array('featurename' => $package->getMachineName()));
       $class = 'features-conflict';
       $label = t('Missing');
     }
@@ -442,7 +439,7 @@ class FeaturesExportForm extends FormBase {
 
     $details = array();
     $details['description'] = array(
-      '#markup' => Xss::filterAdmin($package['description']),
+      '#markup' => Xss::filterAdmin($package->getDescription()),
     );
     $details['table'] = array(
       '#type' => 'details',
@@ -466,17 +463,16 @@ class FeaturesExportForm extends FormBase {
    *   A collection of configuration.
    */
   protected function addUnpackaged(array &$packages, array $config_collection) {
-    $packages['unpackaged'] = array(
-      'machine_name' => 'unpackaged',
+    $packages['unpackaged'] = new Package('unpackaged', [
       'name' => $this->t('Unpackaged'),
       'description' => $this->t('Configuration that has not been added to any package.'),
-      'config' => array(),
+      'config' => [],
       'status' => FeaturesManagerInterface::STATUS_NO_EXPORT,
       'version' => '',
-    );
+    ]);
     foreach ($config_collection as $item_name => $item) {
       if (!$item->getPackage() && !$item->isExtensionProvided()) {
-        $packages['unpackaged']['config'][] = $item_name;
+        $packages['unpackaged']->appendConfig($item_name);
       }
     }
   }
@@ -492,15 +488,16 @@ class FeaturesExportForm extends FormBase {
    *   The form build array.
    */
   public static function preRenderRemoveInvalidCheckboxes(array $form) {
+    /** @var \Drupal\features\Package $package */
     foreach ($form['#packages'] as $package) {
       // Remove checkboxes for packages that:
       // - exist and are disabled, or
       // - have no configuration assigned and are not the profile, or
       // - are the "unpackaged" pseudo-package.
-      if ($package['status'] == FeaturesManagerInterface::STATUS_DISABLED ||
-        (empty($package['config']) && !($package['machine_name'] == $form['#profile_package'])) ||
-        $package['machine_name'] == 'unpackaged') {
-        $form['preview'][$package['machine_name']]['#access'] = FALSE;
+      if ($package->getStatus() == FeaturesManagerInterface::STATUS_DISABLED ||
+        (empty($package->getConfig()) && !($package->getMachineName() == $form['#profile_package'])) ||
+        $package->getMachineName() == 'unpackaged') {
+        $form['preview'][$package->getMachineName()]['#access'] = FALSE;
       }
     }
     return $form;
diff --git a/src/FeaturesExtensionStorages.php b/src/FeaturesExtensionStorages.php
index a8c9554..03b4759 100644
--- a/src/FeaturesExtensionStorages.php
+++ b/src/FeaturesExtensionStorages.php
@@ -72,6 +72,7 @@ class FeaturesExtensionStorages implements FeaturesExtensionStoragesInterface {
       $directory = $list[$name];
       return $this->extensionStorages[$directory]->read($name);
     }
+    return FALSE;
   }
 
   /**
diff --git a/src/FeaturesGenerationMethodBase.php b/src/FeaturesGenerationMethodBase.php
index 91df9e6..0ee841e 100644
--- a/src/FeaturesGenerationMethodBase.php
+++ b/src/FeaturesGenerationMethodBase.php
@@ -81,11 +81,11 @@ abstract class FeaturesGenerationMethodBase implements FeaturesGenerationMethodI
 
     foreach ($packages as &$package) {
       list($full_name, $path) = $this->featuresManager->getExportInfo($package, $bundle);
-      $package['directory'] = $path;
+      $package->setDirectory($path);
 
       // If this is the profile, its directory is already assigned.
-      if (!isset($bundle) || !$bundle->isProfilePackage($package['machine_name'])) {
-        $package['directory'] .= '/' . $full_name;
+      if (!isset($bundle) || !$bundle->isProfilePackage($package->getMachineName())) {
+        $package->setDirectory($package->getDirectory() . '/' . $full_name);
       }
 
       $this->preparePackage($package, $existing_packages, $bundle);
@@ -97,7 +97,7 @@ abstract class FeaturesGenerationMethodBase implements FeaturesGenerationMethodI
   /**
    * Performs any required changes on a package prior to generation.
    *
-   * @param array $package
+   * @param \Drupal\features\Package $package
    *   The package to be prepared.
    * @param array $existing_packages
    *   An array of existing packages with machine names as keys and paths as
@@ -105,6 +105,6 @@ abstract class FeaturesGenerationMethodBase implements FeaturesGenerationMethodI
    * @param \Drupal\features\FeaturesBundleInterface $bundle
    *   Optional bundle used for export
    */
-  abstract protected function preparePackage(array &$package, array $existing_packages, FeaturesBundleInterface $bundle = NULL);
+  abstract protected function preparePackage(Package $package, array $existing_packages, FeaturesBundleInterface $bundle = NULL);
 
 }
diff --git a/src/FeaturesGenerator.php b/src/FeaturesGenerator.php
index 3d94861..525d781 100644
--- a/src/FeaturesGenerator.php
+++ b/src/FeaturesGenerator.php
@@ -146,15 +146,15 @@ class FeaturesGenerator implements FeaturesGeneratorInterface {
         $package = $packages[$package_name];
 
         // The install profile doesn't need renaming.
-        if ($package['type'] != 'profile') {
+        if ($package->getType() != 'profile') {
           unset($packages[$package_name]);
-          $package['machine_name'] = $bundle->getFullName($package['machine_name']);
-          $packages[$package['machine_name']] = $package;
+          $package->setMachineName($bundle->getFullName($package->getMachineName()));
+          $packages[$package->getMachineName()] = $package;
         }
 
         // Set the bundle machine name.
-        $packages[$package['machine_name']]['bundle'] = $bundle->getMachineName();
-        $new_package_names[] = $package['machine_name'];
+        $packages[$package->getMachineName()]['bundle'] = $bundle->getMachineName();
+        $new_package_names[] = $package->getMachineName();
       }
       $this->featuresManager->setPackages($packages);
       $package_names = $new_package_names;
diff --git a/src/FeaturesManager.php b/src/FeaturesManager.php
index a90c547..6022ae7 100644
--- a/src/FeaturesManager.php
+++ b/src/FeaturesManager.php
@@ -92,7 +92,7 @@ class FeaturesManager implements FeaturesManagerInterface {
   /**
    * The packages to be generated.
    *
-   * @var array
+   * @var \Drupal\features\Package[]
    */
   protected $packages;
 
@@ -242,9 +242,9 @@ class FeaturesManager implements FeaturesManagerInterface {
   /**
    * {@inheritdoc}
    */
-  public function setPackage(array &$package) {
-    if (!empty($package['machine_name'])) {
-      $this->packages[$package['machine_name']] = $package;
+  public function setPackage(Package $package) {
+    if ($package->getMachineName()) {
+      $this->packages[$package->getMachineName()] = $package;
     }
   }
 
@@ -253,14 +253,15 @@ class FeaturesManager implements FeaturesManagerInterface {
    */
   public function filterPackages(array $packages, $namespace = '', $only_exported = FALSE) {
     $result = array();
+    /** @var \Drupal\features\Package $package */
     foreach ($packages as $key => $package) {
       // A package matches the namespace if:
       // - it's prefixed with the namespace, or
       // - it's assigned to a bundle named for the namespace, or
       // - we're looking only for exported packages and it's not exported.
-      if (empty($namespace) || (strpos($package['machine_name'], $namespace . '_') === 0) ||
-        (isset($package['bundle']) && $package['bundle'] === $namespace) ||
-        ($only_exported && $package['status'] === FeaturesManagerInterface::STATUS_NO_EXPORT)) {
+      if (empty($namespace) || (strpos($package->getMachineName(), $namespace . '_') === 0) ||
+        ($package->getBundle() && $package->getBundle() === $namespace) ||
+        ($only_exported && $package->getStatus() === FeaturesManagerInterface::STATUS_NO_EXPORT)) {
         $result[$key] = $package;
       }
     }
@@ -317,7 +318,7 @@ class FeaturesManager implements FeaturesManagerInterface {
    * {@inheritdoc}
    */
   public function getExtensionInfo(Extension $extension) {
-    return \Drupal::service('info_parser')->parse($extension->getPathname());
+    return \Drupal::service('info_parser')->parse(\Drupal::root() . '/' . $extension->getPathname());
   }
 
   /**
@@ -442,7 +443,7 @@ class FeaturesManager implements FeaturesManagerInterface {
    */
   public function initPackage($machine_name, $name = NULL, $description = '', $type = 'module', FeaturesBundleInterface $bundle = NULL, Extension $extension = NULL) {
     if (!isset($this->packages[$machine_name])) {
-      return $this->packages[$machine_name] = $this->getPackageArray($machine_name, $name, $description, $type, $bundle, $extension);
+      return $this->packages[$machine_name] = $this->getPackageObject($machine_name, $name, $description, $type, $bundle, $extension);
     }
     return NULL;
   }
@@ -483,12 +484,12 @@ class FeaturesManager implements FeaturesManagerInterface {
         $extension_provided = ($config_collection[$item_name]->isExtensionProvided() === TRUE);
         $already_assigned = !empty($config_collection[$item_name]->getPackage());
         // If this is the profile package, we can reassign extension-provided configuration.
-        $assignable = (!$extension_provided || $this->getAssigner()->getBundle($package['bundle'])->isProfilePackage($package['machine_name']));
+        $assignable = (!$extension_provided || $this->getAssigner()->getBundle($package->getBundle())->isProfilePackage($package->getMachineName()));
         $excluded_from_package = in_array($package_name, $config_collection[$item_name]->getPackageExcluded());
-        $already_in_package = in_array($item_name, $package['config']);
+        $already_in_package = in_array($item_name, $package->getConfig());
         if (($force || (!$already_assigned && $assignable && !$excluded_from_package)) && !$already_in_package) {
           // Add the item to the package's config array.
-          $package['config'][] = $item_name;
+          $package->appendConfig($item_name);
           // Mark the item as already assigned.
           $config_collection[$item_name]->setPackage($package_name);
           // For configuration in the InstallStorage::CONFIG_INSTALL_DIRECTORY
@@ -498,8 +499,7 @@ class FeaturesManager implements FeaturesManagerInterface {
           // InstallStorage::CONFIG_OPTIONAL_DIRECTORY should not create
           // dependencies.
           if ($config_collection[$item_name]->getSubdirectory() === InstallStorage::CONFIG_INSTALL_DIRECTORY && isset($config_collection[$item_name]->getData()['dependencies']['module'])) {
-            $dependencies =& $package['dependencies'];
-            $this->mergeUniqueItems($dependencies, $config_collection[$item_name]->getData()['dependencies']['module']);
+            $package->setDependencies($this->mergeUniqueItems($package->getDependencies(), $config_collection[$item_name]->getData()['dependencies']['module']));
           }
         }
       }
@@ -579,8 +579,9 @@ class FeaturesManager implements FeaturesManagerInterface {
    */
   public function assignInterPackageDependencies(array &$packages) {
     $config_collection = $this->getConfigCollection();
-    foreach ($packages as &$package) {
-      foreach ($package['config'] as $item_name) {
+    /** @var \Drupal\features\Package[] $packages */
+    foreach ($packages as $package) {
+      foreach ($package->getConfig() as $item_name) {
         if (!empty($config_collection[$item_name]->getData()['dependencies']['config'])) {
           foreach ($config_collection[$item_name]->getData()['dependencies']['config'] as $dependency_name) {
             if (isset($config_collection[$dependency_name])) {
@@ -588,13 +589,13 @@ class FeaturesManager implements FeaturesManagerInterface {
               // a dependency on that package.
               if ($config_collection[$dependency_name]->getPackage() && array_key_exists($config_collection[$dependency_name]->getPackage(), $packages)) {
                 $dependency_package = $packages[$config_collection[$dependency_name]->getPackage()];
-                $dependency_bundle = $this->getAssigner()->getBundle($dependency_package['bundle']);
-                $this->mergeUniqueItems($package['dependencies'], [$dependency_bundle->getFullName($dependency_package['machine_name'])]);
+                $dependency_bundle = $this->getAssigner()->getBundle($dependency_package->getBundle());
+                $package->setDependencies($this->mergeUniqueItems($package->getDependencies(), [$dependency_bundle->getFullName($dependency_package->getMachineName())]));
               }
               // Otherwise, if the dependency is provided by an existing
               // feature, add a dependency on that feature.
               elseif ($config_collection[$dependency_name]->getProvidingFeature()) {
-                $this->mergeUniqueItems($package['dependencies'], [$config_collection[$dependency_name]->getProvidingFeature()]);
+                $package->setDependencies($this->mergeUniqueItems($package->getDependencies(), [$config_collection[$dependency_name]->getProvidingFeature()]));
               }
             }
           }
@@ -610,14 +611,18 @@ class FeaturesManager implements FeaturesManagerInterface {
    *
    * Only unique values are retained.
    *
-   * @param array &$items
+   * @param array $items
    *   An array of items.
    * @param array $new_items
    *   An array of new items to be merged in.
+   *
+   * @return array
+   *   The merged, sorted and unique items.
    */
-  protected function mergeUniqueItems(&$items, $new_items) {
+  protected function mergeUniqueItems($items, $new_items) {
     $items = array_unique(array_merge($items, $new_items));
     sort($items);
+    return $items;
   }
 
   /**
@@ -636,16 +641,15 @@ class FeaturesManager implements FeaturesManagerInterface {
    * @param \Drupal\Core\Extension\Extension $extension
    *   (optional) An Extension object.
    *
-   * @return array
+   * @return \Drupal\features\Package
    *   An array of package properties; see
    *   FeaturesManagerInterface::getPackages().
    */
-  protected function getPackageArray($machine_name, $name = NULL, $description = '', $type = 'module', FeaturesBundleInterface $bundle = NULL, Extension $extension = NULL) {
+  protected function getPackageObject($machine_name, $name = NULL, $description = '', $type = 'module', FeaturesBundleInterface $bundle = NULL, Extension $extension = NULL) {
     if (!isset($bundle)) {
       $bundle = $this->getAssigner()->getBundle();
     }
-    $package = [
-      'machine_name' => $machine_name,
+    $package = new Package($machine_name, [
       'name' => isset($name) ? $name : ucwords(str_replace(['_', '-'], ' ', $machine_name)),
       'description' => $description,
       'type' => $type,
@@ -661,13 +665,13 @@ class FeaturesManager implements FeaturesManagerInterface {
       'bundle' => $bundle->isDefault() ? '' : $bundle->getMachineName(),
       'extension' => NULL,
       'info' => [],
-      'config_orig' => [],
-    ];
+      'configOrig' => [],
+    ]);
 
     // If no extension was passed in, look for a match.
     if (!isset($extension)) {
       $module_list = $this->getFeaturesModules($bundle);
-      $full_name = $bundle->getFullName($package['machine_name']);
+      $full_name = $bundle->getFullName($package->getMachineName());
       if (isset($module_list[$full_name])) {
         $extension = $module_list[$full_name];
       }
@@ -676,13 +680,13 @@ class FeaturesManager implements FeaturesManagerInterface {
     // If there is an extension, set extension-specific properties.
     if (isset($extension)) {
       $info = $this->getExtensionInfo($extension);
-      $package['extension'] = $extension;
-      $package['info'] = $info;
-      $package['config_orig'] = $this->listExtensionConfig($extension);
-      $package['status'] = $this->moduleHandler->moduleExists($extension->getName())
+      $package->setExtension($extension);
+      $package->setInfo($info);
+      $package->setConfigOrig($this->listExtensionConfig($extension));
+      $package->setStatus($this->moduleHandler->moduleExists($extension->getName())
         ? FeaturesManagerInterface::STATUS_ENABLED
-        : FeaturesManagerInterface::STATUS_DISABLED;
-      $package['version'] = isset($info['version']) ? $info['version'] : '';
+        : FeaturesManagerInterface::STATUS_DISABLED);
+      $package->setVersion(isset($info['version']) ? $info['version'] : '');
     }
 
     return $package;
@@ -691,25 +695,24 @@ class FeaturesManager implements FeaturesManagerInterface {
   /**
    * Generates and adds .info.yml files to a package.
    *
-   * @param array $package
+   * @param \Drupal\features\Package $package
    *   The package.
    */
-  protected function addInfoFile(array &$package) {
-    // Filter to standard keys of the profiles that we will use in info files.
-    $info_keys = [
-      'name',
-      'description',
-      'type',
-      'core',
-      'dependencies',
-      'themes',
-      'version'
+  protected function addInfoFile(Package $package) {
+    $info = [
+      'name' => $package->getName(),
+      'description' => $package->getDescription(),
+      'type' => $package->getType(),
+      'core' => $package->getCore(),
+      'dependencies' => $package->getDependencies(),
+      'themes' => $package->getThemes(),
+      'version' => $package->getVersion(),
+      'config' => $package->getConfig(),
     ];
-    $info = array_intersect_key($package, array_fill_keys($info_keys, NULL));
 
     // Assign to a "package" named for the profile.
-    if (isset($package['bundle'])) {
-      $bundle = $this->getAssigner()->getBundle($package['bundle']);
+    if ($package->getBundle()) {
+      $bundle = $this->getAssigner()->getBundle($package->getBundle());
     }
     // Save the current bundle in the info file so the package
     // can be reloaded later by the AssignmentPackages plugin.
@@ -721,10 +724,10 @@ class FeaturesManager implements FeaturesManagerInterface {
       unset($info['features']['bundle']);
     }
 
-    if (!empty($package['config'])) {
+    if ($package->getConfig()) {
       foreach (array('excluded', 'required') as $constraint) {
-        if (!empty($package[$constraint])) {
-          $info['features'][$constraint] = $package[$constraint];
+        if (!empty($package->{'get' . $constraint}())) {
+          $info['features'][$constraint] = $package->{'get' . $constraint}();
         }
         else {
           unset($info['features'][$constraint]);
@@ -751,29 +754,29 @@ class FeaturesManager implements FeaturesManagerInterface {
       ];
     }
 
-    $package['files']['info'] = [
-      'filename' => $package['machine_name'] . '.info.yml',
+    $package->appendFile([
+      'filename' => $package->getMachineName() . '.info.yml',
       'subdirectory' => NULL,
       // Filter to remove any empty keys, e.g., an empty themes array.
       'string' => Yaml::encode(array_filter($info))
-    ];
+    ], 'info');
   }
 
   /**
    * Generates and adds files to a given package or profile.
    */
-  protected function addPackageFiles(array &$package) {
+  protected function addPackageFiles(Package $package) {
     $config_collection = $this->getConfigCollection();
     // Ensure the directory reflects the current full machine name.
-    $package['directory'] = $package['machine_name'];
+    $package->setDirectory($package->getMachineName());
     // Only add files if there is at least one piece of configuration
     // present.
-    if (!empty($package['config'])) {
+    if ($package->getConfig()) {
       // Add .info.yml files.
       $this->addInfoFile($package);
 
       // Add configuration files.
-      foreach ($package['config'] as $name) {
+      foreach ($package->getConfig() as $name) {
         $config = $config_collection[$name];
         // The UUID is site-specfic, so don't export it.
         if ($entity_type_id = $this->configManager->getEntityTypeIdByName($name)) {
@@ -788,11 +791,11 @@ class FeaturesManager implements FeaturesManagerInterface {
           $data['permissions'] = [];
           $config->setData($data);
         }
-        $package['files'][$name] = [
+        $package->appendFile([
           'filename' => $config->getName() . '.yml',
           'subdirectory' => $config->getSubdirectory(),
           'string' => Yaml::encode($config->getData())
-        ];
+        ], $name);
       }
     }
   }
@@ -964,19 +967,17 @@ class FeaturesManager implements FeaturesManagerInterface {
   /**
    * {@inheritdoc}
    */
-  public function prepareFiles(array &$packages) {
-    foreach ($packages as &$package) {
+  public function prepareFiles(array $packages) {
+    foreach ($packages as $package) {
       $this->addPackageFiles($package);
     }
-    // Clean up the $package pass by reference.
-    unset($package);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getExportInfo($package, FeaturesBundleInterface $bundle = NULL) {
-    $full_name = isset($bundle) ? $bundle->getFullName($package['machine_name']) : $package['machine_name'];
+  public function getExportInfo(Package $package, FeaturesBundleInterface $bundle = NULL) {
+    $full_name = isset($bundle) ? $bundle->getFullName($package->getMachineName()) : $package->getMachineName();
 
     $path = '';
 
@@ -986,7 +987,7 @@ class FeaturesManager implements FeaturesManagerInterface {
     }
 
     // If this is not the profile package, nest the directory.
-    if (!isset($bundle) || !$bundle->isProfilePackage($package['machine_name'])) {
+    if (!isset($bundle) || !$bundle->isProfilePackage($package->getMachineName())) {
       $path .= empty($path) ? 'modules' : '/modules';
       $export_settings = $this->getExportSettings();
       if (!empty($export_settings['folder'])) {
@@ -1000,12 +1001,12 @@ class FeaturesManager implements FeaturesManagerInterface {
   /**
    * {@inheritdoc}
    */
-  public function detectOverrides(array $feature, $include_new = FALSE) {
+  public function detectOverrides(Package $feature, $include_new = FALSE) {
     /** @var \Drupal\config_update\ConfigDiffInterface $config_diff */
     $config_diff = \Drupal::service('config_update.config_diff');
 
     $different = array();
-    foreach ($feature['config'] as $name) {
+    foreach ($feature->getConfig() as $name) {
       $active = $this->configStorage->read($name);
       $extension = $this->extensionStorages->read($name);
       $extension = !empty($extension) ? $extension : array();
@@ -1015,7 +1016,7 @@ class FeaturesManager implements FeaturesManagerInterface {
     }
 
     if (!empty($different)) {
-      $feature['state'] = FeaturesManagerInterface::STATE_OVERRIDDEN;
+      $feature->setState(FeaturesManagerInterface::STATE_OVERRIDDEN);
     }
     return $different;
   }
@@ -1023,9 +1024,9 @@ class FeaturesManager implements FeaturesManagerInterface {
   /**
    * {@inheritdoc}
    */
-  public function detectNew(array $feature) {
+  public function detectNew(Package $feature) {
     $result = array();
-    foreach ($feature['config'] as $name) {
+    foreach ($feature->getConfig() as $name) {
       $extension = $this->extensionStorages->read($name);
       if (empty($extension)) {
         $result[] = $name;
@@ -1037,10 +1038,10 @@ class FeaturesManager implements FeaturesManagerInterface {
   /**
    * {@inheritdoc}
    */
-  public function detectMissing(array $feature) {
+  public function detectMissing(Package $feature) {
     $config = $this->getConfigCollection();
     $result = array();
-    foreach ($feature['config_orig'] as $name) {
+    foreach ($feature->getConfigOrig() as $name) {
       if (!isset($config[$name])) {
         $result[] = $name;
       }
diff --git a/src/FeaturesManagerInterface.php b/src/FeaturesManagerInterface.php
index 427a900..a854892 100644
--- a/src/FeaturesManagerInterface.php
+++ b/src/FeaturesManagerInterface.php
@@ -94,7 +94,7 @@ interface FeaturesManagerInterface {
   /**
    * Gets an array of packages.
    *
-   * @return array
+   * @return \Drupal\features\Package[]
    *   An array of items, each with the following keys:
    *   - 'machine_name': machine name of the package such as 'example_article'.
    *     'article'.
@@ -131,7 +131,7 @@ interface FeaturesManagerInterface {
   /**
    * Sets an array of packages.
    *
-   * @param array $packages
+   * @param \Drupal\features\Package[] $packages
    *   An array of packages.
    */
   public function setPackages(array $packages);
@@ -142,7 +142,7 @@ interface FeaturesManagerInterface {
    * @param string $machine_name
    *   Full machine name of package.
    *
-   * @return array
+   * @return \Drupal\features\Package
    *   Package data.
    *
    * @see \Drupal\features\FeaturesManagerInterface::getPackages()
@@ -155,22 +155,22 @@ interface FeaturesManagerInterface {
    * NOTE: This does not "export" the package; it simply updates the internal
    * data.
    *
-   * @param array $package
+   * @param \Drupal\features\Package $package
    *   The package.
    */
-  public function setPackage(array &$package);
+  public function setPackage(Package $package);
 
   /**
    * Filters the supplied package list by the given namespace.
    *
-   * @param array $packages
+   * @param \Drupal\features\Package[] $packages
    *   An array of packages.
    * @param string $namespace
    *   The namespace to use.
    * @param bool $only_exported
    *   If true, only filter out packages that are exported
    *
-   * @return array
+   * @return \Drupal\features\Package[]
    *   An array of packages.
    */
   public function filterPackages(array $packages, $namespace = '', $only_exported = FALSE);
@@ -262,7 +262,7 @@ interface FeaturesManagerInterface {
    * @param \Drupal\Core\Extension\Extension $extension
    *   An Extension object.
    *
-   * @return array
+   * @return \Drupal\features\Package
    *   The created package array.
    */
   public function initPackageFromExtension(Extension $extension);
@@ -329,7 +329,7 @@ interface FeaturesManagerInterface {
   /**
    * Assigns dependencies between packages based on configuration dependencies.
    *
-   * @param array $packages
+   * @param \Drupal\features\Package[] $packages
    *   An array of packages.
    */
   public function assignInterPackageDependencies(array &$packages);
@@ -348,6 +348,8 @@ interface FeaturesManagerInterface {
    *
    * @return array
    *   An array with the merged and processed results.
+   *
+   * @fixme Should this be moved to the package object or a related helper?
    */
   public function mergeInfoArray(array $info1, array $info2, array $keys = array());
 
@@ -421,10 +423,10 @@ interface FeaturesManagerInterface {
   /**
    * Iterates through packages and prepares file names and contents.
    *
-   * @param array &$packages
+   * @param array $packages
    *   An array of packages.
    */
-  public function prepareFiles(array &$packages);
+  public function prepareFiles(array $packages);
 
   /**
    * Returns the full name of a config item.
@@ -453,8 +455,8 @@ interface FeaturesManagerInterface {
   /**
    * Returns the full machine name and directory for exporting a package.
    *
-   * @param string $package
-   *   The name of a package.
+   * @param \Drupal\features\Package $package
+   *   The package.
    * @param \Drupal\features\FeaturesBundleInterface $bundle
    *   Optional bundle being used for export.
    *
@@ -462,7 +464,7 @@ interface FeaturesManagerInterface {
    *   An array with the full name as the first item and directory as second
    *   item.
    */
-  public function getExportInfo($package, FeaturesBundleInterface $bundle = NULL);
+  public function getExportInfo(Package $package, FeaturesBundleInterface $bundle = NULL);
 
   /**
    * Determines if the module is a Features package, optinally testing by
@@ -481,7 +483,7 @@ interface FeaturesManagerInterface {
   /**
    * Determines which config is overridden in a package.
    *
-   * @param array $feature
+   * @param \Drupal\features\Package $feature
    *   The package array.
    *   The 'state' property is updated if overrides are detected.
    * @param bool $include_new
@@ -492,31 +494,31 @@ interface FeaturesManagerInterface {
    *
    * @see \Drupal\features\FeaturesManagerInterface::detectNew()
    */
-  public function detectOverrides(array $feature, $include_new = FALSE);
+  public function detectOverrides(Package $feature, $include_new = FALSE);
 
   /**
    * Determines which config has not been exported to the feature.
    *
    * Typically added as an auto-detected dependency.
    *
-   * @param array $feature
+   * @param \Drupal\features\Package $feature
    *   The package array.
    *
    * @return array
    *   The array of config items that are overridden.
    */
-  public function detectNew(array $feature);
+  public function detectNew(Package $feature);
 
   /**
    * Determines which config is exported in the feature but not in the active.
    *
-   * @param array $feature
+   * @param \Drupal\features\Package $feature
    *   The package array.
    *
    * @return array
    *   The array of config items that are missing from active store.
    */
-  public function detectMissing(array $feature);
+  public function detectMissing(Package $feature);
 
   /**
    * Sort the Missing config into order by dependencies.
diff --git a/src/Package.php b/src/Package.php
new file mode 100644
index 0000000..4b53a81
--- /dev/null
+++ b/src/Package.php
@@ -0,0 +1,484 @@
+<?php
+
+namespace Drupal\features;
+
+/**
+ * Defines a value object for storing package related data.
+ *
+ * A package contains of a name, version number, containing config etc.
+ */
+class Package {
+
+  /**
+   * @var string
+   */
+  protected $machineName = '';
+
+  /**
+   * @var string
+   */
+  protected $name = '';
+
+  /**
+   * @var string
+   */
+  protected $description = '';
+
+  /**
+   * @todo This could be fetched from the extension object.
+   *
+   * @var string
+   */
+  protected $version = '';
+
+  /**
+   * @var string
+   */
+  protected $core = '8.x';
+
+  /**
+   * @todo This could be fetched from the extension object.
+   *
+   * @var string
+   */
+  protected $type = 'module';
+
+  /**
+   * @var string[]
+   */
+  protected $themes = [];
+
+  /**
+   * @var string
+   */
+  protected $bundle;
+
+  /**
+   * @var string[]
+   */
+  protected $excluded = [];
+
+  /**
+   * @var string[]
+   */
+  protected $required = [];
+
+  /**
+   * @var array
+   */
+  protected $info = [];
+
+  /**
+   * @var string[]
+   */
+  protected $dependencies = [];
+
+  /**
+   * @todo This could be fetched from the extension object.
+   *
+   * @var int
+   */
+  protected $status;
+
+  /**
+   * @var int
+   */
+  protected $state;
+
+  /**
+   * @todo This could be fetched from the extension object.
+   *
+   * @var string
+   */
+  protected $directory;
+
+  /**
+   * @var string[]
+   */
+  protected $files;
+
+  /**
+   * @var \Drupal\Core\Extension\Extension
+   */
+  protected $extension;
+
+  /**
+   * @var string[]
+   */
+  protected $config = [];
+
+  /**
+   * @var string[]
+   */
+  protected $configOrig = [];
+
+  /**
+   * Creates a new Package instance.
+   *
+   * @param string $machine_name
+   *   The machine name.
+   * @param array $additional_properties
+   *   (optional) Additional properties of the object.
+   */
+  public function __construct($machine_name, array $additional_properties = []) {
+    $this->machineName = $machine_name;
+
+    $properties = get_object_vars($this);
+    foreach ($additional_properties as $property => $value) {
+      if (!array_key_exists($property, $properties)) {
+        throw new \InvalidArgumentException('Invalid property: ' . $property);
+      }
+      $this->{$property} = $value;
+    }
+  }
+
+  /**
+   * @return mixed
+   */
+  public function getMachineName() {
+    return $this->machineName;
+  }
+
+  /**
+   * @return string
+   */
+  public function getName() {
+    return $this->name;
+  }
+
+  /**
+   * @return string
+   */
+  public function getDescription() {
+    return $this->description;
+  }
+
+  /**
+   * @return string
+   */
+  public function getVersion() {
+    return $this->version;
+  }
+
+  /**
+   * @return int
+   */
+  public function getStatus() {
+    return $this->status;
+  }
+
+  /**
+   * @return string[]
+   */
+  public function getConfig() {
+    return $this->config;
+  }
+
+  /**
+   * Append a new filename.
+   *
+   * @param string $config
+   *
+   * @return $this
+   */
+  public function appendConfig($config) {
+    $this->config[] = $config;
+    $this->config = array_unique($this->config);
+    return $this;
+  }
+
+  public function removeConfig($name) {
+    $this->config = array_diff($this->config, [$name]);
+    return $this;
+  }
+
+  /**
+   * @return string
+   */
+  public function getBundle() {
+    return $this->bundle;
+  }
+
+  /**
+   * @return string[]
+   */
+  public function getExcluded() {
+    return $this->excluded;
+  }
+
+  /**
+   * @return string[]
+   */
+  public function getRequired() {
+    return $this->required;
+  }
+
+  /**
+   * @return string[]
+   */
+  public function getConfigOrig() {
+    return $this->configOrig;
+  }
+
+  /**
+   * @return string
+   */
+  public function getCore() {
+    return $this->core;
+  }
+
+  /**
+   * @return string
+   */
+  public function getType() {
+    return $this->type;
+  }
+
+  /**
+   * @return \string[]
+   */
+  public function getThemes() {
+    return $this->themes;
+  }
+
+  /**
+   * @return array
+   */
+  public function getInfo() {
+    return $this->info;
+  }
+
+  /**
+   * @return mixed
+   */
+  public function getState() {
+    return $this->state;
+  }
+
+  /**
+   * @return string
+   */
+  public function getDirectory() {
+    return $this->directory;
+  }
+
+  /**
+   * @return mixed
+   */
+  public function getFiles() {
+    return $this->files;
+  }
+
+  /**
+   * @return \Drupal\Core\Extension\Extension
+   */
+  public function getExtension() {
+    return $this->extension;
+  }
+
+  public function getDependencies() {
+    return $this->dependencies;
+  }
+
+  public function removeDependency($name) {
+    $this->dependencies = array_diff($this->dependencies, [$name]);
+    return $this;
+  }
+
+  public function getDependencyInfo() {
+    return isset($this->info['dependencies']) ? $this->info['dependencies'] : [];
+  }
+
+  public function getFeaturesInfo() {
+    return isset($this->info['features']) ? $this->info['features'] : [];
+  }
+
+  /**
+   * Sets a new machine name.
+   *
+   * @param string $machine_name
+   *   The machine name
+   *
+   * @return $this
+   */
+  public function setMachineName($machine_name) {
+    $this->machineName = $machine_name;
+    return $this;
+  }
+
+  /**
+   * @param string $name
+   *
+   * @return $this
+   */
+  public function setName($name) {
+    $this->name = $name;
+    return $this;
+  }
+
+  /**
+   * @param string $description
+   *
+   * @return $this
+   */
+  public function setDescription($description) {
+    $this->description = $description;
+    return $this;
+  }
+
+  /**
+   * @param string $version
+   *
+   * @return $this
+   */
+  public function setVersion($version) {
+    $this->version = $version;
+    return $this;
+  }
+
+  /**
+   * @param string $bundle
+   *
+   * @return $this
+   */
+  public function setBundle($bundle) {
+    $this->bundle = $bundle;
+    return $this;
+  }
+
+  /**
+   * @param array $info
+   *
+   * @return $this
+   */
+  public function setInfo($info) {
+    $this->info = $info;
+    return $this;
+  }
+
+  /**
+   * @param \string[] $dependencies
+   *
+   * @return $this
+   */
+  public function setDependencies($dependencies) {
+    $this->dependencies = $dependencies;
+    return $this;
+  }
+
+  /**
+   * @param string $dependency
+   *
+   * return $this
+   */
+  public function appendDependency($dependency) {
+    $this->dependencies[] = $dependency;
+    return $this;
+  }
+
+  /**
+   * @param int $status
+   *
+   * @return $this
+   */
+  public function setStatus($status) {
+    $this->status = $status;
+    return $this;
+  }
+
+  /**
+   * @param \string[] $config
+   *
+   * @return $this
+   */
+  public function setConfig($config) {
+    $this->config = $config;
+    return $this;
+  }
+
+  /**
+   * @param bool $excluded
+   */
+  public function setExcluded($excluded) {
+    $this->excluded = $excluded;
+  }
+
+  /**
+   * @param bool $required
+   */
+  public function setRequired($required) {
+    $this->required = $required;
+  }
+
+  /**
+   * @param string $core
+   */
+  public function setCore($core) {
+    $this->core = $core;
+  }
+
+  /**
+   * @param string $type
+   */
+  public function setType($type) {
+    $this->type = $type;
+  }
+
+  /**
+   * @param \string[] $themes
+   */
+  public function setThemes($themes) {
+    $this->themes = $themes;
+  }
+
+  /**
+   * @param int $state
+   */
+  public function setState($state) {
+    $this->state = $state;
+  }
+
+  /**
+   * @param string $directory
+   */
+  public function setDirectory($directory) {
+    $this->directory = $directory;
+  }
+
+  /**
+   * @param \string[] $files
+   */
+  public function setFiles($files) {
+    $this->files = $files;
+  }
+
+  /**
+   * @param array $file_array
+   *
+   * @return $this
+   */
+  public function appendFile(array $file_array, $key = NULL) {
+    if (!isset($key)) {
+      $this->files[] = $file_array;
+    }
+    else {
+      $this->files[$key] = $file_array;
+    }
+    return $this;
+  }
+
+  /**
+   * @param \Drupal\Core\Extension\Extension $extension
+   */
+  public function setExtension($extension) {
+    $this->extension = $extension;
+  }
+
+  /**
+   * @param \string[] $configOrig
+   */
+  public function setConfigOrig($configOrig) {
+    $this->configOrig = $configOrig;
+  }
+
+}
diff --git a/src/Plugin/FeaturesAssignment/FeaturesAssignmentExisting.php b/src/Plugin/FeaturesAssignment/FeaturesAssignmentExisting.php
index 7e0f0f4..265f907 100644
--- a/src/Plugin/FeaturesAssignment/FeaturesAssignmentExisting.php
+++ b/src/Plugin/FeaturesAssignment/FeaturesAssignmentExisting.php
@@ -53,14 +53,15 @@ class FeaturesAssignmentExisting extends FeaturesAssignmentMethodBase {
 
     // Assign config to enabled modules first.
     foreach ($packages as $name => $package) {
-      if ($package['status'] === FeaturesManagerInterface::STATUS_ENABLED) {
-        $this->safeAssignConfig($package['machine_name'], $package['extension']);
+      // @todo Introduce $package->isEnabled() and / or $package->isDisabled().
+      if ($package->getStatus() === FeaturesManagerInterface::STATUS_ENABLED) {
+        $this->safeAssignConfig($package->getMachineName(), $package->getExtension());
       }
     }
     // Now assign to disabled modules.
     foreach ($packages as $name => $info) {
-      if ($package['status'] === FeaturesManagerInterface::STATUS_DISABLED) {
-        $this->safeAssignConfig($package['machine_name'], $package['extension']);
+      if ($package->getStatus() === FeaturesManagerInterface::STATUS_DISABLED) {
+        $this->safeAssignConfig($package->getMachineName(), $package->getExtension());
       }
     }
   }
diff --git a/src/Plugin/FeaturesAssignment/FeaturesAssignmentPackages.php b/src/Plugin/FeaturesAssignment/FeaturesAssignmentPackages.php
index 0c06ce4..51631cf 100644
--- a/src/Plugin/FeaturesAssignment/FeaturesAssignmentPackages.php
+++ b/src/Plugin/FeaturesAssignment/FeaturesAssignmentPackages.php
@@ -34,14 +34,14 @@ class FeaturesAssignmentPackages extends FeaturesAssignmentMethodBase {
     $existing = $this->featuresManager->getFeaturesModules($bundle);
     foreach ($existing as $extension) {
       $package = $this->featuresManager->initPackageFromExtension($extension);
-      $info = $package['info'];
-      $short_name = $this->assigner->getBundle($package['bundle'])->getShortName($extension->getName());
+      $features_info = $package->getFeaturesInfo();
+      $short_name = $this->assigner->getBundle($package->getBundle())->getShortName($extension->getName());
 
-      if (!empty($info['features']['excluded']) || !empty($info['features']['required'])) {
+      if (!empty($features_info['excluded']) || !empty($features_info['required'])) {
         // Copy over package excluded settings, if any.
-        if (!empty($info['features']['excluded'])) {
+        if (!empty($features_info['excluded'])) {
           $config_collection = $this->featuresManager->getConfigCollection();
-          foreach ($info['features']['excluded'] as $config_name) {
+          foreach ($features_info['excluded'] as $config_name) {
             if (isset($config_collection[$config_name])) {
               $package_excluded = $config_collection[$config_name]->getPackageExcluded();
               $package_excluded[] = $short_name;
@@ -51,8 +51,8 @@ class FeaturesAssignmentPackages extends FeaturesAssignmentMethodBase {
           $this->featuresManager->setConfigCollection($config_collection);
         }
         // Assign required components, if any.
-        if (!empty($info['features']['required'])) {
-          $this->featuresManager->assignConfigPackage($short_name, $info['features']['required']);
+        if (!empty($features_info['required'])) {
+          $this->featuresManager->assignConfigPackage($short_name, $features_info['required']);
         }
       }
     }
diff --git a/src/Plugin/FeaturesAssignment/FeaturesAssignmentProfile.php b/src/Plugin/FeaturesAssignment/FeaturesAssignmentProfile.php
index a8c9eb4..62339b4 100644
--- a/src/Plugin/FeaturesAssignment/FeaturesAssignmentProfile.php
+++ b/src/Plugin/FeaturesAssignment/FeaturesAssignmentProfile.php
@@ -97,19 +97,19 @@ class FeaturesAssignmentProfile extends FeaturesAssignmentMethodBase {
               // add a dependency.
               else {
                 $machine_name = $current_bundle->getFullName($config_collection[$item_name]->getPackage());
-                if (!in_array($machine_name, $profile_package['dependencies'])) {
-                  $profile_package['dependencies'][] = $machine_name;
+                if (!in_array($machine_name, $profile_package->getDependencies())) {
+                  $profile_package->appendDependency($machine_name);
                 }
               }
             }
             // Otherwise, copy it over from Standard.
             else {
               $filename = $item_name . '.yml';
-              $profile_package['files'][] = [
+              $profile_package->appendFile([
                 'filename' => $filename,
                 'subdirectory' => $subdirectory,
                 'string' => file_get_contents($standard_directory . '/' . $subdirectory . '/' . $filename)
-              ];
+              ]);
             }
           }
 
@@ -132,11 +132,11 @@ class FeaturesAssignmentProfile extends FeaturesAssignmentMethodBase {
                 $string
               );
               // Add the files to those to be output.
-              $profile_package['files'][$extension] = [
+              $profile_package->appendFile([
                 'filename' => $profile_name . '.' . $extension,
                 'subdirectory' => NULL,
                 'string' => $string
-              ];
+              ], $extension);
             }
           }
         }
@@ -146,8 +146,13 @@ class FeaturesAssignmentProfile extends FeaturesAssignmentMethodBase {
           $info_file_uri = $standard_directory . '/standard.info.yml';
           if (file_exists($info_file_uri)) {
             $profile_info = \Drupal::service('info_parser')->parse($info_file_uri);
-            // Merge in dependencies and themes data.
-            $profile_package = $this->featuresManager->mergeInfoArray($profile_package, $profile_info, ['dependencies', 'themes']);
+            $info = [
+              'dependencies' => $profile_package->getDependencies(),
+              'themes' => $profile_package->getThemes(),
+            ];
+            $info = $this->featuresManager->mergeInfoArray($info, $profile_info);
+            $profile_package->setDependencies($info['dependencies']);
+            $profile_package->setThemes($info['themes']);
           }
         }
         $this->featuresManager->setPackage($profile_package);
diff --git a/src/Plugin/FeaturesGeneration/FeaturesGenerationArchive.php b/src/Plugin/FeaturesGeneration/FeaturesGenerationArchive.php
index 10f4c9f..b89e5e5 100644
--- a/src/Plugin/FeaturesGeneration/FeaturesGenerationArchive.php
+++ b/src/Plugin/FeaturesGeneration/FeaturesGenerationArchive.php
@@ -14,6 +14,7 @@ use Drupal\features\FeaturesGenerationMethodBase;
 use Drupal\Core\Archiver\ArchiveTar;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\features\FeaturesBundleInterface;
+use Drupal\features\Package;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -69,9 +70,9 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
   /**
    * Reads and merges in existing files for a given package or profile.
    */
-  protected function preparePackage(array &$package, array $existing_packages, FeaturesBundleInterface $bundle = NULL) {
-    if (isset($existing_packages[$package['machine_name']])) {
-      $existing_directory = $existing_packages[$package['machine_name']];
+  protected function preparePackage(Package $package, array $existing_packages, FeaturesBundleInterface $bundle = NULL) {
+    if (isset($existing_packages[$package->getMachineName()])) {
+      $existing_directory = $existing_packages[$package->getMachineName()];
       // Scan for all files.
       $files = file_scan_directory($existing_directory, '/.*/');
       foreach ($files as $file) {
@@ -83,8 +84,10 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
           }
         }
         // Merge in the info file.
-        if ($file->name == $package['machine_name'] . '.info') {
-          $package['files']['info']['string'] = $this->mergeInfoFile($package['files']['info']['string'], $file->uri);
+        if ($file->name == $package->getMachineName() . '.info') {
+          $files = $package->getFiles();
+          $files['info']['string'] = $this->mergeInfoFile($package->getFiles()['info']['string'], $file->uri);
+          $package->setFiles($files);
         }
         // Read in remaining files.
         else {
@@ -97,11 +100,11 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
           else {
             $subdirectory = NULL;
           }
-          $package['files'][] = [
+          $package->appendFile([
             'filename' => $file->filename,
             'subdirectory' => $subdirectory,
             'string' => file_get_contents($file->uri)
-          ];
+          ]);
         }
       }
     }
@@ -120,7 +123,7 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
     // Determine the best name for the tar archive.
     // Single package export, so name by package name.
     if (count($packages) == 1) {
-      $filename = current($packages)['machine_name'];
+      $filename = current($packages)->getMachineName();
     }
     // Profile export, so name by profile.
     elseif (isset($bundle) && $bundle->isProfile()) {
@@ -149,7 +152,7 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
     foreach ($packages as $package) {
       if (count($packages) == 1) {
         // Single module export, so don't generate entire modules dir structure.
-        $package['directory'] = $package['machine_name'];
+        $package->setDirectory($package->getMachineName());
       }
       $this->generatePackage($return, $package, $archiver);
     }
@@ -162,16 +165,16 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
    *
    * @param array &$return
    *   The return value, passed by reference.
-   * @param array $package
+   * @param \Drupal\features\Package $package
    *   The package or profile.
    * @param ArchiveTar $archiver
    *   The archiver.
    */
-  protected function generatePackage(array &$return, array $package, ArchiveTar $archiver) {
+  protected function generatePackage(array &$return, Package $package, ArchiveTar $archiver) {
     $success = TRUE;
-    foreach ($package['files'] as $file) {
+    foreach ($package->getFiles() as $file) {
       try {
-        $this->generateFile($package['directory'], $file, $archiver);
+        $this->generateFile($package->getDirectory(), $file, $archiver);
       }
       catch (\Exception $exception) {
         $this->failure($return, $package, $exception);
@@ -189,11 +192,11 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
    *
    * @param array &$return
    *   The return value, passed by reference.
-   * @param array $package
+   * @param \Drupal\features\Package $package
    *   The package or profile.
    */
-  protected function success(array &$return, array $package) {
-    $type = $package['type'] == 'module' ? $this->t('Package') : $this->t('Profile');
+  protected function success(array &$return, Package $package) {
+    $type = $package->getType() == 'module' ? $this->t('Package') : $this->t('Profile');
     $return[] = [
       'success' => TRUE,
       // Archive writing doesn't merit a message, and if done through the UI
@@ -202,7 +205,7 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
       'message' => '@type @package written to archive.',
       'variables' => [
         '@type' => $type,
-        '@package' => $package['name']
+        '@package' => $package->getName(),
       ],
     ];
   }
@@ -212,15 +215,15 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
    *
    * @param array &$return
    *   The return value, passed by reference.
-   * @param array $package
+   * @param \Drupal\features\Package $package
    *   The package or profile.
    * @param \Exception $exception
    *   The exception object.
    * @param string $message
    *   Error message when there isn't an Exception object.
    */
-  protected function failure(array &$return, array $package, \Exception $exception, $message = '') {
-    $type = $package['type'] == 'module' ? $this->t('Package') : $this->t('Profile');
+  protected function failure(array &$return, Package $package, \Exception $exception, $message = '') {
+    $type = $package->getType() == 'module' ? $this->t('Package') : $this->t('Profile');
     $return[] = [
       'success' => FALSE,
       // Archive writing doesn't merit a message, and if done through the UI
@@ -229,7 +232,7 @@ class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements
       'message' => '@type @package not written to archive. Error: @error.',
       'variables' => [
         '@type' => $type,
-        '@package' => $package['name'],
+        '@package' => $package->getName(),
         '@error' => isset($exception) ? $exception->getMessage() : $message,
       ],
     ];
diff --git a/src/Plugin/FeaturesGeneration/FeaturesGenerationWrite.php b/src/Plugin/FeaturesGeneration/FeaturesGenerationWrite.php
index 12bab1e..33ab94d 100644
--- a/src/Plugin/FeaturesGeneration/FeaturesGenerationWrite.php
+++ b/src/Plugin/FeaturesGeneration/FeaturesGenerationWrite.php
@@ -9,6 +9,7 @@ namespace Drupal\features\Plugin\FeaturesGeneration;
 
 use Drupal\features\FeaturesGenerationMethodBase;
 use Drupal\features\FeaturesBundleInterface;
+use Drupal\features\Package;
 
 /**
  * Class for writing packages to the local file system.
@@ -30,24 +31,26 @@ class FeaturesGenerationWrite extends FeaturesGenerationMethodBase {
   /**
    * Reads and merges in existing files for a given package or profile.
    *
-   * @param array &$package
+   * @param \Drupal\features\Package &$package
    *   The package.
    * @param array $existing_packages
    *   An array of existing packages.
    * @param \Drupal\features\FeaturesBundleInterface $bundle
    *   The bundle the package belongs to.
    */
-  protected function preparePackage(array &$package, array $existing_packages, FeaturesBundleInterface $bundle = NULL) {
+  protected function preparePackage(Package $package, array $existing_packages, FeaturesBundleInterface $bundle = NULL) {
     // If this package is already present, prepare files.
-    if (isset($existing_packages[$package['machine_name']])) {
-      $existing_directory = $existing_packages[$package['machine_name']];
+    if (isset($existing_packages[$package->getMachineName()])) {
+      $existing_directory = $existing_packages[$package->getMachineName()];
 
-      $package['directory'] = $existing_directory;
+      $package->setDirectory($existing_directory);
 
       // Merge in the info file.
-      $info_file_uri = $existing_directory . '/' . $package['machine_name'] . '.info.yml';
+      $info_file_uri = $existing_directory . '/' . $package->getMachineName() . '.info.yml';
       if (file_exists($info_file_uri)) {
-        $package['files']['info']['string'] = $this->mergeInfoFile($package['files']['info']['string'], $info_file_uri);
+        $files = $package->getFiles();
+        $files['info']['string'] = $this->mergeInfoFile($package->getFiles()['info']['string'], $info_file_uri);
+        $package->setFiles($files);
       }
 
       // Remove the config directories, as they will be replaced.
@@ -78,8 +81,8 @@ class FeaturesGenerationWrite extends FeaturesGenerationMethodBase {
     $files = \Drupal::state()->get('system.module.files');
     foreach ($packages as $package) {
       $this->generatePackage($return, $package);
-      if (!isset($files[$package['machine_name']]) && isset($package['files']['info'])) {
-        $files[$package['machine_name']] = $package['directory'] . '/' . $package['files']['info']['filename'];
+      if (!isset($files[$package->getMachineName()]) && isset($package->getFiles()['info'])) {
+        $files[$package->getMachineName()] = $package->getDirectory() . '/' . $package->getFiles()['info']['filename'];
       }
     }
 
@@ -94,18 +97,18 @@ class FeaturesGenerationWrite extends FeaturesGenerationMethodBase {
    *
    * @param array &$return
    *   The return value, passed by reference.
-   * @param array $package
+   * @param \Drupal\features\Package $package
    *   The package or profile.
    */
-  protected function generatePackage(array &$return, array $package) {
-    if (empty($package['files'])) {
+  protected function generatePackage(array &$return, Package $package) {
+    if (!$package->getFiles()) {
       $this->failure($return, $package, NULL, t('No configuration was selected to be exported.'));
       return;
     }
     $success = TRUE;
-    foreach ($package['files'] as $file) {
+    foreach ($package->getFiles() as $file) {
       try {
-        $this->generateFile($package['directory'], $file);
+        $this->generateFile($package->getDirectory(), $file);
       }
       catch (\Exception $exception) {
         $this->failure($return, $package, $exception);
@@ -123,19 +126,19 @@ class FeaturesGenerationWrite extends FeaturesGenerationMethodBase {
    *
    * @param array &$return
    *   The return value, passed by reference.
-   * @param array $package
+   * @param \Drupal\features\Package $package
    *   The package or profile.
    */
-  protected function success(array &$return, array $package) {
-    $type = $package['type'] == 'module' ? $this->t('Package') : $this->t('Profile');
+  protected function success(array &$return, Package $package) {
+    $type = $package->getType() == 'module' ? $this->t('Package') : $this->t('Profile');
     $return[] = [
       'success' => TRUE,
       'display' => TRUE,
       'message' => '@type @package written to @directory.',
       'variables' => [
         '@type' => $type,
-        '@package' => $package['name'],
-        '@directory' => $package['directory']
+        '@package' => $package->getName(),
+        '@directory' => $package->getDirectory(),
       ],
     ];
   }
@@ -145,23 +148,23 @@ class FeaturesGenerationWrite extends FeaturesGenerationMethodBase {
    *
    * @param array &$return
    *   The return value, passed by reference.
-   * @param array $package
+   * @param \Drupal\features\Package $package
    *   The package or profile.
    * @param \Exception $exception
    *   The exception object.
    * @param string $message
    *   Error message when there isn't an Exception object.
    */
-  protected function failure(array &$return, array $package, \Exception $exception, $message = '') {
-    $type = $package['type'] == 'module' ? $this->t('Package') : $this->t('Profile');
+  protected function failure(array &$return, Package $package, \Exception $exception, $message = '') {
+    $type = $package->getType() == 'module' ? $this->t('Package') : $this->t('Profile');
     $return[] = [
       'success' => FALSE,
       'display' => TRUE,
       'message' => '@type @package not written to @directory. Error: @error.',
       'variables' => [
         '@type' => $type,
-        '@package' => $package['name'],
-        '@directory' => $package['directory'],
+        '@package' => $package->getName(),
+        '@directory' => $package->getDirectory(),
         '@error' => isset($exception) ? $exception->getMessage() : $message,
       ],
     ];
diff --git a/tests/src/Kernel/FeaturesAssignTest.php b/tests/src/Kernel/FeaturesAssignTest.php
index 43354f3..a01bd6b 100644
--- a/tests/src/Kernel/FeaturesAssignTest.php
+++ b/tests/src/Kernel/FeaturesAssignTest.php
@@ -93,7 +93,7 @@ class FeaturesAssignTest extends KernelTestBase {
       'field.field.node.article.body',
     ];
 
-    $this->assertEquals($expected_config_items, $packages['article']['config'], 'Expected configuration items not present in article package.');  
+    $this->assertEquals($expected_config_items, $packages['article']->getConfig(), 'Expected configuration items not present in article package.');
 
   }
 
@@ -132,8 +132,8 @@ class FeaturesAssignTest extends KernelTestBase {
 
     $this->assertEquals($expected_package_names, array_keys($packages), 'Expected packages not created.');  
 
-    $this->assertTrue(in_array('field.storage.node.body', $packages['core']['config'], 'Expected configuration item not present in core package.'));  
-    $this->assertFalse(in_array('field.field.node.article.body', $packages['core']['config'], 'Unexpected configuration item present in core package.'));  
+    $this->assertTrue(in_array('field.storage.node.body', $packages['core']->getConfig(), 'Expected configuration item not present in core package.'));
+    $this->assertFalse(in_array('field.field.node.article.body', $packages['core']->getConfig(), 'Unexpected configuration item present in core package.'));
 
   }
 
@@ -181,7 +181,7 @@ class FeaturesAssignTest extends KernelTestBase {
       'field.field.node.article.body',
     ];
 
-    $this->assertEquals($expected_config_items, $packages[self::PACKAGE_NAME]['config'], 'Expected configuration items not present in article package.');
+    $this->assertEquals($expected_config_items, $packages[self::PACKAGE_NAME]->getConfig(), 'Expected configuration items not present in article package.');
 
   }
 
diff --git a/tests/src/Kernel/FeaturesGenerateTest.php b/tests/src/Kernel/FeaturesGenerateTest.php
index 9e51ef8..479efa9 100644
--- a/tests/src/Kernel/FeaturesGenerateTest.php
+++ b/tests/src/Kernel/FeaturesGenerateTest.php
@@ -52,7 +52,7 @@ class FeaturesGenerateTest extends KernelTestBase {
 
     $this->featuresManager->initPackage(self::PACKAGE_NAME, 'My test package');
     $package = $this->featuresManager->getPackage(self::PACKAGE_NAME);
-    $package['config'][] = 'system.site';
+    $package->appendConfig('system.site');
     $this->featuresManager->setPackage($package);
   }
 
diff --git a/tests/src/Unit/FeaturesManagerTest.php b/tests/src/Unit/FeaturesManagerTest.php
index 6e9fd09..446ad6c 100644
--- a/tests/src/Unit/FeaturesManagerTest.php
+++ b/tests/src/Unit/FeaturesManagerTest.php
@@ -7,13 +7,27 @@
 
 namespace Drupal\Tests\features\Unit;
 
+use Drupal\Component\Serialization\Yaml;
+use Drupal\config_update\ConfigDiffInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\ConfigManagerInterface;
+use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Extension\Extension;
+use Drupal\Core\Extension\InfoParser;
+use Drupal\Core\Extension\InfoParserInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\features\Entity\FeaturesBundle;
 use Drupal\features\FeaturesAssignerInterface;
 use Drupal\features\FeaturesBundleInterface;
 use Drupal\features\ConfigurationItem;
+use Drupal\features\FeaturesExtensionStoragesInterface;
 use Drupal\features\FeaturesManager;
 use Drupal\features\FeaturesManagerInterface;
+use Drupal\features\Package;
 use Drupal\Tests\UnitTestCase;
+use org\bovigo\vfs\vfsStream;
+use Prophecy\Argument;
 
 /**
  * @coversDefaultClass Drupal\features\FeaturesManager
@@ -32,6 +46,26 @@ class FeaturesManagerTest extends UnitTestCase {
   protected $entityManager;
 
   /**
+   * @var \Drupal\Core\Config\StorageInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $configStorage;
+
+  /**
+   * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $configFactory;
+
+  /**
+   * @var \Drupal\Core\Config\ConfigManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $configManager;
+
+  /**
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $moduleHandler;
+
+  /**
    * {@inheritdoc}
    */
   public function setUp() {
@@ -45,11 +79,11 @@ class FeaturesManagerTest extends UnitTestCase {
     $this->entityManager->expects($this->any())
       ->method('getDefinition')
       ->willReturn($entity_type);
-    $config_factory = $this->getMock('\Drupal\Core\Config\ConfigFactoryInterface');
-    $storage = $this->getMock('Drupal\Core\Config\StorageInterface');
-    $config_manager = $this->getMock('Drupal\Core\Config\ConfigManagerInterface');
-    $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->featuresManager = new FeaturesManager($this->entityManager, $config_factory, $storage, $config_manager, $module_handler);
+    $this->configFactory = $this->getMock(ConfigFactoryInterface::class);
+    $this->configStorage = $this->getMock(StorageInterface::class);
+    $this->configManager = $this->getMock(ConfigManagerInterface::class);
+    $this->moduleHandler = $this->getMock(ModuleHandlerInterface::class);
+    $this->featuresManager = new FeaturesManager($this->entityManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler);
 
     $string_translation = $this->getStringTranslationStub();
     $container = new ContainerBuilder();
@@ -161,28 +195,26 @@ class FeaturesManagerTest extends UnitTestCase {
     $this->featuresManager->setConfigCollection($this->getAssignInterPackageDependenciesConfigCollection());
 
     $packages = [
-      'package' => [
-        'machine_name' => 'package',
+      'package' => new Package('package', [
         'config' => ['example.config', 'example.config3'],
         'dependencies' => [],
         'bundle' => '',
-      ],
-      'package2' => [
-        'machine_name' => 'package2',
+      ]),
+      'package2' => new Package('package2', [
         'config' => ['example.config2'],
         'dependencies' => [],
         'bundle' => '',
-      ],
+      ]),
     ];
 
     $expected = $packages;
     // example.config3 has a providing_feature but no assigned package.
-    $expected['package']['dependencies'][] = 'my_other_feature';
+    $expected['package']->setDependencies(['my_other_feature']);
     // my_package2 provides configuration required by configuration in
     // my_package.
     // Because package assignments take precedence over providing_feature ones,
     // package2 should have been assigned rather than my_feature.
-    $expected['package']['dependencies'][] = 'package2';
+    $expected['package']->setDependencies(['my_other_feature', 'package2']);
     $this->featuresManager->setPackages($packages);
 
     $this->featuresManager->assignInterPackageDependencies($packages);
@@ -204,28 +236,26 @@ class FeaturesManagerTest extends UnitTestCase {
     $this->featuresManager->setConfigCollection($this->getAssignInterPackageDependenciesConfigCollection());
 
     $packages = [
-      'package' => [
-        'machine_name' => 'package',
+      'package' => new Package('package', [
         'config' => ['example.config', 'example.config3'],
         'dependencies' => [],
         'bundle' => 'giraffe',
-      ],
-      'package2' => [
-        'machine_name' => 'package2',
+      ]),
+      'package2' => new Package('package2', [
         'config' => ['example.config2'],
         'dependencies' => [],
         'bundle' => 'giraffe',
-      ],
+      ]),
     ];
 
     $expected = $packages;
     // example.config3 has a providing_feature but no assigned package.
-    $expected['package']['dependencies'][] = 'my_other_feature';
+    $expected['package']->setDependencies(['my_other_feature']);
     // my_package2 provides configuration required by configuration in
     // my_package.
     // Because package assignments take precedence over providing_feature ones,
     // package2 should have been assigned rather than my_feature.
-    $expected['package']['dependencies'][] = 'package2';
+    $expected['package']->setDependencies(['my_other_feature', 'package2']);
     $this->featuresManager->setPackages($packages);
 
     $this->featuresManager->assignInterPackageDependencies($packages);
@@ -263,4 +293,393 @@ class FeaturesManagerTest extends UnitTestCase {
     $this->assertEquals('', $config_collection[1]->getPackage());
   }
 
+
+  /**
+   * @covers ::detectMissing
+   */
+  public function testDetectMissing() {
+    $package = new Package('test-package', [
+      'configOrig' => ['test_config', 'test_config_non_existing'],
+    ]);
+
+    $config_collection = [];
+    $config_collection['test_config'] = new ConfigurationItem('test_config', []);
+    $this->featuresManager->setConfigCollection($config_collection);
+
+    $this->assertEquals(['test_config_non_existing'], $this->featuresManager->detectMissing($package));
+  }
+
+  /**
+   * @covers ::detectOverrides
+   */
+  public function testDetectOverrides() {
+    $config_diff = $this->prophesize(ConfigDiffInterface::class);
+    $config_diff->same(Argument::cetera())->will(function($args) {
+      return $args[0] == $args[1];
+    });
+    \Drupal::getContainer()->set('config_update.config_diff', $config_diff->reveal());
+
+    $package = new Package('test-package', [
+      'config' => ['test_config', 'test_overridden'],
+    ]);
+
+    $config_storage = $this->prophesize(StorageInterface::class);
+    $config_storage->read('test_config')->willReturn([
+      'key' => 'value',
+    ]);
+    $config_storage->read('test_overridden')->willReturn([
+      'key2' => 'value2',
+    ]);
+
+    $extension_storage = $this->prophesize(FeaturesExtensionStoragesInterface::class);
+    $extension_storage->read('test_config')->willReturn([
+      'key' => 'value',
+    ]);
+    $extension_storage->read('test_overridden')->willReturn([
+      'key2' => 'value0',
+    ]);
+
+
+    $features_manager = new TestFeaturesManager($this->entityManager, $this->configFactory, $config_storage->reveal(), $this->configManager, $this->moduleHandler);
+    $features_manager->setExtensionStorages($extension_storage->reveal());
+
+    $this->assertEquals(['test_overridden'], $features_manager->detectOverrides($package));
+  }
+
+  /**
+   * @covers ::assignConfigPackage
+   */
+  public function testAssignConfigPackageWithNonExtensionProvidedConfig() {
+    $config_collection = [
+      'test_config' => new ConfigurationItem('test_config', []),
+      'test_config2' => new ConfigurationItem('test_config2', []),
+    ];
+    $this->featuresManager->setConfigCollection($config_collection);
+
+    $package = new Package('test_package');
+    $this->featuresManager->setPackage($package);
+
+    $this->featuresManager->assignConfigPackage('test_package', ['test_config', 'test_config2']);
+
+    $this->assertEquals(['test_config', 'test_config2'], $this->featuresManager->getPackage('test_package')->getConfig());
+  }
+
+  /**
+   * @covers ::assignConfigPackage
+   */
+  public function testAssignConfigPackageWithExtensionProvidedConfig() {
+    $config_collection = [
+      'test_config' => new ConfigurationItem('test_config', []),
+      'test_config2' => new ConfigurationItem('test_config2', [], ['extensionProvided' => TRUE]),
+    ];
+    $this->featuresManager->setConfigCollection($config_collection);
+
+    $feature_assigner = $this->prophesize(FeaturesAssignerInterface::class);
+    $feature_assigner->getBundle(NULL)->willReturn(new FeaturesBundle(['machine_name' => 'default'], 'features_bundle'));
+    $this->featuresManager->setAssigner($feature_assigner->reveal());
+
+    $package = new Package('test_package');
+    $original_package = clone $package;
+
+    $this->featuresManager->setPackage($package);
+    $this->featuresManager->assignConfigPackage('test_package', ['test_config', 'test_config2']);
+    $this->assertEquals(['test_config'], $this->featuresManager->getPackage('test_package')->getConfig(), 'just assign new packages');
+
+    $this->featuresManager->setPackage($original_package);
+    $this->featuresManager->assignConfigPackage('test_package', ['test_config', 'test_config2'], TRUE);
+    $this->assertEquals(['test_config', 'test_config2'], $this->featuresManager->getPackage('test_package')->getConfig(), 'just assign new packages');
+  }
+
+  /**
+   * @covers ::initPackageFromExtension
+   * @covers ::getPackageObject
+   */
+  public function testInitPackageFromNonInstalledExtension() {
+    $extension = new Extension($this->root, 'module', 'modules/test_module/test_module.info.yml');
+
+    $info_parser = $this->prophesize(InfoParserInterface::class);
+    $info_parser->parse($this->root . '/modules/test_module/test_module.info.yml')->willReturn([
+      'name' => 'Test module',
+      'description' => 'test description',
+      'type' => 'module',
+    ]);
+    \Drupal::getContainer()->set('info_parser', $info_parser->reveal());
+
+    $bundle = $this->prophesize(FeaturesBundle::class);
+    $bundle->getShortName('test_module')->willReturn('test_module');
+    $bundle->isDefault()->willReturn(TRUE);
+
+    $assigner = $this->prophesize(FeaturesAssignerInterface::class);
+    $assigner->findBundle(Argument::any())->willReturn($bundle->reveal());
+    $this->featuresManager->setAssigner($assigner->reveal());
+
+    $result = $this->featuresManager->initPackageFromExtension($extension);
+    $this->assertEquals('test_module', $result->getMachineName());
+    $this->assertEquals('Test module', $result->getName());
+    $this->assertEquals('test description', $result->getDescription());
+    $this->assertEquals('module', $result->getType());
+
+    $this->assertEquals(FeaturesManagerInterface::STATUS_DISABLED, $result->getStatus());
+  }
+
+  /**
+   * @covers ::initPackageFromExtension
+   * @covers ::getPackageObject
+   */
+  public function testInitPackageFromInstalledExtension() {
+    $extension = new Extension($this->root, 'module', 'modules/test_module/test_module.info.yml');
+
+    $info_parser = $this->prophesize(InfoParserInterface::class);
+    $info_parser->parse($this->root . '/modules/test_module/test_module.info.yml')->willReturn([
+      'name' => 'Test module',
+      'description' => 'test description',
+      'type' => 'module',
+    ]);
+    \Drupal::getContainer()->set('info_parser', $info_parser->reveal());
+
+    $bundle = $this->prophesize(FeaturesBundle::class);
+    $bundle->getShortName('test_module')->willReturn('test_module');
+    $bundle->isDefault()->willReturn(TRUE);
+
+    $assigner = $this->prophesize(FeaturesAssignerInterface::class);
+    $assigner->findBundle(Argument::any())->willReturn($bundle->reveal());
+    $this->featuresManager->setAssigner($assigner->reveal());
+
+    $this->moduleHandler->expects($this->any())
+      ->method('moduleExists')
+      ->with('test_module')
+      ->willReturn(TRUE);
+
+    $result = $this->featuresManager->initPackageFromExtension($extension);
+    $this->assertEquals(FeaturesManagerInterface::STATUS_ENABLED, $result->getStatus());
+  }
+
+  public function testDetectNewWithNoConfig() {
+    $package = new Package('test_feature');
+
+    $this->assertEmpty($this->featuresManager->detectNew($package));
+  }
+
+  public function testDetectNewWithNoNewConfig() {
+    $package = new Package('test_feature', ['config' => ['test_config']]);
+
+    $extension_storage = $this->prophesize(FeaturesExtensionStoragesInterface::class);
+    $extension_storage->read('test_config')->willReturn([
+      'key' => 'value',
+    ]);
+
+    $features_manager = new TestFeaturesManager($this->entityManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler);
+    $features_manager->setExtensionStorages($extension_storage->reveal());
+
+    $this->assertEmpty($features_manager->detectNew($package));
+  }
+
+  public function testDetectNewWithNewConfig() {
+    $package = new Package('test_feature', ['config' => ['test_config']]);
+
+    $extension_storage = $this->prophesize(FeaturesExtensionStoragesInterface::class);
+    $extension_storage->read('test_config')->willReturn(FALSE);
+
+    $features_manager = new TestFeaturesManager($this->entityManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler);
+    $features_manager->setExtensionStorages($extension_storage->reveal());
+
+    $this->assertEquals(['test_config'], $features_manager->detectNew($package));
+  }
+
+  /**
+   * @todo This could have of course much more test coverage.
+   *
+   * @covers ::mergeInfoArray
+   *
+   * @dataProvider providerTestMergeInfoArray
+   */
+  public function testMergeInfoArray($expected, $info1, $info2, $keys = []) {
+    $this->assertSame($expected, $this->featuresManager->mergeInfoArray($info1, $info2, $keys));
+  }
+
+  public function providerTestMergeInfoArray() {
+    $data = [];
+    $data['empty-info'] = [[], [], []];
+    $data['override-features'] = [
+      ['features' => ['a', 'b']],
+      ['features' => ['1', '2']],
+      ['features' => ['a', 'b']]
+    ];
+    $data['override-features-and-info'] = [
+      ['name' => 'New name', 'core' => '8.x', 'features' => ['a', 'b']],
+      ['name' => 'Old name', 'core' => '8.x', 'features' => ['1', '2']],
+      ['name' => 'New name', 'features' => ['a', 'b']]
+    ];
+    $data['dependency-merging'] = [
+      ['dependencies' => ['a', 'b', 'c', 'd', 'e']],
+      ['dependencies' => ['b', 'd', 'c']],
+      ['dependencies' => ['a', 'b', 'e']],
+      [],
+    ];
+
+    return $data;
+  }
+
+  public function testInitPackageWithNewPackage() {
+    $bundle = new FeaturesBundle(['machine_name' => 'default'], 'features_bundle');
+
+    $features_manager = new TestFeaturesManager($this->entityManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler);
+    $features_manager->setAllModules([]);
+
+    $package = $features_manager->initPackage('test_feature', 'test name', 'test description', 'module', $bundle);
+
+    $this->assertInstanceOf(Package::class, $package);
+    $this->assertEquals('test_feature', $package->getMachineName());
+    $this->assertEquals('test name', $package->getName());
+    $this->assertEquals('test description', $package->getDescription());
+    $this->assertEquals('module', $package->getType());
+    $this->assertEquals('', $package->getBundle());
+    $this->assertEquals([], $package->getFeaturesInfo());
+  }
+
+  public function testInitPackageWithExistingPackage() {
+    $bundle = new FeaturesBundle(['machine_name' => 'default'], 'features_bundle');
+
+    $features_manager = new TestFeaturesManager($this->entityManager, $this->configFactory, $this->configStorage, $this->configManager, $this->moduleHandler);
+
+    vfsStream::setup('drupal');
+    \Drupal::getContainer()->set('app.root', 'vfs://drupal');
+    vfsStream::create([
+      'modules' => [
+        'test_feature' => [
+          'test_feature.info.yml' => <<<EOT
+name: Test feature 2
+type: module
+core: 8.x
+description: test description 2
+features:
+  config:
+    - test_config
+EOT
+          ,
+        ],
+      ],
+    ]);
+    $extension = new Extension('vfs://drupal', 'module', 'modules/test_feature/test_feature.info.yml');
+    $features_manager->setAllModules(['test_feature' => $extension]);
+
+    $this->moduleHandler->expects($this->any())
+      ->method('exists')
+      ->with('test_feature')
+      ->willReturn(TRUE);
+
+    $info_parser = new InfoParser();
+    \Drupal::getContainer()->set('info_parser', $info_parser);
+
+    $package = $features_manager->initPackage('test_feature', 'test name', 'test description', 'module', $bundle);
+
+    $this->assertInstanceOf(Package::class, $package);
+    $this->assertEquals(['config' => ['test_config']], $package->getFeaturesInfo());
+  }
+
+  /**
+   * @covers ::prepareFiles
+   */
+  public function testPrepareFiles() {
+    $packages = [];
+    $packages['test_feature'] = new Package('test_feature', [
+      'config' => ['test_config'],
+      'name' => 'Test feature',
+    ]);
+
+    $config_collection = [];
+    $config_collection['test_config'] = new ConfigurationItem('test_config', ['foo' => 'bar']);
+
+    $this->featuresManager->setConfigCollection($config_collection);
+    $this->featuresManager->prepareFiles($packages);
+
+    $files = $packages['test_feature']->getFiles();
+    $this->assertCount(2, $files);
+    $this->assertEquals('test_feature.info.yml', $files['info']['filename']);
+    $this->assertEquals(Yaml::encode([
+      'name' => 'Test feature',
+      'type' => 'module',
+      'core' => '8.x',
+      'config' => ['test_config'],
+      'features' => TRUE,
+    ]), $files['info']['string']);
+
+    $this->assertEquals('test_config.yml', $files['test_config']['filename']);
+    $this->assertEquals(Yaml::encode([
+      'foo' => 'bar'
+    ]), $files['test_config']['string']);
+  }
+
+  /**
+   * @covers ::getExportInfo
+   */
+  public function testGetExportInfoWithoutBundle() {
+    $config_factory = $this->getConfigFactoryStub([
+      'features.settings' => [
+        'export' => [
+          'folder' => 'custom',
+        ],
+      ],
+    ]);
+    $this->featuresManager = new FeaturesManager($this->entityManager, $config_factory, $this->configStorage, $this->configManager, $this->moduleHandler);
+
+    $package = new Package('test_feature');
+    $result = $this->featuresManager->getExportInfo($package);
+
+    $this->assertEquals(['test_feature', 'modules/custom'], $result);
+  }
+
+  /**
+   * @covers ::getExportInfo
+   */
+  public function testGetExportInfoWithBundle() {
+    $config_factory = $this->getConfigFactoryStub([
+      'features.settings' => [
+        'export' => [
+          'folder' => 'custom',
+        ],
+      ],
+    ]);
+    $this->featuresManager = new FeaturesManager($this->entityManager, $config_factory, $this->configStorage, $this->configManager, $this->moduleHandler);
+
+    $package = new Package('test_feature');
+    $bundle = new FeaturesBundle(['machine_name' => 'test_bundle'], 'features_bundle');
+
+    $result = $this->featuresManager->getExportInfo($package, $bundle);
+
+    $this->assertEquals(['test_bundle_test_feature', 'modules/custom'], $result);
+  }
+
+}
+
+class TestFeaturesManager extends FeaturesManager {
+
+  protected $allModules;
+
+  /**
+   * @param \Drupal\features\FeaturesExtensionStoragesInterface $extensionStorages
+   */
+  public function setExtensionStorages($extensionStorages) {
+    $this->extensionStorages = $extensionStorages;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllModules() {
+    if (isset($this->allModules)) {
+      return $this->allModules;
+    }
+    return parent::getAllModules();
+  }
+
+  /**
+   * @param mixed $all_modules
+   */
+  public function setAllModules($all_modules) {
+    $this->allModules = $all_modules;
+    return $this;
+  }
+
 }
+
