diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/config/install/session_cache.settings.yml b/config/install/session_cache.settings.yml
new file mode 100644
index 0000000..6c316ac
--- /dev/null
+++ b/config/install/session_cache.settings.yml
@@ -0,0 +1,3 @@
+storage_method: 1
+expire_period: 7
+sid_source: 0
diff --git a/lib/Drupal/session_cache/Form/SessionCacheSettingsForm.php b/lib/Drupal/session_cache/Form/SessionCacheSettingsForm.php
deleted file mode 100644
index b52d3de..0000000
--- a/lib/Drupal/session_cache/Form/SessionCacheSettingsForm.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\session_cache\Form\SessionCacheSettingsForm.
- */
-
-namespace Drupal\session_cache\Form;
-
-use Drupal\system\SystemConfigFormBase;
-
-/**
- * Menu callback and form-builder for session cache configuration settings.
- */
-class SessionCacheSettingsForm extends SystemConfigFormBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormID() {
-    return 'session_cache_settings';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, array &$form_state) {
-    // Not sure which of these first two lines is better
-    //$this->configFactory->get('session_cache.settings')
-    $config = config('session_cache.settings');
-
-    $form['storage_method'] = array(
-      '#type' => 'radios',
-      '#title' => t('Where should user session data be stored?'),
-      '#default_value' => $config->get('storage_method') ?: SESSION_CACHE_STORAGE_SESSION,
-      '#options' => array(
-        SESSION_CACHE_STORAGE_COOKIE  => t("on the user's computer, in a cookie"),
-        SESSION_CACHE_STORAGE_DB_CORE => t("on the server, on core's cache database"),
-        SESSION_CACHE_STORAGE_SESSION => t('on the server, in $_SESSION memory')
-      ),
-      '#description' => t('The first two mechanisms will NOT write to or read from $_SESSION so are generally a good choice when your site uses Varnish or similar page caching engine.')
-    );
-
-    $expire_period = (float) $config->get('expire_period');
-    if ($expire_period <= 0.0) {
-      $expire_period = SESSION_CACHE_DEFAULT_EXPIRATION_DAYS;
-    }
-    $form['expire_period'] = array(
-      '#type' => 'textfield',
-      '#size' => 4,
-      '#title' => t('Expiration time for the database cache and cookies created via this module'),
-      '#field_suffix' => t('days'),
-      '#default_value' => $expire_period,
-      '#description' => t('You may use decimals, eg 0.25 equates to 6 hours.<br/>$_SESSION expiration is set via the server configuration. See the <em>sites/default/settings.php</em> file for details.')
-    );
-
-    $form['use_uid_as_sid'] = array(
-      '#type' => 'checkbox',
-      '#title' => t("Remember the user's session from one browser to the next"),
-      '#default_value' => $config->get('use_uid_as_sid') ?: FALSE,
-      '#description' => t('Applies to authenticated users only and does not work for the cookie and $_SESSION storage mechanisms.')
-    );
-
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, array &$form_state) {
-    // Not sure which of these first two lines is better
-    //$this->configFactory->get('session_cache.settings')
-    config('session_cache.settings')
-      ->set('storage_method', $form_state['values']['storage_method'])
-      ->set('expire_period',  $form_state['values']['expire_period'])
-      ->set('use_uid_as_sid', $form_state['values']['use_uid_as_sid'])
-      ->save();
-
-    parent::submitForm($form, $form_state);
-  }
-}
diff --git a/session_cache.admin.inc b/session_cache.admin.inc
new file mode 100644
index 0000000..f4a1342
--- /dev/null
+++ b/session_cache.admin.inc
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * @file
+ * session_cache.admin.inc
+ */
+
+/**
+ * Menu callback for admin settings.
+ *
+ * Contrib modules can add more options by implementing hook_form_alter().
+ */
+function session_cache_admin_config() {
+  $form['session_cache_storage_method'] = array(
+    '#type' => 'radios',
+    '#title' => t('Where should user session data be stored?'),
+    '#default_value' => \Drupal::config('session_cache.settings')->get('session_cache_storage_method'),
+    '#options' => array(
+      SESSION_CACHE_STORAGE_COOKIE  => t("on the user's computer, in a cookie"),
+      SESSION_CACHE_STORAGE_DB_CORE => t("on the server, on core's cache database"),
+      SESSION_CACHE_STORAGE_SESSION => t('on the server, in $_SESSION memory'),
+    ),
+    '#description' => t('When using Varnish or similar page caching engine for anonymous users, do not use $_SESSION and have a page exclusion strategy in place.'),
+  );
+
+  $options = array(
+    SESSION_CACHE_COOKIE_FOR_SID => t('via their cookie'),
+    SESSION_CACHE_UID_FOR_SID => t('via their login id, via cookie for anonymous users (works cross-browser for authenticated users)'),
+    SESSION_CACHE_IP_ADDRESS_FOR_SID => t('via their IP address')
+  );
+  $form['session_cache_sid_source'] = array(
+      '#type' => 'radios',
+      '#title' => t('Method for identifying the user in database and file-based storage methods'),
+      '#options' => $options,
+      '#default_value' => \Drupal::config('session_cache.settings')->get('session_cache_sid_source'),
+      '#description' => t('None of the above apply to the $_SESSION and cookie storage mechanisms.'),
+  );
+
+  $expire_period = (float) \Drupal::config('session_cache.settings')->get('session_cache_expire_period');
+  if ($expire_period <= 0.0) {
+    $expire_period = SESSION_CACHE_DEFAULT_EXPIRATION_DAYS;
+  }
+  $form['session_cache_expire_period'] = array(
+    '#type' => 'textfield',
+    '#size' => 4,
+    '#title' => t('Expiration time for the database cache and cookies created via this module'),
+    '#field_suffix' => t('days'),
+    '#default_value' => $expire_period,
+    '#description' => t('You may use decimals, eg 0.25 equates to 6 hours.<br/>$_SESSION expiration is set via the server configuration. See the <em>sites/default/settings.php</em> file for details.'),
+  );
+
+  return system_settings_form($form);
+}
diff --git a/session_cache.api.php b/session_cache.api.php
new file mode 100644
index 0000000..7f6bc5c
--- /dev/null
+++ b/session_cache.api.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @file
+ * API documentation for Session Cache API module.
+ *
+ * Cookies and database session caches created through this API expire after a
+ * UI-configurable number of hours or days. $_SESSIONs expire according to the
+ * global configuration -- see the comments in
+ * sites/default/files/default.settings.php.
+ */
+
+/**
+ * Write data to the user session, whatever the storage mechanism may be.
+ *
+ * @param string $bin
+ *   Unique id, eg a string prefixed by the module name.
+ * @param mixed $data
+ *   A number or string, an object, a multi-dimensional array etc.
+ *   $bin is the identifier you choose for the data you want to store. To
+ *   guarante uniqueness, you could prefix it with the name of the module that
+ *   you use this API with.
+ *   Use NULL to erase the bin; it may be auto-recreated and refilled at any
+ *   time by calling the function again with a non-NULL data argument.
+ */
+function session_cache_set($bin, $data) {
+  // See the following modules for examples:
+  // o IP Geolocation Views & Maps.
+  // o Views Global Filter.
+}
+
+/**
+ * Read data from the user session, given its bin id.
+ *
+ * @param string $bin
+ *   unique id eg a string prefixed by the module name
+ *
+ * @return mixed
+ *   the cache data
+ */
+function session_cache_get($bin) {
+  // See the following modules for examples:
+  // o IP Geolocation Views & Maps.
+  // o Views Global Filter.
+}
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Implement this set-hook to complete your own storage mechanism.
+ *
+ * @param int $storage_method
+ *   the storage method
+ * @param string $bin
+ *   the name of the bin under which to store $data
+ * @param mixed $data
+ *   the data to store
+ */
+function hook_session_cache_set($storage_method, $bin, $data) {
+  if ($storage_method != MYMODULE_SESSION_STORAGE) {
+    return;
+  }
+  // Store $data in $bin, using your module's mechanism.
+  // For an example see the submodule: session_cache_file.module
+}
+
+/**
+ * Implement this get-hook to complete your own storage mechanism.
+ *
+ * @param int $storage_method
+ *   the storage methd
+ * @param string $bin
+ *   the name of the bin to retrieve cached data from
+ *
+ * @return mixed
+ *   the cached data
+ */
+function hook_session_cache_get($storage_method, $bin) {
+  if ($storage_method != MYMODULE_SESSION_STORAGE) {
+    return NULL;
+  }
+  // ... your retrieval mechanism goes here.
+  $data = "your data based on $bin";
+  return $data;
+}
+
+/**
+ * Implement this hook to make your storage mechanism selectable in the UI.
+ */
+function hook_form_session_cache_admin_config_alter(&$form, &$form_state) {
+  $form['session_cache_storage_method']['#options'][MYMODULE_SESSION_STORAGE]
+    = t('my great storage method');
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/session_cache.info.yml b/session_cache.info.yml
index 0a0cafc..d1c1d05 100644
--- a/session_cache.info.yml
+++ b/session_cache.info.yml
@@ -1,6 +1,6 @@
 name: Session Cache API
-type: module
 description: A super simple API that avoids $_SESSION to offer caching engine friendly ways to save and recall user state.
 version: VERSION
 core: 8.x
 configure: admin/config/development/session-cache
+type: module
diff --git a/session_cache.install b/session_cache.install
deleted file mode 100644
index 38d225b..0000000
--- a/session_cache.install
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/**
- * @file
- * session_cache.install
- */
-
-/**
- * Implements hook_schema().
- *
- * This creates the 'cache_session_cache' table in drupal_install_schema()
- * This table can then be used in cache('session_cache')->set(...) and
- * cache('session_cache')->get(...) calls.
- * The table is dropped via drupal_uninstall_schema() when the module is
- * uninstalled.
- */
-function session_cache_schema() {
-  // Use the generic cache table for caching things not separated out into
-  // their own tables. Contrib modules may also use this to store cached items.
-  $schema['cache_session_cache'] = system_schema_cache_8007();
-  $schema['cache_session_cache']['description'] = 'Cache table for Session Cache API';
-  return $schema;
-}
diff --git a/session_cache.links.menu.yml b/session_cache.links.menu.yml
new file mode 100644
index 0000000..b88203e
--- /dev/null
+++ b/session_cache.links.menu.yml
@@ -0,0 +1,5 @@
+session_cache.admin_config:
+  route_name: session_cache.admin_config
+  title: 'Session Cache API'
+  description: 'Select the session storage mechanism.'
+  parent: system.admin_config_development
diff --git a/session_cache.module b/session_cache.module
index 3b7e453..38ca716 100644
--- a/session_cache.module
+++ b/session_cache.module
@@ -16,6 +16,10 @@ const SESSION_CACHE_STORAGE_COOKIE  = 3;
 const SESSION_CACHE_STORAGE_DB_CORE = 2;
 const SESSION_CACHE_STORAGE_SESSION = 1;
 
+const SESSION_CACHE_COOKIE_FOR_SID     = 0;
+const SESSION_CACHE_UID_FOR_SID        = 1;
+const SESSION_CACHE_IP_ADDRESS_FOR_SID = 2;
+
 const SESSION_CACHE_DEFAULT_EXPIRATION_DAYS = 7.0;
 
 /**
@@ -29,7 +33,7 @@ function session_cache_set($bin, $data) {
   if (!isset($bin)) {
     return;
   }
-  $method = config('session_cache.settings')->get('storage_method') ?: SESSION_CACHE_STORAGE_SESSION;
+  $method = \Drupal::config('session_cache.settings')->get('storage_method');
 
   switch ($method) {
 
@@ -52,33 +56,35 @@ function session_cache_set($bin, $data) {
     case SESSION_CACHE_STORAGE_DB_CORE:
       $sid = session_cache_get_sid();
       if ($data == NULL) {
-        cache('session_cache')->delete("$bin:$sid");
+        \Drupal::cache('cache_session_cache')->invalidate("$bin:$sid");
       }
       else {
-        cache('session_cache')->set("$bin:$sid", $data, session_cache_expiration_time());
+        \Drupal::cache('cache_session_cache')->set("$bin:$sid", $data, session_cache_expiration_time());
       }
       return;
 
     case SESSION_CACHE_STORAGE_SESSION:
-      $_SESSION[$bin] = $data; // $data==NULL means unset()
+      // $data == NULL means unset().
+      $_SESSION[$bin] = $data;
       return;
 
     default:
-      module_invoke_all('session_cache_set', $method, $bin, $data);
+      \Drupal::moduleHandler()->invokeAll('session_cache_set', [$method, $bin, $data]);
   }
 }
 
 /**
  * Read data from the user session, given its bin id.
  *
- * @param string $bin, unique id eg a string prefixed by the module name
+ * @param string $bin
+ *   unique id eg a string prefixed by the module name
  */
 function session_cache_get($bin) {
 
   if (!isset($bin)) {
     return NULL;
   }
-  $method = config('session_cache.settings')->get('storage_method') ?: SESSION_CACHE_STORAGE_SESSION;
+  $method = \Drupal::config('session_cache.settings')->get('storage_method');
 
   switch ($method) {
 
@@ -88,14 +94,14 @@ function session_cache_get($bin) {
 
     case SESSION_CACHE_STORAGE_DB_CORE:
       $sid = session_cache_get_sid();
-      $cached = cache('session_cache')->get("$bin:$sid");
-      return is_object($cached) ? $cached->data : NULL;
+      $cache = \Drupal::cache('cache_session_cache')->get("$bin:$sid");
+      return is_object($cache) ? $cache->data : NULL;
 
     case SESSION_CACHE_STORAGE_SESSION:
       return isset($_SESSION) && isset($_SESSION[$bin]) ? $_SESSION[$bin] : NULL;
 
     default:
-      return module_invoke_all('session_cache_get', $method, $bin);
+      return \Drupal::moduleHandler()->invokeAll('session_cache_get', [$method, $bin]);
   }
 }
 
@@ -103,7 +109,7 @@ function session_cache_get($bin) {
  * Implements hook_cron().
  */
 function session_cache_cron() {
-  if (config('session_cache.settings')->get('storage_method') == SESSION_CACHE_STORAGE_DB_CORE) {
+  if (\Drupal::config('session_cache.settings')->get('storage_method') == SESSION_CACHE_STORAGE_DB_CORE) {
     // Don't believe we need to do anything. When a cache item has expired,
     // after the time provided in the cache()->set() call, the item is up for
     // grabs by the garbage collector at any time.
@@ -116,18 +122,18 @@ function session_cache_cron() {
  *
  * Typically only called when a database storage mechanism is used.
  *
- * @return sid
+ * @return string $sid
  */
 function session_cache_get_sid() {
   global $user;
 
-  if (!empty($user->uid) && config('session_cache.settings')->get('use_uid_as_sid')) {
+  if (!empty($user->uid) && \Drupal::config('session_cache.settings')->get('use_uid_as_sid')) {
     return 'user' . $user->uid; // good if concurrent sessions for the same authenticated user are NOT required
   }
   if (empty($_COOKIE['Drupal_session_cache_sid'])) {
 
     // Don't use core's session id. Security not a problem, so keep it short.
-    $sid = drupal_substr(session_id(), 0, 12);
+    $sid = \Drupal\Component\Utility\Unicode::substr(session_id(), 0, 12);
     // If setcookie() fails, then everything still works, but a new session will
     // be created when logging in or out.
     // Don't use "global $cookie_domain", as it may contain '.localhost', which
@@ -141,19 +147,13 @@ function session_cache_get_sid() {
   return $sid;
 }
 
-function session_cache_expiration_time() {
-  $expire_period_days = config('session_cache.settings')->get('expire_period') ?: SESSION_CACHE_DEFAULT_EXPIRATION_DAYS;
-  return REQUEST_TIME + 24*60*60 * $expire_period_days;
-}
-
 /**
- * Implements hook_menu().
+ * Returns the date/time that the session cache will expire.
+ *
+ * @return int
+ *   UNIX time stamp
  */
-function session_cache_menu() {
-  $items['admin/config/development/session-cache'] = array(
-    'title' => 'Session Cache API',
-    'description' => 'Select the session cache storage mechanism.',
-    'route_name' => 'session_cache_settings',
-  );
-  return $items;
+function session_cache_expiration_time() {
+  $expire_period_days = \Drupal::config('session_cache.settings')->get('expire_period') ?: SESSION_CACHE_DEFAULT_EXPIRATION_DAYS;
+  return REQUEST_TIME + 24*60*60 * $expire_period_days;
 }
diff --git a/session_cache.routing.yml b/session_cache.routing.yml
index 86c9e07..d726f1f 100644
--- a/session_cache.routing.yml
+++ b/session_cache.routing.yml
@@ -1,6 +1,7 @@
-session_cache_settings:
-  pattern: '/admin/config/development/session-cache'
+session_cache.admin_config:
+  path: /admin/config/development/session_cache
   defaults:
-    _form: '\Drupal\session_cache\Form\SessionCacheSettingsForm'
+    _title: 'Session Cache API'
+    _form: \Drupal\session_cache\Form\SessionCacheAdminConfig
   requirements:
-    _permission: 'administer site configuration'
\ No newline at end of file
+    _permission: 'administer site configuration'
diff --git a/session_cache_file/session_cache_file.info.yml b/session_cache_file/session_cache_file.info.yml
index e64e9f7..a962dd5 100644
--- a/session_cache_file/session_cache_file.info.yml
+++ b/session_cache_file/session_cache_file.info.yml
@@ -1,9 +1,7 @@
-name: Session Cache File
-type: module
-description: Extends Session Cache API with a file storage implementation.
-version: VERSION
-core: 8.x
+name: 'Session Cache File'
+description: 'Extends the Session Cache API with a file storage implementation.'
 dependencies:
   - session_cache
-configure: admin/config/development/session-cache
-
+core: 8.x
+project: session_cache
+type: module
diff --git a/session_cache_file/session_cache_file.install b/session_cache_file/session_cache_file.install
index adfdd22..44e9ff6 100644
--- a/session_cache_file/session_cache_file.install
+++ b/session_cache_file/session_cache_file.install
@@ -6,6 +6,27 @@
  */
 
 /**
+ * Implements hook_requirements().
+ */
+function session_cache_file_requirements($phase) {
+  if ($phase != 'runtime') {
+    return;
+  }
+  $t = 't';
+  $requirements['session_cache_file']['title'] = $t('Session Cache File cache');
+  $session_cache_root = session_cache_file_directory();
+  if ($session_cache_root) {
+    $requirements['session_cache_file']['value'] = $t('Installed at %path', array('%path' => $session_cache_root));
+    $requirements['session_cache_file']['severity'] = REQUIREMENT_OK;
+  }
+  else {
+    $requirements['session_cache_file']['value'] = $t('Not set or could not be created. Check directory permissions or re-configure <a href="@url">here</a>.', array('@url' => \Drupal\Core\Url::fromRoute('system.file_system_settings')));
+    $requirements['session_cache_file']['severity'] = REQUIREMENT_ERROR;
+  }
+  return $requirements;
+}
+
+/**
  * Implements hook_uninstall().
  */
 function session_cache_file_uninstall() {
@@ -19,26 +40,26 @@ function session_cache_file_uninstall() {
 
 /**
  * Recursively delete a directory and all files in it.
- * 
- * PHP's rmdir() only deletes if the directory is empty so we empty directories
- * recursively before calling rmdir().
+ *
+ * PHP's rmdir() only deletes if the directory is empty.
  *
  * @param string $dir
+ *   The directory to be removed with all its contents.
  */
 function session_cache_file_rrmdir($dir) {
- if (is_dir($dir)) {
-   $files = scandir($dir);
-   foreach ($files as $file) {
-     if ($file != '.' && $file != '..') {
-       if (is_dir("$dir/$file")) {
-         session_cache_file_rrmdir("$dir/$file");
-       }
-       else {
-         unlink("$dir/$file");
-       }
-     }
-   }
-   return rmdir($dir);
- }
- return FALSE;
+  if (is_dir($dir)) {
+    $files = scandir($dir);
+    foreach ($files as $file) {
+      if ($file != '.' && $file != '..') {
+        if (is_dir("$dir/$file")) {
+          session_cache_file_rrmdir("$dir/$file");
+        }
+        else {
+          unlink("$dir/$file");
+        }
+      }
+    }
+    return rmdir($dir);
+  }
+  return FALSE;
 }
diff --git a/session_cache_file/session_cache_file.module b/session_cache_file/session_cache_file.module
index 05f9f45..af14aa6 100644
--- a/session_cache_file/session_cache_file.module
+++ b/session_cache_file/session_cache_file.module
@@ -11,24 +11,39 @@
  * See the session_cache/README.txt for more info.
  */
 
-const SESSION_CACHE_STORAGE_FILE = 5;
+define('SESSION_CACHE_FILE_STORAGE_FILE', 5);
 
-/*
- * Implements hook_form_FORMID_alter().
+/**
+ * Implements hook_form_FORM_ID_alter().
  */
-function session_cache_file_form_session_cache_settings_alter(&$form, &$form_state) {
-  $form['storage_method']['#options'][SESSION_CACHE_STORAGE_FILE] =
-    t('on the server, as a small file');
+function session_cache_file_form_session_cache_admin_config_alter(&$form, &$form_state) {
+  $form['session_cache_storage_method']['#options'][SESSION_CACHE_FILE_STORAGE_FILE]
+    = t('on the server, as a small file');
 }
 
+/**
+ * Returns the directory path to where session cache files are stored.
+ *
+ * @param string $bin
+ *   The bin for which the path is to be returned, NULL for the base path.
+ *
+ * @return boolean|string
+ *   The existing or newly created path, or FALSE if there was an error.
+ */
 function session_cache_file_directory($bin = NULL) {
-  $path = config('system.file')->get('path.private');  // typically: sites/default/files/private
+  $path = \Drupal\Core\StreamWrapper\PrivateStream::basePath();
+
+  // Typically $path == 'sites/default/files/private'.
   if (empty($path)) {
     drupal_set_message(t('Session Cache File: the <strong>Private file system path</strong> is not set. Please configure it <a href="@url">here</a>.',
-      array('@url' => url('admin/config/media/file-system'))), 'warning', FALSE);
+      array('@url' => \Drupal\Core\Url::fromRoute('system.file_system_settings'))), 'warning', FALSE);
     return FALSE;
   }
-  $path = DRUPAL_ROOT . "/$path/session_cache";
+  // Use the absolute path as is, if it starts with a slash.
+  $path = preg_match('/^\//', $path) ? $path : \Drupal::root() . "/$path";
+  $path .= '/session_cache';
+
+  // @todo Use file_prepare_directory(&$path, FILE_CREATE_DIRECTORY)
   if (!file_exists($path) && !@mkdir($path)) {
     drupal_set_message(t('Session cache directory %path could not be created.',
       array('%path' => $path)), 'error', FALSE);
@@ -49,7 +64,7 @@ function session_cache_file_directory($bin = NULL) {
  * Implements hook_session_cache_set().
  */
 function session_cache_file_session_cache_set($method, $bin, $data) {
-  if ($method != SESSION_CACHE_STORAGE_FILE) {
+  if ($method != SESSION_CACHE_FILE_STORAGE_FILE) {
     return;
   }
   $path = session_cache_file_directory($bin);
@@ -68,14 +83,12 @@ function session_cache_file_session_cache_set($method, $bin, $data) {
  * Implements hook_session_cache_get().
  */
 function session_cache_file_session_cache_get($method, $bin) {
-  if ($method != SESSION_CACHE_STORAGE_FILE) {
+  if ($method != SESSION_CACHE_FILE_STORAGE_FILE) {
     return NULL;
   }
   $path = session_cache_file_directory($bin);
   $sid = session_cache_get_sid();
-  $data = $path && $sid && file_exists("$path/$sid")
-    ? unserialize(file_get_contents("$path/$sid"))
-    : NULL;
+  $data = $path && $sid && file_exists("$path/$sid") ? unserialize(file_get_contents("$path/$sid")) : NULL;
   return $data;
 }
 
@@ -95,7 +108,7 @@ function session_cache_file_cron() {
             if (strpos($filename, '.') !== 0) {
               $filespec = "$session_cache_root/$bin_dir/$filename";
               $last_modified = filemtime($filespec);
-              if ($last_modified && (time() - $last_modified > SESSION_CACHE_DEFAULT_EXPIRATION_DAYS *24*60*60)) {
+              if ($last_modified && (time() - $last_modified > SESSION_CACHE_DEFAULT_EXPIRATION_DAYS * 24 * 60 * 60)) {
                 if (!@unlink($filespec)) {
                   drupal_set_message(t('Could not delete expired session cache file %file.', array('%file' => $filespec)), 'warning');
                 }
@@ -107,23 +120,3 @@ function session_cache_file_cron() {
     }
   }
 }
-
-/**
- * Implements hook_requirements().
- */
-function session_cache_file_requirements($phase) {
-  if ($phase != 'runtime') {
-    return;
-  }
-  $requirements['session_cache_file']['title'] = t('Session Cache File cache');
-  $session_cache_root = session_cache_file_directory();
-  if ($session_cache_root) {
-    $requirements['session_cache_file']['value'] = t('Installed at %path', array('%path' => $session_cache_root));
-    $requirements['session_cache_file']['severity'] = REQUIREMENT_OK;
-  }
-  else {
-    $requirements['session_cache_file']['value'] = t('Not set or could not be created. Check directory permissions or re-configure <a href="@url">here</a>.', array('@url' => url('admin/config/media/file-system')));
-    $requirements['session_cache_file']['severity'] = REQUIREMENT_ERROR;
-  }
-  return $requirements;
-}
diff --git a/src/Form/SessionCacheAdminConfig.php b/src/Form/SessionCacheAdminConfig.php
new file mode 100644
index 0000000..e07f3de
--- /dev/null
+++ b/src/Form/SessionCacheAdminConfig.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\session_cache\Form\SessionCacheAdminConfig.
+ */
+
+namespace Drupal\session_cache\Form;
+
+use Drupal\Core\Form\ConfigFormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
+
+class SessionCacheAdminConfig extends ConfigFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'session_cache_admin_config';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $config = $this->config('session_cache.settings');
+
+    foreach (Element::children($form) as $variable) {
+      $config->set($variable, $form_state->getValue($form[$variable]['#parents']));
+    }
+    $config->save();
+
+    if (method_exists($this, '_submitForm')) {
+      $this->_submitForm($form, $form_state);
+    }
+
+    parent::submitForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditableConfigNames() {
+    return ['session_cache.settings'];
+  }
+
+  public function buildForm(array $form, \Drupal\Core\Form\FormStateInterface $form_state) {
+    $form['storage_method'] = [
+      '#type' => 'radios',
+      '#title' => t('Where should user session data be stored?'),
+      '#default_value' => \Drupal::config('session_cache.settings')->get('storage_method'),
+      '#options' => [
+        SESSION_CACHE_STORAGE_COOKIE => t("on the user's computer, in a cookie"),
+        SESSION_CACHE_STORAGE_DB_CORE => t("on the server, on core's cache database"),
+        SESSION_CACHE_STORAGE_SESSION => t('on the server, in $_SESSION memory'),
+      ],
+      '#description' => t('When using Varnish or similar page caching engine for anonymous users, do not use $_SESSION and have a page exclusion strategy in place.'),
+    ];
+
+    $options = [
+      SESSION_CACHE_COOKIE_FOR_SID => t('via their cookie'),
+      SESSION_CACHE_UID_FOR_SID => t('via their login id, via cookie for anonymous users (works cross-browser for authenticated users)'),
+      SESSION_CACHE_IP_ADDRESS_FOR_SID => t('via their IP address'),
+    ];
+    $form['sid_source'] = [
+      '#type' => 'radios',
+      '#title' => t('Method for identifying the user in database and file-based storage methods'),
+      '#options' => $options,
+      '#default_value' => \Drupal::config('session_cache.settings')->get('sid_source'),
+      '#description' => t('None of the above apply to the $_SESSION and cookie storage mechanisms.'),
+    ];
+
+    $expire_period = (float) \Drupal::config('session_cache.settings')->get('expire_period');
+    if ($expire_period <= 0.0) {
+      $expire_period = SESSION_CACHE_DEFAULT_EXPIRATION_DAYS;
+    }
+    $form['expire_period'] = [
+      '#type' => 'textfield',
+      '#size' => 4,
+      '#title' => t('Expiration time for the database cache and cookies created via this module'),
+      '#field_suffix' => t('days'),
+      '#default_value' => $expire_period,
+      '#description' => t('You may use decimals, eg 0.25 equates to 6 hours.<br/>$_SESSION expiration is set via the server configuration. See the <em>sites/default/settings.php</em> file for details.'),
+    ];
+
+    return parent::buildForm($form, $form_state);
+  }
+
+}
