]> Kevux Git Server - koopa/commitdiff
Update: initial import, including incomplete work
authorKevin Day <thekevinday@gmail.com>
Sat, 17 Dec 2016 20:24:08 +0000 (14:24 -0600)
committerKevin Day <thekevinday@gmail.com>
Sat, 17 Dec 2016 20:28:21 +0000 (14:28 -0600)
I wanted to complete all of the common/base work first, but I ended up getting side-tracked.
With the holidays, I realize that I would not finish this in time and I needed to at the very least ensure all of my hard work is not lost.

Much of the base work is complete except for the following:
- HTML and HTML tag related processing.
- Form processing.

I have not decided how I want to design forms and I expect this to be the weakest part of my code design.
How this will end up being developed will directly affect how the HTML processing code is built.

32 files changed:
README.md
common/base/classes/base_access.php [new file with mode: 0644]
common/base/classes/base_ascii.php [new file with mode: 0644]
common/base/classes/base_charset.php [new file with mode: 0644]
common/base/classes/base_cookie.php [new file with mode: 0644]
common/base/classes/base_database.php [new file with mode: 0644]
common/base/classes/base_debug.php [new file with mode: 0644]
common/base/classes/base_email.php [new file with mode: 0644]
common/base/classes/base_error.php [new file with mode: 0644]
common/base/classes/base_form.php [new file with mode: 0644]
common/base/classes/base_html.php [new file with mode: 0644]
common/base/classes/base_http.php [new file with mode: 0644]
common/base/classes/base_http_status.php [new file with mode: 0644]
common/base/classes/base_languages.php [new file with mode: 0644]
common/base/classes/base_ldap.php [new file with mode: 0644]
common/base/classes/base_log.php [new file with mode: 0644]
common/base/classes/base_markup.php [new file with mode: 0644]
common/base/classes/base_mime.php [new file with mode: 0644]
common/base/classes/base_return.php [new file with mode: 0644]
common/base/classes/base_rfc_char.php [new file with mode: 0644]
common/base/classes/base_rfc_string.php [new file with mode: 0644]
common/base/classes/base_session.php [new file with mode: 0644]
common/base/classes/base_utf8.php [new file with mode: 0644]
common/theme/classes/theme_ajax.php [new file with mode: 0644]
common/theme/classes/theme_dom.php [new file with mode: 0644]
common/theme/classes/theme_form.php [new file with mode: 0644]
common/theme/classes/theme_html.php [new file with mode: 0644]
common/theme/classes/theme_ical.php [new file with mode: 0644]
common/theme/classes/theme_markup.php [new file with mode: 0644]
common/theme/classes/theme_rss.php [new file with mode: 0644]
documentation/naming.txt [new file with mode: 0644]
documentation/requirements.txt [new file with mode: 0644]

index 7423663e393d7c0e0adf02d19b94a2cc805e9eb2..e5ab2368003bed6f2f2d0d01e080469a540dd707 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,2 +1,13 @@
 # koopa
 Open-Source (GPL-3) PHP CMS-like project/library focused on database first originally based on drupal.
+
+Instead of building a CMS around PHP, this project aims to build a library around database and then extend it into PHP (and possibly more languages).
+
+Goals of this project:
+1) Provide a professional grade open-source project that encourages deviating from core.
+2) Build from the database up instead of PHP down.
+3) Follow all standards claimed to follow and explicitly communicate all deviations or violations of followed standards.
+4) Design in such a way to encourage hacking and personalizing.
+5) Provide a consistent coding style and standard.
+6) Provide a project not for beginners but instead for intermediate, advanced, and professional level users.
+7) Favor read operations at the expense of write operations (an optimization for websites).
diff --git a/common/base/classes/base_access.php b/common/base/classes/base_access.php
new file mode 100644 (file)
index 0000000..ee7bfea
--- /dev/null
@@ -0,0 +1,238 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing system access.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+require_once('common/base/classes/base_session.php');
+require_once('common/base/classes/base_ldap.php');
+
+/**
+ * A class for managing roles.
+ *
+ * Roles defined here are general top-level roles used for separating database activity.
+ * The intentions here is to keep the roles as simple and as few as possible while allowing considerable flexibility.
+ * This should cut down on the complexity of the database access control.
+ *
+ * Additional granularity may be supplied via PHP access checks or by extending this class.
+ *
+ * Roles:
+ * - None: no access to anything.
+ * - Public: access to only public information (users who are not logged in have this, such as anonymous).
+ * - System: account is a machine and should not be a human (such as with cron jobs).
+ * - User: account is a user and that user is logged in.
+ * - Requester: account is for requesting something, generally via some sort of form.
+ * - Drafter: account is for making templates, drafts, ideas, etc.. (this is a lesser form of "editer").
+ * - Editer: account is for editors who add/manage/create content.
+ * - Reviewer: account is for users who review something (such as a user who approves content for publishing).
+ * - Publisher: account is for users who perform publishing (marking content available and complete).
+ * - Manager: account is for users who manager the entire system. This is a non-technical administration account.
+ * - Administer: account is for users who have full administrative access to the system. This is a technical administration account and supercedes Manager.
+ */
+class c_base_roles {
+  const NONE       = 0;
+  const PUBLIC     = 1;
+  const SYSTEM     = 2;
+  const USER       = 3;
+  const REQUESTER  = 4;
+  const DRAFTER    = 5;
+  const EDITER     = 6;
+  const REVIEWER   = 7;
+  const PUBLISHER  = 8;
+  const MANAGER    = 9;
+  const ADMINISTER = 10;
+
+  private $public;
+  private $system;
+  private $user;
+  private $requester;
+  private $drafter;
+  private $editer;
+  private $reviewer;
+  private $publisher;
+  private $manager;
+  private $administer;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->public     = TRUE;
+    $this->system     = FALSE;
+    $this->user       = FALSE;
+    $this->requester  = FALSE;
+    $this->drafter    = FALSE;
+    $this->editer     = FALSE;
+    $this->reviewer   = FALSE;
+    $this->publisher  = FALSE;
+    $this->manager    = FALSE;
+    $this->administer = FALSE;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->public);
+    unset($this->system);
+    unset($this->user);
+    unset($this->requester);
+    unset($this->drafter);
+    unset($this->editer);
+    unset($this->reviewer);
+    unset($this->publisher);
+    unset($this->manager);
+    unset($this->administer);
+  }
+
+  /**
+   * Assign a role.
+   *
+   * When role is set to NONE, and value is TRUE, then all roles are set to FALSE.
+   * When role is set to NONE and value is FALSE, nothing happens.
+   *
+   * @param int $role
+   *   The role id to assign.
+   * @param bool $value
+   *   Set the role value to TRUE/FALSE.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_role($role, $value) {
+    if (!is_int($role) || !is_bool($value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($role === self::NONE) {
+      if ($value) {
+        $this->public     = FALSE;
+        $this->system     = FALSE;
+        $this->user       = FALSE;
+        $this->requester  = FALSE;
+        $this->drafter    = FALSE;
+        $this->editer     = FALSE;
+        $this->reviewer   = FALSE;
+        $this->publisher  = FALSE;
+        $this->manager    = FALSE;
+        $this->administer = FALSE;
+      }
+    }
+    elseif ($role === self::PUBLIC) {
+      $this->public = $value;
+    }
+    elseif ($role === self::SYSTEM) {
+      $this->system = $value;
+    }
+    elseif ($role === self::USER) {
+      $this->user = $value;
+    }
+    elseif ($role === self::REQUESTER) {
+      $this->requester = $value;
+    }
+    elseif ($role === self::DRAFTER) {
+      $this->drafter = $value;
+    }
+    elseif ($role === self::EDITER) {
+      $this->editer = $value;
+    }
+    elseif ($role === self::REVIEWER) {
+      $this->reviewer = $value;
+    }
+    elseif ($role === self::PUBLISHER) {
+      $this->publisher = $value;
+    }
+    elseif ($role === self::MANAGER) {
+      $this->manager = $value;
+    }
+    elseif ($role === self::ADMINISTER) {
+      $this->administer = $value;
+    }
+    else {
+      return new c_base_return_false();
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the current status of the specified role.
+   *
+   * When role is set to NONE, TRUE is returned when all values are set to FALSE.
+   * When role is set to NONE, FALSE is returned when any values are set to TRUE.
+   *
+   * @param int $role
+   *   The role id to get the value of.
+   *
+   * @return c_base_return_status
+   *   TRUE on enabled, FALSE on disabled.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_role($role) {
+    if (!is_int($role)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($role === self::NONE) {
+      if (!($this->public || $this->system || $this->user || $this->requester || $this->drafter || $this->editer || $this->reviewer || $this->publisher || $this->manager || $this->administer)) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::PUBLIC) {
+      if ($this->public) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::SYSTEM) {
+      if ($this->system) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::USER) {
+      if ($this->user) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::REQUESTER) {
+      if ($this->requester) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::DRAFTER) {
+      if ($this->drafter) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::EDITER) {
+      if ($this->editer) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::REVIEWER) {
+      if ($this->reviewer) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::PUBLISHER) {
+      if ($this->publisher) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::MANAGER) {
+      if ($this->manager) {
+        return new c_base_return_true();
+      }
+    }
+    elseif ($role === self::ADMINISTER) {
+      if ($this->administer) {
+        return new c_base_return_true();
+      }
+    }
+
+    return new c_base_return_false();
+  }
+}
diff --git a/common/base/classes/base_ascii.php b/common/base/classes/base_ascii.php
new file mode 100644 (file)
index 0000000..e926520
--- /dev/null
@@ -0,0 +1,139 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing ASCII.
+ */
+
+/**
+ * A class for managing ASCII.
+ */
+class c_base_ascii {
+  const NULL                = 0;
+  const START_OF_HEADER     = 1;
+  const START_OF_TEXT       = 2;
+  const END_OF_TEXT         = 3;
+  const END_OF_TRANSMISSION = 4;
+  const ENQUIRY             = 5;
+  const ACKNOWLEDGEMENT     = 6;
+  const BELL                = 7;
+  const BACKSPACE           = 8;
+  const TAB_HORIZONTAL      = 9;
+  const NEW_LINE            = 10;
+  const TAB_VERTICAL        = 11;
+  const FORM_FEED           = 12;
+  const CARRIAGE_RETURN     = 13;
+  const SHIFT_OUT           = 14;
+  const SHIFT_IN            = 15;
+  const DATA_LINK_ESCAPE    = 16;
+  const DEVICE_CONTROL_1    = 17;
+  const DEVICE_CONTROL_2    = 18;
+  const DEVICE_CONTROL_3    = 19;
+  const DEVICE_CONTROL_4    = 20;
+  const ACKNOWLEDGEMENT_NOT = 21;
+  const SYNCHRONOUS_IDLE    = 22;
+  const END_OF_BLOCK        = 23;
+  const CANCEL              = 24;
+  const END_OF_MEDIUM       = 25;
+  const SUBSTITUTE          = 26;
+  const ESCAPE              = 27;
+  const SEPARATOR_FILE      = 28;
+  const SEPARATOR_GROUP     = 29;
+  const SEPARATOR_RECORD    = 30;
+  const SEPARATOR_UNIT      = 31;
+  const SPACE               = 32;
+  const EXCLAMATION         = 33;
+  const QUOTE_DOUBLE        = 34;
+  const HASH                = 35;
+  const DOLLAR              = 36;
+  const PERCENT             = 37;
+  const AMPERSAND           = 38;
+  const QUOTE_SINGLE        = 39;
+  const PARENTHESIS_OPEN    = 40;
+  const PARENTHESIS_CLOSE   = 41;
+  const ASTERISK            = 42;
+  const PLUS                = 43;
+  const COMMA               = 44;
+  const MINUS               = 45;
+  const PERIOD              = 46;
+  const SLASH_FORWARD       = 47;
+  const ZERO                = 48;
+  const ONE                 = 49;
+  const TWO                 = 50;
+  const THREE               = 51;
+  const FOUR                = 52;
+  const FIVE                = 53;
+  const SIX                 = 54;
+  const SEVEN               = 55;
+  const EIGHT               = 56;
+  const NINE                = 57;
+  const COLON               = 58;
+  const COLON_SEMI          = 59;
+  const LESS_THAN           = 60;
+  const EQUAL               = 61;
+  const GREATER_THAN        = 62;
+  const QUESTION_MARK       = 63;
+  const AT                  = 64;
+  const UPPER_A             = 65;
+  const UPPER_B             = 66;
+  const UPPER_C             = 67;
+  const UPPER_D             = 68;
+  const UPPER_E             = 69;
+  const UPPER_F             = 70;
+  const UPPER_G             = 71;
+  const UPPER_H             = 72;
+  const UPPER_I             = 73;
+  const UPPER_J             = 74;
+  const UPPER_K             = 75;
+  const UPPER_L             = 76;
+  const UPPER_M             = 77;
+  const UPPER_N             = 78;
+  const UPPER_O             = 79;
+  const UPPER_P             = 80;
+  const UPPER_Q             = 81;
+  const UPPER_R             = 82;
+  const UPPER_S             = 83;
+  const UPPER_T             = 84;
+  const UPPER_U             = 85;
+  const UPPER_V             = 86;
+  const UPPER_W             = 87;
+  const UPPER_X             = 88;
+  const UPPER_Y             = 89;
+  const UPPER_Z             = 90;
+  const BRACKET_OPEN        = 91;
+  const SLASH_BACKWARD      = 92;
+  const BRACKET_CLOSE       = 93;
+  const CARET               = 94;
+  const UNDERSCORE          = 95;
+  const GRAVE               = 96;
+  const LOWER_A             = 97;
+  const LOWER_B             = 98;
+  const LOWER_C             = 99;
+  const LOWER_D             = 100;
+  const LOWER_E             = 101;
+  const LOWER_F             = 102;
+  const LOWER_G             = 103;
+  const LOWER_H             = 104;
+  const LOWER_I             = 105;
+  const LOWER_J             = 106;
+  const LOWER_K             = 107;
+  const LOWER_L             = 108;
+  const LOWER_M             = 109;
+  const LOWER_N             = 110;
+  const LOWER_O             = 111;
+  const LOWER_P             = 112;
+  const LOWER_Q             = 113;
+  const LOWER_R             = 114;
+  const LOWER_S             = 115;
+  const LOWER_T             = 116;
+  const LOWER_U             = 117;
+  const LOWER_V             = 118;
+  const LOWER_W             = 119;
+  const LOWER_X             = 120;
+  const LOWER_Y             = 121;
+  const LOWER_Z             = 122;
+  const BRACE_OPEN          = 123;
+  const PIPE                = 124;
+  const BRACE_CLOSE         = 125;
+  const TILDE               = 126;
+  const DELETE              = 127;
+}
diff --git a/common/base/classes/base_charset.php b/common/base/classes/base_charset.php
new file mode 100644 (file)
index 0000000..e1e8383
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing common charsets.
+ */
+
+/**
+ * A class for managing common rfc character sets.
+ */
+class c_base_charset {
+  const UNDEFINED   = 0;
+  const ASCII       = 1;
+  const UTF_8       = 2;
+  const UTF_16      = 3;
+  const UTF_32      = 4;
+  const ISO_8859_1  = 5;
+  const ISO_8859_2  = 6;
+  const ISO_8859_3  = 7;
+  const ISO_8859_4  = 8;
+  const ISO_8859_5  = 9;
+  const ISO_8859_6  = 10;
+  const ISO_8859_7  = 11;
+  const ISO_8859_8  = 12;
+  const ISO_8859_9  = 13;
+  const ISO_8859_10 = 14;
+  const ISO_8859_11 = 15;
+  const ISO_8859_12 = 16;
+  const ISO_8859_13 = 17;
+  const ISO_8859_14 = 18;
+  const ISO_8859_15 = 19;
+  const ISO_8859_16 = 20;
+
+  /**
+   * Determine if the given code is a valid charset code.
+   *
+   * @param int $charset
+   *   The integer to validate.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_is_valid($charset) {
+    if (!is_int($charset)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($charset < self::ASCII || $charset > self::ISO_8859_16) {
+      return new c_base_return_false();
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Convert the given code to a string.
+   *
+   * @param int $charset
+   *   The integer to convert.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The string is returned on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_to_string($charset) {
+    if (!is_int($charset)) {
+      return c_base_return_error::s_false();
+    }
+
+    switch ($charset) {
+      case self::UTF_8:
+        return c_base_return_string::s_new('UTF-8');
+      case self::ASCII:
+        return c_base_return_string::s_new('ASCII');
+      case self::UTF_16:
+        return c_base_return_string::s_new('UTF-16');
+      case self::UTF_32:
+        return c_base_return_string::s_new('UTF-32');
+      case self::ISO_8859_1:
+        return c_base_return_string::s_new('ISO-8859-1');
+      case self::ISO_8859_2:
+        return c_base_return_string::s_new('ISO-8859-2');
+      case self::ISO_8859_3:
+        return c_base_return_string::s_new('ISO-8859-3');
+      case self::ISO_8859_4:
+        return c_base_return_string::s_new('ISO-8859-4');
+      case self::ISO_8859_5:
+        return c_base_return_string::s_new('ISO-8859-5');
+      case self::ISO_8859_6:
+        return c_base_return_string::s_new('ISO-8859-6');
+      case self::ISO_8859_7:
+        return c_base_return_string::s_new('ISO-8859-7');
+      case self::ISO_8859_8:
+        return c_base_return_string::s_new('ISO-8859-8');
+      case self::ISO_8859_9:
+        return c_base_return_string::s_new('ISO-8859-9');
+      case self::ISO_8859_10:
+        return c_base_return_string::s_new('ISO-8859-10');
+      case self::ISO_8859_11:
+        return c_base_return_string::s_new('ISO-8859-11');
+      case self::ISO_8859_12:
+        return c_base_return_string::s_new('ISO-8859-12');
+      case self::ISO_8859_13:
+        return c_base_return_string::s_new('ISO-8859-13');
+      case self::ISO_8859_14:
+        return c_base_return_string::s_new('ISO-8859-14');
+      case self::ISO_8859_15:
+        return c_base_return_string::s_new('ISO-8859-15');
+      case self::ISO_8859_16:
+        return c_base_return_string::s_new('ISO-8859-16');
+    }
+
+    return c_base_return_error::s_false();
+  }
+}
diff --git a/common/base/classes/base_cookie.php b/common/base/classes/base_cookie.php
new file mode 100644 (file)
index 0000000..8cd55b7
--- /dev/null
@@ -0,0 +1,846 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing cookies.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+/**
+ * A generic cookie management class.
+ *
+ * The cookie stored via this class will store the data as an array encoded in json format.
+ *
+ * This class overrides c_base_return_array() such that some of its return values are in a different form than expected.
+ * This will utilize c_base_return_* as return values.
+ *
+ * @see: http://us.php.net/manual/en/features.cookies.php
+ * @see: setcookie()
+ */
+class c_base_cookie extends c_base_return_array {
+  const DEFAULT_LIFETIME = 172800; // 48 hours
+  const DEFAULT_PATH = '/';
+  const CHECKSUM_ALGORITHM = 'sha256';
+
+  private $name;
+  private $secure;
+  private $max_age;
+  private $expires;
+  private $path;
+  private $domain;
+  private $http_only;
+  private $first_only;
+  private $value;
+
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->name = NULL;
+    $this->secure = TRUE;
+    $this->max_age = NULL;
+    $this->expires = NULL;
+    $this->path = self::DEFAULT_PATH;
+    $this->domain = NULL;
+    $this->http_only = FALSE;
+    $this->first_only = TRUE;
+    $this->value = array();
+
+    $this->p_set_lifetime_default();
+
+    parent::__construct();
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->name);
+    unset($this->secure);
+    unset($this->max_age);
+    unset($this->expires);
+    unset($this->path);
+    unset($this->domain);
+    unset($this->http_only);
+    unset($this->first_only);
+    unset($this->value);
+
+    parent::__destruct();
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, array());
+  }
+
+  /**
+   * Assigns the cookie name.
+   *
+   * @param string $name
+   *   The cookie name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_name($name) {
+    if (!is_string($name) || empty($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (mb_strlen($name) == 0 || preg_match('/^(\w|-)+$/iu', $name) != 1) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->name = preg_replace('/(^\s+)|(\s+$)/us', '', rawurlencode($name));
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored cookie name.
+   *
+   * @return c_base_return_string
+   *   The cookie name string or NULL if undefined.
+   */
+  public function get_name() {
+    return c_base_return_string::s_new($this->name);
+  }
+
+  /**
+   * Assigns the cookie secure flag.
+   *
+   * This tells the browser that the cookie should only be used in SSL/HTTPS communications.
+   * This is best left enabled for security reasons.
+   *
+   * @param bool $secure
+   *   The security flag for the cookie.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_secure($secure) {
+    if (!is_bool($secure)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->secure = $secure;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored cookie secure flag.
+   *
+   * @return c_base_return_bool
+   *   The cookie secure flag setting.
+   */
+  public function get_secure() {
+    // this flag should never be undefined, if it is NULL, then force the default.
+    if (is_null($this->secure)) {
+      $this->secure = TRUE;
+    }
+
+    return c_base_return_bool::s_new($this->secure);
+  }
+
+  /**
+   * Assigns the expires timestamp of the cookie.
+   *
+   * This tells the browser until what date the cookie will be valid for.
+   * This cannot be higher than php's 'session.cookie_lifetime' value.
+   * If not specified, then the default is to use PHP's 'session.cookie_lifetime' based on when this class was created.
+   *
+   * A value of 0 in both max age and expires will designate a session cookie.
+   *
+   * @param int|null $expires
+   *   A unix timestamp representing the date in which this cookie expires.
+   *   A value of NULL disabled the usage of expires.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: self::set_max_age()
+   */
+  public function set_expires($expires) {
+    if (!is_null($expires) && (!is_int($expires) || $this->expires < 0)) {
+      if (is_string($max_age) && is_numeric($expires)) {
+        $expires = (int) $expires;
+
+        if ($expires < 0) {
+          return c_base_return_error::s_false();
+        }
+      }
+      else {
+        return c_base_return_error::s_false();
+      }
+    }
+
+    $this->expires = $expires;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored cookie expires timestamp.
+   *
+   * @return c_base_return_int
+   *   The expiration unix timestamp for the cookie.
+   *
+   * @see: self::get_max_age()
+   */
+  public function get_expires() {
+    if (is_null($this->expires) && is_null($this->max_age)) {
+      $this->p_set_lifetime_default();
+    }
+
+    return c_base_return_int::s_new($this->expires);
+  }
+
+  /**
+   * Assigns the max age of the cookie.
+   *
+   * This tells the browser how long the cookie will be valid for.
+   * This cannot be higher than php's 'session.cookie_lifetime' value.
+   * If not specified, then the default is to use PHP's 'session.cookie_lifetime' based on when this class was created.
+   *
+   * If max age is specified, but expires is not, then expires will be auto-calculated and provided for compatibility with old clients.
+   *
+   * A value of 0 in both max age and expires will designate a session cookie.
+   *
+   * @param int|null $max_age
+   *   Number of seconds (max-age cookie flag).
+   *   A value of NULL disabled the usage of max age.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: self::set_expires()
+   */
+  public function set_max_age($max_age) {
+    if (!is_null($max_age) && (!is_int($max_age) || $this->max_age < 0)) {
+      if (is_string($max_age) && is_numeric($max_age)) {
+        $max_age = (int) $max_age;
+
+        if ($max_age < 0) {
+          return c_base_return_error::s_false();
+        }
+      }
+      else {
+        return c_base_return_error::s_false();
+      }
+    }
+
+    $this->max_age = $max_age;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored cookie max age value.
+   *
+   * @return c_base_return_int
+   *   The expiration unix timestamp for the cookie.
+   *   FALSE (without error bit) is returned if there is no value as per $timestamp parameter.
+   *
+   * @see: self::get_expires()
+   */
+  public function get_max_age() {
+    if (is_null($this->expires) && is_null($this->max_age)) {
+      $this->p_set_lifetime_default();
+    }
+
+    return c_base_return_int::s_new($this->max_age);
+  }
+
+  /**
+   * Assigns the cookie path.
+   *
+   * @param string $path
+   *   The path for the cookie.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_path($path) {
+    if (!is_string($path) || empty($path)) {
+      return c_base_return_error::s_false();
+    }
+
+    // sanitize the path string, only allowing the path portion of the url.
+    $parsed = parse_url($path, PHP_URL_PATH);
+    if ($parsed === FALSE) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    $this->path = preg_replace('/(^\s+)|(\s+$)/us', '', $parsed);
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored cookie path.
+   *
+   * @return c_base_return_string
+   *   The cookie path.
+   */
+  public function get_path() {
+    // this flag should never be undefined, if it is NULL, then force the default.
+    if (is_null($this->path)) {
+      $this->path = self::DEFAULT_PATH;
+    }
+
+    return c_base_return_string::s_new($this->path);
+  }
+
+  /**
+   * Assigns the cookie domain.
+   *
+   * @param string $domain
+   *   The domain for the cookie.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_domain($domain) {
+    if (!is_string($domain) || empty($domain)) {
+      return c_base_return_error::s_false();
+    }
+
+    // sanitize the domain string, only allowing the host portion of the url.
+    $parsed = parse_url('stub://' . $domain, PHP_URL_HOST);
+    if ($parsed === FALSE) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    $this->domain = preg_replace('/(^\s+)|(\s+$)/us', '', $parsed);
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored cookie domain.
+   *
+   * @return c_base_return_string
+   *   The cookie domain or null if undefined.
+   */
+  public function get_domain() {
+    return c_base_return_string::s_new($this->domain);
+  }
+
+  /**
+   * Assigns the cookie http only flag.
+   *
+   * Set this to TRUE to only allow http protocol to utilize this cookie.
+   * According to the PHP documentation, this prohibits javascript from accessing the cookie.
+   *
+   * @param bool $http_only
+   *   The http-only status for the cookie.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_http_only($http_only) {
+    if (!is_bool($http_only)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->http_only = $http_only;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored cookie http only flag.
+   *
+   * @return c_base_return_bool
+   *   The cookie http only flag.
+   */
+  public function get_http_only() {
+    // this flag should never be undefined, if it is NULL, then force the default.
+    if (is_null($this->http_only)) {
+      $this->http_only = FALSE;
+    }
+
+    return c_base_return_bool::s_new($this->http_only);
+  }
+
+  /**
+   * Assigns the cookie http firsty-party flag.
+   *
+   * Set this to TRUE to only allow the cookie to be used as first party only.
+   * According to the PHP documentation, this tells browsers to never allow this to be used as a thirdy-party cookie.
+   *
+   * @param bool $first_only
+   *   The first-only status for the cookie.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_first_only($first_only) {
+    if (!is_bool($first_only)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->first_only = $first_only;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored cookie http only flag.
+   *
+   * @return c_base_return_bool
+   *   The cookie http only flag.
+   */
+  public function get_first_only() {
+    // this flag should never be undefined, if it is NULL, then force the default.
+    if (is_null($this->http_only)) {
+      $this->http_only = FALSE;
+    }
+
+    return c_base_return_bool::s_new($this->http_only);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * Cookies values associated with this class are only stored as an array.
+   * Be sure to wrap your values in an array.
+   * The array key 'checksum' will be created if one does not already exist when building the cookie.
+   *
+   * This overrides the parent class function of set_value().
+   * Expect return values to be of the form c_base_return_*.
+   *
+   * @param array $value
+   *   Any value so long as it is an array.
+   *   NULL is not allowed.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_array($value)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->value = $value;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Return the value.
+   *
+   * This overrides the parent class function of set_value().
+   * Expect return values to be of the form c_base_return_*.
+   *
+   * @return c_base_return_array $value
+   *   The value array stored within this class.
+   *   NULL may be returned if there is no defined valid array.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !is_array($this->value)) {
+      $this->value = array();
+    }
+
+    return c_base_return_array::s_new($this->value);
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return array $value
+   *   The value array stored within this class.
+   */
+  public function get_value_exact() {
+    // the value should not be changed into c_base_return_array because 'exact' should consistently mean 'exact'.
+    return parent::get_value_exact();
+  }
+
+  /**
+   * Return the value at a specific index in the array.
+   *
+   * This overrides the parent class function.
+   * Expect return values to be of the form c_base_return_*.
+   *
+   * @param string|null $key
+   *   A specific array key to load.
+   * @param string|null $type
+   *   (optional) Perform sanity checking of return value based on a known type.
+   *
+   *   If not provided, then a 'raw' type of c_base_return_value is returned.
+   *   - This may contain things like a normal PHP TRUE or FALSE.
+   *
+   *   Supported known types:
+   *     'bool': a boolean value, returns c_base_return_value.
+   *     'int': an integer value, returns c_base_return_int.
+   *     'float': a float value, returns c_base_return_float.
+   *     'string': a string value, return c_base_return_string.
+   *     'array': an array value, return c_base_return_array.
+   *     'object': an object value, return c_base_return_object.
+   *     'resource': a generic resource value, return c_base_return_resource.
+   *     'stream': a stream resource value, return c_base_return_stream.
+   *     'socket': a socket resource value, return c_base_return_socket.
+   *     'null': a NULL value, return c_base_return_value.
+   *
+   * @return c_base_return_status|c_base_return_value
+   *   A c_base_return_array is returned when no $key is defined.
+   *   A c_base_return_value, or more specific, is returned if $type is provided for known types.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_value_at($key, $type = NULL) {
+    if (!is_string($key) || empty($key)) {
+      return c_base_return_error::s_false();
+    }
+
+    // if type is supplied, it must be string.
+    if (!is_null($type) && (!is_string($type) || empty($type))) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($this->value)) {
+      $this->value = array();
+    }
+
+    if (!array_key_exists($key, $this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($type)) {
+      if ($type == 'bool') {
+        if (is_bool($this->value[$key])) {
+          return c_base_return_bool::s_new($this->value[$key]);
+        }
+      }
+      elseif ($type == 'int') {
+        if (is_int($this->value[$key])) {
+          return c_base_return_int::s_new($this->value[$key]);
+        }
+      }
+      elseif ($type == 'float') {
+        if (is_float($this->value[$key])) {
+          return c_base_return_float::s_new($this->value[$key]);
+        }
+      }
+      elseif ($type == 'string') {
+        if (is_string($this->value[$key])) {
+          return c_base_return_string::s_new($this->value[$key]);
+        }
+      }
+      elseif ($type == 'array') {
+        if (is_array($this->value[$key])) {
+          return c_base_return_array::s_new($this->value[$key]);
+        }
+      }
+      elseif ($type == 'object') {
+        if (is_object($this->value[$key])) {
+          return c_base_return_object::s_new($this->value[$key]);
+        }
+      }
+      elseif ($type == 'resource') {
+        if (is_resource($this->value[$key])) {
+          return c_base_return_resource::s_new($this->value[$key]);
+        }
+      }
+      elseif ($type == 'stream') {
+        if (is_stream($this->value[$key])) {
+          return c_base_return_stream::s_new($this->value[$key]);
+        }
+      }
+      elseif ($type == 'socket') {
+        if (is_socket($this->value[$key])) {
+          return c_base_return_socket::s_new($this->value[$key]);
+        }
+      }
+      elseif ($type == 'null') {
+        if (is_null($this->value[$key])) {
+          return c_base_return_value::s_new($this->value[$key]);
+        }
+      }
+
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_value::s_new($this->value[$key]);
+  }
+
+  /**
+   * Save the cookie to the HTTP headers for sending to the client.
+   *
+   * This function sends an HTTP header and therefore should only be used when ready to send headers.
+   *
+   * Both name and value are required to be set before calling this function.
+   *
+   * The functions setcookie() and setrawcookie() do not provide advanced customization.
+   * Instead of using those functions, use header() to directly generate the cookie.
+   *
+   * @param bool $checksum
+   *   When set to TRUE, the array will be converted to a json string and have a checksum created for it.
+   *   This checksum value will then be placed inside the array and a final json string will be submitted.
+   *
+   *   Warning: any top-level key in the array with the name of 'checksum' will be lost when using this.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: self::validate()
+   * @see: setcookie()
+   * @see: setrawcookie()
+   * @see: header()
+   */
+  public function do_push($checksum = TRUE) {
+    if (is_null($this->name) || is_null($this->value)) {
+      c_base_return_error::s_false();
+    }
+
+    if ($checksum) {
+      unset($this->value['checksum']);
+      $this->value['checksum'] = $this->p_build_checksum();
+
+      if (is_null($this->value['checksum'])) {
+        unset($this->value['checksum']);
+        return c_base_return_error::s_false();
+      }
+    }
+
+    // @todo: consider adding support for assigning the json depth setting.
+    $json = json_encode($this->value);
+    if ($json === FALSE) {
+      unset($json);
+      return c_base_return_error::s_false();
+    }
+
+    $value = rawurlencode(preg_replace('/(^\s+)|(\s+$)/us', '', $json));
+    unset($json);
+
+    //$result = setrawcookie($this->name, $value, $this->max_age, $this->path, $this->domain, $this->secure, $this->http_only);
+    $cookie = 'Set-Cookie: ' . rawurlencode($this->name) . '=' . $value . ';';
+
+    if (!is_null($this->domain)) {
+      $cookie .= ' domain=' . $this->domain . ';';
+    }
+
+    if (!is_null($this->path)) {
+      $cookie .= ' path=' . $this->path . ';';
+    }
+
+    if (!is_null($this->max_age)) {
+      $cookie .= ' max-age=' . $this->max_age . ';';
+
+      // provide an expires for compatibility purposes if one is not specified.
+      if (is_null($this->expires)) {
+        $cookie .= ' expires=' . gmdate('D, d-M-Y H:i:s T', strtotime('+' . $this->max_age . ' seconds')) . ';';
+      }
+    }
+
+    if (!is_null($this->expires)) {
+      $cookie .= ' expires=' . gmdate('D, d-M-Y H:i:s T', $this->expires) . ';';
+    }
+
+    if ($this->secure) {
+      $cookie .= ' secure;';
+    }
+
+    if ($this->http_only) {
+      $cookie .= ' httponly;';
+    }
+
+    if ($this->first_only) {
+      $cookie .= ' first-party;';
+    }
+
+    header($cookie, FALSE);
+
+    unset($cookie);
+    unset($value);
+
+    return new c_base_return_true();
+  }
+
+/**
+   * Deletes the cookie by setting both the expires and max-age to -1.
+   *
+   * This does not need to be called when updating the cookie.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: self::push()
+   */
+  public function delete() {
+    $original_max_age = $this->max_age;
+    $original_expires = $this->expires;
+
+    $this->max_age = -1;
+    $this->expires = -1;
+
+    $result = $this->push(FALSE);
+
+    $this->max_age = $original_max_age;
+    $this->expires = $original_expires;
+
+    unset($original_max_age);
+    unset($original_expires);
+
+    return $result;
+  }
+
+  /**
+   * Retrieve the cookie from the HTTP headers sent by the client.
+   *
+   * This class object will be populated with the cookies settings.
+   * The cookie value will be cleared if the cookie exists.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function do_pull() {
+    if (!isset($_COOKIE) || !array_key_exists($this->name, $_COOKIE)) {
+      // This is not an error, but there is no cookie to pull.
+      // simply return false without the error flag set.
+      return new c_base_return_false();
+    }
+
+    $json = rawurldecode($_COOKIE[$this->name]);
+    $value = json_decode($json, TRUE);
+    unset($json);
+
+    if ($value === FALSE) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->value = $value;
+    unset($value);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assigns a default value for the expiration based on php's session.cookie_lifetime.
+   */
+  private function p_set_lifetime_default() {
+    $lifetime = ini_get('session.cookie_lifetime');
+    if ($lifetime <= 0) {
+      $lifetime = self::DEFAULT_LIFETIME;
+    }
+
+    $this->max_age = $lifetime;
+    unset($lifetime);
+  }
+
+  /**
+   * Validate a checksum key.
+   *
+   * This is only meaningful when called after self::do_pull() is used.
+   *
+   * If a checksum key exists, will validate that the contents of the value are consistent with the checksum.
+   * This is useful to protect data from alterations, be it defect or accident.
+   * This does not protect against malicious activities because the malicious user could simply regenerate the checksum after their changes.
+   *
+   * @return c_base_return_status
+   *   TRUE when the checksum validates, FALSE when the checksum fails or there is no checksum.
+   *   On error FALSE is returned with the error bit set.
+   */
+  public function validate() {
+    if (!is_array($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!array_key_exists('checksum', $this->value)) {
+      return new c_base_return_false();
+    }
+
+    $checksum = $this->p_build_checksum();
+    if ($this->value['checksum'] == $checksum) {
+      unset($checksum);
+      return new c_base_return_true();
+    }
+    unset($checksum);
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Builds a checksum of the value array.
+   *
+   * This does not assign the checksum to the array.
+   * The checksum is only assigned by the do_push() or do_pull() functions.
+   *
+   * If the values are changed after this call, then this checksum will be invalid.
+   *
+   * @see: self::do_pull()
+   * @see: self::do_push()
+   */
+  public function build_checksum() {
+    $checksum = $this->p_build_checksum();
+    if (is_string($checksum)) {
+      return c_base_return_string::s_new($checksum);
+    }
+    unset($checksum);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Generates a checksum of the value array.
+   *
+   * Any existing checksum key is preserved.
+   *
+   * @return string
+   *   A generated checksum.
+   *
+   * @see: hash()
+   */
+  private function p_build_checksum() {
+    if (!is_array($this->value)) {
+      $this->value = array();
+    }
+
+    $has_checksum = array_key_exists('checksum', $this->value);
+    $checksum = NULL;
+    if ($has_checksum) {
+      $checksum = $this->value['checksum'];
+      unset($this->value['checksum']);
+    }
+
+    $json = json_encode($this->value);
+    if ($json === FALSE) {
+      if ($has_checksum) {
+        $this->value['checksum'] = $checksum;
+      }
+
+      unset($has_checksum);
+      unset($checksum);
+      unset($json);
+      return NULL;
+    }
+
+    $generated = hash(c_base_cookie::CHECKSUM_ALGORITHM, $json);
+    if ($has_checksum) {
+      $this->value['checksum'] = $checksum;
+    }
+
+    unset($has_checksum);
+    unset($checksum);
+    unset($json);
+
+    return $generated;
+  }
+}
diff --git a/common/base/classes/base_database.php b/common/base/classes/base_database.php
new file mode 100644 (file)
index 0000000..19360c0
--- /dev/null
@@ -0,0 +1,2711 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing SQL database communication.
+ *
+ * Following SQL strictly and avoiding non-standard SQL is normally the preferred design.
+ * This is being deviated from for the following reasons:
+ * - SQL fails to define a standard of communication, so connecting to and talking to databases is inconsistent.
+ * - SQL fails to be unclear in certain cases that cause different SQL engines to process the code differently.
+ * - PHP's PDO randomly decides not to support some important functionality with PostgreSQL that it supports across other databases and with non-PDO PHP postgresql code.
+ * - A vast majority of open source projects out there use non-standard MySQL-specific code as a basis, so it is about time that some project optimized to PostgreSQL instead.
+ *   - There are numerous cases where MySQL appears to perform better than Postgresql in tests with projects like Drupal.
+ *   - This is a flawed logic given that extra, wasteful, code is added to work undo non-standard which will obviously make Postgresql perform slower.
+ * - Much of the advanced functionality of PostgreSQL is going to be used to write a far more secure and well rounded product that other open source databases ever could.
+ * - This project is not like Drupal and others in that it is not designed around PHP and uses SQL, instead, it is designed around SQL and uses PHP.
+ *
+ * One of the particular designs is to use persistent connections as much as possible.
+ * - At any point in time a transaction needs to be performed, do not use the persistent connection, instead create a new connection.
+ * - At any point in time a lock needs to be used, do not use the persistent connection, instead create a new connection.
+ * - For connection re-cycling, persistent connections should be used because they carry over.
+ *   - Anonymous connections should in general use persistent.
+ *   - Non-anonymous connections should not use persistent connections.
+ *   - Non-anonymous connections may keep an anonymous persistent connection for use such as "public preview" of something.
+ * - A reason against persistent connections is the inability to directly close them.
+ *   - This is a major weakness and may prevent me from using this persistent connection design (much testing is required).
+ *
+ * @see: http://us.php.net/manual/en/features.persistent-connections.php
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+/**
+ * A generic class for storing and creating a database connection string.
+ *
+ * @see: http://us.php.net/manual/en/function.pg-pconnect.php
+ */
+class c_base_connection_string extends c_base_return_string {
+  const DATA_CLEAR_TEXT_LENGTH = 4096;
+
+  private $host;
+  private $host_addr;
+  private $port;
+  private $dbname;
+  private $user;
+  private $password;
+  private $connect_timeout;
+  private $options;
+  private $ssl_mode;
+  private $service;
+
+  private $error;
+
+  /**
+   * Class destructor.
+   */
+  public function __construct() {
+    $this->host = NULL;
+    $this->host_addr = NULL;
+    $this->port = NULL;
+    $this->dbname = NULL;
+    $this->user = NULL;
+    $this->password = NULL;
+    $this->connect_timeout = NULL;
+    $this->options = NULL;
+    $this->ssl_mode = NULL;
+    $this->service = NULL;
+
+    $this->error = NULL;
+
+    parent->__construct();
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    $this->clear();
+
+    unset($this->host);
+    unset($this->host_addr);
+    unset($this->port);
+    unset($this->dbname);
+    unset($this->user);
+    unset($this->password);
+    unset($this->connect_timeout);
+    unset($this->options);
+    unset($this->ssl_mode);
+    unset($this->service);
+
+    unset($this->error);
+
+    parent->__destruct();
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, '');
+  }
+
+  /**
+   * Assign host information.
+   *
+   * @param string $host
+   *   Host information string, such as hostname or ip address.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_host($host) {
+    if (!is_string($host)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->host = $host;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the host information.
+   *
+   * @return c_base_return_string
+   *   The host information string.
+   */
+  public function get_host() {
+    if (!is_string($this->host)) {
+      $this->host = '';
+    }
+
+    return c_base_return_string::s_new($this->host);
+  }
+
+  /**
+   * Assign host address information.
+   *
+   * @param string $host_addr
+   *   Host address information string.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_host_addr($host_addr) {
+    if (!is_string($host_addr)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->host_addr = $host_addr;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the host address information.
+   *
+   * @return c_base_return_string
+   *   The host address information string.
+   */
+  public function get_host_addr() {
+    if (!is_string($this->host_addr)) {
+      $this->host_addr = '';
+    }
+
+    return c_base_return_string::s_new($this->host_addr);
+  }
+
+  /**
+   * Assign port number.
+   *
+   * @param int $port
+   *   Port number.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_port($port) {
+    if (!is_int($port)) {
+      if (is_string($port) && is_numeric($port)) {
+        $port = (int) $port;
+        if ($port < 0) {
+          return c_base_return_error::s_false();
+        }
+      }
+      else {
+        return c_base_return_error::s_false();
+      }
+    }
+
+    $this->port = $port;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the port number.
+   *
+   * @return c_base_return_int
+   *   The port number.
+   */
+  public function get_port() {
+    if (!is_int($this->port)) {
+      $this->port = 0;
+    }
+
+    return c_base_return_int::s_new($this->port);
+  }
+
+  /**
+   * Assign database name.
+   *
+   * @param string $dbname
+   *   The database name string.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_dbname($dbname) {
+    if (!is_string($dbname)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->dbname = $dbname;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the database name.
+   *
+   * @return c_base_return_string
+   *   The database name string.
+   */
+  public function get_dbname() {
+    if (!is_string($this->dbname)) {
+      $this->dbname = '';
+    }
+
+    return c_base_return_string::s_new($this->dbname);
+  }
+
+  /**
+   * Assign user name.
+   *
+   * @param string $user
+   *   The user name string.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_user($user) {
+    if (!is_string($user)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->user = $user;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the user name.
+   *
+   * @return c_base_return_string
+   *   The user name string.
+   */
+  public function get_user() {
+    if (!is_string($this->user)) {
+      $this->user = '';
+    }
+
+    return c_base_return_string::s_new($this->user);
+  }
+
+  /**
+   * Assign password.
+   *
+   * @param string|null $password
+   *   The password string.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_password($password) {
+    if (!is_null($password) && !is_string($password)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->password = $password;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the password.
+   *
+   * @return c_base_return_string|c_base_return_false
+   *   The password string.
+   *   FALSE is returned if there is no assigned password.
+   */
+  public function get_password() {
+    if (is_null($this->password)) {
+      return new c_base_return_false();
+    }
+
+    if (!is_string($this->password)) {
+      $this->password = '';
+    }
+
+    return c_base_return_string::s_new($this->password);
+  }
+
+  /**
+   * Assign connect timeout.
+   *
+   * @param int $connect_timeout
+   *   Connect timeout number.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_connect_timeout($connect_timeout) {
+    if (!is_int($connect_timeout)) {
+      if (is_string($connect_timeout) && is_numeric($connect_timeout)) {
+        $connect_timeout = (int) $connect_timeout;
+        if ($connect_timeout < 0) {
+          return c_base_return_error::s_false();
+        }
+      }
+      else {
+        return c_base_return_error::s_false();
+      }
+    }
+
+    $this->connect_timeout = $connect_timeout;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the connect timeout.
+   *
+   * @return c_base_return_int
+   *   The connect timeout number.
+   */
+  public function get_connect_timeout() {
+    if (!is_int($this->connect_timeout)) {
+      $this->connect_timeout = 0;
+    }
+
+    return c_base_return_int::s_new($this->connect_timeout);
+  }
+
+  /**
+   * Assign options information.
+   *
+   * @param string $options
+   *   Options information string.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_options($options) {
+    if (!is_string($options)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->options = $options;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the options information.
+   *
+   * @return c_base_return_string
+   *   The options information string.
+   */
+  public function get_options() {
+    if (!is_string($this->options)) {
+      $this->options = '';
+    }
+
+    return c_base_return_string::s_new($this->options);
+  }
+
+  /**
+   * Assign ssl mode information.
+   *
+   * @param string $ssl_mode
+   *   SSL Mode information string.
+   *   One of the following:
+   *   - 'disable'
+   *   - 'allow'
+   *   - 'prefer'
+   *   - 'require'
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_ssl_mode($ssl_mode) {
+    if (!is_string($ssl_mode)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->ssl_mode = $ssl_mode;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the ssl mode information.
+   *
+   * @return c_base_return_string
+   *   The ssl mode information string.
+   */
+  public function get_ssl_mode() {
+    if (!is_string($this->ssl_mode)) {
+      $this->ssl_mode = '';
+    }
+
+    return c_base_return_string::s_new($this->ssl_mode);
+  }
+
+  /**
+   * Assign service information.
+   *
+   * @param string $service
+   *   Service information string.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_service($service) {
+    if (!is_string($service)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->service = $service;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the service information.
+   *
+   * @return c_base_return_string
+   *   The service information string.
+   */
+  public function get_service() {
+    if (!is_string($this->service)) {
+      $this->service = '';
+    }
+
+    return c_base_return_string::s_new($this->service);
+  }
+
+  /**
+   * Builds the connection string based on the current settings.
+   *
+   * The built string is stored inside of this objects 'value' parameter.
+   */
+  public function build() {
+    $this->value = '';
+
+    if (!empty($this->host)) {
+      $this->value .= ' host=' . $this->p_escape_string($this->host);
+    }
+
+    if (!empty($this->host_addr)) {
+      $this->value .= ' host_addr=' . $this->p_escape_string($this->host_addr);
+    }
+
+    if (!empty($this->port)) {
+      $this->value .= ' port=' . $this->p_escape_string($this->port);
+    }
+
+    if (!empty($this->dbname)) {
+      $this->value .= ' dbname=' . $this->p_escape_string($this->dbname);
+    }
+
+    if (!empty($this->user)) {
+      $this->value .= ' user=' . $this->p_escape_string($this->user);
+    }
+
+    if (!empty($this->password)) {
+      $this->value .= ' password=' . $this->p_escape_string($this->password);
+    }
+
+    if (!empty($this->connect_timeout)) {
+      $this->value .= ' connect_timeout=' . $this->p_escape_string($this->connect_timeout);
+    }
+
+    if (!empty($this->options)) {
+      $this->value .= ' options=' . $this->p_escape_string($this->options);
+    }
+
+    if (!empty($this->ssl_mode)) {
+      $this->value .= ' sslmode=' . $this->p_escape_string($this->ssl_mode);
+    }
+
+    if (!empty($this->service)) {
+      $this->value .= ' service=' . $this->p_escape_string($this->service);
+    }
+  }
+
+  /**
+   * Clears the connection string.
+   *
+   * This should be cleared after use because the string generally contains a password.
+   * Uses an unproven technique in an attempt to 'delete' the string from memory and then unallocating the resource.
+   *
+   * This does not perform the garbage collection, but it is suggested that the caller consider calling gc_collect_cycles().
+   *
+   * @see: gc_collect_cycles()
+   */
+  public function clear() {
+    $this->value = str_repeat(' ', self::DATA_CLEAR_TEXT_LENGTH);
+    unset($this->value);
+  }
+
+  /**
+   * Escape the postgresql connection string.
+   *
+   * According to the documentation, both ' and \ must be escaped with a \.
+   *
+   * @param string string
+   *   The string to escape.
+   *
+   * @return string
+   *   The escaped string.
+   */
+  private function p_escape_string($string) {
+    $escaped = str_replace('\\', '\\\\', $string);
+    $escaped = str_replace('\'', '\\\'', $escaped);
+
+    return $escaped;
+  }
+}
+
+/**
+ * A generic class for managing database connections.
+ *
+ * errata:
+ *   There are a number of cases where PHP's postgresql documentation is unclear on what 'failure' is.
+ *   In certain cases, it mentions failure as an error.
+ *   In other cases, it just says failure.
+ *   If failure happens as a part of normal, expected behavior, then it should not be construed as an error because failure is expected behavior.
+ *   If failure happens due to something unexpected or invalid, then it should be construed as an error.
+ *   This is a context issue and I may be overthinking it.
+ *   I will need to come back later and review my return results.
+ *   For now, I am assuming failure means error as that seems like the most obvious interpretation.
+ *
+ * @require class c_base_return
+ * @require class c_base_session
+ */
+class c_base_database {
+  private $session;
+  private $persistent;
+  private $database;
+  private $connection_string;
+  private $asynchronous;
+
+  private $connected;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->session = NULL;
+    $this->persistent = NULL;
+    $this->database = NULL;
+    $this->connection_string = NULL;
+    $this->asynchronous = NULL;
+
+    $this->connected = NULL;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    if (is_resource($this->database)) {
+      $this->do_disconnect();
+    }
+
+    if (is_object($this->connection_string) && $this->connection_string instanceof c_base_connection_string) {
+      $this->connection_string->clear();
+    }
+
+    unset($this->session);
+    unset($this->persistent);
+    unset($this->database);
+    unset($this->connection_string);
+
+    unset($this->connected);
+  }
+
+  /**
+   * Assign a session to the database.
+   *
+   * @param c_base_session $session
+   *   An already processed and configured session object.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_session($session) {
+    if (!is_object($session) || !($session instanceof c_base_session)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->session = $session;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the session information.
+   *
+   * @return c_base_session_return
+   */
+  public function get_session() {
+    if (!is_object($session) || !($session instanceof c_base_session)) {
+      $this->session = new c_base_session();
+    }
+
+    return c_base_session_return::s_value_exact($this->session);
+  }
+
+  /**
+   * Assign a connection string to the database.
+   *
+   * @param c_base_connection_string $connection_string
+   *   An already processed and configured connection string object.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_connection_string($connection_string) {
+    if (!is_object($connection_string) || !($connection_string instanceof c_base_connection_string)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->connection_string = $connection_string;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the connection string.
+   *
+   * @return c_base_connection_string_return
+   */
+  public function get_connection_string() {
+    if (!is_object($connection_string) || !($connection_string instanceof c_base_connection_string)) {
+      $this->connection_string = new c_base_connection_string();
+    }
+
+    return $this->connection_string;
+  }
+
+  /**
+   * Enable or Disable a persistent database connection.
+   *
+   * When using persistent connections, make sure that there is at least 2*max_connections of shared_buffers.
+   * Where max_connections is the number of connections you intend to allow for all systems and services to access the database.
+   *
+   * PHP's php.ini option 'pgsql.max_persistent' should be used to control the number of persistent connection limits (with -1 being infinite).
+   *
+   * If a persistent connection is created, then it cannot be closed with the normal close function.
+   * However, the close() function still cleans up some resources related to persistent connection and should still be executed.
+   *
+   * Avoid using persistent connection for cases where there is a table lock or transaction in use.
+   *
+   * @param bool $persistent
+   *   TRUE to enable a persistent connection, FALSE otherwise.
+   *
+   * @param c_base_return_status
+   *   TRUE on success, FALSE otherwise
+   */
+  public function set_persistent($persistent) {
+    if (!is_bool($persistent)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->persistent = $persistent;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Return the persistent connection status.
+   *
+   * @return c_base_return_status
+   *   TRUE on enabled, FALSE on disabled.
+   */
+  public function get_persistent() {
+    if (!is_bool($this->persistent)) {
+      $this->persistent = FALSE;
+    }
+
+    if ($this->persistent) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Enable or Disable an asynchronous database connection.
+   *
+   * @param bool $asynchronous
+   *   TRUE to enable a asynchronous connection, FALSE otherwise.
+   *
+   * @param c_base_return_status
+   *   TRUE on success, FALSE otherwise
+   */
+  public function set_asynchronous($asynchronous) {
+    if (!is_bool($asynchronous)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->asynchronous = $asynchronous;
+  }
+
+  /**
+   * Return the asynchronous connection status.
+   *
+   * @return c_base_return_status
+   *   TRUE on enabled, FALSE on disabled.
+   */
+  public function get_asynchronous() {
+    if (!is_bool($this->asynchronous)) {
+      $this->asynchronous = FALSE;
+    }
+
+    if ($this->asynchronous) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Open the connection to the database.
+   *
+   * This will create a new connection if persistent connection is not enabled.
+   *
+   * @param bool $force
+   *   (optional) When TRUE, passes PGSQL_CONNECT_FORCE_NEW to force a new connection to be made.
+   *
+   * @param c_base_return_status
+   *   TRUE on success, FALSE otherwise
+   *   c_base_return_true is returned if the database is connected.
+   *   c_base_return_false is returned if the database is disconnected.
+   *   The error flag is set if there is a problem.
+   *   If the database is already connected when this is called, c_base_return_true is returned with the error flag set.
+   *
+   * @see: pg_connect()
+   */
+  public function do_connect($force = FALSE) {
+    if (!is_bool($force)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_null($this->connection_string)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_resource($this->database)) {
+      return c_base_return_error::s_true();
+    }
+
+    $type = 0;
+    if ($force) {
+      $type += PGSQL_CONNECT_FORCE_NEW;
+    }
+
+    if ($this->asynchronous) {
+      $type += PGSQL_CONNECT_ASYNC;
+    }
+
+    // make sure the connection string is built before using.
+    $this->connection_string->build();
+
+    // Both pg_connect() and pg_pconnect() throw errors and the functions do not support try {} .. catch { statements.
+    // the only way to prevent unwanted error reporting (allowing the caller to the reporting) is to use @.
+    // The @ is considered bad practice, but there is no alternative in this case.
+    if ($this->persistent) {
+      $database = @pg_pconnect($this->connection_string->get_value_exact(), $type);
+    }
+    else {
+      $database = @pg_connect($this->connection_string->get_value_exact(), $type);
+    }
+
+    unset($type);
+    if ($database === FALSE) {
+      unset($database);
+      return c_base_return_error::s_false();
+    }
+
+    $this->database = $database;
+    unset($database);
+
+    // set the default encoding to unicode.
+    pg_set_client_encoding($this->database, 'UTF8');
+
+    $this->connected = TRUE;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Close the connection to the database.
+   *
+   * The PHP documentation states the following:
+   *   If there is open large object resource on the connection, do not close the connection before closing all large object resources.
+   *
+   * @param c_base_return_status
+   *   TRUE on success, FALSE otherwise
+   *
+   * @see: pg_close()
+   */
+  public function do_disconnect() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (pg_close($this->database)) {
+      $this->connected = FALSE;
+      return new c_base_return_true();
+    }
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Flush outbound query data.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE on partial flush.
+   *   FALSE with error flag set on error.
+   *
+   * @see: pg_flush()
+   */
+  public function do_flush() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_flush($this->database);
+    if ($result === TRUE) {
+      return new c_base_return_true();
+    }
+
+    if ($result === FALSE) {
+      return c_base_return_error::s_false();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Returns whether or not a connection has been established to the database.
+   *
+   * @return c_base_return_status
+   *   TRUE on connected, FALSE otherwise.
+   */
+  public function is_connected() {
+    if ($this->connected === TRUE && pg_connection_status($this->database) === PGSQL_CONNECTION_OK) {
+      return c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Returns the connection busy status.
+   *
+   * This is used for asynchronous connections.
+   *
+   * @return c_base_return_status
+   *   TRUE on busy, FALSE when not busy.
+   *   Error flag is set on error.
+   *
+   * @see: pg_connection_busy()
+   */
+  public function is_busy() {
+    if (!$this->asynchronous) {
+      return c_base_return_error::s_false();
+    }
+
+    if (pg_connection_busy($this->database)) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Returns the parameter status.
+   *
+   * @param string $name
+   *   Name of the parameter to get the status of.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   String containing the status or FALSE on failure.
+   *
+   * @see: pg_parameter_status()
+   */
+  public function get_parameter_status($name) {
+    if (!is_string($name) || empty($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_parameter_status($this->database, $name);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($result);
+  }
+
+  /**
+   * Returns the connection busy status.
+   *
+   * This is used for asynchronous connections.
+   *
+   * @return c_base_return_int
+   *   The integer is returned on success or failure.
+   *   The failure flag will be set accordingly.
+   *
+   * @see: pg_connect_poll()
+   */
+  public function do_poll() {
+    if (!$this->asynchronous) {
+      return c_base_return_error::value(0, 'c_base_return_int');
+    }
+
+    return c_base_return_int::s_value_exact(pg_connect_poll($this->database));
+  }
+
+  /**
+   * Resets the connection (perform a reconnect).
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: pg_connection_reset()
+   */
+  public function do_reset() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (pg_connection_reset($this->database)) {
+      return new c_base_return_true();
+    }
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Returns the connection status.
+   *
+   * @return c_base_return_int
+   *   PGSQL_CONNECTION_OK or PGSQL_CONNECTION_BAD.
+   *
+   * @see: pg_connection_status()
+   */
+  public function get_status() {
+    return c_base_return_int::s_new(pg_connection_status($this->database));
+  }
+
+  /**
+   * Ping the connection and attempt to reconnect if broken.
+   *
+   * This calls pg_status() and then pg_connection_reset() instead of pg_ping() so that a more granular return result can be provided.
+   *
+   * @return c_base_return_status
+   *   TRUE if ping was successful, FALSE if ping failed, but was successfully restarted.
+   *   FALSE with error flag set is returned on error or if ping failed and restart failed.
+   *
+   * @see: pg_ping()
+   * @see: pg_status()
+   * @see: pg_connection_reset()
+   */
+  public function do_ping() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (pg_status($this->database) === PGSQL_CONNECTION_OK) {
+      return new c_base_return_true();
+    }
+
+    if (pg_connection_reset($this->database) === TRUE) {
+      return new c_base_return_false();
+    }
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Escape an SQL literal (an SQL string).
+   *
+   * PostgreSQL requires the database connection to be used (or provides its own) when escaping something.
+   * Use this function so that the associated connection gets used instead of some unknown connection.
+   *
+   * @param string $literal
+   *   The string to escape.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The string to be returned.
+   *   FALSE is returned on error
+   *
+   * @see: pg_escape_literal()
+   */
+  public function escape_literal($literal) {
+    if (!is_string($literal)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_value_exact(pg_escape_literal($this->database, $literal));
+  }
+
+  /**
+   * Escape an SQL bytea, a blob like-type specific to PostgreSQL.
+   *
+   * PostgreSQL requires the database connection to be used (or provides its own) when escaping something.
+   * Use this function so that the associated connection gets used instead of some unknown connection.
+   *
+   * @param string $bytea
+   *   The string to escape.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The string to be returned.
+   *   FALSE is returned on error
+   *
+   * @see: pg_escape_bytea()
+   */
+  public function escape_bytea($bytea) {
+    if (!is_string($bytea)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_value_exact(pg_escape_bytea($this->database, $bytea));
+  }
+
+  /**
+   * Escape an SQL identifier (such as a column or table name).
+   *
+   * PostgreSQL requires the database connection to be used (or provides its own) when escaping something.
+   * Use this function so that the associated connection gets used instead of some unknown connection.
+   *
+   * @param string $identifier
+   *   The string to escape.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The string to be returned.
+   *   FALSE is returned on error
+   *
+   * @see: pg_escape_identifier()
+   */
+  public function escape_identifier($identifier) {
+    if (!is_string($identifier)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_value_exact(pg_escape_identifier($this->database, $identifier));
+  }
+
+  /**
+   * Unescape an SQL bytea, a blob like-type specific to PostgreSQL.
+   *
+   * PostgreSQL requires the database connection to be used (or provides its own) when escaping something.
+   * Use this function so that the associated connection gets used instead of some unknown connection.
+   *
+   * @param string $bytea
+   *   The string to unescape.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The string to be returned.
+   *   FALSE is returned on error
+   *
+   * @see: pg_unescape_bytea()
+   */
+  public function unescape_bytea($bytea) {
+    if (!is_string($bytea)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_value_exact(pg_unescape_bytea($this->database, $bytea));
+  }
+
+  /**
+   * Assigns the client encoding.
+   *
+   * @param string $encoding
+   *   The client encoding to assign
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The string to be returned.
+   *   FALSE is returned on error
+   *
+   * @see: pg_set_client_encoding()
+   */
+  public function set_client_encoding($encoding) {
+    if (!is_string($encoding)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    // this function has a strange return status.
+    // 0 is returned instead of TRUE on success, -1 is returned instead of FALSE on error.
+    if (pg_set_client_encoding($this->database, $encoding) === 0) {
+      return new c_base_return_true();
+    }
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Returns the client encoding.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The string to be returned.
+   *   FALSE is returned on error
+   *
+   * @see: pg_client_encoding()
+   */
+  public function get_client_encoding() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $encoding = pg_client_encoding($this->database);
+    if ($encoding === FALSE) {
+      unset($encoding);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_value_exact($encoding);
+  }
+
+  /**
+   * Flushes input on the connection.
+   *
+   * Do not confuse this with the flush command.
+   * The flush command flushsehs the query output.
+   * This flushes input.
+   *
+   * This is likely useful for asynchronous connections.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: pg_consume_input()
+   */
+  public function consume_input() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (pg_consume_input($this->database)) {
+      return new c_base_return_true();
+    }
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Converts an associated array into a suitable SQL statement.
+   *
+   * @param string $table
+   *   The name of the table.
+   * @param string $array
+   *   The associative array to convert.
+   * @param int $options
+   *   (optional) Additional options, such as:
+   *   - PGSQL_CONV_IGNORE_DEFAULT
+   *   - PGSQL_CONV_FORCE_NULL
+   *   - PGSQL_CONV_IGNORE_NOT_NULL
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   The converted array or FALSE on error.
+   *
+   * @see: pg_convert()
+   */
+  public function do_convert($table, $array, $options = 0) {
+    if (!is_string($table) || empty($table)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($array)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($options)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $converted = pg_connect_status($this->database, $table, $array, $options);
+    if ($converted === FALSE) {
+      unset($converted);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_value_exact($converted);
+  }
+
+  /**
+   * Execute a given SQL query statement and wait for results.
+   *
+   * When set to asynchronous, this effectively calls pg_send_execute().
+   *
+   * @param string $name
+   *   The unique name of a query prepared by self::prepare().
+   * @param array $parameters
+   *   (optional) Parameters to be substituted.
+   *   These are all converted to strings.
+   *
+   * @return c_base_return_query|c_base_return_status
+   *   Query resource is returned on success, FALSE otherwise.
+   *
+   * @see: self::query()
+   * @see: self::prepare()
+   * @see: pg_execute()
+   * @see: pg_send_execute()
+   * @see: pg_query()
+   * @see: pg_query_params()
+   * @see: pg_query_send()
+   * @see: pg_query_send_params()
+   * @see: pg_prepare()
+   * @see: pg_send_prepare()
+   */
+  public function do_execute($name, $parameters = array()) {
+    if (!is_string($name) || empty($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($parameters)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($this->asynchronous) {
+      $result = pg_send_execute($this->database, $name, $parameters);
+    }
+    else {
+      $result = pg_execute($this->database, $name, $parameters);
+    }
+
+    if (is_resource($result)) {
+      return c_base_database_result::s_new($result);
+    }
+    unset($result);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Process a single SQL query statement.
+   *
+   * When set to asynchronous, this effectively calls pg_send_query() or pg_query_send_params().
+   *
+   * @param string $query
+   *   The query statement to execute.
+   * @param array $parameters
+   *   (optional) Parameters to be substituted.
+   *   These are all converted to strings.
+   *
+   *   Any bytea field must not be used as a parameter.
+   *   Instead, use pg_escape_bytea() or a large object function.
+   *
+   * @return c_base_database_result|c_base_return_status
+   *   Query resource is returned on success, FALSE otherwise.
+   *
+   * @see: self::execute()
+   * @see: self::prepare()
+   * @see: pg_execute()
+   * @see: pg_send_execute()
+   * @see: pg_query()
+   * @see: pg_query_params()
+   * @see: pg_query_send()
+   * @see: pg_query_send_params()
+   * @see: pg_prepare()
+   * @see: pg_send_prepare()
+   */
+  public function do_query($query, $parameters = array()) {
+    if (!is_string($query) || empty($query)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($parameters)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($this->asynchronous) {
+      if (empty($parameters)) {
+        $result = pg_send_query($this->database, $query);
+      }
+      else {
+        $result = pg_send_query_params($this->database, $query, $parameters);
+      }
+    }
+    else {
+      if (empty($parameters)) {
+        $result = pg_query($this->database, $query);
+      }
+      else {
+        $result = pg_query_params($this->database, $query, $parameters);
+      }
+    }
+
+    if (is_resource($result)) {
+      return c_base_database_result::s_new($result);
+    }
+    unset($result);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Prepares an SQL statement for multiple uses.
+   *
+   * When set to asynchronous, this effectively calls pg_send_prepare().
+   *
+   * @param string $name
+   *   A unique name for the prepared statement so that it can be executed via self::execute().
+   * @param string $query
+   *   The query statement to execute.
+   *
+   * @return c_base_database_result|c_base_return_status
+   *   Query resource is returned on success, FALSE otherwise.
+   *
+   * @see: self::execute()
+   * @see: self::query()
+   * @see: pg_execute()
+   * @see: pg_send_execute()
+   * @see: pg_prepare()
+   * @see: pg_send_prepare()
+   */
+  public function do_prepare($name) {
+    if (!is_string($name) || empty($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($query) || empty($query)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($parameters)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($this->asynchronous) {
+      $result = pg_send_prepare($this->database, $name, $query);
+    }
+    else {
+      $result = pg_prepare($this->database, $name, $query);
+    }
+
+    if (is_resource($result)) {
+      return c_base_database_result::s_new($result);
+    }
+    unset($result);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Get the result of an asynchronous query.
+   *
+   * This is only useful when a query is asynchronous.
+   *
+   * @return c_base_database_result|c_base_return_status
+   *   A database result is returned on success.
+   *   FALSE is returned on failure.
+   *   When asynchronous is not enabled, FALSE is returned without an error flag set.
+   */
+  public function get_result() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!$this->asynchronous) {
+      return new c_base_return_false();
+    }
+
+    $result = pg_get_result($this->database);
+    if (is_resource($result)) {
+      return c_base_database_result::s_new($result);
+    }
+    unset($result);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Cancel an asynchronous query.
+   *
+   * This is only useful when a query is asynchronous.
+   *
+   * It is unspecific as to whether this cancels the last query sent.
+   * This appears to be the case, so it is difficulty to cancel multiple queries or even know which query is or is not still running (or waiting).
+   * It is further difficult to cancel all queries as there is no way to know if FALSe means there was an error or if there are no longer any queries to cancel.
+   * With any luck, I will come across documentation that clarifies this behavior and discover functions to better handle this.
+   *
+   * The documentation does suggest that it prints out messages such as:
+   *   "First call to pg_get_result(): Resource id #3
+   *    Resource id #3 has 3 records"
+   * But this is rather useless as it is not a return value to process.
+   * Furthermore, it clobbers output, which may be a problem.
+   *
+   * Due to the immaturity or limitations of functions like this, it is recommended that asynchronous calls not be used for anything that needs to be reliably controlled.
+   *
+   * One possible approach is to implement ones own caching mechanisms.
+   * Implement an array "stack" that stores all queries planned to be executed.
+   * Then use pg_connection_busy() to check if the execution is available.
+   * And pop an item when not busy and send the query.
+   * This design, however, means that only a single a synchronous query operation may be performed at any given time.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned on success.
+   *   FALSE is returned on failure.
+   *   When asynchronous is not enabled, FALSE is returned without an error flag set.
+   *
+   * @see: pg_cancel_query()
+   */
+  public function do_cancel() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!$this->asynchronous) {
+      return new c_base_return_false();
+    }
+
+    if (pg_cancel_query($this->database)) {
+      return new c_base_return_true();
+    }
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Perform an SQL INSERT query.
+   *
+   * The asynchronous behavior is inconsistent with other functions.
+   * Instead of having an insert_send() function, it uses an option called PGSQL_DML_ASYNC.
+   * But that then triggers the call to pg_convert() so suddenly a new option PGSQL_DML_NO_CONV must be used to ensure this does not unintentionally happen.
+   *
+   * This is better documentation from PHP's pg_last_oid() on insert:
+   *   To get the value of a SERIAL field in an inserted row, it is necessary to use the PostgreSQL CURRVAL function, naming the sequence whose last value is required.
+   *   If the name of the sequence is unknown, the pg_get_serial_sequence PostgreSQL 8.0 function is necessary.
+   *   PostgreSQL 8.1 has a function LASTVAL that returns the value of the most recently used sequence in the session.
+   *   This avoids the need for naming the sequence, table or column altogether.
+   *
+   * @param string $table
+   *   The name of the table to insert into.
+   * @param array $values
+   *   An associative array of values to insert.
+   * @param null|int $options
+   *   (optional) If not NULL, pg_convert() is called against $values with these options passed to it.
+   *   The exception to this is when any of the following are passed:
+   *   - PGSQL_DML_NO_CONV
+   *   - PGSQL_DML_ESCAPE
+   *
+   *   All options are:
+   *   - PGSQL_CONV_OPTS
+   *   - PGSQL_DML_NO_CONV
+   *   - PGSQL_DML_ESCAPE
+   *   - PGSQL_DML_EXEC
+   *   - PGSQL_DML_ASYNC (auto-added or auto-removed when asynchronous is enabled or disabled)
+   *   - PGSQL_DML_STRING
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   TRUE on success, FALSE on failure.
+   *   If PGSQL_DML_STRING is set, a string is returned on success.
+   *
+   *   Its unclear as to what the returned string is, but it can be assumed to be a value, such as an serialized number that was incremented by this operation.
+   *
+   * @see: pg_insert()
+   * @see: pg_convert()
+   * @see: pg_last_oid()
+   */
+  public function do_insert($table, $values, $options = NULL) {
+    if (!is_string($table) || empty($table)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($values)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($values) || !is_int($options)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->p_handle_asynchronous_options_parameter($options);
+
+    if (is_null($options)) {
+      $result = pg_insert($this->database, $table, $values);
+    }
+    else {
+      $result = pg_insert($this->database, $table, $values, $options);
+    }
+
+    if (!is_null($options) && $options & PGSQL_DML_STRING) {
+      if (is_string($result)) {
+        return c_base_return_string::s_new($result);
+      }
+    }
+    elseif ($result === TRUE) {
+      unset($result);
+      return new c_base_return_true();
+    }
+    unset($result);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Perform an SQL UPDATE.
+   *
+   * The asynchronous behavior is inconsistent with other functions.
+   * Instead of having an update_send() function, it uses an option called PGSQL_DML_ASYNC.
+   * But that then triggers the call to pg_convert() so suddenly a new option PGSQL_DML_NO_CONV must be used to ensure this does not unintentionally happen.
+   *
+   * @param string $table
+   *   The name of the table to insert into.
+   * @param array $values
+   *   An associative array of values to insert.
+   * @param array $conditions
+   *   An associative array of conditions that each row must meet to be updated.
+   * @param null|int $options
+   *   (optional) If not NULL, pg_convert() is called against $values with these options passed to it.
+   *   The exception to this is when any of the following are passed:
+   *   - PGSQL_DML_NO_CONV
+   *   - PGSQL_DML_ESCAPE
+   *
+   *   All options are:
+   *   - PGSQL_CONV_FORCE_NULL
+   *   - PGSQL_CONV_OPTS
+   *   - PGSQL_DML_NO_CONV
+   *   - PGSQL_DML_ESCAPE
+   *   - PGSQL_DML_EXEC
+   *   - PGSQL_DML_ASYNC (auto-added or auto-removed when asynchronous is enabled or disabled)
+   *   - PGSQL_DML_STRING
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   TRUE on success, FALSE on failure.
+   *   If PGSQL_DML_STRING is set, a string is returned on success.
+   *
+   *   Its unclear as to what the returned string is, but it can be assumed to be a value, such as an serialized number that was incremented by this operation.
+   *
+   * @see: pg_update()
+   * @see: pg_convert()
+   */
+  function do_update($table, $values, $conditions, $options = NULL) {
+    if (!is_string($table) || empty($table)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($values)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($conditions)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($values) || !is_int($options)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->p_handle_asynchronous_options_parameter($options);
+
+    if (is_null($options)) {
+      $result = pg_update($this->database, $table, $values, $conditions);
+    }
+    else {
+      $result = pg_update($this->database, $table, $values, $conditions, $options);
+    }
+
+    if (!is_null($options) && $options & PGSQL_DML_STRING) {
+      if (is_string($result)) {
+        return c_base_return_string::s_new($result);
+      }
+    }
+    elseif ($result === TRUE) {
+      unset($result);
+      return new c_base_return_true();
+    }
+    unset($result);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Perform an SQL SELECT.
+   *
+   * The asynchronous behavior is inconsistent with other functions.
+   * Instead of having a select_send() function, it uses an option called PGSQL_DML_ASYNC.
+   * But that then triggers the call to pg_convert() so suddenly a new option PGSQL_DML_NO_CONV must be used to ensure this does not unintentionally happen.
+   *
+   * @param string $table
+   *   The name of the table to insert into.
+   * @param array $conditions
+   *   An associative array of conditions that each row must meet to be updated.
+   * @param null|int $options
+   *   (optional) If not NULL, pg_convert() is called against $values with these options passed to it.
+   *   The exception to this is when any of the following are passed:
+   *   - PGSQL_DML_NO_CONV
+   *   - PGSQL_DML_ESCAPE
+   *
+   *   All options are:
+   *   - PGSQL_CONV_FORCE_NULL
+   *   - PGSQL_CONV_OPTS
+   *   - PGSQL_DML_NO_CONV
+   *   - PGSQL_DML_ESCAPE
+   *   - PGSQL_DML_EXEC
+   *   - PGSQL_DML_ASYNC (auto-added or auto-removed when asynchronous is enabled or disabled)
+   *   - PGSQL_DML_STRING
+   *
+   * @return c_base_return_status|c_base_return_string|c_base_return_array
+   *   TRUE or an array on success, FALSE on failure.
+   *   If PGSQL_DML_STRING is set, a string is returned on success.
+   *
+   *   Its unclear as to what the returned string is, but it can be assumed to be a value, such as an serialized number that was incremented by this operation.
+   *
+   *   The PHP documentation states that the return value is TRUE on success but it also states that it returns an array containing all selected records on success.
+   *   This is confusing and a return value of an array makes the most sense.
+   *
+   * @see: pg_select()
+   * @see: http://us.php.net/manual/en/function.pg-select.php
+   */
+  function do_select($table, $conditions, $options = NULL) {
+    if (!is_string($table) || empty($table)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($values)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($conditions)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->p_handle_asynchronous_options_parameter($options);
+
+    if (is_null($options)) {
+      $result = pg_select($this->database, $table, $conditions);
+    }
+    else {
+      $result = pg_select($this->database, $table, $conditions, $options);
+    }
+
+    if (!is_null($options) && $options & PGSQL_DML_STRING) {
+      if (is_string($result)) {
+        return c_base_return_string::s_new($result);
+      }
+    }
+    elseif (is_array($result)) {
+      return c_base_return_array::s_new($result);
+    }
+    elseif ($result === TRUE) {
+      unset($result);
+      return new c_base_return_true();
+    }
+    unset($result);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Perform an SQL DELETE.
+   *
+   * The asynchronous behavior is inconsistent with other functions.
+   * Instead of having a delete_send() function, it uses an option called PGSQL_DML_ASYNC.
+   * But that then triggers the call to pg_convert() so suddenly a new option PGSQL_DML_NO_CONV must be used to ensure this does not unintentionally happen.
+   *
+   * @param string $table
+   *   The name of the table to insert into.
+   * @param array $conditions
+   *   An associative array of conditions that each row must meet to be updated.
+   * @param null|int $options
+   *   (optional) If not NULL, pg_convert() is called against $values with these options passed to it.
+   *   The exception to this is when any of the following are passed:
+   *   - PGSQL_DML_NO_CONV
+   *   - PGSQL_DML_ESCAPE
+   *
+   *   All options are:
+   *   - PGSQL_CONV_FORCE_NULL
+   *   - PGSQL_CONV_OPTS
+   *   - PGSQL_DML_NO_CONV
+   *   - PGSQL_DML_ESCAPE
+   *   - PGSQL_DML_EXEC
+   *   - PGSQL_DML_ASYNC (auto-added or auto-removed when asynchronous is enabled or disabled)
+   *   - PGSQL_DML_STRING
+   *
+   * @return c_base_return_status|c_base_return_string|c_base_return_array
+   *   TRUE on success, FALSE on failure.
+   *   If PGSQL_DML_STRING is set, a string is returned on success.
+   *
+   *   Its unclear as to what the returned string is, but it can be assumed to be a value, such as an serialized number that was incremented by this operation.
+   *
+   * @see: pg_delete()
+   */
+  function do_delete() {
+    if (!is_string($table) || empty($table)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($values)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($conditions)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->p_handle_asynchronous_options_parameter($options);
+
+    if (is_null($options)) {
+      $result = pg_select($this->database, $table, $conditions);
+    }
+    else {
+      $result = pg_select($this->database, $table, $conditions, $options);
+    }
+
+    if (!is_null($options) && $options & PGSQL_DML_STRING) {
+      if (is_string($result)) {
+        return c_base_return_string::s_new($result);
+      }
+    }
+    elseif ($result === TRUE) {
+      unset($result);
+      return new c_base_return_true();
+    }
+    unset($result);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Request for additional information provided by a table.
+   *
+   * This may includes things like comments and similar database-style documentation.
+   *
+   * @param string $table
+   *   The name of the table.
+   * @param bool $extended
+   *   TRUE for extended additional information, FALSE for normal additional information.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array containing the additional information or FALSE on error.
+   *
+   * @see: pg_meta_data()
+   */
+  public function get_meta_data($table, $extended = FALSE) {
+    if (!is_string($table) || empty($table)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($extended)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_meta_data($this->database, $table, $extended);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($result);
+  }
+
+  /**
+   * Sets the verbosity of reported errors.
+   *
+   * @param int $verbosity
+   *   One of the following:
+   *   - PGSQL_ERRORS_TERSE
+   *   - PGSQL_ERRORS_DEFAULT
+   *   - PGSQL_ERRORS_VERBOSE
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The previous verbosity level on success, FALSE otherwise.
+   *
+   * @see: pg_set_error_verbosity()
+   */
+  public function set_error_verbosity($verbosity) {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($verbosity)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new(pg_set_error_verbosity($this->database, $verbosity));
+  }
+
+  /**
+   * Returns the last error message string.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   Message string on success, FALSE otherwise.
+   *
+   * @see: pg_last_error()
+   */
+  public function get_last_error() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_last_error($this->database);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($result);
+  }
+
+  /**
+   * Returns the last notice message string.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   Message string on success, FALSE otherwise.
+   *
+   * @see: pg_last_notice()
+   */
+  public function get_last_notice() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_last_notice($this->database);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($result);
+  }
+
+  /**
+   * Returns the state of a transaction in the database.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   Transaction state success, FALSE otherwise.
+   *   The returned transaction state integer is expected to be one of the following:
+   *   - PGSQL_TRANSACTION_IDLE: currently idle.
+   *   - PGSQL_TRANSACTION_ACTIVE: command is currently executing.
+   *   - PGSQL_TRANSACTION_INTRANS: idle, in a valid transaction block.
+   *   - PGSQL_TRANSACTION_INERROR: idle, in a failed transaction block.
+   *   - PGSQL_TRANSACTION_UNKNOWN: invalid connection.
+   *   - PGSQL_TRANSACTION_ACTIVE: query sent to server, but not yet completed.
+   *
+   * @see: pg_transaction_status()
+   */
+  public function get_transaction_status() {
+    if (!is_resource($this->database)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_transaction_status($this->database);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($result);
+  }
+
+  /**
+   * Make sure the options parameter has PGSQL_DML_ASYNC set appropriately.
+   *
+   * @param int|null $options
+   *   The options number to add or remove PGSQL_DML_ASYNC based on asynchronous status.
+   */
+  private function p_handle_asynchronous_options_parameter(&$options) {
+    if ($this->asynchronous) {
+      if (is_null($options)) {
+        $options = PGSQL_DML_ASYNC | PGSQL_DML_NO_CONV;
+      }
+      else {
+        if ($options & PGSQL_DML_ASYNC == 0) {
+          $options += PGSQL_DML_ASYNC;
+        }
+      }
+    }
+    else {
+      if (!is_null($options)) {
+        if ($options & PGSQL_DML_ASYNC > 0) {
+          $options -= PGSQL_DML_ASYNC;
+        }
+      }
+    }
+  }
+}
+
+/**
+ * A generic class for managing database query results.
+ *
+ * It is important to note that the fetch_*() functions may return NULL as the database result.
+ * This NULL is not stored as NULL, but is instead stored as c_base_return_value with the value set to NULL.
+ * Therefore, one should use caution when using a c_base_return_value::get_value_exact() call as NULL values will not be returned.
+ * It is recommended that only the c_base_return_value::get_value() call be used when accessing the result value.
+ */
+class c_base_database_result extends c_base_return_resource {
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    if (is_resource($this->value)) {
+      pg_free_result($this->value);
+    }
+
+    parent::__destruct();
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+
+  /**
+   * Fetch all columns in result set.
+   *
+   * This is not fetching all columns in the result set as in all column names.
+   * Instead it is fetch all values from every row that belongs to a single column.
+   *
+   * This would be more aptly named something like fetch_all_column_rows($column).
+   * But even that is not the best of names.
+   *
+   * To determine a columns number by name, use pg_field_num().
+   *
+   * @param int $column
+   *   The column number to fetch.
+   *
+   * @return c_base_return_status|c_base_return_value
+   *   The value on success, FALSE otherwise.
+   *
+   * @see: self::field_number()
+   * @see: pg_fetch_all_columns()
+   * @see: pg_field_num()
+   */
+  public function fetch_all_columns($column) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+  }
+
+  /**
+   * Fetch all rows from result set as an array.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   The value on success, FALSE otherwise.
+   *
+   * @see: pg_fetch_all()
+   */
+  public function fetch_all() {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_fetch_all($this->value);
+    if ($result === FALSE) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($result);
+  }
+
+  /**
+   * Fetch array from result set.
+   *
+   * @param null|int $row
+   *   (optional) The number of the row to fetch.
+   *   If NULL, the next row is fetched.
+   * @param int $type
+   *   (optional) Specify the type of array returned:
+   *   - PGSQL_ASSOC = return an associative array (just like when calling pg_fetch_assoc()).
+   *   - PGSQL_NUM = return an array with numerical indeces.
+   *   - PGSQL_BOTH = return with both associated indeces and numeric indeces.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   The value on success, FALSE otherwise.
+   *
+   * @see: pg_fetch_array()
+   */
+  public function fetch_array($row = NULL, $type = PGSQL_ASSOC) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($row) && (!is_int($row) || $row < 0)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_fetch_array($this->value, $row, $column);
+    if ($result === FALSE) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($result);
+  }
+
+  /**
+   * Fetch object from result set.
+   *
+   * @param null|int $row
+   *   (optional) The number of the row to fetch.
+   *   If NULL, the next row is fetched.
+   * @param null|string $class
+   *   When not NULL, then is a string represnting the name of a type of class.
+   *   The return object will be returned as this type of object.
+   * @param null|array $parameters
+   *   When $class is not NULL and this is not NULL, then this contains parameters to pass to the object of type $class during initialization.
+   *
+   * @return c_base_return_status|c_base_return_object
+   *   The value on success, FALSE otherwise.
+   *   Even if a custom class is specified, a valid object is always returned as c_base_return_object.
+   *   This is done for simplicity purposes.
+   *   For types that inherit c_base_return_object, it should be simple to cast this return result to that child class.
+   *
+   * @see: pg_fetch_object()
+   */
+  public function fetch_object($row = NULL, $class = NULL, $parameters = NULL) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($row) && (!is_int($row) || $row < 0)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($class) && !is_string($class)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($parameters) && !is_array($parameters)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_string($class) && !empty($class)) {
+      $result = pg_fetch_object($this->value, $row, $class, $parameters);
+    }
+    else {
+      $result = pg_fetch_object($this->value, $row);
+    }
+
+    if ($result === FALSE) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_object::s_new($result);
+  }
+
+  /**
+   * Fetch a result from result set.
+   *
+   * @param null|int $row
+   *   The number of the row to fetch.
+   *   If NULL, the next row is fetched.
+   * @param string|int $column
+   *   A string representing the column name or an integer representing the column number.
+   *
+   * @return c_base_return_status|c_base_return_value
+   *   The value on success, FALSE otherwise.
+   *
+   * @see: pg_fetch_result()
+   */
+  public function fetch_result($row, $column) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($row) && !is_int($row)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($row) && !is_string($row) || is_int($row) && $row < 0 || is_string($row) && empty($row)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($row)) {
+      $result = pg_fetch_result($this->value, $row, $column);
+    }
+    else {
+      $result = pg_fetch_result($this->value, $column);
+    }
+
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_value::s_new($result);
+  }
+
+  /**
+   * Fetch row from result set.
+   *
+   * @param null|int $row
+   *   (optional) The number of the row to fetch.
+   *   If NULL, the next row is fetched.
+   *
+   * @return c_base_return_status|c_base_return_value
+   *   The value on success, FALSE otherwise.
+   *
+   * @see: pg_fetch_row()
+   */
+  public function fetch_row($row = NULL) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($row) && (!is_int($row) || $row < 0)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_fetch_row($this->value, $row);
+    if ($result === FALSE) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_value::s_new($result);
+  }
+
+  /**
+   * Returns an individual field code of an error port.
+   *
+   * @param null|int $code
+   *   When not NULL this is a field code, such as:
+   *   - PGSQL_DIAG_SEVERITY
+   *   - PGSQL_DIAG_SQLSTATE
+   *   - PGSQL_DIAG_MESSAGE_PRIMARY
+   *   - PGSQL_DIAG_MESSAGE_DETAIL
+   *   - PGSQL_DIAG_MESSAGE_HINT
+   *   - PGSQL_DIAG_STATEMENT_POSITION
+   *   - PGSQL_DIAG_INTERNAL_POSITION
+   *   - PGSQL_DIAG_INTERNAL_QUERY
+   *   - PGSQL_DIAG_CONTEXT
+   *   - PGSQL_DIAG_SOURCE_FILE
+   *   - PGSQL_DIAG_SOURCE_LINE
+   *   - PGSQL_DIAG_SOURCE_FUNCTION
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   String is returned if found, a NULL is returned if not found, and FALSE is returned on error (with error flag set).
+   *
+   * @see: pg_result_error()
+   * @see: pg_result_error_field()
+   */
+  public function error($code = NULL) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($code) && !is_int($code)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_null($code)) {
+      $result = pg_result_error($this->value);
+    }
+    else {
+      $result = pg_result_error_field($this->value, $code);
+    }
+
+    if (is_null($result) || is_string($result) && mb_strlen($result) == 0) {
+      unset($result);
+      return new c_base_return_null();
+    }
+
+    return c_base_return_string::s_new($result);
+  }
+
+  /**
+   * Returns the number of rows affected by the SQL statement associated with this result set.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   The number of items (be it instances, records, or rows) that are affected by any INSERT, UPDATE, DELETE, or SELECT queries.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: pg_affected_rows()
+   */
+  public function affected_rows() {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new(pg_affected_rows($this->value));
+  }
+
+  /**
+   * Get the number of rows in the result set.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The number of rows or FALSE on failure.
+   *
+   * @see: pg_num_rows()
+   */
+  public function number_of_rows() {
+    if (!is_resource($this->value)) {
+      return new c_base_return_false();
+    }
+
+    $result = pg_num_rows($this->value);
+    if ($result < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($result);
+  }
+
+  /**
+   * Get the number of columns in the result set.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The number of rows or FALSE on failure.
+   *
+   * @see: pg_num_fields()
+   */
+  public function number_of_columns() {
+    if (!is_resource($this->value)) {
+      return new c_base_return_false();
+    }
+
+    $result = pg_num_fields($this->value);
+    if ($result < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($result);
+  }
+
+  /**
+   * Returns the name of a column at the given location in the result set.
+   *
+   * @param int $number
+   *   The column number of the field to get the name of.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   The name of the field or FALSE on error.
+   *
+   * @see: pg_field_name()
+   */
+  public function field_name($number) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($number) || $number < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_field_name($this->value, $number);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($result);
+  }
+
+  /**
+   * Returns the number of a column with the given name in the result set.
+   *
+   * @param string $name
+   *   The column name of the field to get the number of.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The number of the field or FALSE on error.
+   *
+   * @see: pg_field_num()
+   */
+  public function field_number($name) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($name) || mb_strlen($name) == 0) {
+      return c_base_return_error::s_false();
+    }
+
+    // this returnes -1 on error and >= 0 on success, so translate the codes appropriately.
+    $result = pg_field_number($this->value, $name);
+    if ($result < 0) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($result);
+  }
+
+  /**
+   * Returns the 'printed length' of a field in the result set.
+   *
+   * This is the number of characters used to represent a result value.
+   *
+   * @param int|null $row
+   *   The row number to fetch.
+   * @param string|int $name_or_number
+   *   The column name or column number of the field to get the printed length of.
+   *   When passed as an integer, this is the column number.
+   *   When passed as a string, this is a column name.
+   *   Therefore a value of '0' would be a column named 0 and a value of 0 would be column number 0.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The printed length of the 'field' or FALSE on error.
+   *
+   * @see: self::field_bytes()
+   * @see: pg_field_prtlen()
+   */
+  public function field_length($row, $name_or_number) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($row) && (!is_int($row) || $row < 0)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($name_or_number) && !(is_string($name_or_number) && mb_strlen($name) > 0)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_null($row)) {
+      $result = pg_field_prtlen($this->value, $name_or_number);
+    }
+    else {
+      $result = pg_field_prtlen($this->value, $row, $name_or_number);
+    }
+
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($result);
+  }
+
+  /**
+   * Return the size of the column.
+   *
+   * @param int $column
+   *   The column number to return the size of.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The size of the column or FALSE on error.
+   *   The returned size may be -1, in which case means the size is variable.
+   *
+   * @see: self::field_length()
+   * @see: pg_field_size()
+   */
+  public function field_bytes($column) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($column) || $column < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_size($this->value, $column);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($result);
+  }
+
+  /**
+   * Return the name or oid of the tables field.
+   *
+   * The word usage here is confusing.
+   * Documentation states that this function 'returns the name or oid of the "Tables Field"'.
+   * Really, what this appears to do is return name or oid of the "Columns Table".
+   *
+   * @param int $column
+   *   The column number to return the table name for.
+   * @param bool $oid
+   *   (optional) When TRUE, the oid is retuned instead of the table name.
+   *
+   * @return c_base_return_status|c_base_return_int|c_base_return_string
+   *   The name of the table that the given column belongs to or FALSE on error.
+   *   If oid is set to TRUE, then the oid.
+   *
+   * @see: pg_field_table()
+   * @see: http://www.postgresql.org/docs/current/static/datatype-oid.html
+   */
+  public function field_table($column, $oid = FALSE) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($column) || $column < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($oid)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_field_table($this->value, $column, $oid);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    if ($oid) {
+      return c_base_return_int::s_new($result);
+    }
+
+    return c_base_return_string::s_new($result);
+  }
+
+  /**
+   * Return the oid of the column.
+   *
+   * @param int $column
+   *   The column number to return the oid of.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The oid of the requested column to or FALSE on error.
+   *
+   * @see: pg_field_type_oid()
+   * @see: http://www.postgresql.org/docs/current/static/datatype-oid.html
+   */
+  public function field_type_oid($column) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($column) || $column < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_field_type_oid($this->value, $column);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($result);
+  }
+
+  /**
+   * Returns the last row's oid.
+   *
+   * This is used to retrieve an OID assigned to an inserted row.
+   * Oid is now optional, so try using pg_result_status() instead.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   A string containing the oid assigned to the most recently inserted row on success, FALSE otherwise.
+   *
+   * @see: pg_last_oid()
+   * @see: pg_result_status()
+   * @see: http://www.postgresql.org/docs/current/static/datatype-oid.html
+   */
+  public function last_oid() {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_last_oid($this->database);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($result);
+  }
+
+  /**
+   * Return the storage type of the column.
+   *
+   * @param int $column
+   *   The column number to return the oid of.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The oid of the requested column to or FALSE on error.
+   *
+   * @see: pg_field_type()
+   */
+  public function field_type($column) {
+    if (!is_resource($this->value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($column) || $column < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = pg_field_type($this->value, $column);
+    if ($result === FALSE) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($result);
+  }
+
+  /**
+   * Free's memory allocated to the class.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned on success, FALSE is returned if nothing to free.
+   *   FALSE with the error flag set is returned on error.
+   *
+   * @see: pg_free_result()
+   */
+  public function free_result() {
+    if (!is_resource($this->value)) {
+      return new c_base_return_false();
+    }
+
+    if (pg_free_result($this->value)) {
+      return new c_base_return_true();
+    }
+
+    return c_base_return_error::s_false();
+  }
+}
+
+/**
+ * A generic class for building a query array.
+ *
+ * A query array is essentially an array whose keys define how the query is to be generated.
+ * Future work is planned, but this is essentially being provided a stub until then.
+ */
+class c_base_database_query extends c_base_return_array {
+  const OPERATION_NONE = 0;
+  const OPERATION_SELECT = 1;
+
+  const ALIAS_PREFIX_TABLE = 't_';
+  const ALIAS_PREFIX_COLUMN = 'c_';
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->p_initialize();
+
+    parent::__construct();
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    parent::__destruct();
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, array());
+  }
+
+  /**
+   * Import and process an exported array.
+   *
+   * This string will be validated and processed before being saved.
+   * Invalid data will be lost.
+   *
+   * @param string $import
+   *   A json encoded array reflecting the contents of this object.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function import($import) {
+    if (!is_string($import) || empty($import)) {
+      return c_base_return_error::s_false();
+    }
+
+    $decoded = json_decode($import, TRUE);
+    if (!is_array($decoded) || empty($decoded)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->p_initialize();
+
+    $this->value = $decoded;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Export the values of this object into a json string.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   FALSE is returned when there is no content to export.
+   *   Otherwise, a json encoded string is returned.
+   */
+  public function export() {
+    if ($this->count_table == 0 || empty($this->value)) {
+      return new c_base_return_false();
+    }
+
+    // only the value array needs to be exported.
+    // everything else has to be re-created on import for security reasons.
+    $encoded = json_encode($this->value);
+    if (!is_string($encoded)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($encoded);
+  }
+
+  /**
+   * Perform initialization of all variables in this class.
+   */
+  private function p_initialize() {
+    unset($this->value);
+    $this->value = array();
+  }
+}
diff --git a/common/base/classes/base_debug.php b/common/base/classes/base_debug.php
new file mode 100644 (file)
index 0000000..2815dc7
--- /dev/null
@@ -0,0 +1,240 @@
+<?php
+/**
+ * @file
+ * Provides a class for performing debugging.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+/**
+ * A generic class for performing debugging.
+ */
+class c_base_debug {
+  private static $ps_debugging = FALSE;
+
+  private $time_start;
+  private $time_stop;
+
+  private $memory_usage_start;
+  private $memory_usage_stop;
+  private $memory_usage_peak;
+  private $memory_allocated_start;
+  private $memory_allocated_stop;
+  private $memory_allocated_peak;
+
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->time_start = NULL;
+    $this->time_stop = NULL;
+
+    $this->memory_usage_start = NULL;
+    $this->memory_usage_stop = NULL;
+    $this->memory_usage_peak = NULL;
+    $this->memory_allocated_start = NULL;
+    $this->memory_allocated_stop = NULL;
+    $this->memory_allocated_peak = NULL;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->time_start);
+    unset($this->time_stop);
+
+    unset($this->memory_usage_start);
+    unset($this->memory_usage_stop);
+    unset($this->memory_usage_peak);
+    unset($this->memory_allocated_start);
+    unset($this->memory_allocated_stop);
+    unset($this->memory_allocated_peak);
+  }
+
+  /**
+   * Turn on/off debugging for every instance of this class.
+   *
+   * @param bool $debug
+   *   Set to TRUE to enable debugging, FALSE to disable.
+   *
+   * @param c_base_return_status
+   *   TRUE is returned on success, FALSE otherwise.
+   */
+  public static function s_set_debugging($debug) {
+    if (!is_bool($debug)) {
+      return c_base_return_error::s_false();
+    }
+
+    self::$ps_debugging = $debug;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get debugging enabled/disabled status.
+   *
+   * @return c_base_return_status
+   *   TRUE when debugging is enabled, FALSE otherwise.
+   */
+  public static function s_get_debugging() {
+    if (self::$ps_debugging) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Begin recording the time and memory consumption.
+   */
+  public function do_start_consumption_recording() {
+    if (!self::$ps_debugging) {
+      return;
+    }
+
+    $this->time_start = microtime(TRUE);
+    $this->memory_usage_start = memory_get_usage();
+    $this->memory_allocated_start = memory_get_usage(TRUE);
+  }
+
+  /**
+   * Recording begin time and memory consumption.
+   */
+  public function do_stop_consumption_recording() {
+    if (!self::$ps_debugging) {
+      return;
+    }
+
+    // don't do anything if the start consumption has yet to be called.
+    if (is_null($this->time_start)) {
+      return;
+    }
+
+    $this->time_stop = microtime(TRUE);
+    $this->memory_usage_stop = memory_get_usage();
+    $this->memory_allocated_stop = memory_get_usage(TRUE);
+
+    $this->memory_usage_peak = memory_get_peak_usage();
+    $this->memory_allocated_peak = memory_get_peak_usage(TRUE);
+  }
+
+  /**
+   * Record end time and memory consumption.
+   */
+  public function do_reset_consumption_recording() {
+    if (!self::$ps_debugging) {
+      return;
+    }
+
+    $this->time_start = NULL;
+    $this->time_stop = NULL;
+
+    $this->memory_usage_start = NULL;
+    $this->memory_usage_stop = NULL;
+    $this->memory_usage_peak = NULL;
+
+    $this->memory_allocated_start = NULL;
+    $this->memory_allocated_stop = NULL;
+    $this->memory_allocated_peak = NULL;
+  }
+
+  /**
+   * Get the amount of time consumed between the requested start/stop commands.
+   *
+   * @param bool $milliseconds
+   *   Return time in milliseconds when TRUE, otherwise return time in microseconds.
+   *
+   * @return c_return_status|c_return_int
+   *   An integer is returned representing the time difference or FALSE with error bit set is returned on error.
+   *   If debugging is disabled, then no error bit will be set on FALSE.
+   *
+   * @see: do_start_consumption_recording()
+   * @see: do_stop_consumption_recording()
+   * @see: do_reset_consumption_recording()
+   */
+  public function get_consumption_time($milliseconds = TRUE) {
+    if (!self::$ps_debugging) {
+      return new c_base_return_false();
+    }
+
+    if (!is_bool($milliseconds)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_null($this->time_start) || is_null($this->time_stop)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($milliseconds) {
+      return c_base_return_int::s_new(($this->time_stop - $this->time_start) * 1000);
+    }
+
+    return c_base_return_int::s_new($this->time_stop - $this->time_start);
+  }
+
+  /**
+   * Get the amount of memory used between the requested start/stop commands.
+   *
+   * @param int $option
+   *   When set to 1: return peak usage.
+   *   When set to 2: return stop usage.
+   *   When set to 3: return the difference between the start and stop usage (may be negative).
+   * @param bool $megabytes
+   *   Return the value in megabytes when TRUE, otherwise return the value in bytes.
+   *
+   * @return c_return_status|c_return_int
+   *   An integer is returned representing the time difference or FALSE with error bit set is returned on error.
+   *   If debugging is disabled, then no error bit will be set on FALSE.
+   *
+   * @see: do_start_consumption_recording()
+   * @see: do_stop_consumption_recording()
+   * @see: do_reset_consumption_recording()
+   */
+  public function get_consumption_memory_usage($option = 1, $megabytes = TRUE) {
+    if (!self::$ps_debugging) {
+      return new c_base_return_false();
+    }
+
+    if (!is_int($option) || $option < 1 || $option > 3 || !is_bool($megabytes)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($option == 1) {
+      if (is_null($this->memory_usage_peak)) {
+        return c_base_return_error::s_false();
+      }
+
+      if ($megabytes) {
+        return c_base_return_int::s_new($this->memory_usage_peak / 1024 / 1024);
+      }
+
+      return c_base_return_int::s_new($this->memory_usage_peak);
+    }
+    elseif ($option == 2) {
+      if (is_null($this->time_stop)) {
+        return c_base_return_error::s_false();
+      }
+
+      if ($megabytes) {
+        return c_base_return_int::s_new($this->memory_usage_stop / 1024 / 1024);
+      }
+
+      return c_base_return_int::s_new($this->memory_usage_stop);
+    }
+    else {
+      if (is_null($this->time_start) || is_null($this->time_stop)) {
+        return c_base_return_error::s_false();
+      }
+
+      if ($megabytes) {
+        return c_base_return_int::s_new(($this->memory_usage_stop - $this->memory_usage_start) / 1024 / 1024);
+      }
+
+      return c_base_return_int::s_new($this->memory_usage_stop - $this->memory_usage_start);
+    }
+  }
+}
diff --git a/common/base/classes/base_email.php b/common/base/classes/base_email.php
new file mode 100644 (file)
index 0000000..7cd98a6
--- /dev/null
@@ -0,0 +1,608 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing e-mail related functionality.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+require_once('common/base/classes/base_ascii.php');
+require_once('common/base/classes/base_utf8.php');
+require_once('common/base/classes/base_rfc_string.php');
+
+/**
+ * A generic class for managing the e-mail related functionality.
+ *
+ * PHP fails to follow the more recent rfc standards at this time.
+ * A custom implementation is provided to handle and process the rfc standards:
+ * - rfc 5322
+ * - rfc 6854
+ * - rfc 7231
+ *
+ * @see: https://tools.ietf.org/html/rfc5322#section-3.4
+ * @see: https://tools.ietf.org/html/rfc6854
+ * @see: https://tools.ietf.org/html/rfc7231#section-5.5.1
+ *
+ * @require class c_base_rfc_string
+ * @require class c_base_ascii
+ * @require class c_base_utf8
+ */
+class c_base_email extends c_base_rfc_string {
+  const LINE_LENGTH_LIMIT_SOFT = 78;
+  const LINE_LENGTH_LIMIT_HARD = 998;
+
+
+  /**
+   * Decode and check that the given e-mail address is valid.
+   *
+   * Validation is done according to rfc5322, rfc6854, and rfc7231.
+   *
+   * E-mails will be processed in the following approximate manners:
+   *   [[name_group]: ["[name_human]"] <[name_machine]@[name_address]>, [name_machine]@[name_address]];
+   *
+   * Comments can get out of hand but is still technical part of the standard, so the following must also be supported:
+   *  [[name_group]: ([comment])["[name_human]"]([comment]) <([comment])[name_machine]([comment])@([comment])[name_address]([comment])>, ([comment])[name_machine]([comment])@([comment])[name_address]([comment])];
+   *
+   * This is incredibly prone to abuse, mis-use, and exploitation.
+   * To prevent abuse, comments will be stripped out of the e-mail.
+   *
+   * @param string $email
+   *   The email to validate and decode.
+   * @param null|bool $custom_processing
+   *   @fixme: on second thought, it might be cleaner to implement these "custom_processing" cases in separate functions.
+   *   (optional) If NULL, then support entire email possibilities.
+   *   If FALSE, then only process a single (ungrouped) email with entire remaining possibilities.
+   *   IF TRUE, then support only the machine name portions of a single e-mail (the "id_left @ id_right" part of what is called "message id").
+   *
+   * @return array
+   *   A decoded e-mail split into its different parts inside an array.
+   *   An array key called 'invalid' exists to designate that the uri is invalid.
+   *
+   * @see: base_rfc_string::pr_rfc_string_prepare()
+   * @see: https://tools.ietf.org/html/rfc5322#section-3.4
+   * @see: https://tools.ietf.org/html/rfc6854
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.5.1
+   */
+  private static function p_parse_email_full($email) {
+    $result = array(
+      'emails' => array(),
+      'invalid' => FALSE,
+    );
+
+    $processed = array();
+    $group = NULL;
+    $group_id = NULL;
+    $process_machine_part = FALSE;
+    $current = 0;
+    $current_string = NULL;
+    $current_chunk_start = 0;
+    $delimited = FALSE;
+    $name_human = NULL;
+    $name_machine = NULL;
+    $name_group = NULL;
+
+    $email_text = $this->pr_rfc_string_prepare($email);
+    if ($email_text['invalid']) {
+      unset($email_text);
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    $stop = count($email_text['codepoints']) + 1;
+
+    $is_quoted = FALSE;
+    $is_named_human = FALSE;
+    $is_named_machine = FALSE;
+    $has_found_comment = FALSE;
+    for (; $current < $stop; $current++) {
+      if ($email_text['codepoints'][$current] == c_base_ascii::LESS_THAN) {
+        if (!$is_quoted && !$is_named_human && !is_null($current_string)) {
+          $name_human = $current_string;
+          $is_named_human = TRUE;
+        }
+
+        $current_string = NULL;
+        $has_found_comment = FALSE;
+
+        $parsed = p_parse_email_machine($email_text['codepoints'], $email_text['characters'], $current, $stop, c_base_ascii::GREATER_THAN);
+        $current = $parse_result['current'];
+
+        // if the proper stop point was reached, then the '<' is a valid opening.
+        if ($parsed['stopped_at'] && !$parsed['invalid'] && !empty($parsed['name_machine'])) {
+          $name_machine = $parsed['name_machine'] . '@' . $parsed['name_address'];
+          $result['emails'][$name_machine] = array(
+            'name_human' => $name_human,
+            'name_machine' => $name_machine,
+            'name_address' => $parsed['name_address'],
+          );
+
+          $current_chunk_start = $current + 1;
+          $is_named_machine = TRUE;
+
+          unset($parsed);
+          continue;
+        }
+
+        $result['invalid'] = TRUE;
+        unset($parsed);
+        break;
+      }
+      elseif ($email_text['codepoints'][$current] == c_base_ascii::QUOTE_DOUBLE) {
+        if ($is_quoted || $is_named_human || $is_named_machine) {
+          // cannot have more than one quoted or unquoted string and human names cannot follow machine names.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $current_string = NULL;
+        $has_found_comment = FALSE;
+
+        $is_quoted = TRUE;
+        $closing_quote = FALSE;
+        for (; $current < $stop; $current++) {
+
+          if ($email_text['codepoints'][$current] == c_base_ascii::SLASH_BACKWARD) {
+            if ($current + 1 >= $stop) {
+              $result['invalid'] = TRUE;
+              break;
+            }
+
+            // only a double quote may be delimited, otherwise the backwards slash is invalid.
+            if ($email_text['codepoints'][$current + 1] == c_base_ascii::QUOTE_DOUBLE) {
+              $current++;
+            }
+            else {
+              $result['invalid'] = TRUE;
+              break;
+            }
+          }
+          elseif ($email_text['codepoints'][$current] == c_base_ascii::QUOTE_DOUBLE) {
+            $closing_quote = TRUE;
+            break;
+          }
+          elseif ($this->pr_rfc_char_is_crlf($email_text['codepoints'][$current])) {
+            // not allowed inside a quoted string.
+            $result['invalid'] = TRUE;
+            break;
+          }
+
+          $name_human .= $email[$current];
+        }
+
+        if (!$closing_quote) {
+          $result['invalid'] = TRUE;
+          unset($closing_quote);
+          break;
+        }
+        unset($closing_quote);
+
+        $current_chunk_start = $current + 1;
+        $is_named_human = TRUE;
+
+        continue;
+      }
+      elseif ($this->pr_rfc_char_is_fws($email_text['codepoints'][$current])) {
+        // though unusual, starting with whitespace appears to be technically allowed unless I am misunderstanding or overlooking something.
+        if (!$is_named_human && !$is_named_machine && is_null($current_string)) {
+          continue;
+        }
+      }
+      elseif ($email_text['codepoints'][$current] == c_base_ascii::PARENTHESIS_OPEN) {
+        if ($has_found_comment) {
+          // there may be only one comment between non-comments.
+          $result['invalid'] = TRUE;
+          unset($parsed);
+          break;
+        }
+
+        // start with a comment and comments may have comments within themselves.
+        // though bizarre, this appears to be technically allowed unless I am misunderstanding or overlooking something.
+        // this includes delimiters.
+        $parsed = $this->pr_rfc_string_is_comment($email_text['codepoints'], $email_text['characters'], $current, $stop);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          unset($parsed);
+          break;
+        }
+
+        $has_found_comment = TRUE;
+        $current_chunk_start = $current + 1;
+
+        unset($parsed);
+        continue;
+      }
+      elseif ($email_text['codepoints'][$current] == c_base_ascii::COLON) {
+        if ($is_named_human || $is_named_machine) {
+          // A colon may not be specified following human or machine names.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        if (is_null($current_string)) {
+          // A colon without any preceding valid text is considered an invalid group (groups must have group names).
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        // @todo: implement this via another loop or function due to the complexity.
+        //        do not nullify $current_string because it is needed to define/provide the name of the group.
+        // @todo: if the colon is supplied, but double quotes were used, then does this mean it is a double quoted group name? (in which case the test against $is_named_human is invalid.)
+      }
+      elseif ($email_text['codepoints'][$current] == c_base_ascii::AT) {
+        if ($is_named_human || $is_named_machine) {
+          // when a human name is supplied, then the e-mail address must be inside of '<' and '>'.
+          // multiple machine names may not be specified.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        if ($current == $current_chunk_start) {
+          // if the machine name starts with an '@', then it is invalid.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $current_string = NULL;
+        $has_found_comment = FALSE;
+
+        // the machine name is processed using the current chunk start instead of the current value because the '@' is not the first character in the machine name.
+        $parsed = p_parse_email_machine($email_text['codepoints'], $email_text['characters'], $current_chunk_start, $stop);
+        $current = $parse_result['current'];
+
+        // if the proper stop point was reached, then a valid machine name was found.
+        if ($parsed['stopped_at'] && !$parsed['invalid'] && !empty($parsed['name_machine'])) {
+          $name_machine = $parsed['name_machine'] . '@' . $parsed['name_address'];
+          $result['emails'][$name_machine] = array(
+            'name_human' => $name_human,
+            'name_machine' => $name_machine,
+            'name_address' => $parsed['name_address'],
+          );
+
+          $current_chunk_start = $current + 1;
+
+          unset($parsed);
+          continue;
+        }
+
+        $result['invalid'] = TRUE;
+        unset($parsed);
+        break;
+      }
+
+      $current_string .= $email_text['characters'][$current];
+    }
+    unset($name_group);
+    unset($name_machine);
+    unset($name_human);
+    unset($delimited);
+    unset($is_quoted);
+    unset($is_named_human);
+    unset($is_named_machine);
+    unset($has_found_comment);
+    unset($process_machine_part);
+    unset($current);
+    unset($current_string);
+    unset($current_chunk_start);
+    unset($group_id);
+    unset($group);
+    unset($processed);
+    unset($stop);
+    unset($email_text);
+
+    return $result;
+  }
+
+  private static function p_parse_email_single($email, $start = NULL, $stop = NULL) {
+    // @todo: implement this.
+  }
+
+  /**
+   * Process only the machine part of a single e-mail address.
+   *
+   * This parses an e-mail string of the form: [name_machine]@[name_address].
+   *
+   * The standard allows for (comment)[name_machine](comment)@(comment)[name_address](comment).
+   * - These comments will be ignored and stripped out.
+   *
+   * This does not validate [name_address] to be a correct host name or ip address.
+   * @todo: consider doing this filter_var() and options like: FILTER_VALIDATE_IP (I must review whether or not FILTER_VALIDATE_IP is standards compliant).
+   *
+   * The arguments $start, $stop, and $end_at_brace allow for the other more complex email parsers to call this function when necessary.
+   *
+   * @param array $email_codes
+   *   An array containing codepoint values for an e-mail address string.
+   * @param array $email_characters
+   *   An array containing characters for the e-mail address string.
+   * @param null|int $start
+   *   (optional) Specify a starting point in which to begin processing.
+   * @param null|int $stop
+   *   (optional) Specify a stopping point in which to end processing.
+   *
+   * @param bool|int $stop_at
+   *   (optional) When FALSE, this function returns only when the end of string or an invalid character is found (including brace).
+   *   Invalid characters result in an error.
+   *   When not FALSE, this is an integer representing a single character to halt processing on.
+   *
+   * @return array
+   *   An array containing the processed pieces.
+   *   The array key 'invalid' is set TRUE on error.
+   *   The array key 'current' is set to location in the string of where processing stopped.
+   *   - For example, if execution was stopped on an unexpected brace, the location would be the position of that specific brace.
+   */
+  private static function p_parse_email_machine($email_codes, $email_characters, $start = NULL, $stop = NULL, $stop_at = FALSE) {
+    $result = array(
+      'name_machine' => '',
+      'name_address' => '',
+      'invalid' => FALSE,
+      'current' => 0,
+      'stopped_at' => FALSE,
+    );
+
+    if (!is_null($start)) {
+      $result['current'] = $start;
+    }
+
+    if (is_null($stop)) {
+      $stop = count($email_codes) + 1;
+    }
+
+    // first check for and ignore comments at the start.
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $email_codes[$result['current']];
+
+      if ($code === $stop_at) {
+        // stopping at this point means that the address is invalid.
+        $result['invalid'] = TRUE;
+        $result['stopped_at'] = TRUE;
+        break;
+      }
+
+      if ($email[$current] == '(') {
+        $parsed = $this->pr_rfc_string_is_comment($email_codes, $email_characters, $result['current'], $stop);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          unset($parsed);
+          break;
+        }
+        unset($parsed);
+
+        // there may be multiple comments, so do not break at this point.
+      }
+      elseif (!$this->pr_rfc_char_is_fws($code)) {
+        // the first non-comment, non-fws char should be the start of the [machine_name].
+        break;
+      }
+    }
+    unset($code);
+
+    if ($result['current'] >= $stop) {
+      $result['invalid'] = TRUE;
+    }
+
+    if ($result['invalid']) {
+      return $result;
+    }
+
+    // process [machine_name].
+    $started = FALSE;
+    $stopped = FALSE;
+    $comments = FALSE;
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $email_codes[$result['current']];
+
+      if ($code === $stop_at) {
+        // stopping at this point means that the address is invalid.
+        $result['invalid'] = TRUE;
+        $result['stopped_at'] = TRUE;
+        break;
+      }
+
+      if ($code == c_base_ascii::PARENTHESIS_OPEN) {
+        if ($started) {
+          $started = FALSE;
+          $stopped = TRUE;
+        }
+
+        $comments = TRUE;
+
+        // comments may follow a machine name.
+        $parsed = $this->pr_rfc_string_is_comment($email_codes, $email_characters, $result['current'], $stop);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          unset($parsed);
+          break;
+        }
+        unset($parsed);
+
+        continue;
+      }
+      elseif ($this->pr_rfc_char_is_fws($code)) {
+        if (!$started || $stopped) {
+          // ignore leading/trailing whitespace.
+          continue;
+        }
+
+        // whitespace at this point should mean a stop point has been reached.
+        $started = FALSE;
+        $stopped = TRUE;
+        $comments = FALSE;
+      }
+      elseif ($code != c_base_ascii::PERIOD && !$this->pr_rfc_char_is_atext($code)) {
+        if ($code != c_base_ascii::AT) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $result['current']++;
+        break;
+      }
+      elseif ($stopped) {
+        // comments or whitespace may not appear between text.
+        $result['invalid'] = TRUE;
+        break;
+      }
+      elseif (!$started) {
+        $comments = FALSE;
+        $started = TRUE;
+      }
+
+      $result['name_machine'] .= $email_characters[$result['current']];
+    }
+    unset($code);
+    unset($started);
+    unset($stopped);
+    unset($comments);
+
+    if ($result['current'] >= $stop) {
+      $result['invalid'] = TRUE;
+    }
+
+    if ($result['invalid']) {
+      return $result;
+    }
+
+    // comments may appear before the [name_address], skip past them.
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = self::pr_char_to_code($email[$result['current']]);
+
+      if ($code === $stop_at) {
+        // stopping at this point means that the address is invalid.
+        $result['invalid'] = TRUE;
+        $result['stopped_at'] = TRUE;
+        break;
+      }
+
+      if ($code == c_base_ascii::PARENTHESIS_OPEN) {
+        $parsed = $this->pr_rfc_string_is_comment($email_codes, $email_characters, $result['current'], $stop);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          unset($parsed);
+          break;
+        }
+        unset($parsed);
+
+        // there may be multiple comments, so do not break at this point.
+      }
+      elseif (!$this->pr_rfc_char_is_fws($code)) {
+        // the first non-comment, non-fws char should be the start of the [ip_address].
+        break;
+      }
+    }
+    unset($code);
+
+    if ($result['current'] >= $stop) {
+      $result['invalid'] = TRUE;
+    }
+
+    if ($result['invalid']) {
+      return $result;
+    }
+
+    // process [name_address].
+    if ($email_codes[$result['current']] == c_base_ascii::BRACKET_OPEN) {
+      // process as a literal domain.
+      for (; $result['current'] < $stop; $result['current']++) {
+        $code = $email_codes[$result['current']];
+
+        if ($code === $stop_at) {
+          // stopping at this point means that the address is invalid.
+          $result['invalid'] = TRUE;
+          $result['stopped_at'] = TRUE;
+          break;
+        }
+
+        if (!$this->pr_rfc_char_is_dtext($code)) {
+          if ($code != c_base_ascii::BRACKET_CLOSE) {
+            $result['invalid'] = TRUE;
+            unset($stop);
+            return $result;
+          }
+
+          $result['current']++;
+          break;
+        }
+
+        $result['name_address'] .= $email_characters[$result['name_address']];
+      }
+      unset($code);
+    }
+    else {
+      // process as a non-literal domain.
+      for (; $result['current'] < $stop; $result['current']++) {
+        $code = $email_codes[$result['current']];
+
+        if ($code === $stop_at) {
+          // stopping at this point, there is no way to know if it is valid or not.
+          // @todo: this would be a good place to perform the name_address spot check for valid ip or host name.
+          $result['stopped_at'] = TRUE;
+          break;
+        }
+
+        if ($code != c_base_ascii::PERIOD && !$this->pr_rfc_char_is_atext($code)) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $result['name_address'] .= $email_characters[$result['name_address']];
+      }
+      unset($code);
+    }
+
+    // @todo: the spot to check if name_address is a valid ip or host name.
+
+    if ($result['invalid']) {
+      return $result;
+    }
+
+    // comments may appear after the [name_address], skip past them.
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $email_codes[$result['current']];
+
+      if ($code === $stop_at) {
+        // this is a valid name, comments are irrelevant.
+        // that said, the comment was not completed, so it may be invalid.
+        $result['stopped_at'] = TRUE;
+        break;
+      }
+
+      if ($code == c_base_ascii::PARENTHESIS_OPEN) {
+        $parsed = $this->pr_rfc_string_is_comment($email_codes, $email_characters, $result['current'], $stop);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          unset($parsed);
+          break;
+        }
+        unset($parsed);
+
+        // there may be multiple comments, so do not break at this point.
+      }
+      elseif (!$this->pr_rfc_char_is_fws($code)) {
+        // the first non-comment, non-fws char should be the start of the [ip_address].
+        break;
+      }
+    }
+    unset($code);
+
+    if ($current < $stop) {
+      // There should be nothing at the end of a valid string.
+      // If there is, then this is an invalid e-mail.
+      // If stop_at is not FALSE, then stopping before the final stop point is not automatically a problem.
+      if ($stop_at !== FALSE) {
+        $result['invalid'] = TRUE;
+      }
+    }
+
+    return $result;
+  }
+}
diff --git a/common/base/classes/base_error.php b/common/base/classes/base_error.php
new file mode 100644 (file)
index 0000000..829e500
--- /dev/null
@@ -0,0 +1,475 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing return values.
+ */
+
+/*
+  const KERNEL = 0;
+  const USER = 1;
+  const MAIL = 2;
+  const DAEMON = 3;
+  const SECURITY = 4;
+  const MESSAGES = 5;
+  const PRINTER = 6;
+  const NETWORK = 7;
+  const UUCP = 8;
+  const CLOCK = 9;
+  const AUTHORIZATION = 10;
+  const FTP = 11;
+  const NTP = 12;
+  const AUDIT = 13;
+  const ALERT = 14;
+  const CRON = 15;
+  const LOCAL_0 = 16;
+  const LOCAL_1 = 17;
+  const LOCAL_2 = 18;
+  const LOCAL_3 = 19;
+  const LOCAL_4 = 20;
+  const LOCAL_5 = 21;
+  const LOCAL_6 = 22;
+  const LOCAL_7 = 23;
+*/
+
+/**
+ * A generic class for managing errors.
+ *
+ * This class is a dependency of classes provided by base_return.php.
+ * Therefore, it is an exception case to the use of base_return classes as a return value.
+ *
+ * @todo: write this based on my cf_error code.
+ */
+class c_base_error {
+  const EMERGENCY = 0;
+  const ALERT = 1;
+  const CRITICAL = 2;
+  const ERROR = 3;
+  const WARNING = 4;
+  const NOTICE = 5;
+  const INFORMATIONAL = 6;
+  const DEBUG = 7;
+  const UNKNOWN = 8;
+
+  const DEFAULT_BACKTRACE_LIMIT = 4;
+
+  private $name;
+  private $message;
+  private $details;
+  private $severity;
+  private $limit;
+  private $backtrace;
+  private $code;
+  private $reported;
+
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->name = NULL;
+    $this->message = NULL;
+    $this->details = NULL;
+    $this->severity = NULL;
+    $this->limit = NULL;
+    $this->backtrace = NULL;
+    $this->code = NULL;
+    $this->reported = NULL;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->name);
+    unset($this->message);
+    unset($this->details);
+    unset($this->severity);
+    unset($this->limit);
+    unset($this->backtrace);
+    unset($this->code);
+    unset($this->reported);
+  }
+
+  /**
+   * Write an error log message and returns the resulting new error object.
+   *
+   * This will silently ignore invalid arguments, with the exception of the reporting controls.
+   * For reporting information, use self::get_reporting().
+   *
+   * @todo: this is incomplete.
+   *
+   * @param string|null $name
+   *   (optional) The name of something associated with a problem.
+   *   This is often a variable name or a function name.
+   * @param string|null $message
+   *   (optional) A message string describing the problem, in some manner.
+   * @param array|null $details
+   *   (optional) An array containing further, detailed, information.
+   * @param int|null $code
+   *   (optional) An integer identifying the message in some manner.
+   * @param int|null $severity
+   *   (optional) A number representing the severity level.
+   *   The c_base_error constants, such as EMERGENCY or ERROR should be use here.
+   *   This defaults to: self::ERROR.
+   * @param int|bool|null $limit
+   *   (optional) A number representing the backtrace limit.
+   *   If set to FALSE, then no backtrace is generated.
+   * @param bool $report
+   *   (optional) If TRUE, then report the error using the appropriate methods.
+   *   @fixme: it would probably be best to make this an object that knows how to report so that the object can do what it needs to do.
+   *
+   * @return c_base_error
+   *   Always returns a newly created c_base_error object.
+   *   No error status is ever returned.
+   *
+   * @see: self::get_reporting()
+   */
+  public static function s_log($name = NULL, $message = NULL, $details = NULL, $code = NULL, $severity = NULL, $limit = NULL, $report = TRUE) {
+    $class = __CLASS__;
+    $entry = new $class();
+    unset($class);
+
+    if (is_string($name)) {
+      $entry->set_name($name);
+    }
+    elseif (is_null($this->name)) {
+      $entry->set_name('');
+    }
+
+    if (is_string($message)) {
+      $entry->set_message($message);
+    }
+    elseif (is_null($this->message)) {
+      $entry->set_message('');
+    }
+
+    if (is_array($details)) {
+      $entry->set_details($details);
+    }
+    elseif (is_null($this->details)) {
+      $entry->set_details(array());
+    }
+
+    if (is_int($code)) {
+      $entry->set_code($code);
+    }
+    elseif (is_null($this->message)) {
+      $entry->set_code(0);
+    }
+
+    if (is_int($severity) && $severity >= self::EMERGENCY && $severity < self::UNKNOWN) {
+      $entry->set_severity($severity);
+    }
+    elseif (is_null($this->message)) {
+      $entry->set_severity(self::ERROR);
+    }
+
+    if ($limit === FALSE || (is_int($limit) && $limit >= 0)) {
+      $entry->set_limit($limit);
+    }
+    elseif (is_null($this->limit)) {
+      $entry->set_limit(self::DEFAULT_BACKTRACE_LIMIT);
+    }
+
+    // @todo: call self::p_backtrace() accordingly.
+
+    if (is_bool($report) && $report) {
+      // @todo: use the report object to report the problem.
+      // @fixme: this is setup as a bool, but I know I need to create and use a report object (which has yet to be created).
+      $this->reported = NULL;
+    }
+    else {
+      $this->reported = NULL;
+    }
+
+    return $entry;
+  }
+
+  /**
+   * Assign an error name string.
+   *
+   * @param string $name
+   *   An error name string
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_name($name) {
+    if (!is_string($name)) {
+      return FALSE;
+    }
+
+    $this->name = $name;
+  }
+
+  /**
+   * Returns the assigned name.
+   *
+   * @return string|null
+   *   A name to associate with the error, such as a variable or function name.
+   *   NULL is returned if not defined.
+   */
+  public function get_name() {
+    return $this->name;
+  }
+
+  /**
+   * Assign an error message string.
+   *
+   * @param string $message
+   *   An error message string
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_message($message) {
+    if (!is_string($message)) {
+      return FALSE;
+    }
+
+    $this->message = $message;
+  }
+
+  /**
+   * Returns the assigned message.
+   *
+   * @return string|null
+   *   An error message string or NULL if not defined.
+   */
+  public function get_message() {
+    return $this->message;
+  }
+
+  /**
+   * Assigns the details array.
+   *
+   * @param array $details
+   *   An array of details.
+   *
+   * @param bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_details($details) {
+    if (!is_array($details)) {
+      return FALSE;
+    }
+
+    $this->details = $details;
+    return TRUE;
+  }
+
+  /**
+   * Returns the details array.
+   *
+   * The details array is defined by the caller and may have any structure, so long as it is an array.
+   *
+   * @return array|null
+   *   An array of additional details or NULL if not defined.
+   */
+  public function get_details() {
+    return $this->details;
+  }
+
+  /**
+   * Assigns a severity level.
+   *
+   * @param int $severity
+   *   A severity integer, representing the severity level.
+   *   Such as self::ERROR.
+   */
+  public function set_severity($severity) {
+    if (!is_int($severity) || $severity < 0 || $severity > 7) {
+      return FALSE;
+    }
+
+    $this->severity = $severity;
+    return TRUE;
+  }
+
+  /**
+   * Returns the currently assigned severity level.
+   *
+   * @return int
+   *   The currently assigned severity level.
+   *   This defaults to self::ERROR when undefined.
+   */
+  public function get_severity() {
+    if (is_null($this->severity)) {
+      $this->severity = self::ERROR;
+    }
+
+    return $this->severity;
+  }
+
+  /**
+   * Assigns a limit for use with debug_backtrace().
+   *
+   * @param int|bool $limit
+   *   An integer representing the number of backtraces.
+   *   A value of 0 means no limit (use with care, can result in performance/resource/timeout problems).
+   *   A value of FALSE means disable backtrace for errors.
+   *
+   * @see: c_base_error::set_backtrace()
+   */
+  public function set_limit($limit) {
+    if ($limit !== FALSE || !is_int($limit) || $limit < 0) {
+      return FALSE;
+    }
+
+    $this->limit = $limit;
+    return TRUE;
+  }
+
+  /**
+   * Returns the currently assigned limit.
+   *
+   * @return int|bool
+   *   The currently assigned limit.
+   *   This defaults to self::DEFAULT_BACKTRACE_LIMIT.
+   *
+   * @see: c_base_error::set_backtrace()
+   */
+  public function get_limit() {
+    if ($limit !== FALSE || !is_int($limit) || $limit < 0) {
+      $this->limit = self::DEFAULT_BACKTRACE_LIMIT;
+    }
+
+    return $this->limit;
+  }
+
+  /**
+   * Assigns a backtrace.
+   *
+   * This is auto-performed by the class.
+   * All settings should be assigned prior to utilizing this.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: c_base_error::set_limit()
+   */
+  public function set_backtrace() {
+    $this->p_backtrace(1);
+
+    return TRUE;
+  }
+
+  /**
+   * Returns the backtrace object.
+   *
+   * @return object|null
+   *   A populate backtrace object or NULL if no backtrace is defined.
+   *
+   * @see: c_base_error::set_limit()
+   */
+  public function get_backtrace() {
+    return $this->backtrace;
+  }
+
+  /**
+   * Assign an error code integer.
+   *
+   * A code is used to categorize the error in some manner.
+   *
+   * @param int $code
+   *   An error code integer
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_code($code) {
+    if (!is_string($code)) {
+      return FALSE;
+    }
+
+    $this->code = $code;
+  }
+
+  /**
+   * Returns the assigned code.
+   *
+   * A code is used to categorize the error in some manner.
+   *
+   * @return int|null
+   *   A code to associate with the error.
+   *   NULL is returned if not defined.
+   */
+  public function get_code() {
+    return $this->code;
+  }
+
+  /**
+   * Assigns a reported.
+   *
+   * The reported variable is auto-processed by this class.
+   * All this does is reset the value to NULL.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: c_base_error::set_limit()
+   */
+  public function set_reported() {
+    unset($this->reported);
+
+    $this->reported = NULL;
+
+    return TRUE;
+  }
+
+  /**
+   * Returns the reported object.
+   *
+   * Use this to determine the results of the last report status.
+   *
+   * @return object|null
+   *   A populate reported object or NULL if no report was performed.
+   *
+   * @see: c_base_error::set_limit()
+   */
+  public function get_reported() {
+    return $this->reported;
+  }
+
+  /**
+   * Build the debug backtrace.
+   *
+   * This will not include this function in the backtrace.
+   * The backtrace will be stored as an object.
+   *
+   * @param int $count
+   *   (optional) Assign a custom count that is used to prevent unecessary function calls from being included in the backtrace.
+   *   This is essentially to remove the functions called to generate this backtrace, which do not matter.
+   *   Instead, only the function where the error happens should the backtrace limit count apply.
+   *   This does nothing when the limit is set to 0 (which means unlimited).
+   *
+   * @see: debug_backtrace()
+   */
+  private function p_backtrace($count = 0) {
+    if ($this->limit === FALSE) {
+      $this->backtrace = NULL;
+      return;
+    }
+
+    // Make sure unnecessary backtrace logs are not part of the count.
+    $limit = $this->limit;
+    if ($this->limit > 0) {
+      $limit = $this->limit + 1;
+
+      if (is_int($count) && $count > 0) {
+        $limit += $count;
+      }
+    }
+
+    $this->backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit);
+    unset($limit);
+
+    // Remove unecessary backtrace logs.
+    $count = $count + 1;
+    $i = 0;
+    for (; $i < $count; $i++) {
+      array_shift($this->backtrace);
+    }
+    unset($i);
+  }
+}
diff --git a/common/base/classes/base_form.php b/common/base/classes/base_form.php
new file mode 100644 (file)
index 0000000..2f724be
--- /dev/null
@@ -0,0 +1,1494 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing system forms.
+ *
+ * This is currently a draft/brainstorm and is subject to be completely rewritten/redesigned.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+require_once('common/base/classes/base_mime.php');
+require_once('common/base/classes/base_http.php');
+require_once('common/base/classes/base_charset.php');
+require_once('common/base/classes/base_language.php');
+
+// use 'mutable' and have it set to FALSE for server-side only data that cannot be changed client side.
+// - drupal uses 'readonly' and 'disabled', but those are presentation specific, so they must not be used or related to 'mutable' (or in this case, unmutable).
+
+// associated form (sessions) with timestamp, ip address, and agent?
+// generate unique key and/or checksum for each form (and in http header).
+
+/**
+ * A generic class for form attribute types.
+ */
+class c_base_form_attributes {
+  const ATTRIBUTE_NONE                   = 0;
+  const ATTRIBUTE_ACCESS_KEY             = 1; // single letter
+  const ATTRIBUTE_ACCEPT                 = 2; // c_base_mime integer
+  const ATTRIBUTE_ACTION                 = 3; // string, URL
+  const ATTRIBUTE_ACTION                 = 4; // text
+  const ATTRIBUTE_AUTO_COMPLETE          = 5; // on or off, use TRUE/FALSE
+  const ATTRIBUTE_AUTO_FOCUS             = 6; // autofocus, use TRUE/FALSE
+  const ATTRIBUTE_CLASS                  = 7; // array of strings
+  const ATTRIBUTE_CHALLENGE              = 8; // challenge, use TRUE/FALSE
+  const ATTRIBUTE_CHARACTER_SET          = 9; // c_base_charset integer
+  const ATTRIBUTE_CHECKED                = 10; // checked, use TRUE/FALSE
+  const ATTRIBUTE_COLUMNS                = 11; // number
+  const ATTRIBUTE_CONTENT_EDITABLE       = 12; // TRUE, FALSE, INHERIT
+  const ATTRIBUTE_DIRECTION              = 13; // ltr, rtl, auto
+  const ATTRIBUTE_DIRECTION_NAME         = 14; // text, inputname.dir
+  const ATTRIBUTE_DISABLED               = 15; // disabled, use TRUE/FALSE
+  const ATTRIBUTE_ENCODING_TYPE          = 16; // c_base_mime integer
+  const ATTRIBUTE_FOR                    = 17; // text, element id
+  const ATTRIBUTE_FORM                   = 18; // text, form id
+  const ATTRIBUTE_FORM_ACTION            = 19; // text, url
+  const ATTRIBUTE_FORM_ENCODE_TYPE       = 20; // c_base_mime integer
+  const ATTRIBUTE_FORM_METHOD            = 21; // get or post, use HTTP_METHOD_GET and HTTP_METHOD_POST
+  const ATTRIBUTE_FORM_NO_VALIDATED      = 22; // formnovalidate, use TRUE/FALSE
+  const ATTRIBUTE_FORM_TARGET            = 23; // text, _blank, _self, _parent, _top, URL
+  const ATTRIBUTE_HIDDEN                 = 24; // TRUE/FALSE
+  const ATTRIBUTE_KEY_TYPE               = 25; // text, rsa, dsa, ec
+  const ATTRIBUTE_LABEL                  = 26; // text
+  const ATTRIBUTE_LANG                   = 27; // i_base_language, int
+  const ATTRIBUTE_LIST                   = 28; // text, datalist_id
+  const ATTRIBUTE_MAXIMUM                = 29; // number, date
+  const ATTRIBUTE_MAXIMUM_LENGTH         = 30; // number
+  const ATTRIBUTE_MINIMUM                = 31; // number, date
+  const ATTRIBUTE_MULTIPLE               = 32; // multiple, use TRUE/FALSE
+  const ATTRIBUTE_NAME                   = 33; // TRUE/FALSE
+  const ATTRIBUTE_NO_VALIDATE            = 34; // text
+  const ATTRIBUTE_ON_ABORT               = 35; // text
+  const ATTRIBUTE_ON_AFTER_PRINT         = 36; // text
+  const ATTRIBUTE_ON_ANIMATION_END       = 37; // text
+  const ATTRIBUTE_ON_ANIMATION_ITERATION = 38; // text
+  const ATTRIBUTE_ON_ANIMATION_start     = 39; // text
+  const ATTRIBUTE_ON_BEFORE_UNLOAD       = 40; // text
+  const ATTRIBUTE_ON_BEFORE_PRINT        = 41; // text
+  const ATTRIBUTE_ON_BLUR                = 42; // text
+  const ATTRIBUTE_ON_CLICK               = 43; // text
+  const ATTRIBUTE_ON_CONTEXT_MENU        = 44; // text
+  const ATTRIBUTE_ON_COPY                = 45; // text
+  const ATTRIBUTE_ON_CUT                 = 46; // text
+  const ATTRIBUTE_ON_CAN_PLAY            = 47; // text
+  const ATTRIBUTE_ON_CAN_PLAY_THROUGH    = 48; // text
+  const ATTRIBUTE_ON_CHANGE              = 49; // text
+  const ATTRIBUTE_ON_DOUBLE_CLICK        = 50; // text
+  const ATTRIBUTE_ON_DRAG                = 51; // text
+  const ATTRIBUTE_ON_DRAG_END            = 52; // text
+  const ATTRIBUTE_ON_DRAG_ENTER          = 53; // text
+  const ATTRIBUTE_ON_DRAG_LEAVE          = 54; // text
+  const ATTRIBUTE_ON_DRAG_OVER           = 55; // text
+  const ATTRIBUTE_ON_DRAG_START          = 56; // text
+  const ATTRIBUTE_ON_DROP                = 57; // text
+  const ATTRIBUTE_ON_DURATION_CHANGE     = 58; // text
+  const ATTRIBUTE_ON_ERROR               = 59; // text
+  const ATTRIBUTE_ON_EMPTIED             = 60; // text
+  const ATTRIBUTE_ON_ENDED               = 61; // text
+  const ATTRIBUTE_ON_ERROR               = 62; // text
+  const ATTRIBUTE_ON_FOCUS               = 63; // text
+  const ATTRIBUTE_ON_FOCUS_IN            = 64; // text
+  const ATTRIBUTE_ON_FOCUS_OUT           = 65; // text
+  const ATTRIBUTE_ON_HASH_CHANGE         = 66; // text
+  const ATTRIBUTE_ON_INPUT               = 67; // text
+  const ATTRIBUTE_ON_INVALID             = 68; // text
+  const ATTRIBUTE_ON_KEY_DOWN            = 69; // text
+  const ATTRIBUTE_ON_KEY_PRESS           = 70; // text
+  const ATTRIBUTE_ON_KEY_UP              = 71; // text
+  const ATTRIBUTE_ON_LOAD                = 72; // text
+  const ATTRIBUTE_ON_LOADED_DATA         = 73; // text
+  const ATTRIBUTE_ON_LOADED_META_DATA    = 74; // text
+  const ATTRIBUTE_ON_LOAD_START          = 75; // text
+  const ATTRIBUTE_ON_MOUSE_DOWN          = 76; // text
+  const ATTRIBUTE_ON_MOUSE_ENTER         = 77; // text
+  const ATTRIBUTE_ON_MOUSE_LEAVE         = 78; // text
+  const ATTRIBUTE_ON_MOUSE_MOVE          = 79; // text
+  const ATTRIBUTE_ON_MOUSE_OVER          = 80; // text
+  const ATTRIBUTE_ON_MOUSE_OUT           = 81; // text
+  const ATTRIBUTE_ON_MOUSE_UP            = 82; // text
+  const ATTRIBUTE_ON_MESSAGE             = 83; // text
+  const ATTRIBUTE_ON_MOUSE_WHEEL         = 84; // text
+  const ATTRIBUTE_ON_OPEN                = 85; // text
+  const ATTRIBUTE_ON_ONLINE              = 86; // text
+  const ATTRIBUTE_ON_OFFLINE             = 87; // text
+  const ATTRIBUTE_ON_PAGE_SHOW           = 88; // text
+  const ATTRIBUTE_ON_PAGE_HIDE           = 89; // text
+  const ATTRIBUTE_ON_PASTE               = 90; // text
+  const ATTRIBUTE_ON_PAUSE               = 91; // text
+  const ATTRIBUTE_ON_PLAY                = 92; // text
+  const ATTRIBUTE_ON_PLAYING             = 93; // text
+  const ATTRIBUTE_ON_PROGRESS            = 94; // text
+  const ATTRIBUTE_ON_POP_STATE           = 95; // text
+  const ATTRIBUTE_ON_RESIZE              = 96; // text
+  const ATTRIBUTE_ON_RESET               = 97; // text
+  const ATTRIBUTE_ON_RATE_CHANGE         = 98; // text
+  const ATTRIBUTE_ON_SCROLL              = 99; // text
+  const ATTRIBUTE_ON_SEARCH              = 100; // text
+  const ATTRIBUTE_ON_SELECT              = 101; // text
+  const ATTRIBUTE_ON_SUBMIT              = 102; // text
+  const ATTRIBUTE_ON_SEEKED              = 103; // text
+  const ATTRIBUTE_ON_SEEKING             = 104; // text
+  const ATTRIBUTE_ON_STALLED             = 105; // text
+  const ATTRIBUTE_ON_SUSPEND             = 106; // text
+  const ATTRIBUTE_ON_SHOW                = 107; // text
+  const ATTRIBUTE_ON_STORAGE             = 108; // text
+  const ATTRIBUTE_ON_TIME_UPDATE         = 109; // text
+  const ATTRIBUTE_ON_TRANSITION_END      = 110; // text
+  const ATTRIBUTE_ON_TOGGLE              = 111; // text
+  const ATTRIBUTE_ON_TOUCH_CANCEL        = 112; // text
+  const ATTRIBUTE_ON_TOUCH_END           = 113; // text
+  const ATTRIBUTE_ON_TOUCH_MOVE          = 114; // text
+  const ATTRIBUTE_ON_TOUCH_START         = 115; // text
+  const ATTRIBUTE_ON_UNLOAD              = 116; // text
+  const ATTRIBUTE_ON_VOLUME_CHANGE       = 117; // text
+  const ATTRIBUTE_ON_WAITING             = 118; // text
+  const ATTRIBUTE_ON_WHEEL               = 119; // text
+  const ATTRIBUTE_PATTERN                = 120; // text, regular expression
+  const ATTRIBUTE_PLACE_HOLDER           = 121; // text
+  const ATTRIBUTE_READONLY               = 122; // readonly, use TRUE/FALSE
+  const ATTRIBUTE_REQUIRED               = 123; // required, use TRUE/FALSE
+  const ATTRIBUTE_ROWS                   = 124; // number
+  const ATTRIBUTE_SELECTED               = 125; // selected, use TRUE/FALSE
+  const ATTRIBUTE_SIZE                   = 126; // number
+  const ATTRIBUTE_SOURCE                 = 127; // url
+  const ATTRIBUTE_SPELLCHECK             = 125; // TRUE/FALSE
+  const ATTRIBUTE_STEP                   = 128; // number
+  const ATTRIBUTE_STYLE                  = 129; // text
+  const ATTRIBUTE_TAB_INDEX              = 130; // number
+  const ATTRIBUTE_TARGET                 = 131; // text, _blank, _self, _parent, _top, URL
+  const ATTRIBUTE_TITLE                  = 132; // text
+  const ATTRIBUTE_TRANSLATE              = 133; // text
+  const ATTRIBUTE_TYPE                   = 134; // see TYPE_ constanst below.
+  const ATTRIBUTE_VALUE                  = 135; // text
+  const ATTRIBUTE_WRAP                   = 136; // hard, soft
+}
+
+/**
+ * A generic class for storing a single item of form data.
+ *
+ * Each tag is expected to have a unique id.
+ * The internal id is an integer, primarily intended to be used in databases for performance reasons.
+ * The form id is a string that can be identical to the internal tag id.
+ * The form id is intended to represent the form tag id or form tag name as used on HTML form id tags.
+ *
+ */
+class c_base_form_data_item {
+
+
+/**
+ * A generic class for form tag types.
+ *
+ * @see: https://www.w3.org/TR/html5/forms.html#forms
+ */
+class c_base_form_tags {
+  const TYPE_NONE    = 0;
+  const TYPE_TEXT    = 1;
+  const TYPE_BOOLEAN = 2;
+  const TYPE_INTEGER = 3;
+  const TYPE_FLOAT   = 4;
+
+  private $id_internal = NULL;
+  private $id_form = NULL;
+  private $type = self::TYPE_NONE;
+  private $data = NULL;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->id_internal = NULL;
+    $this->id_form = NULL;
+    $this->type = self::TYPE_NONE;
+    $this->data = NULL;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->id_internal);
+    unset($this->id_form);
+    unset($this->type);
+    unset($this->data);
+  }
+
+  /**
+   * Assign the unique id which is intended to uniquely identify the form.
+   *
+   * @param int $id_internal
+   *   The internal numeric id to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_id_internal($id_internal) {
+    if (!is_int($id_internal)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->id_internal = $id_internal;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the unique id which is intended to uniquely identify the form.
+   *
+   * @return c_base_return_string|c_base_return_false
+   *   The unique internal id assigned to this object.
+   *   FALSE is returned if the unique numeric tag id is not set.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_id_internal() {
+    if (!isset($this->id_internal)) {
+      return new c_base_return_false();
+    }
+
+    return new c_base_return_string($this->id_internal);
+  }
+
+  /**
+   * Assign the unique id which is intended to represent the form id string.
+   *
+   * @param string $id_form
+   *   The form id string to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_id_form($id_form) {
+    if (!is_string($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->id_form = $id_form;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the unique id which is intended to represent the form id string.
+   *
+   * @return c_base_return_string|c_base_return_false
+   *   The form id string assigned to this object.
+   *   FALSE is returned if the unique numeric tag id is not set.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_id_form() {
+    if (!isset($this->id_form)) {
+      return new c_base_return_false();
+    }
+
+    return new c_base_return_string($this->id_form);
+  }
+
+  /**
+   * Assign the data.
+   *
+   * @param int|string|float|bool|null $data
+   *   The data whose type is directly dependend on $this->type.
+   *   Failure to assign the proper type will result in an error.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_data($data) {
+    if ($this->type === self::TYPE_NONE) {
+      if (!is_null($data)) {
+        return c_base_return_error::s_false();
+      }
+    }
+    elseif ($this->type === self::TYPE_TEXT) {
+      if (!is_string($data)) {
+        return c_base_return_error::s_false();
+      }
+    }
+    elseif ($this->type === self::TYPE_BOOLEAN) {
+      if (!is_bool($data)) {
+        return c_base_return_error::s_false();
+      }
+    }
+    elseif ($this->type === self::TYPE_NUMBER) {
+      if (!is_int($data)) {
+        return c_base_return_error::s_false();
+      }
+    }
+    elseif ($this->type === self::TYPE_FLOAT) {
+      if (!is_float($data)) {
+        return c_base_return_error::s_false();
+      }
+    }
+    else {
+      return c_base_return_error::s_false();
+    }
+
+    $this->data = $data;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the assigned data.
+   *
+   * @return c_base_return_status|c_base_return_int|c_base_return_float|c_base_return_string|c_base_return_bool|c_base_return_null
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_data() {
+    if ($this->type === self::TYPE_NONE) {
+      return new c_base_return_null();
+    }
+    elseif ($this->type === self::TYPE_TEXT) {
+      return new c_base_return_string($this->data);
+    }
+    elseif ($this->type === self::TYPE_BOOLEAN) {
+      if ($this->data) {
+        return new c_base_return_true();
+      }
+
+      return new c_base_return_false();
+    }
+    elseif ($this->type === self::TYPE_NUMBER) {
+      return new c_base_return_int($this->data);
+    }
+    elseif ($this->type === self::TYPE_FLOAT) {
+      return new c_base_return_float($this->data);
+    }
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Assign the type of data to be stored in this object.
+   *
+   * Assigning this will reset/clear the data.
+   *
+   * @param int $type
+   */
+  public function set_type($type) {
+    if ($type === self::TYPE_NONE) {
+      $this->type = $type;
+      $this->data = NULL;
+    }
+    elseif ($type === self::TYPE_TEXT) {
+      $this->type = $type;
+      $this->data = '';
+    }
+    elseif ($type === self::TYPE_BOOLEAN) {
+      $this->type = $type;
+      $this->data = FALSE;
+    }
+    elseif ($type === self::TYPE_NUMBER) {
+      $this->type = $type;
+      $this->data = 0;
+    }
+    elseif ($type === self::TYPE_FLOAT) {
+      $this->type = $type;
+      $this->data = 0.0;
+    }
+    else {
+      return c_base_return_error::s_false();
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the assigned type.
+   *
+   * @return c_base_return_status|c_base_return_int
+   */
+  public function get_type() {
+    return new c_base_return_int($this->type);
+  }
+}
+
+/**
+ * A generic class for storing form data.
+ *
+ * Each tag is expected to have a unique id.
+ * The internal id is an integer, primarily intended to be used in databases for performance reasons.
+ * The form id is a string that can be identical to the internal id.
+ * The form id is intended to represent the form id or form name as used on HTML form id tags.
+ *
+ */
+class c_base_form_data extends c_base_form_data_item {
+  private $data = array();
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    super.__construct();
+
+    $this->data = array();
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    super.__destruct();
+
+    unset($this->data);
+  }
+
+}
+
+// below will be moved into output/theme areas.
+/**
+ * A generic class for form tags.
+ *
+ * The unique tag id is an integer to be used for internal purposes but may be exposed on output.
+ * If the id attribute is defined, then on output the id attribute is used for the HTML tag.
+ *
+ * @see: https://www.w3.org/TR/html5/forms.html#forms
+ */
+class c_base_form_tag {
+  const TAG_NONE      = 0;
+  const TAG_INPUT     = 1;
+  const TAG_RADIO     = 2;
+  const TAG_SUBMIT    = 3;
+  const TAG_TEXT_AREA = 4;
+  const TAG_FIELD_SET = 5;
+  const TAG_SELECT    = 6;
+  const TAG_OPTION    = 7;
+  const TAG_DATA_LIST = 8;
+  const TAG_KEY_GEN   = 9;
+  const TAG_OUTPUT    = 10;
+
+  const TYPE_NONE           = 0;
+  const TYPE_TEXT           = 1;
+  const TYPE_BUTTON         = 2;
+  const TYPE_CHECKBOX       = 3;
+  const TYPE_COLOR          = 4;
+  const TYPE_DATE           = 5;
+  const TYPE_DATETIME       = 6;
+  const TYPE_DATETIME_LOCAL = 7;
+  const TYPE_EMAIL          = 8;
+  const TYPE_FILE           = 9;
+  const TYPE_HIDDEN         = 10;
+  const TYPE_IMAGE          = 11;
+  const TYPE_MONTH          = 12;
+  const TYPE_NUMBER         = 13;
+  const TYPE_PASSWORD       = 14;
+  const TYPE_RADIO          = 15;
+  const TYPE_RANGE          = 16;
+  const TYPE_RESET          = 17;
+  const TYPE_SEARCH         = 18;
+  const TYPE_SUBMIT         = 19;
+  const TYPE_TELEPHONE      = 20;
+  const TYPE_TEXT           = 21;
+  const TYPE_TIME           = 22;
+  const TYPE_URL            = 23;
+  const TYPE_WEEK           = 24;
+
+  private $id;
+  private $type;
+  private $attributes;
+  private $parent;
+  private $children;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->id = NULL;
+    $this->type = self::TAG_NONE;
+    $this->attributes = array();
+    $this->parent = NULL;
+    $this->children = array();
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->id);
+    unset($this->type);
+    unset($this->attributes);
+    unset($this->parent);
+    unset($this->children);
+  }
+
+  /**
+   * Assign the internal unique numeric tag id.
+   *
+   * @param int $id
+   *   The internal numeric id to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_id($id) {
+    if (!is_int($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->id = $id;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the unique numeric tag id assigned to this object.
+   *
+   * @return c_base_return_int|c_base_return_false
+   *   The tag type assigned to this class.
+   *   FALSE is returned if the unique numeric tag id is not set.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_id() {
+    if (!isset($this->id)) {
+      return new c_base_return_false();
+    }
+
+    return new c_base_return_int($this->id);
+  }
+
+  /**
+   * Assign the specified tag type.
+   *
+   * @param int $type
+   *   The tag type to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_type($type) {
+    if (!is_int($type)) {
+      return c_base_return_error::s_false();
+    }
+
+    switch ($type) {
+      case self::TAG_NONE:
+      case self::TAG_INPUT:
+      case self::TAG_RADIO:
+      case self::TAG_SUBMIT:
+      case self::TAG_TEXT_AREA:
+      case self::TAG_FIELD_SET:
+      case self::TAG_SELECT:
+      case self::TAG_OPTION:
+      case self::TAG_DATA_LIST:
+      case self::TAG_KEY_GEN:
+      case self::TAG_OUTPUT:
+        break;
+      default:
+        return new c_base_return_false();
+    }
+
+    $this->type = $type;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the tag type assigned to this object.
+   *
+   * @return c_base_return_int
+   *   The tag type assigned to this class.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_type() {
+    if (!isset($this->type)) {
+      $this->type = self::TAG_NONE;
+    }
+
+    return new c_base_return_int($this->type);
+  }
+
+  /**
+   * Assign the specified tag.
+   *
+   * @param int $attribute
+   *   The attribute to assign.
+   * @param $value
+   *   The value of the attribute.
+   *   The actual value type is specific to each attribute type.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_attribute($attribute, $value) {
+    if (!is_int($attribute)) {
+      return c_base_return_error::s_false();
+    }
+
+    switch ($attribute) {
+      case c_base_form_attributes::ATTRIBUTE_NONE:
+        unset($this->attribute[$attribute]);
+        return new c_base_return_true();
+
+      case c_base_form_attributes::ATTRIBUTE_ACTION:
+      case c_base_form_attributes::ATTRIBUTE_DIRECTION_NAME:
+      case c_base_form_attributes::ATTRIBUTE_FOR:
+      case c_base_form_attributes::ATTRIBUTE_FORM:
+      case c_base_form_attributes::ATTRIBUTE_FORM_ACTION:
+      case c_base_form_attributes::ATTRIBUTE_FORM_TARGET:
+      case c_base_form_attributes::ATTRIBUTE_KEY_TYPE:
+      case c_base_form_attributes::ATTRIBUTE_LABEL:
+      case c_base_form_attributes::ATTRIBUTE_LIST:
+      case c_base_form_attributes::ATTRIBUTE_NAME:
+      case c_base_form_attributes::ATTRIBUTE_ON_ABORT:
+      case c_base_form_attributes::ATTRIBUTE_ON_AFTER_PRINT:
+      case c_base_form_attributes::ATTRIBUTE_ON_ANIMATION_END:
+      case c_base_form_attributes::ATTRIBUTE_ON_ANIMATION_ITERATION:
+      case c_base_form_attributes::ATTRIBUTE_ON_ANIMATION_start:
+      case c_base_form_attributes::ATTRIBUTE_ON_BEFORE_UNLOAD:
+      case c_base_form_attributes::ATTRIBUTE_ON_BEFORE_PRINT:
+      case c_base_form_attributes::ATTRIBUTE_ON_BLUR:
+      case c_base_form_attributes::ATTRIBUTE_ON_CLICK:
+      case c_base_form_attributes::ATTRIBUTE_ON_CONTEXT_MENU:
+      case c_base_form_attributes::ATTRIBUTE_ON_COPY:
+      case c_base_form_attributes::ATTRIBUTE_ON_CUT:
+      case c_base_form_attributes::ATTRIBUTE_ON_CAN_PLAY:
+      case c_base_form_attributes::ATTRIBUTE_ON_CAN_PLAY_THROUGH:
+      case c_base_form_attributes::ATTRIBUTE_ON_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_DOUBLE_CLICK:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_END:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_ENTER:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_LEAVE:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_OVER:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_START:
+      case c_base_form_attributes::ATTRIBUTE_ON_DROP:
+      case c_base_form_attributes::ATTRIBUTE_ON_DURATION_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_ERROR:
+      case c_base_form_attributes::ATTRIBUTE_ON_EMPTIED:
+      case c_base_form_attributes::ATTRIBUTE_ON_ENDED:
+      case c_base_form_attributes::ATTRIBUTE_ON_ERROR:
+      case c_base_form_attributes::ATTRIBUTE_ON_FOCUS:
+      case c_base_form_attributes::ATTRIBUTE_ON_FOCUS_IN:
+      case c_base_form_attributes::ATTRIBUTE_ON_FOCUS_OUT:
+      case c_base_form_attributes::ATTRIBUTE_ON_HASH_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_INPUT:
+      case c_base_form_attributes::ATTRIBUTE_ON_INVALID:
+      case c_base_form_attributes::ATTRIBUTE_ON_KEY_DOWN:
+      case c_base_form_attributes::ATTRIBUTE_ON_KEY_PRESS:
+      case c_base_form_attributes::ATTRIBUTE_ON_KEY_UP:
+      case c_base_form_attributes::ATTRIBUTE_ON_LOAD:
+      case c_base_form_attributes::ATTRIBUTE_ON_LOADED_DATA:
+      case c_base_form_attributes::ATTRIBUTE_ON_LOADED_META_DATA:
+      case c_base_form_attributes::ATTRIBUTE_ON_LOAD_START:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_DOWN:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_ENTER:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_LEAVE:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_MOVE:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_OVER:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_OUT:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_UP:
+      case c_base_form_attributes::ATTRIBUTE_ON_MESSAGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_WHEEL:
+      case c_base_form_attributes::ATTRIBUTE_ON_OPEN:
+      case c_base_form_attributes::ATTRIBUTE_ON_ONLINE:
+      case c_base_form_attributes::ATTRIBUTE_ON_OFFLINE:
+      case c_base_form_attributes::ATTRIBUTE_ON_PAGE_SHOW:
+      case c_base_form_attributes::ATTRIBUTE_ON_PAGE_HIDE:
+      case c_base_form_attributes::ATTRIBUTE_ON_PASTE:
+      case c_base_form_attributes::ATTRIBUTE_ON_PAUSE:
+      case c_base_form_attributes::ATTRIBUTE_ON_PLAY:
+      case c_base_form_attributes::ATTRIBUTE_ON_PLAYING:
+      case c_base_form_attributes::ATTRIBUTE_ON_PROGRESS:
+      case c_base_form_attributes::ATTRIBUTE_ON_POP_STATE:
+      case c_base_form_attributes::ATTRIBUTE_ON_RESIZE:
+      case c_base_form_attributes::ATTRIBUTE_ON_RESET:
+      case c_base_form_attributes::ATTRIBUTE_ON_RATE_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_SCROLL:
+      case c_base_form_attributes::ATTRIBUTE_ON_SEARCH:
+      case c_base_form_attributes::ATTRIBUTE_ON_SELECT:
+      case c_base_form_attributes::ATTRIBUTE_ON_SUBMIT:
+      case c_base_form_attributes::ATTRIBUTE_ON_SEEKED:
+      case c_base_form_attributes::ATTRIBUTE_ON_SEEKING:
+      case c_base_form_attributes::ATTRIBUTE_ON_STALLED:
+      case c_base_form_attributes::ATTRIBUTE_ON_SUSPEND:
+      case c_base_form_attributes::ATTRIBUTE_ON_SHOW:
+      case c_base_form_attributes::ATTRIBUTE_ON_STORAGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_TIME_UPDATE:
+      case c_base_form_attributes::ATTRIBUTE_ON_TRANSITION_END:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOGGLE:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_CANCEL:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_END:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_MOVE:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_START:
+      case c_base_form_attributes::ATTRIBUTE_ON_UNLOAD:
+      case c_base_form_attributes::ATTRIBUTE_ON_VOLUME_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_WAITING:
+      case c_base_form_attributes::ATTRIBUTE_ON_WHEEL:
+      case c_base_form_attributes::ATTRIBUTE_PATTERN:
+      case c_base_form_attributes::ATTRIBUTE_PLACE_HOLDER:
+      case c_base_form_attributes::ATTRIBUTE_READONLY:
+      case c_base_form_attributes::ATTRIBUTE_REQUIRED:
+      case c_base_form_attributes::ATTRIBUTE_ROWS:
+      case c_base_form_attributes::ATTRIBUTE_SELECTED:
+      case c_base_form_attributes::ATTRIBUTE_SIZE:
+      case c_base_form_attributes::ATTRIBUTE_SOURCE:
+      case c_base_form_attributes::ATTRIBUTE_STEP:
+      case c_base_form_attributes::ATTRIBUTE_TYPE:
+      case c_base_form_attributes::ATTRIBUTE_WRAP:
+      case c_base_form_attributes::ATTRIBUTE_VALUE:
+        if (!is_string($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case c_base_form_attributes::ATTRIBUTE_FORM_NO_VALIDATED:
+      case c_base_form_attributes::ATTRIBUTE_AUTO_COMPLETE:
+      case c_base_form_attributes::ATTRIBUTE_AUTO_FOCUS:
+      case c_base_form_attributes::ATTRIBUTE_CHALLENGE:
+      case c_base_form_attributes::ATTRIBUTE_CHECKED:
+      case c_base_form_attributes::ATTRIBUTE_DISABLED:
+      case c_base_form_attributes::ATTRIBUTE_MULTIPLE:
+        if (!is_bool($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case c_base_form_attributes::ATTRIBUTE_ACCEPT:
+      case c_base_form_attributes::ATTRIBUTE_FORM_ENCODE_TYPE:
+        if (!this->pr_validate_value_mime_type($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case c_base_form_attributes::ATTRIBUTE_COLUMNS:
+      case c_base_form_attributes::ATTRIBUTE_MAXIMUM:
+      case c_base_form_attributes::ATTRIBUTE_MAXIMUM_LENGTH:
+      case c_base_form_attributes::ATTRIBUTE_MINIMUM:
+        if (!is_int($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case c_base_form_attributes::ATTRIBUTE_FORM_METHOD:
+        if (!this->pr_validate_value_http_method($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      default:
+        return new c_base_return_false();
+    }
+
+    $this->attribute[$attribute] = $value;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the attributes assigned to this object.
+   *
+   * @return c_base_return_array
+   *   The attributes assigned to this class.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_attributes() {
+    if (!isset($this->attributes) && !is_array($this->attributes)) {
+      $this->attributes = array();
+    }
+
+    return new c_base_return_array($this->attributes);
+  }
+
+  /**
+   * Get the value of a single attribute assigned to this object.
+   *
+   * @param int $attribute
+   *   The attribute to assign.
+   *
+   * @return c_base_return_int|c_base_return_string|c_base_return_bool|c_base_return_false
+   *   The value assigned to the attribte (the data type is different per attribute).
+   *   FALSE is returned if the element does not exist.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_attribute($attribute) {
+    if (!is_int($attribute)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!isset($this->attributes) && !is_array($this->attributes)) {
+      $this->attributes = array();
+    }
+
+    if (array_key_exists($attribute, $this->attributes)) {
+      switch ($attribute) {
+        case c_base_form_attributes::ATTRIBUTE_NONE:
+          // should not be possible, so consider this an error (attributes set to NONE are actually unset from the array).
+          return c_base_return_error::s_false();
+
+        case c_base_form_attributes::ATTRIBUTE_ACTION:
+        case c_base_form_attributes::ATTRIBUTE_DIRECTION_NAME:
+        case c_base_form_attributes::ATTRIBUTE_FOR:
+        case c_base_form_attributes::ATTRIBUTE_FORM:
+        case c_base_form_attributes::ATTRIBUTE_FORM_ACTION:
+        case c_base_form_attributes::ATTRIBUTE_FORM_TARGET:
+        case c_base_form_attributes::ATTRIBUTE_KEY_TYPE:
+        case c_base_form_attributes::ATTRIBUTE_LABEL:
+        case c_base_form_attributes::ATTRIBUTE_LIST:
+        case c_base_form_attributes::ATTRIBUTE_NAME:
+        case c_base_form_attributes::ATTRIBUTE_ON_ABORT:
+        case c_base_form_attributes::ATTRIBUTE_ON_AFTER_PRINT:
+        case c_base_form_attributes::ATTRIBUTE_ON_ANIMATION_END:
+        case c_base_form_attributes::ATTRIBUTE_ON_ANIMATION_ITERATION:
+        case c_base_form_attributes::ATTRIBUTE_ON_ANIMATION_start:
+        case c_base_form_attributes::ATTRIBUTE_ON_BEFORE_UNLOAD:
+        case c_base_form_attributes::ATTRIBUTE_ON_BEFORE_PRINT:
+        case c_base_form_attributes::ATTRIBUTE_ON_BLUR:
+        case c_base_form_attributes::ATTRIBUTE_ON_CLICK:
+        case c_base_form_attributes::ATTRIBUTE_ON_CONTEXT_MENU:
+        case c_base_form_attributes::ATTRIBUTE_ON_COPY:
+        case c_base_form_attributes::ATTRIBUTE_ON_CUT:
+        case c_base_form_attributes::ATTRIBUTE_ON_CAN_PLAY:
+        case c_base_form_attributes::ATTRIBUTE_ON_CAN_PLAY_THROUGH:
+        case c_base_form_attributes::ATTRIBUTE_ON_CHANGE:
+        case c_base_form_attributes::ATTRIBUTE_ON_DOUBLE_CLICK:
+        case c_base_form_attributes::ATTRIBUTE_ON_DRAG:
+        case c_base_form_attributes::ATTRIBUTE_ON_DRAG_END:
+        case c_base_form_attributes::ATTRIBUTE_ON_DRAG_ENTER:
+        case c_base_form_attributes::ATTRIBUTE_ON_DRAG_LEAVE:
+        case c_base_form_attributes::ATTRIBUTE_ON_DRAG_OVER:
+        case c_base_form_attributes::ATTRIBUTE_ON_DRAG_START:
+        case c_base_form_attributes::ATTRIBUTE_ON_DROP:
+        case c_base_form_attributes::ATTRIBUTE_ON_DURATION_CHANGE:
+        case c_base_form_attributes::ATTRIBUTE_ON_ERROR:
+        case c_base_form_attributes::ATTRIBUTE_ON_EMPTIED:
+        case c_base_form_attributes::ATTRIBUTE_ON_ENDED:
+        case c_base_form_attributes::ATTRIBUTE_ON_ERROR:
+        case c_base_form_attributes::ATTRIBUTE_ON_FOCUS:
+        case c_base_form_attributes::ATTRIBUTE_ON_FOCUS_IN:
+        case c_base_form_attributes::ATTRIBUTE_ON_FOCUS_OUT:
+        case c_base_form_attributes::ATTRIBUTE_ON_HASH_CHANGE:
+        case c_base_form_attributes::ATTRIBUTE_ON_INPUT:
+        case c_base_form_attributes::ATTRIBUTE_ON_INVALID:
+        case c_base_form_attributes::ATTRIBUTE_ON_KEY_DOWN:
+        case c_base_form_attributes::ATTRIBUTE_ON_KEY_PRESS:
+        case c_base_form_attributes::ATTRIBUTE_ON_KEY_UP:
+        case c_base_form_attributes::ATTRIBUTE_ON_LOAD:
+        case c_base_form_attributes::ATTRIBUTE_ON_LOADED_DATA:
+        case c_base_form_attributes::ATTRIBUTE_ON_LOADED_META_DATA:
+        case c_base_form_attributes::ATTRIBUTE_ON_LOAD_START:
+        case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_DOWN:
+        case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_ENTER:
+        case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_LEAVE:
+        case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_MOVE:
+        case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_OVER:
+        case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_OUT:
+        case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_UP:
+        case c_base_form_attributes::ATTRIBUTE_ON_MESSAGE:
+        case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_WHEEL:
+        case c_base_form_attributes::ATTRIBUTE_ON_OPEN:
+        case c_base_form_attributes::ATTRIBUTE_ON_ONLINE:
+        case c_base_form_attributes::ATTRIBUTE_ON_OFFLINE:
+        case c_base_form_attributes::ATTRIBUTE_ON_PAGE_SHOW:
+        case c_base_form_attributes::ATTRIBUTE_ON_PAGE_HIDE:
+        case c_base_form_attributes::ATTRIBUTE_ON_PASTE:
+        case c_base_form_attributes::ATTRIBUTE_ON_PAUSE:
+        case c_base_form_attributes::ATTRIBUTE_ON_PLAY:
+        case c_base_form_attributes::ATTRIBUTE_ON_PLAYING:
+        case c_base_form_attributes::ATTRIBUTE_ON_PROGRESS:
+        case c_base_form_attributes::ATTRIBUTE_ON_POP_STATE:
+        case c_base_form_attributes::ATTRIBUTE_ON_RESIZE:
+        case c_base_form_attributes::ATTRIBUTE_ON_RESET:
+        case c_base_form_attributes::ATTRIBUTE_ON_RATE_CHANGE:
+        case c_base_form_attributes::ATTRIBUTE_ON_SCROLL:
+        case c_base_form_attributes::ATTRIBUTE_ON_SEARCH:
+        case c_base_form_attributes::ATTRIBUTE_ON_SELECT:
+        case c_base_form_attributes::ATTRIBUTE_ON_SUBMIT:
+        case c_base_form_attributes::ATTRIBUTE_ON_SEEKED:
+        case c_base_form_attributes::ATTRIBUTE_ON_SEEKING:
+        case c_base_form_attributes::ATTRIBUTE_ON_STALLED:
+        case c_base_form_attributes::ATTRIBUTE_ON_SUSPEND:
+        case c_base_form_attributes::ATTRIBUTE_ON_SHOW:
+        case c_base_form_attributes::ATTRIBUTE_ON_STORAGE:
+        case c_base_form_attributes::ATTRIBUTE_ON_TIME_UPDATE:
+        case c_base_form_attributes::ATTRIBUTE_ON_TRANSITION_END:
+        case c_base_form_attributes::ATTRIBUTE_ON_TOGGLE:
+        case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_CANCEL:
+        case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_END:
+        case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_MOVE:
+        case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_START:
+        case c_base_form_attributes::ATTRIBUTE_ON_UNLOAD:
+        case c_base_form_attributes::ATTRIBUTE_ON_VOLUME_CHANGE:
+        case c_base_form_attributes::ATTRIBUTE_ON_WAITING:
+        case c_base_form_attributes::ATTRIBUTE_ON_WHEEL:
+        case c_base_form_attributes::ATTRIBUTE_PATTERN:
+        case c_base_form_attributes::ATTRIBUTE_READONLY:
+        case c_base_form_attributes::ATTRIBUTE_REQUIRED:
+        case c_base_form_attributes::ATTRIBUTE_ROWS:
+        case c_base_form_attributes::ATTRIBUTE_SELECTED:
+        case c_base_form_attributes::ATTRIBUTE_SIZE:
+        case c_base_form_attributes::ATTRIBUTE_SOURCE:
+        case c_base_form_attributes::ATTRIBUTE_STEP:
+        case c_base_form_attributes::ATTRIBUTE_TYPE:
+        case c_base_form_attributes::ATTRIBUTE_WRAP:
+        case c_base_form_attributes::ATTRIBUTE_PLACE_HOLDER:
+        case c_base_form_attributes::ATTRIBUTE_VALUE:
+          return c_base_return_string::s_new($value);
+
+        case c_base_form_attributes::ATTRIBUTE_FORM_NO_VALIDATED:
+        case c_base_form_attributes::ATTRIBUTE_AUTO_COMPLETE:
+        case c_base_form_attributes::ATTRIBUTE_AUTO_FOCUS:
+        case c_base_form_attributes::ATTRIBUTE_CHALLENGE:
+        case c_base_form_attributes::ATTRIBUTE_CHECKED:
+        case c_base_form_attributes::ATTRIBUTE_DISABLED:
+        case c_base_form_attributes::ATTRIBUTE_MULTIPLE:
+          return c_base_return_bool::s_new($value);
+
+        case c_base_form_attributes::ATTRIBUTE_ACCEPT:
+        case c_base_form_attributes::ATTRIBUTE_FORM_ENCODE_TYPE:
+          return c_base_return_int::s_new($value);
+
+        case c_base_form_attributes::ATTRIBUTE_COLUMNS:
+        case c_base_form_attributes::ATTRIBUTE_MAXIMUM:
+        case c_base_form_attributes::ATTRIBUTE_MAXIMUM_LENGTH:
+        case c_base_form_attributes::ATTRIBUTE_MINIMUM:
+          return c_base_return_int::s_new($value);
+
+        case c_base_form_attributes::ATTRIBUTE_FORM_METHOD:
+          return c_base_return_int::s_new($value);
+
+        default:
+          return new c_base_return_false();
+      }
+    }
+
+    $this->attribute[$attribute] = $value;
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign the specified numeric id as the parent tag.
+   *
+   * @param int $parent_id
+   *   The numeric id to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_parent($parent_id) {
+    if (!is_int($parent_id)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->parent = $parent_id;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the parent tag numeric id associated with this.
+   *
+   * @return c_base_return_int|c_base_return_false
+   *   The tag type assigned to this class.
+   *   FALSE without error bit is set when no parent is defined.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_parent() {
+    if (!isset($this->parent)) {
+      return new c_base_return_false();
+    }
+
+    return new c_base_return_int($this->parent);
+  }
+
+  /**
+   * Add a child tag.
+   *
+   * @param c_base_form_tag $child
+   *   The numeric id to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_child($child_tag) {
+    if (!($child_tag instanceof c_base_form_tag)) {
+      return c_base_return_error::s_false();
+    }
+
+    // this requires the class to have a tag id assigned.
+    if (!is_int($this->id)) {
+      return c_base_return_error::s_false();
+    }
+
+    $child_id = $child_tag->get_id();
+    if (!($child_id instanceof c_base_return_int)) {
+      unset($child_id);
+      return c_base_return_error::s_false();
+    }
+    $child_id = $child_id->get_value_exact();
+
+    $child_tag->set_parent($this->id);
+    $this->children[$child_id] = $child_tag;
+    unset($child_id);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the specified child by its unique numeric tag id.
+   *
+   * @param int $child_id
+   *   The unique numeric tag id of the child.
+   *
+   * @return c_base_form_return_tag|c_base_return_false
+   *   The tag type assigned to this class.
+   *   FALSE without error bit is set when the child is not defined.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_child($child_id) {
+    if (!is_array($this->children) || !array_key_exists($child_id, $this->children)) {
+      return new c_base_return_false();
+    }
+
+    if (!($this->children[$child_id] instanceof c_base_form_tag)) {
+      return c_base_return_error::s_false();
+    }
+
+    return new c_base_form_return_tag($this->children[$child_id]);
+  }
+
+  /**
+   * Assign an array of child tags.
+   *
+   * @param array $children
+   *   An array of children to assign.
+   *   The array key must be an integer or it is ignore.
+   *   The array value must be a c_base_form_tag instance or it is ignored.
+   * @param bool $append
+   *   (optional) When TRUE, child elements are appended.
+   *   When FALSE, any existing children are removed.
+   *
+   * @return c_base_return_int|c_base_return_false
+   *   An integer representing the number of children successfully added.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_children($children, $append = FALSE) {
+    if (!is_array($children)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!$append) {
+      $this->children = array();
+    }
+
+    $added = 0;
+    foreach ($children as $child_id => $child_tag) {
+      if (!is_numeric($child_id) || !($child_tag instanceof c_base_form_tag)) {
+        continue;
+      }
+
+      $child_tag_id = $child_tag->get_id();
+      if (!($child_tag_id instanceof c_base_return_int) || $child_id != $child_tag_id->get_value_exact()) {
+        unset($child_tag_id);
+        continue;
+      }
+      unset($child_tag_id);
+
+      $child_tag->set_parent($this->id);
+      $this->children[(int) $child_id] = $child_tag;
+      $added++;
+    }
+    unset($child_id);
+    unset($child_tag);
+
+    return new c_base_return_int($added);
+  }
+
+  /**
+   * Get all assigned child tags.
+   *
+   * @return c_base_return_array|c_base_return_false
+   *   The array of child tags.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_children() {
+    if (!is_array($this->children)) {
+      $this->children = array();
+    }
+
+    return new c_base_return_array($this->children);
+  }
+
+  /**
+   * Protected function for mime values.
+   *
+   * @param int $value
+   *   The value of the attribute populate from c_base_mime.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE otherwise.
+   */
+  protected function pr_validate_value_mime_type($value) {
+    if (!is_int($value)) {
+      return FALSE;
+    }
+
+    switch ($value) {
+      case c_base_mime::CATEGORY_UNKNOWN:
+      case c_base_mime::CATEGORY_PROVIDED:
+      case c_base_mime::CATEGORY_STREAM:
+      case c_base_mime::CATEGORY_TEXT:
+      case c_base_mime::CATEGORY_IMAGE:
+      case c_base_mime::CATEGORY_AUDIO:
+      case c_base_mime::CATEGORY_VIDEO:
+      case c_base_mime::CATEGORY_DOCUMENT:
+      case c_base_mime::CATEGORY_CONTAINER:
+      case c_base_mime::CATEGORY_APPLICATION:
+      case c_base_mime::TYPE_UNKNOWN:
+      case c_base_mime::TYPE_PROVIDED:
+      case c_base_mime::TYPE_STREAM:
+      case c_base_mime::TYPE_TEXT_PLAIN:
+      case c_base_mime::TYPE_TEXT_HTML:
+      case c_base_mime::TYPE_TEXT_RSS:
+      case c_base_mime::TYPE_TEXT_ICAL:
+      case c_base_mime::TYPE_TEXT_CSV:
+      case c_base_mime::TYPE_TEXT_XML:
+      case c_base_mime::TYPE_TEXT_CSS:
+      case c_base_mime::TYPE_TEXT_JS:
+      case c_base_mime::TYPE_TEXT_JSON:
+      case c_base_mime::TYPE_TEXT_RICH:
+      case c_base_mime::TYPE_TEXT_XHTML:
+      case c_base_mime::TYPE_TEXT_PS:
+      case c_base_mime::TYPE_IMAGE_PNG:
+      case c_base_mime::TYPE_IMAGE_GIF:
+      case c_base_mime::TYPE_IMAGE_JPEG:
+      case c_base_mime::TYPE_IMAGE_BMP:
+      case c_base_mime::TYPE_IMAGE_SVG:
+      case c_base_mime::TYPE_IMAGE_TIFF:
+      case c_base_mime::TYPE_AUDIO_WAV:
+      case c_base_mime::TYPE_AUDIO_OGG:
+      case c_base_mime::TYPE_AUDIO_MP3:
+      case c_base_mime::TYPE_AUDIO_MP4:
+      case c_base_mime::TYPE_AUDIO_MIDI:
+      case c_base_mime::TYPE_VIDEO_MPEG:
+      case c_base_mime::TYPE_VIDEO_OGG:
+      case c_base_mime::TYPE_VIDEO_H264:
+      case c_base_mime::TYPE_VIDEO_QUICKTIME:
+      case c_base_mime::TYPE_VIDEO_DV:
+      case c_base_mime::TYPE_VIDEO_JPEG:
+      case c_base_mime::TYPE_VIDEO_WEBM:
+      case c_base_mime::TYPE_DOCUMENT_LIBRECHART:
+      case c_base_mime::TYPE_DOCUMENT_LIBREFORMULA:
+      case c_base_mime::TYPE_DOCUMENT_LIBREGRAPHIC:
+      case c_base_mime::TYPE_DOCUMENT_LIBREPRESENTATION:
+      case c_base_mime::TYPE_DOCUMENT_LIBRESPREADSHEET:
+      case c_base_mime::TYPE_DOCUMENT_LIBRETEXT:
+      case c_base_mime::TYPE_DOCUMENT_LIBREHTML:
+      case c_base_mime::TYPE_DOCUMENT_PDF:
+      case c_base_mime::TYPE_DOCUMENT_ABIWORD:
+      case c_base_mime::TYPE_DOCUMENT_MSWORD:
+      case c_base_mime::TYPE_DOCUMENT_MSEXCEL:
+      case c_base_mime::TYPE_DOCUMENT_MSPOWERPOINT:
+      case c_base_mime::TYPE_CONTAINER_TAR:
+      case c_base_mime::TYPE_CONTAINER_CPIO:
+      case c_base_mime::TYPE_CONTAINER_JAVA:
+        break;
+      default:
+        return FALSE;
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Protected function for http method values.
+   *
+   * Only GET and POST methods are supported.
+   *
+   * @param int $value
+   *   The value of the attribute populate from http method.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE otherwise.
+   */
+  protected function pr_validate_value_http_method($value) {
+    if (!is_int($value)) {
+      return FALSE;
+    }
+
+    switch ($value) {
+      case c_base_http::HTTP_METHOD_NONE:
+      case c_base_http::HTTP_METHOD_GET:
+      case c_base_http::HTTP_METHOD_POST:
+        break;
+      default:
+        return FALSE;
+    }
+
+    return TRUE;
+  }
+}
+
+/**
+ * A generic class for form fields.
+ *
+ * The unique tag id is an integer to be used for internal purposes but may be exposed on output.
+ * If the id attribute is defined, then on output the id attribute is used for the HTML tag.
+ *
+ * This uses a simple approach to store different form inputs.
+ * Each input has a single depth group (with NULL being the default group).
+ * The input fields do not relate directly to the form input.
+ *
+ * @see: https://www.w3.org/TR/html5/forms.html#forms
+ */
+class c_base_form {
+  private $id;
+  private $inputs;
+  private $attributes;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->id = NULL;
+    $this->tags = array();
+    $this->attributes = array();
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->id);
+    unset($this->tags);
+    unset($this->attributes);
+  }
+
+  /**
+   * Assign a unique numeric id to represent this form.
+   *
+   * @param int $id
+   *   The unique form tag id to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_id($id) {
+    if (!is_int($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->id = $id;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the unique id assigned to this object.
+   *
+   * @return c_base_return_int|c_base_return_false
+   *   The unique numeric id assigned to this object.
+   *   FALSE is returned if no id is assigned.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_id() {
+    if (!is_int($this->id)) {
+      return c_base_return_false();
+    }
+
+    return new c_base_return_int($this->id);
+  }
+
+  /**
+   * Assign the specified tag.
+   *
+   * @param int $attribute
+   *   The attribute to assign.
+   * @param $value
+   *   The value of the attribute.
+   *   The actual value type is specific to each attribute type.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_attribute($attribute, $value) {
+    if (!is_int($attribute)) {
+      return c_base_return_error::s_false();
+    }
+
+    switch ($attribute) {
+      case c_base_form_attributes::ATTRIBUTE_NONE:
+        unset($this->attribute[$attribute]);
+        return new c_base_return_true();
+
+      case c_base_form_attributes::ATTRIBUTE_ACTION:
+      case c_base_form_attributes::ATTRIBUTE_DIRECTION_NAME:
+      case c_base_form_attributes::ATTRIBUTE_FOR:
+      case c_base_form_attributes::ATTRIBUTE_FORM:
+      case c_base_form_attributes::ATTRIBUTE_FORM_ACTION:
+      case c_base_form_attributes::ATTRIBUTE_FORM_TARGET:
+      case c_base_form_attributes::ATTRIBUTE_KEY_TYPE:
+      case c_base_form_attributes::ATTRIBUTE_LABEL:
+      case c_base_form_attributes::ATTRIBUTE_LIST:
+      case c_base_form_attributes::ATTRIBUTE_NAME:
+      case c_base_form_attributes::ATTRIBUTE_ON_ABORT:
+      case c_base_form_attributes::ATTRIBUTE_ON_AFTER_PRINT:
+      case c_base_form_attributes::ATTRIBUTE_ON_ANIMATION_END:
+      case c_base_form_attributes::ATTRIBUTE_ON_ANIMATION_ITERATION:
+      case c_base_form_attributes::ATTRIBUTE_ON_ANIMATION_start:
+      case c_base_form_attributes::ATTRIBUTE_ON_BEFORE_UNLOAD:
+      case c_base_form_attributes::ATTRIBUTE_ON_BEFORE_PRINT:
+      case c_base_form_attributes::ATTRIBUTE_ON_BLUR:
+      case c_base_form_attributes::ATTRIBUTE_ON_CLICK:
+      case c_base_form_attributes::ATTRIBUTE_ON_CONTEXT_MENU:
+      case c_base_form_attributes::ATTRIBUTE_ON_COPY:
+      case c_base_form_attributes::ATTRIBUTE_ON_CUT:
+      case c_base_form_attributes::ATTRIBUTE_ON_CAN_PLAY:
+      case c_base_form_attributes::ATTRIBUTE_ON_CAN_PLAY_THROUGH:
+      case c_base_form_attributes::ATTRIBUTE_ON_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_DOUBLE_CLICK:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_END:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_ENTER:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_LEAVE:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_OVER:
+      case c_base_form_attributes::ATTRIBUTE_ON_DRAG_START:
+      case c_base_form_attributes::ATTRIBUTE_ON_DROP:
+      case c_base_form_attributes::ATTRIBUTE_ON_DURATION_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_ERROR:
+      case c_base_form_attributes::ATTRIBUTE_ON_EMPTIED:
+      case c_base_form_attributes::ATTRIBUTE_ON_ENDED:
+      case c_base_form_attributes::ATTRIBUTE_ON_ERROR:
+      case c_base_form_attributes::ATTRIBUTE_ON_FOCUS:
+      case c_base_form_attributes::ATTRIBUTE_ON_FOCUS_IN:
+      case c_base_form_attributes::ATTRIBUTE_ON_FOCUS_OUT:
+      case c_base_form_attributes::ATTRIBUTE_ON_HASH_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_INPUT:
+      case c_base_form_attributes::ATTRIBUTE_ON_INVALID:
+      case c_base_form_attributes::ATTRIBUTE_ON_KEY_DOWN:
+      case c_base_form_attributes::ATTRIBUTE_ON_KEY_PRESS:
+      case c_base_form_attributes::ATTRIBUTE_ON_KEY_UP:
+      case c_base_form_attributes::ATTRIBUTE_ON_LOAD:
+      case c_base_form_attributes::ATTRIBUTE_ON_LOADED_DATA:
+      case c_base_form_attributes::ATTRIBUTE_ON_LOADED_META_DATA:
+      case c_base_form_attributes::ATTRIBUTE_ON_LOAD_START:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_DOWN:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_ENTER:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_LEAVE:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_MOVE:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_OVER:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_OUT:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_UP:
+      case c_base_form_attributes::ATTRIBUTE_ON_MESSAGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_MOUSE_WHEEL:
+      case c_base_form_attributes::ATTRIBUTE_ON_OPEN:
+      case c_base_form_attributes::ATTRIBUTE_ON_ONLINE:
+      case c_base_form_attributes::ATTRIBUTE_ON_OFFLINE:
+      case c_base_form_attributes::ATTRIBUTE_ON_PAGE_SHOW:
+      case c_base_form_attributes::ATTRIBUTE_ON_PAGE_HIDE:
+      case c_base_form_attributes::ATTRIBUTE_ON_PASTE:
+      case c_base_form_attributes::ATTRIBUTE_ON_PAUSE:
+      case c_base_form_attributes::ATTRIBUTE_ON_PLAY:
+      case c_base_form_attributes::ATTRIBUTE_ON_PLAYING:
+      case c_base_form_attributes::ATTRIBUTE_ON_PROGRESS:
+      case c_base_form_attributes::ATTRIBUTE_ON_POP_STATE:
+      case c_base_form_attributes::ATTRIBUTE_ON_RESIZE:
+      case c_base_form_attributes::ATTRIBUTE_ON_RESET:
+      case c_base_form_attributes::ATTRIBUTE_ON_RATE_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_SCROLL:
+      case c_base_form_attributes::ATTRIBUTE_ON_SEARCH:
+      case c_base_form_attributes::ATTRIBUTE_ON_SELECT:
+      case c_base_form_attributes::ATTRIBUTE_ON_SUBMIT:
+      case c_base_form_attributes::ATTRIBUTE_ON_SEEKED:
+      case c_base_form_attributes::ATTRIBUTE_ON_SEEKING:
+      case c_base_form_attributes::ATTRIBUTE_ON_STALLED:
+      case c_base_form_attributes::ATTRIBUTE_ON_SUSPEND:
+      case c_base_form_attributes::ATTRIBUTE_ON_SHOW:
+      case c_base_form_attributes::ATTRIBUTE_ON_STORAGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_TIME_UPDATE:
+      case c_base_form_attributes::ATTRIBUTE_ON_TRANSITION_END:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOGGLE:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_CANCEL:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_END:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_MOVE:
+      case c_base_form_attributes::ATTRIBUTE_ON_TOUCH_START:
+      case c_base_form_attributes::ATTRIBUTE_ON_UNLOAD:
+      case c_base_form_attributes::ATTRIBUTE_ON_VOLUME_CHANGE:
+      case c_base_form_attributes::ATTRIBUTE_ON_WAITING:
+      case c_base_form_attributes::ATTRIBUTE_ON_WHEEL:
+      case c_base_form_attributes::ATTRIBUTE_PATTERN:
+      case c_base_form_attributes::ATTRIBUTE_PLACE_HOLDER:
+      case c_base_form_attributes::ATTRIBUTE_READONLY:
+      case c_base_form_attributes::ATTRIBUTE_REQUIRED:
+      case c_base_form_attributes::ATTRIBUTE_ROWS:
+      case c_base_form_attributes::ATTRIBUTE_SELECTED:
+      case c_base_form_attributes::ATTRIBUTE_SIZE:
+      case c_base_form_attributes::ATTRIBUTE_SOURCE:
+      case c_base_form_attributes::ATTRIBUTE_STEP:
+      case c_base_form_attributes::ATTRIBUTE_TYPE:
+      case c_base_form_attributes::ATTRIBUTE_WRAP:
+      case c_base_form_attributes::ATTRIBUTE_VALUE:
+        if (!is_string($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case c_base_form_attributes::ATTRIBUTE_FORM_NO_VALIDATED:
+      case c_base_form_attributes::ATTRIBUTE_AUTO_COMPLETE:
+      case c_base_form_attributes::ATTRIBUTE_AUTO_FOCUS:
+      case c_base_form_attributes::ATTRIBUTE_CHALLENGE:
+      case c_base_form_attributes::ATTRIBUTE_CHECKED:
+      case c_base_form_attributes::ATTRIBUTE_DISABLED:
+      case c_base_form_attributes::ATTRIBUTE_MULTIPLE:
+        if (!is_bool($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case c_base_form_attributes::ATTRIBUTE_ACCEPT:
+      case c_base_form_attributes::ATTRIBUTE_FORM_ENCODE_TYPE:
+        if (!this->pr_validate_value_mime_type($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case c_base_form_attributes::ATTRIBUTE_COLUMNS:
+      case c_base_form_attributes::ATTRIBUTE_MAXIMUM:
+      case c_base_form_attributes::ATTRIBUTE_MAXIMUM_LENGTH:
+      case c_base_form_attributes::ATTRIBUTE_MINIMUM:
+        if (!is_int($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case c_base_form_attributes::ATTRIBUTE_FORM_METHOD:
+        if (!this->pr_validate_value_http_method($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      default:
+        return new c_base_return_false();
+    }
+
+    $this->attribute[$attribute] = $value;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the attributes assigned to this object.
+   *
+   * @return c_base_return_array
+   *   The attributes assigned to this class.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_attributes() {
+    if (!isset($this->attributes) && !is_array($this->attributes)) {
+      $this->attributes = array();
+    }
+
+    return new c_base_return_array($this->attributes);
+  }
+}
+
diff --git a/common/base/classes/base_html.php b/common/base/classes/base_html.php
new file mode 100644 (file)
index 0000000..a53b165
--- /dev/null
@@ -0,0 +1,459 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing HTML5 Markup.
+ *
+ * This is currently a draft/brainstorm and is subject to be completely rewritten/redesigned.
+ *
+ * @see: https://www.w3.org/TR/html5/
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+/**
+ * A generic class for HTML tag attributes.
+ *
+ * This is for the internal storage of the attributes and not external.
+ * Externally, every attribute type is a string.
+ * The internal uses PHP structures where easily possible.
+ * Special case string values, such as percentage symbols, are not used internally.
+ *
+ * @todo: should the class name include internal?
+ * @todo: should external be in the html output class?
+ * @todo: what about form processing/validation (which has external values)?
+ * ---- Above is old comments to be reviewed. ----
+ *
+ * A generic class for HTML tag attributes.
+ * This should accept and handle all
+ *
+ * @see: https://www.w3.org/TR/html5/forms.html#forms
+ */
+class c_base_html_attribute_values {
+  const TYPE_NONE                  = 0;
+  const TYPE_BOOLEAN               = 1; // https://www.w3.org/TR/html5/infrastructure.html#boolean-attributes
+  const TYPE_ENUMERATED            = 2; // https://www.w3.org/TR/html5/infrastructure.html#keywords-and-enumerated-attributes
+  const TYPE_NUMBER                = 3; // https://www.w3.org/TR/html5/infrastructure.html#numbers
+  const TYPE_NUMBER_SIGNED         = 4; // https://www.w3.org/TR/html5/infrastructure.html#signed-integers
+  const TYPE_NUMBER_UNSIGNED       = 5; // https://www.w3.org/TR/html5/infrastructure.html#non-negative-integers
+  const TYPE_NUMBER_FLOAT          = 6; // https://www.w3.org/TR/html5/infrastructure.html#floating-point-numbers
+  const TYPE_NUMBER_DIMENSION      = 7; // https://www.w3.org/TR/html5/infrastructure.html#percentages-and-dimensions
+  const TYPE_NUMBER_LIST           = 8; // https://www.w3.org/TR/html5/infrastructure.html#lists-of-integers
+  const TYPE_NUMBER_DIMENSION_LIST = 9; // https://www.w3.org/TR/html5/infrastructure.html#lists-of-dimensions
+  const TYPE_DATE                  = 10; // https://www.w3.org/TR/html5/infrastructure.html#dates-and-times
+  const TYPE_DATE_MONTH            = 11; // https://www.w3.org/TR/html5/infrastructure.html#months
+  const TYPE_DATE_DATES            = 12; // https://www.w3.org/TR/html5/infrastructure.html#dates
+  const TYPE_DATE_DATES_YEARLESS   = 13; // https://www.w3.org/TR/html5/infrastructure.html#yearless-dates
+  const TYPE_DATE_TIMES            = 14; // https://www.w3.org/TR/html5/infrastructure.html#times
+  const TYPE_DATE_DATES_FLOATING   = 15; // https://www.w3.org/TR/html5/infrastructure.html#floating-dates-and-times
+  const TYPE_DATE_TIMEZONE         = 16; // https://www.w3.org/TR/html5/infrastructure.html#time-zones
+  const TYPE_DATE_GLOBAL           = 17; // https://www.w3.org/TR/html5/infrastructure.html#global-dates-and-times
+  const TYPE_DATE_WEEKS            = 18; // https://www.w3.org/TR/html5/infrastructure.html#weeks
+  const TYPE_DATE_DURATION         = 19; // https://www.w3.org/TR/html5/infrastructure.html#durations
+  const TYPE_DATE_VAGUE            = 20; // https://www.w3.org/TR/html5/infrastructure.html#vaguer-moments-in-time
+  const TYPE_COLOR                 = 21; // https://www.w3.org/TR/html5/infrastructure.html#colors
+  const TYPE_TOKENS_SPACE          = 22; // https://www.w3.org/TR/html5/infrastructure.html#space-separated-tokenss
+  const TYPE_TOKENS_COMMA          = 23; // https://www.w3.org/TR/html5/infrastructure.html#comma-separated-tokens
+  const TYPE_REFERENCE             = 24; // https://www.w3.org/TR/html5/infrastructure.html#syntax-references
+  const TYPE_MEDIA                 = 25; // https://www.w3.org/TR/html5/infrastructure.html#mq
+  const TYPE_URL                   = 26; // https://www.w3.org/TR/html5/infrastructure.html#urls
+
+  const VALUE_NONE      = 0;
+  const VALUE_TRUE      = 1;
+  const VALUE_FALSE     = 2;
+  const VALUE_INHERITED = 3;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    // do nothing.
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    // do nothing.
+  }
+
+  /**
+   * Validate that value is a boolean.
+   *
+   * @param int $value
+   *   The value to validate.
+   * @param bool $include_inherited
+   *   (optional) When TRUE, the "Inherited" state is supported.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * see: https://www.w3.org/TR/html5/infrastructure.html#boolean-attributes
+   */
+  public static function is_boolean($value, $include_inherited = FALSE) {
+    if (!is_int($value)) {
+      return new c_base_return_false();
+    }
+
+    if (!is_bool($include_inherited)) {
+      return c_base_return_error::s_false();
+    }
+
+    switch ($value) {
+      self::VALUE_NONE:
+      self::VALUE_TRUE:
+      self::VALUE_FALSE:
+        return new c_base_return_true();
+      self::VALUE_INHERITED:
+        if ($include_inherited) {
+          return new c_base_return_true();
+        }
+        break;
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Validate that value is an enumerated (or keyword) value.
+   *
+   * @param string $value
+   *   The value to validate.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#keywords-and-enumerated-attributes
+   */
+  public static function is_enumerated($value) {
+    if (is_string($value)) {
+      // an enumerated (or keyword) value is simply a fancy name for string/text.
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Validate that value is a number value.
+   *
+   * @param int $value
+   *   The value to validate.
+   * @param bool $as_unsigned
+   *   (optional) Wnen TRUE, number is treated as unsigned.
+   *   When FALSE, number is treated as signed.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#numbers
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#non-negative-integers
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#time-zones
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#global-dates-and-times
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#durations
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#colors
+   */
+  public static function is_number($value, $as_unsigned = FALSE) {
+    if (!is_bool($as_unsigned)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($as_unsigned) {
+      if (is_int($value)) {
+        if ($value >= 0) {
+          return new c_base_return_true();
+        }
+      }
+    }
+    else {
+      if (is_int($value)) {
+        return new c_base_return_true();
+      }
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Validate that value is a float value.
+   *
+   * @param float $value
+   *   The value to validate.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#floating-point-numbers
+   */
+  public static function is_float($value) {
+    if (is_float($value)) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Validate that value is an unsigned number value.
+   *
+   * This is functionaly the same as a float or an unsigned integer.
+   *
+   * @param float $value
+   *   The value to validate.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::is_float()
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#percentages-and-dimensions
+   */
+  public static function is_dimension($value) {
+    if (is_int($value) || is_float($value)) {
+      if ($value >= 0) {
+        return new c_base_return_true();
+      }
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Validate that value is a list of numbers.
+   *
+   * @param array $value
+   *   The value to validate.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#lists-of-integers
+   */
+  public static function is_number_list($value) {
+    if (!is_array($value)) {
+      return new c_base_return_false();
+    }
+
+    foreach ($value as $part) {
+      if (!is_int($part)) {
+        unset($part);
+        return c_base_return_false();
+      }
+    }
+    unset($part);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Validate that value is a list of dimensions.
+   *
+   * @param array $value
+   *   The value to validate.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#lists-of-dimensions
+   */
+  public static function is_dimension_list($value) {
+    if (!is_array($value)) {
+      return new c_base_return_false();
+    }
+
+    foreach ($value as $part) {
+      if (!is_int($part) && !is_float($part)) {
+        unset($part);
+        return c_base_return_false();
+      }
+    }
+    unset($part);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Validate that value is a date or time value.
+   *
+   * A date or time is expected to be a unix timestamp, which is a valid integer.
+   *
+   * @param int $value
+   *   The value to validate.
+   * @param bool $as_float
+   *   (optional) When TRUE, allow the date to be a float.
+   *   When FALSE, date is treated as an integer.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#dates-and-times
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#months
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#floating-dates-and-times
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#vaguer-moments-in-time
+   */
+  public static function is_date($value, $as_float = FALSE) {
+    if (!is_bool($as_float)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($as_float) {
+      if (is_float($value)) {
+        return new c_base_return_true();
+      }
+    }
+    else {
+      if (is_int($value)) {
+        return new c_base_return_true();
+      }
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Validate that value is a list of dates or times.
+   *
+   * @param array $value
+   *   The value to validate.
+   * @param bool $as_float
+   *   (optional) When TRUE, allow the date to be a float.
+   *   When FALSE, date is treated as an integer.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#dates
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#yearless-dates
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#times
+   */
+  public static function is_date_list($value, $as_float = FALSE) {
+    if (!is_bool($as_float)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($value)) {
+      return new c_base_return_false();
+    }
+
+    if ($as_float) {
+      foreach ($value as $part) {
+        if (!is_float($part)) {
+          unset($part);
+          return c_base_return_false();
+        }
+      }
+      unset($part);
+    }
+    else {
+      foreach ($value as $part) {
+        if (!is_int($part)) {
+          unset($part);
+          return c_base_return_false();
+        }
+      }
+      unset($part);
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Validate that value is a token value.
+   *
+   * This processes a single token.
+   * There are two types:
+   * 1) Space Separated
+   * 2) Comma Separated
+   *
+   * Perhaps I am misreading but the standard does not seem clear about other symbols.
+   * For now, I am just allowing them (I can come back later and fix this at any time).
+   *
+   * @param string $value
+   *   The value to validate.
+   * @param bool $comma_separated
+   *   (optional) When TRUE, token is treated as a comma separated token.
+   *   When FALSE, token is treated as space separated token.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#space-separated-tokens
+   * @see: https://www.w3.org/TR/html5/infrastructure.html#comma-separated-tokens
+   */
+  public static function is_token($value, $comma_separated = FALSE) {
+    if (!is_bool($comma_separated)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_string($value)) {
+      if ($comma_Separated) {
+        if (strpos($value, ',') !== FALSE) {
+          return new c_base_return_true();
+        }
+      }
+      else {
+        if (preg_match('/\s/i', $value) === 0) {
+          return new c_base_return_true();
+        }
+      }
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Validate that value is a list of syntax references.
+   *
+   * @param array $value
+   *   The value to validate.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * https://www.w3.org/TR/html5/infrastructure.html#syntax-references
+   */
+  public static function is_syntax_reference($value) {
+    if (!is_string($value)) {
+      return new c_base_return_false();
+    }
+
+    if (preg_match('/^#(^s)+/i', $value) > 0) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Validate that value is a list of syntax references.
+   *
+   * @param array $value
+   *   The value to validate.
+   *
+   * @return c_base_return_status
+   *   TRUE on valid, FALSE on invalid.
+   *   FALSE with error bit set is returned on error.
+   *
+   * https://www.w3.org/TR/html5/infrastructure.html#urls
+   */
+  public static function is_url($value) {
+    if (!is_string($value)) {
+      return new c_base_return_false();
+    }
+
+    // @todo.
+
+    return new c_base_return_false();
+  }
+}
diff --git a/common/base/classes/base_http.php b/common/base/classes/base_http.php
new file mode 100644 (file)
index 0000000..bcdc0ac
--- /dev/null
@@ -0,0 +1,7911 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing the HTTP protocol.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+require_once('common/base/classes/base_charset.php');
+require_once('common/base/classes/base_rfc_string.php');
+require_once('common/base/classes/base_utf8.php');
+require_once('common/base/classes/base_email.php');
+require_once('common/base/classes/base_languages.php');
+require_once('common/base/classes/base_http_status.php');
+require_once('common/base/classes/base_cookie.php');
+require_once('common/base/classes/base_mime.php');
+
+/**
+ * A generic class for managing the HTTP protocol.
+ *
+ * @see: http://www.iana.org/assignments/message-headers/message-headers.xhtml
+ *
+ * @require class base_email
+ * @require class base_rfc_string
+ * @require class base_utf8
+ */
+class c_base_http extends c_base_rfc_string {
+  // standard request headers
+  const REQUEST_ACCEPT                         = 1;
+  const REQUEST_ACCEPT_CHARSET                 = 2;
+  const REQUEST_ACCEPT_ENCODING                = 3;
+  const REQUEST_ACCEPT_LANGUAGE                = 4;
+  const REQUEST_ACCEPT_DATETIME                = 5;
+  const REQUEST_ACCESS_CONTROL_REQUEST_METHOD  = 6;
+  const REQUEST_ACCESS_CONTROL_REQUEST_HEADERS = 7;
+  const REQUEST_AUTHORIZATION                  = 8;
+  const REQUEST_CACHE_CONTROL                  = 9;
+  const REQUEST_CONNECTION                     = 10;
+  const REQUEST_COOKIE                         = 11;
+  const REQUEST_CONTENT_LENGTH                 = 12;
+  const REQUEST_CONTENT_TYPE                   = 13;
+  const REQUEST_DATE                           = 14;
+  const REQUEST_EXPECT                         = 15;
+  const REQUEST_FROM                           = 16;
+  const REQUEST_HOST                           = 17;
+  const REQUEST_IF_MATCH                       = 18;
+  const REQUEST_IF_MODIFIED_SINCE              = 19;
+  const REQUEST_IF_NONE_MATCH                  = 20;
+  const REQUEST_IF_RANGE                       = 21;
+  const REQUEST_IF_UNMODIFIED_SINCE            = 22;
+  const REQUEST_MAX_FORWARDS                   = 23;
+  const REQUEST_ORIGIN                         = 24;
+  const REQUEST_PRAGMA                         = 25;
+  const REQUEST_PROXY_AUTHORIZATION            = 26;
+  const REQUEST_RANGE                          = 27;
+  const REQUEST_REFERER                        = 28;
+  const REQUEST_TE                             = 29;
+  const REQUEST_USER_AGENT                     = 30;
+  const REQUEST_UPGRADE                        = 31;
+  const REQUEST_VIA                            = 32;
+  const REQUEST_WARNING                        = 33;
+  const REQUEST_UNKNOWN                        = 999;
+
+  // non-standard, but supported, request headers
+  const REQUEST_X_REQUESTED_WITH        = 1001;
+  const REQUEST_X_FORWARDED_FOR         = 1002;
+  const REQUEST_X_FORWARDED_HOST        = 1003;
+  const REQUEST_X_FORWARDED_PROTO       = 1004;
+  const REQUEST_CHECKSUM_HEADER         = 1005;
+  const REQUEST_CHECKSUM_HEADERS        = 1006;
+  const REQUEST_CHECKSUM_CONTENT        = 1007;
+  const REQUEST_CONTENT_ENCODING        = 1008;
+  const REQUEST_SIGNATURE_PG            = 1009;
+
+  // standard response headers
+  const RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN      = 1;
+  const RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS = 2;
+  const RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS    = 3;
+  const RESPONSE_ACCESS_CONTROL_MAX_AGE           = 4;
+  const RESPONSE_ACCESS_CONTROL_ALLOW_METHODS     = 5;
+  const RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS     = 6;
+  const RESPONSE_ACCEPT_PATCH                     = 7;
+  const RESPONSE_ACCEPT_RANGES                    = 8;
+  const RESPONSE_AGE                              = 9;
+  const RESPONSE_ALLOW                            = 10;
+  const RESPONSE_CACHE_CONTROL                    = 11;
+  const RESPONSE_CONNECTION                       = 12;
+  const RESPONSE_CONTENT_DISPOSITION              = 13;
+  const RESPONSE_CONTENT_ENCODING                 = 14;
+  const RESPONSE_CONTENT_LANGUAGE                 = 15;
+  const RESPONSE_CONTENT_LENGTH                   = 16;
+  const RESPONSE_CONTENT_LOCATION                 = 17;
+  const RESPONSE_CONTENT_RANGE                    = 18;
+  const RESPONSE_CONTENT_TYPE                     = 19;
+  const RESPONSE_DATE                             = 20;
+  const RESPONSE_ETAG                             = 21;
+  const RESPONSE_EXPIRES                          = 22;
+  const RESPONSE_LAST_MODIFIED                    = 23;
+  const RESPONSE_LINK                             = 24;
+  const RESPONSE_LOCATION                         = 25;
+  const RESPONSE_PRAGMA                           = 26;
+  const RESPONSE_PROXY_AUTHENTICATE               = 27;
+  const RESPONSE_PUBLIC_KEY_PINS                  = 28;
+  const RESPONSE_RETRY_AFTER                      = 29;
+  const RESPONSE_SERVER                           = 30;
+  const RESPONSE_SET_COOKIE                       = 31;
+  const RESPONSE_STATUS                           = 32;
+  const RESPONSE_STRICT_TRANSPORT_SECURITY        = 33;
+  const RESPONSE_TRAILER                          = 34;
+  const RESPONSE_TRANSFER_ENCODING                = 35;
+  const RESPONSE_UPGRADE                          = 36;
+  const RESPONSE_VARY                             = 37;
+  const RESPONSE_WARNING                          = 38;
+  const RESPONSE_WWW_AUTHENTICATE                 = 39;
+  const RESPONSE_PROTOCOL                         = 40;
+
+  // non-standard, but supported, response headers.
+  const RESPONSE_REFRESH                   = 1001;
+  const RESPONSE_X_CONTENT_SECURITY_POLICY = 1002;
+  const RESPONSE_X_CONTENT_TYPE_OPTIONS    = 1003;
+  const RESPONSE_X_UA_COMPATIBLE           = 1004;
+  const RESPONSE_CHECKSUM_HEADER           = 1005;
+  const RESPONSE_CHECKSUM_HEADERS          = 1006;
+  const RESPONSE_CHECKSUM_CONTENT          = 1007;
+  const RESPONSE_CONTENT_REVISION          = 1008;
+  const RESPONSE_DATE_ACTUAL               = 1009;
+
+  // accept delimiters (the syntax for the separators can be confusing and misleading)
+  const DELIMITER_ACCEPT_SUP   = ',';
+  const DELIMITER_ACCEPT_SUB   = ';';
+  const DELIMITER_ACCEPT_SUB_0 = 'q';
+  const DELIMITER_ACCEPT_SUB_1 = '=';
+
+  const ACCEPT_LANGUAGE_CLASS_DEFAULT = 'c_base_language_us_limited';
+
+  // cache control options
+  const CACHE_CONTROL_NO_CACHE         = 1;
+  const CACHE_CONTROL_NO_STORE         = 2;
+  const CACHE_CONTROL_NO_TRANSFORM     = 3;
+  const CACHE_CONTROL_MAX_AGE          = 4;
+  const CACHE_CONTROL_MAX_AGE_S        = 5;
+  const CACHE_CONTROL_MAX_STALE        = 6;
+  const CACHE_CONTROL_MIN_FRESH        = 7;
+  const CACHE_CONTROL_ONLY_IF_CACHED   = 8;
+  const CACHE_CONTROL_PUBLIC           = 9;
+  const CACHE_CONTROL_PRIVATE          = 10;
+  const CACHE_CONTROL_MUST_REVALIDATE  = 11;
+  const CACHE_CONTROL_PROXY_REVALIDATE = 12;
+
+  // supported checksums
+  const CHECKSUM_MD2      = 1;
+  const CHECKSUM_MD4      = 2;
+  const CHECKSUM_MD5      = 3;
+  const CHECKSUM_SHA1     = 4;
+  const CHECKSUM_SHA224   = 5;
+  const CHECKSUM_SHA256   = 6;
+  const CHECKSUM_SHA384   = 7;
+  const CHECKSUM_SHA512   = 8;
+  const CHECKSUM_CRC32    = 9;
+  const CHECKSUM_PG       = 10; // such as: GPG or PGP.
+
+  // checksum actions
+  const CHECKSUM_ACTION_NONE = 0;
+  const CHECKSUM_ACTION_AUTO = 1;
+  const CHECKSUM_ACTION_MANUAL = 2;
+
+  // checksum whats
+  const CHECKSUM_WHAT_FULL     = 1;
+  const CHECKSUM_WHAT_PARTIAL  = 2;
+  const CHECKSUM_WHAT_SIGNED   = 3;
+  const CHECKSUM_WHAT_UNSIGNED = 4;
+
+  // uri path types
+  const URI_PATH_SITE = 1; // such as: '//example.com/main/index.html'
+  const URI_PATH_BASE = 2; // such as: '/main/index.html'
+  const URI_PATH_THIS = 3; // such as: 'index.html'
+
+  // uri host ip addresses
+  const URI_HOST_IPV4 = 1;
+  const URI_HOST_IPV6 = 2;
+  const URI_HOST_IPVX = 3;
+  const URI_HOST_NAME = 4;
+
+  // transfer encoding choices
+  const ENCODING_CHUNKED  = 1;
+  const ENCODING_COMPRESS = 2;
+  const ENCODING_DEFLATE  = 3;
+  const ENCODING_GZIP     = 4; // Compression Options: -1 -> 9.
+  const ENCODING_BZIP     = 5; // Compression Options: 1 -> 9.
+  const ENCODING_LZO      = 6; // Compression Options: LZO1_99, LZO1A_99, LZO1B_999, LZO1C_999, LZO1F_999, LZO1X_999, LZO1Y_999, LZO1Z_999, LZO2A_999 (and many more).
+  const ENCODING_XZ       = 7;
+  const ENCODING_EXI      = 8;
+  const ENCODING_IDENTITY = 9;
+  const ENCODING_SDCH     = 10;
+  const ENCODING_PG       = 11;
+
+  // timestamps
+  const TIMESTAMP_RFC_5322 = 'D, d M Y H:i:s T'; // rfc5322 is the preferred/recommended format.
+  const TIMESTAMP_RFC_1123 = 'D, d M Y H:i:s O';
+  const TIMESTAMP_RFC_850  = 'l, d-M-y H:i:s T';
+
+  // http methods
+  const HTTP_METHOD_NONE    = 0;
+  const HTTP_METHOD_GET     = 1; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods
+  const HTTP_METHOD_HEAD    = 2; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods
+  const HTTP_METHOD_POST    = 3; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods
+  const HTTP_METHOD_PUT     = 4; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods
+  const HTTP_METHOD_DELETE  = 5; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods
+  const HTTP_METHOD_TRACE   = 6; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Security
+  const HTTP_METHOD_OPTIONS = 7; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods
+  const HTTP_METHOD_CONNECT = 8;
+  const HTTP_METHOD_PATCH   = 9; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods
+  const HTTP_METHOD_TRACK   = 10; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Security
+  const HTTP_METHOD_DEBUG   = 11; // https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Security
+
+  private $headers;
+  private $headers_sent;
+  private $request;
+  private $request_time;
+  private $response;
+
+  private $content;
+  private $content_is_file;
+  private $buffer_enabled;
+
+  private $language_class;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->headers = NULL;
+    $this->headers_sent = FALSE;
+    $this->request = array();
+    $this->request_time = NULL;
+    $this->response = array();
+
+    $this->content = NULL;
+    $this->content_is_file = NULL;
+    $this->buffer_enabled = FALSE;
+
+    $this->language_class = NULL;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->headers);
+    unset($this->headers_sent);
+    unset($this->request);
+    unset($this->request_time);
+    unset($this->response);
+
+    unset($this->content);
+    unset($this->content_is_file);
+    unset($this->buffer_enabled);
+
+    unset($this->language_class);
+  }
+
+  /**
+   * Get the HTTP request array.
+   *
+   * Load the entire HTTP request array or a specific request field.
+   *
+   * @param int|null $header_name
+   *   (optional) The numeric id of the request or NULL to load all requests.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   The HTTP request array or an array containing the request field information.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_request($header_name = NULL) {
+    if (is_null($header_name)) {
+      return c_base_return_array::s_new($this->request);
+    }
+
+    if (!is_int($header_name) || !array_key_exists($header_name, $this->request)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->request[$header_name]);
+  }
+
+  /**
+   * Get the HTTP response array.
+   *
+   * Load the entire HTTP response array.
+   *
+   * @return c_base_return_array
+   *   The HTTP response array.
+   */
+  public function get_response() {
+    return c_base_return_array::s_new($this->response);
+  }
+
+  /**
+   * Assign the class name as the language class string.
+   *
+   * @param string $class_name
+   *   A string name representing an object that is a sub-class of i_base_language.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_language_class($class_name) {
+    if (!is_string($class_name) || !is_subclass_of('i_base_language', $class_name) ) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->language_class = $class_name;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the language class string currently assigned to this class.
+   *
+   * @return c_base_return_string
+   *   The language class string.
+   */
+  public function get_language_class() {
+    return c_return_status_string::s_new($this->language_class);
+  }
+
+  /**
+   * Get the HTTP request timestamp..
+   *
+   * @return c_base_return_float|c_base_return_status
+   *   The HTTP request time.
+   *   FALSE without error bit is returned when the request timestamp has not yet been loaded
+   */
+  public function get_request_time() {
+    if (is_null($this->request_time)) {
+      return new c_base_return_false();
+    }
+
+    return c_base_return_float::s_new($this->request_time);
+  }
+
+  /**
+   * Load, process, and interpret all of the supported http request headers.
+   */
+  public function do_load_request() {
+    if (!is_array($this->headers)) {
+      $this->p_get_all_headers();
+    }
+
+    // force the request array to be defined.
+    $this->request = array();
+
+    $initialize_keys = array(
+      self::REQUEST_ACCEPT,
+      self::REQUEST_ACCEPT_CHARSET,
+      self::REQUEST_ACCEPT_ENCODING,
+      self::REQUEST_ACCEPT_LANGUAGE,
+      self::REQUEST_ACCEPT_DATETIME,
+      self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD,
+      self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS,
+      self::REQUEST_AUTHORIZATION,
+      self::REQUEST_CACHE_CONTROL,
+      self::REQUEST_CONNECTION,
+      self::REQUEST_COOKIE,
+      self::REQUEST_CONTENT_LENGTH,
+      self::REQUEST_CONTENT_TYPE,
+      self::REQUEST_DATE,
+      self::REQUEST_EXPECT,
+      self::REQUEST_FROM,
+      self::REQUEST_HOST,
+      self::REQUEST_IF_MATCH,
+      self::REQUEST_IF_MODIFIED_SINCE,
+      self::REQUEST_IF_NONE_MATCH,
+      self::REQUEST_IF_RANGE,
+      self::REQUEST_IF_UNMODIFIED_SINCE,
+      self::REQUEST_MAX_FORWARDS,
+      self::REQUEST_ORIGIN,
+      self::REQUEST_PRAGMA,
+      self::REQUEST_PROXY_AUTHORIZATION,
+      self::REQUEST_RANGE,
+      self::REQUEST_REFERER,
+      self::REQUEST_TE,
+      self::REQUEST_USER_AGENT,
+      self::REQUEST_UPGRADE,
+      self::REQUEST_VIA,
+      self::REQUEST_WARNING,
+      self::REQUEST_X_REQUESTED_WITH,
+      self::REQUEST_X_FORWARDED_FOR,
+      self::REQUEST_X_FORWARDED_HOST,
+      self::REQUEST_X_FORWARDED_PROTO,
+      self::REQUEST_CHECKSUM_HEADER,
+      self::REQUEST_CHECKSUM_HEADERS,
+      self::REQUEST_CHECKSUM_CONTENT,
+    );
+
+    foreach ($initialize_keys as $initialize_key) {
+      $this->request[$initialize_key] = array(
+        'defined' => FALSE,
+        'invalid' => FALSE,
+        'data' => array(
+        ),
+      );
+    }
+    unset($initialize_key);
+    unset($initialize_keys);
+
+    // build an array of headers so that unknown/unsupported headers can still be processed.
+    $headers = array_flip(array_keys($this->headers));
+
+    // additional keys for specific cases.
+    $this->request[self::REQUEST_ACCEPT]['types'] = array();
+
+
+    if (array_key_exists('accept', $this->headers)) {
+      $this->p_load_request_accept();
+      unset($headers['accept']);
+    }
+
+    if (array_key_exists('accept_language', $this->headers)) {
+      $this->p_load_request_accept_language();
+      unset($headers['accept_language']);
+    }
+
+    if (array_key_exists('accept_encoding', $this->headers)) {
+      $this->p_load_request_accept_encoding();
+      unset($headers['accept_encoding']);
+    }
+
+    if (array_key_exists('accept_charset', $this->headers)) {
+      $this->p_load_request_accept_charset();
+      unset($headers['accept_charset']);
+    }
+
+    if (array_key_exists('accept_datetime', $this->headers)) {
+      $this->p_load_request_accept_datetime();
+      unset($headers['accept_datetime']);
+    }
+
+    // @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+    if (array_key_exists('access_control_request_method', $this->headers)) {
+      $this->p_load_request_rawish('access_control_request_method', $this->REQUEST_ACCESS_CONTROL_REQUEST_METHOD, 256);
+      unset($headers['access_control_request_method']);
+    }
+
+    // @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+    if (array_key_exists('access_control_request_headers', $this->headers)) {
+      $this->p_load_request_rawish('access_request_allow_headers', $this->REQUEST_ACCESS_CONTROL_REQUEST_HEADERS, 256);
+      unset($headers['access_control_request_headers']);
+    }
+
+    // @see: https://tools.ietf.org/html/rfc7235#section-4.2
+    if (array_key_exists('authorization', $this->headers)) {
+      $this->p_load_request_rawish('authorization', $this->REQUEST_AUTHORIZATION, 4096);
+      unset($headers['authorization']);
+    }
+
+    if (array_key_exists('cache_control', $this->headers)) {
+      $this->p_load_request_cache_control();
+      unset($headers['cache_control']);
+    }
+
+    if (array_key_exists('connection', $this->headers)) {
+      $this->p_load_request_connection();
+      unset($headers['connection']);
+    }
+
+    if (array_key_exists('pragma', $this->headers)) {
+      $this->p_load_request_pragma();
+      unset($headers['pragma']);
+    }
+
+    if (is_array($_COOKIE) && !empty($_COOKIE)) {
+      $this->p_load_request_cookies();
+      unset($headers['cookie']);
+    }
+
+    if (array_key_exists('content_length', $this->headers)) {
+      $this->p_load_request_content_length();
+      unset($headers['content_length']);
+    }
+
+    if (array_key_exists('content_md5', $this->headers)) {
+      $this->p_load_request_content_md5();
+      unset($headers['content_md5']);
+    }
+
+    if (array_key_exists('content_checksum', $this->headers)) {
+      $this->p_load_request_content_checksum();
+      unset($headers['content_checksum']);
+    }
+
+    if (array_key_exists('content_type', $this->headers)) {
+      $this->p_load_request_content_type();
+      unset($headers['content_type']);
+    }
+
+    if (array_key_exists('date', $this->headers)) {
+      $this->p_load_request_date();
+      unset($headers['date']);
+    }
+
+    if (array_key_exists('expect', $this->headers)) {
+      $this->p_load_request_expect();
+      unset($headers['expect']);
+    }
+
+    if (array_key_exists('from', $this->headers)) {
+      $this->p_load_request_from();
+      unset($headers['from']);
+    }
+
+    if (array_key_exists('host', $this->headers)) {
+      $this->p_load_request_host();
+      unset($headers['host']);
+    }
+
+    if (array_key_exists('if_match', $this->headers)) {
+      $this->p_load_request_if_match();
+      unset($headers['if_match']);
+    }
+
+    if (array_key_exists('if_none_match', $this->headers)) {
+      $this->p_load_request_if_none_match();
+      unset($headers['if_none_match']);
+    }
+
+    if (array_key_exists('if_modified_since', $this->headers)) {
+      $this->p_load_request_if_modified_since();
+      unset($headers['if_modified_since']);
+    }
+
+    if (array_key_exists('if_unmodified_since', $this->headers)) {
+      $this->p_load_request_if_unmodified_since();
+      unset($headers['if_unmodified_since']);
+    }
+
+    if (array_key_exists('if_range', $this->headers)) {
+      $this->p_load_request_if_range();
+      unset($headers['if_range']);
+    }
+
+    if (array_key_exists('range', $this->headers)) {
+      $this->p_load_request_range();
+      unset($headers['range']);
+    }
+
+    if (array_key_exists('max_forwards', $this->headers)) {
+      $this->p_load_request_max_forwards();
+      unset($headers['max_forwards']);
+    }
+
+    if (array_key_exists('origin', $this->headers)) {
+      $this->p_load_request_origin();
+      unset($headers['origin']);
+    }
+
+    // @see: https://tools.ietf.org/html/rfc7235#section-4.4
+    if (array_key_exists('proxy_authorization', $this->headers)) {
+      $this->p_load_request_rawish('proxy_authorization', $this->REQUEST_PROXY_AUTHORIZATION, 4096);
+      unset($headers['proxy_authorization']);
+    }
+
+    if (array_key_exists('referer', $this->headers)) {
+      $this->p_load_request_referer();
+      unset($headers['referer']);
+    }
+
+    if (array_key_exists('te', $this->headers)) {
+      $this->p_load_request_te();
+      unset($headers['te']);
+    }
+
+    if (array_key_exists('user_agent', $this->headers)) {
+      $this->p_load_request_user_agent();
+      unset($headers['user_agent']);
+    }
+
+    if (array_key_exists('upgrade', $this->headers)) {
+      $this->p_load_request_upgrade();
+      unset($headers['upgrade']);
+    }
+
+    if (array_key_exists('via', $this->headers)) {
+      $this->p_load_request_via();
+      unset($headers['via']);
+    }
+
+    if (array_key_exists('warning', $this->headers)) {
+      $this->p_load_request_warning();
+      unset($headers['warning']);
+    }
+
+    if (array_key_exists('x_requested_with', $this->headers)) {
+      $this->p_load_request_rawish('x_requested_with', $this->REQUEST_X_REQUESTED_WITH, 64);
+      unset($headers['x_requested_with']);
+    }
+
+    if (array_key_exists('x_forwarded_for', $this->headers)) {
+      $this->p_load_request_rawish('x_forwarded_for', $this->REQUEST_X_FORWARDED_for, 512);
+      unset($headers['x_forwarded_for']);
+    }
+
+    if (array_key_exists('x_forwarded_host', $this->headers)) {
+      $this->p_load_request_rawish('x_forwarded_host', $this->REQUEST_X_FORWARDED_HOST, 512);
+      unset($headers['x_forwarded_host']);
+    }
+
+    if (array_key_exists('x_forwarded_proto', $this->headers)) {
+      $this->p_load_request_rawish('x_forwarded_proto', $this->REQUEST_X_FORWARDED_PROTO, 64);
+      unset($headers['x_forwarded_proto']);
+    }
+
+    if (array_key_exists('checksum_header', $this->headers)) {
+      $this->p_load_request_checksum_header();
+      unset($headers['checksum_header']);
+    }
+
+    if (array_key_exists('checksum_headers', $this->headers)) {
+      $this->p_load_request_checksum_headers();
+      unset($headers['checksum_headers']);
+    }
+
+    if (array_key_exists('checksum_content', $this->headers)) {
+      $this->p_load_request_checksum_content();
+      unset($headers['checksum_content']);
+    }
+
+
+    // process all unknown headers and store them in the unknown key, with a max size of 256.
+    if (!empty($headers)) {
+      foreach ($headers as $header_name => $header_value) {
+        $this->p_load_request_unknown($header_name, self::REQUEST_UNKNOWN, 256);
+      }
+      unset($header_name);
+      unset($header_value);
+    }
+    unset($headers);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: access-control-allow-origin.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @param string $uri
+   *   The uri to assign to the specified header.
+   *   The wildcard character '*' is also allowed.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function set_response_access_control_allow_origin($uri) {
+    if (!is_string($uri)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($uri == c_base_ascii::ASTERISK) {
+      $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN] = array('wildcard' => TRUE);
+    }
+    else {
+      $parsed = $this->p_parse_uri($uri);
+      if ($parsed['invalid']) {
+        unset($parsed);
+        return c_base_return_error::s_false();
+      }
+      unset($parsed['invalid']);
+
+      $parsed['wildcard'] = FALSE;
+      $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN] = $parsed;
+      unset($parsed);
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: access-control-allow-credentials.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @param bool $allow_credentials
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function set_response_access_control_allow_credentials($allow_credentials) {
+    if (!is_bool($allow_credentials)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS] = $allow_credentials;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: access-control-expose-headers.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   * @todo: should this be written in the same way as set_response_allow()?
+   *
+   * @param string $header_name
+   *   The header name to assign to the specified header.
+   * @param bool $append
+   *   (optional) If TRUE, then append the header name.
+   *   If FALSE, then assign the header name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function set_response_access_control_expose_headers($header_name, $append = TRUE) {
+    if (!is_string($header_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    $parsed = $this->p_prepare_token($header_name);
+    if ($parsed === FALSE) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    if ($append) {
+      $this->response[self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS][$parsed] = $parsed;
+    }
+    else {
+      $this->response[self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS] = array($parsed => $parsed);
+    }
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: access-control-max-age.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @param int|float $timestamp
+   *   The timestamp to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function set_response_access_control_max_age($timestamp) {
+    if (!is_int($timestamp) && !is_float($timestamp)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_ACCESS_CONTROL_MAX_AGE] = $timestamp;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: access-control-allow-methods.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @param int $method
+   *   The code representing the method to assign to the specified header.
+   * @param bool $append
+   *   (optional) If TRUE, then append the header name.
+   *   If FALSE, then assign the header name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function set_response_access_control_allow_methods($method, $append = TRUE) {
+    if (!is_int($uri)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($append) {
+      $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS][$method] = $method;
+    }
+    else {
+      $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS] = array($method => $method);
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: access-control-allow-headers.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @param string $header_name
+   *   The header name to assign to the specified header.
+   * @param bool $append
+   *   (optional) If TRUE, then append the header name.
+   *   If FALSE, then assign the header name.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function set_response_access_control_allow_headers($header_name, $append = TRUE) {
+    if (!is_string($header_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    $parsed = $this->p_prepare_token($header_name);
+    if ($parsed === FALSE) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    if ($append) {
+      $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS][$parsed] = $parsed;
+    }
+    else {
+      $this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS] = array($parsed => $parsed);
+    }
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: accept-patch.
+   *
+   * @param string $media_type
+   *   The media type to assign to the specified header.
+   * @param bool $append
+   *   (optional) If TRUE, then append the header name.
+   *   If FALSE, then assign the header name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::pr_rfc_string_is_media_type()
+   * @see: https://tools.ietf.org/html/rfc5789#section-3.1
+   * @see: https://tools.ietf.org/html/rfc2616#section-3.7
+   */
+  public function set_response_accept_patch($media_type, $append = TRUE) {
+    if (!is_string($media_type)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    $text = $this->pr_rfc_string_prepare($media_type);
+    if ($text['invalid']) {
+      unset($text);
+
+      return c_base_return_error::s_false();
+    }
+
+    $parsed = $this->pr_rfc_string_is_media_type($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($parsed['invalid']) {
+      unset($parsed);
+
+      return c_base_return_error::s_false();
+    }
+    unset($parsed['invalid']);
+    unset($parsed['current']);
+
+    if ($append) {
+      $this->response[self::RESPONSE_ACCEPT_PATCH][] = $parsed;
+    }
+    else {
+      $this->response[self::RESPONSE_ACCEPT_PATCH] = array($parsed);
+    }
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: accept_ranges.
+   *
+   * @param string $ranges
+   *   An string representing ranges to assign to the specified header with the following structure:
+   *   - 1*(tchar)
+   *
+   *   Common ranges are:
+   *   - bytes
+   *   - none
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7233#section-2.3
+   * @see: https://tools.ietf.org/html/rfc7233#section-3.1
+   */
+  public function set_response_accept_ranges($ranges) {
+    if (!is_string($ranges)) {
+      return c_base_return_error::s_false();
+    }
+
+    $parsed = $this->p_prepare_token($ranges);
+    if ($parsed === FALSE) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_ACCEPT_RANGES] = $parsed;
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: age.
+   *
+   * @param int $seconds
+   *   The number of seconds to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.1
+   */
+  public function set_response_age($seconds) {
+    if (!is_int($seconds)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_AGE] = $seconds;
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: allow.
+   *
+   * When HTTP_METHOD_NONE is passed, this forces all existing values to be removed ($append will be ignored).
+   *
+   * @param int $allow
+   *   A code representing the specific allow method.
+   * @param bool $append
+   *   (optional) If TRUE, then append the header name.
+   *   If FALSE, then assign the header name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.4.1
+   */
+  public function set_response_allow($allow, $append = TRUE) {
+    if (!is_int($allow)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    switch ($allow) {
+      case self::HTTP_METHOD_NONE:
+      case self::HTTP_METHOD_GET:
+      case self::HTTP_METHOD_HEAD:
+      case self::HTTP_METHOD_POST:
+      case self::HTTP_METHOD_PUT:
+      case self::HTTP_METHOD_DELETE:
+      case self::HTTP_METHOD_TRACE:
+      case self::HTTP_METHOD_OPTIONS:
+      case self::HTTP_METHOD_CONNECT:
+      case self::HTTP_METHOD_PATCH:
+      case self::HTTP_METHOD_TRACK:
+      case self::HTTP_METHOD_DEBUG:
+        break;
+      default:
+        return c_base_return_error::s_false();
+    }
+
+    if ($allow == self::HTTP_METHOD_NONE) {
+      $this->response[self::RESPONSE_ALLOW] = array($allow => $allow);
+      return new c_base_return_true();
+    }
+
+    if ($append) {
+      $this->response[self::RESPONSE_ALLOW][$allow] = $allow;
+    }
+    else {
+      $this->response[self::RESPONSE_ALLOW] = array($allow => $allow);
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: cache-control.
+   *
+   * According to the standard, the cache-control directive has the following structure:
+   * -  1*(tchar) *("=" (1*(tchar) / quoted-string))
+   *
+   * It then later describes the use of multiple-cache control directives using commas to separate.
+   * This is very misleading and so the standards own definition of "cache-directive" is inconsistent with itself.
+   *
+   * Based on what I have seen in practice, the cache-control directive should instead be treated as:
+   * 1*(tchar) *("=" 1*(1*(tchar) / quoted-string) *(*(wsp) "," *(wsp) 1*(tchar) *("=" 1*(1*(tchar) / quoted-string))
+   *
+   * @param int $directive_name
+   *   May be an integer id code representing the name or a literal string name of the directive.
+   * @param string $directive_value
+   *   (optional) The value of the directive, if one exists, which has the following structure:
+   *   - 1*(1*(tchar) / quoted-string)
+   * @param bool $append
+   *   (optional) If TRUE, then append the header name.
+   *   If FALSE, then assign the header name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.2
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.2.3
+   */
+  public function set_response_cache_control($directive_name, $directive_value = NULL, $append = TRUE) {
+    if (!is_int($directive_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($directive_value) && !is_string($directive_value)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    switch($directive_name) {
+      case self::CACHE_CONTROL_NO_CACHE:
+      case self::CACHE_CONTROL_NO_STORE:
+      case self::CACHE_CONTROL_NO_TRANSFORM:
+      case self::CACHE_CONTROL_MAX_AGE:
+      case self::CACHE_CONTROL_MAX_AGE_S:
+      case self::CACHE_CONTROL_MAX_STALE:
+      case self::CACHE_CONTROL_MIN_FRESH:
+      case self::CACHE_CONTROL_ONLY_IF_CACHED:
+      case self::CACHE_CONTROL_PUBLIC:
+      case self::CACHE_CONTROL_PRIVATE:
+      case self::CACHE_CONTROL_MUST_REVALIDATE:
+      case self::CACHE_CONTROL_PROXY_REVALIDATE:
+        break;
+
+      default:
+        return c_base_return_error::s_false();
+    }
+
+    $parsed_directive_value = NULL;
+    if (!is_null($directive_value)) {
+      $text = $this->pr_rfc_string_prepare($directive_value);
+      if ($text['invalid']) {
+        unset($text);
+
+        return c_base_return_error::s_false();
+      }
+
+      $parsed = $this->pr_rfc_string_is_token_quoted($text['ordinals'], $text['characters']);
+      unset($text);
+
+      if ($parsed['invalid']) {
+        unset($parsed);
+
+        return c_base_return_error::s_false();
+      }
+      unset($parsed);
+    }
+
+    if ($append) {
+      $this->response[self::RESPONSE_CACHE_CONTROL][$directive_name] = $directive_value;
+    }
+    else {
+      $this->response[self::RESPONSE_CACHE_CONTROL] = array($directive_name => $directive_value);
+    }
+
+    unset($parsed_directive_value);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: connection.
+   *
+   * @param string $header_name
+   *   The header name to assign as a connection-specific field.
+   *   These header fields apply only to the immediate client.
+   *   The header name format is:
+   *   - 1*(tchar)
+   * @param bool $append
+   *   (optional) If TRUE, then append the header name.
+   *   If FALSE, then assign the header name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7230#section-6.1
+   */
+  public function set_response_connection($header_name, $append = TRUE) {
+    if (!is_string($header_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    $parsed = $this->p_prepare_token($header_name);
+    if ($parsed === FALSE) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    if ($append) {
+      $this->response[self::RESPONSE_CONNECTION][$parsed] = $parsed;
+    }
+    else {
+      $this->response[self::RESPONSE_CONNECTION] = array($parsed => $parsed);
+    }
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response content (the data/body).
+   *
+   * The term 'content' and 'body' seem to be used interchangably.
+   * The header fields often refer to this as 'content', therefore this will be called 'content' and neither 'body' nor 'data'.
+   *
+   * This can either be a representation of one or more files (by their filename) or a string (binary or otherwise).
+   * - Appending a file after a string is assigned or vice-versa will result in the previous data being deleted.
+   *
+   * @param string $content
+   *   The content to assign.
+   *   This could be either a string or binary a binary string.
+   *   If $is_file is TRUE, then this is the local filename to the content.
+   * @param bool $append
+   *   (optional) If TRUE, then append the content or filename.
+   *   If FALSE, then assign the content or filename.
+   * @param bool $is_file
+   *   (optional) If TRUE, then $content is treated as a file name.
+   *   If FALSE, then is_file will be assigned to FALSE.
+   *   If $append is TRUE, then this argument is ignored.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_content($content, $append = TRUE, $is_file = FALSE) {
+    if (!is_string($content)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($is_file)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($append) {
+      if ($this->content_is_file) {
+        if (is_array($this->content)) {
+          $this->content[] = $content;
+        }
+        else {
+          $this->content = array($content);
+        }
+
+
+      }
+      else {
+        if (is_array($this->content)) {
+          $this->content = $content;
+        }
+        else {
+          $this->content .= $content;
+        }
+      }
+    }
+    else {
+      unset($this->content);
+
+      if ($is_file) {
+        $this->content_is_file = TRUE;
+        $this->content = array($content);
+      }
+      else {
+        $this->content_is_file = FALSE;
+        $this->content = $content;
+      }
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: content-disposition.
+   *
+   * The standard defines this as:
+   * - 1*(tchar) *(";" 1*(token) "=" 1*(1*(tchar) / 1*(quoted_string)))
+   *
+   * @param ?? $disposition
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc6266#section-4
+   */
+  public function set_response_content_disposition($disposition) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: content-encoding.
+   *
+   * The standard is horrible at describing the differences between content-encoding and transfer-encoding.
+   * Here is my understanding of the RFCs:
+   *
+   * Content Encoding:
+   * - Defines how the content is encoding as a complete entire content structure.
+   * - This is directly related to the mime-type, such that if there was a tarball like: example.html.gz, then content-encoding could be set to gzip to represent the mimetype for example.html (which might be: text/html).
+   *
+   * Transfer Encoding:
+   * - Defines the encoding applied to the content for the purpos of transmitting.
+   * - This is completely unrelated to the mime-type such that the example.html.gz could still be gzipped (such as using a different compression level) but it would represent example.html.gz and not example.html.gz.gz.
+   *
+   * This becomes more convoluted because the standard for content encoding states that it does not refer to encodings "inherent" to the content type.
+   * - If that is the case, then the file example.html.gz would have no content-encoding because the file being transmitted is example.html.gz.
+   *
+   * The standard uses "Content-Encoding: gzip" as an example, even though gzip is used as a transfer-encoding.
+   * - This means that the encoding is not about the content, but instead of how the content is being transferred (which sounds an awful lot like the words "transfer" and "encoding").
+   *
+   * That said, many sites and services out there appear to use (and document) that content-encoding is used for compressing data on transfer.
+   * - This directly conflicts with the standards requirements as the compression is done for the transfer.
+   *
+   * Now, with the transfer-encoding header, there is a major issue in which the standard states (under 3.3.2. Content-Length):
+   * - "A sender MUST NOT send a Content-Length header field in any message that contains a Transfer-Encoding header field."
+   * - This ruins and breaks some of the functionality and benefits (including security implications) provided by Content-Length.
+   * - This also contradicts its purpose of representing the message and the content.
+   * - Given that the content-length represents the content and not the transport of the data, requiring content-length to be removed is outright idiotic.
+   *
+   * Chunked transfer was originally designed with the idea that the size of the content is unknown.
+   * - In practice, many servers always send chunked data for performance reasons (such as progressive load jpeg).
+   * - In many of these cases where chunked is used, the content size is known.
+   *
+   * PHP (or possibly the web server) appears to almost always send chunked data.
+   * - This effectively forces a requirement of transfer-encoding: chunked, but never sets that and browsers still accept chunked transfer without the transfer encoding specifying it.
+   * - Clients still support receiving data in chunks despite a complete lack of the transfer-encoding header (by which the standard states an error must be thrown).
+   *
+   * Content-Length is used to tell clients how large the content is.
+   * - This has performance, integrity, and even security implications and is generally a good idea.
+   *
+   * Multiple content-encodings should also be avoided because multiple values in content-length does not relate to the multiple content-encodings.
+   * - The content-length is supposed to represent the content and not the transmittion of the content and not allowing a content-length to match every content-encoding is yet another contradiction.
+   * - Effectively, content-length, when used with multiple values is treated as a transfer-encoding length (which is the contradiction!):
+   *   - "If a message is received that has multiple Content-Length header fields with field-values consisting of the same decimal value, or a single Content-Length header field with a field value containing a list of identical decimal values (e.g., "Content-Length: 42, 42"), indicating that duplicate Content-Length header fields have been generated or combined by an upstream message processor, then the recipient MUST either reject the message as invalid or replace the duplicated field-values with a single valid Content-Length field containing that decimal value prior to determining the message body length or forwarding the message.".
+   * - This leaves content-length with three possible (contradictory) interpretations:
+   *   1) content-length represents the original content (doing this causes invalid message-length errors on web browsers).
+   *   2) content-length represents the length of only the first content encoding (doing this probably causes errors on the web browsers).
+   *   3) content-length represents the length of the last encoding (there is no way to get or confirm the length based on the headers for each individual encoding).
+   *   - The problem is, using #3 (which is the only one that seems to work), content-length must be interepreted as a transfer-encoding content-length (again, there is that annoying contradiction).
+   *
+   * In summary, I recommend the following:
+   * - Never use transfer-encoding, it contradicts itself and content-encoding.
+   * - Avoid multiple content-encodings.
+   * - The standard is poorly written and many clients do not follow the standard (probably due to its poor design).
+   *
+   * @todo: this should be an array of values in the order in which the encoding is applied.
+   *
+   * @param int $encoding
+   *   The encoding to assign to the specified header.
+   * @param bool $append
+   *   (optional) If TRUE, then append the header name.
+   *   If FALSE, then assign the header name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::set_response_transfer_encoding()
+   * @see: self::set_response_content_length()
+   * @see: https://tools.ietf.org/html/rfc7231#section-3.1.2.2
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.1
+   */
+  public function set_response_content_encoding($encoding, $append = TRUE) {
+    if (!is_int($encoding)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    switch ($encoding) {
+      case self::ENCODING_CHUNKED:
+      case self::ENCODING_COMPRESS:
+      case self::ENCODING_DEFLATE:
+      case self::ENCODING_GZIP:
+      case self::ENCODING_BZIP:
+      case self::ENCODING_LZO:
+      case self::ENCODING_XZ:
+      case self::ENCODING_EXI:
+      case self::ENCODING_IDENTITY:
+      case self::ENCODING_SDCH:
+      case self::ENCODING_PG:
+        break;
+      default:
+        return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_CONTENT_ENCODING] = $encoding;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: content-language.
+   *
+   * @param int|null $language
+   *   The language code to assign to the specified header.
+   *   If NULL, then the default language according the the given language class is used.
+   *   If NULL and the default language is not set, then FALSE with error bit set is returned.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-3.1.3.2
+   */
+  public function set_response_content_language($language = NULL) {
+    if (!is_null($language) && !is_int($language)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_null($language)) {
+      if (!is_object($this->language_class) || !($this->language_class instanceof i_base_language)) {
+        return c_base_return_error::s_false();
+      }
+
+      $default = $this->language_class->s_get_default_id();
+      if ($default instanceof c_base_return_false) {
+        unset($default);
+        return c_base_return_error::s_false();
+      }
+
+      $this->response[self::RESPONSE_CONTENT_LANGUAGE] = $default;
+      unset($default);
+    }
+    else {
+      if ($language_class->s_get_names_by_id($language) instanceof c_base_return_false) {
+        return c_base_return_error::s_false();
+      }
+
+      $this->response[self::RESPONSE_CONTENT_LANGUAGE] = $language;
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: content-length.
+   *
+   * Read the function comments for self::set_response_transfer_encoding().
+   *
+   * @param int|null $length
+   *   (optional) The content-length, representing the total number of octals (8-bits).
+   *   Set to NULL for auto-calculation from already assigned data.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE without error bit set is returned when the Transfer-Encoding header field is assigned.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::set_response_content_encoding()
+   * @see: self::set_response_transfer_encoding()
+   * @see: self::p_calculate_content_length()
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.2
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.3
+   */
+  public function set_response_content_length($length = NULL) {
+    if (!is_null($length) && !is_int($length) || $length < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    // From the RFC: "A sender MUST NOT send a Content-Length header field in any message that contains a Transfer-Encoding header field."
+    if (array_key_exists(self::RESPONSE_TRANSFER_ENCODING, $this->response)) {
+      return new c_base_return_false();
+    }
+
+    if (is_null($length)) {
+      if (is_null($this->content)) {
+        $this->response[self::RESPONSE_CONTENT_LENGTH] = 0;
+      }
+      else {
+        if ($this->content_is_file) {
+          $this->response[self::RESPONSE_CONTENT_LENGTH] = 0;
+
+          foreach ($this->content as $filename) {
+            if (!file_exists($filename)) {
+              unset($filename);
+              // @todo: provide a file not found error.
+              return c_base_return_error::s_false();
+            }
+
+            $this->response[self::RESPONSE_CONTENT_LENGTH] += filesize($filename);
+          }
+        }
+        else {
+          $this->response[self::RESPONSE_CONTENT_LENGTH] = $this->p_calculate_content_length($this->content);
+        }
+      }
+    }
+    else {
+      $this->response[self::RESPONSE_CONTENT_LENGTH] = $length;
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: content_range.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_content_range($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: content_type.
+   *
+   * @todo: implement a thorough sanity check, this currently uses a simple check.
+   *
+   * @param string $content_type
+   *   The content type to assign to the specified header.
+   * @param int $charset
+   *   (optional) The character set to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::set_response_encoding()
+   * @see: https://tools.ietf.org/html/rfc7231#section-3.1.1.5
+   */
+  public function set_response_content_type($content_type, $charset = c_base_charset::UTF_8) {
+    if (!is_string($content_type)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!c_base_charset::s_is_valid($charset)) {
+      return c_base_return_error::s_false();
+    }
+
+    // perform a very basic syntax check.
+    if (strpos($content_type, ';')) {
+      return c_base_return_error::s_false();
+    }
+
+    $content_type_part = mb_split('/', $content_type);
+
+    if (count($content_type_part) != 2) {
+      unset($content_type_part);
+      return c_base_return_error::s_false();
+    }
+    unset($content_type_part);
+
+    $this->response[self::RESPONSE_CONTENT_TYPE] = array(
+      'type' => mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $content_type)),
+      'charset' => $charset,
+    );
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: date.
+   *
+   * @param int|float|null $timestamp
+   *   (optional) The timestamp to assign to the specified header.
+   *   When NULL, the $request_time timestamp defined by this class is used.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.1.2
+   */
+  public function set_response_date($timestamp = NULL) {
+    if (is_null($timestamp)) {
+      if (is_null($this->request_time)) {
+        $this->request_time = microtime(TRUE);
+      }
+
+      $this->response[self::RESPONSE_DATE_ACTUAL] = $this->request_time;
+      return new c_base_return_true();
+    }
+
+    if (!is_int($timestamp) && !is_float($timestamp)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_DATE] = $timestamp;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: date_actual.
+   *
+   * This is identical to the HTTP response header: date.
+   * The purpose of this is to allow clients to still receive the correct/actual date when HTTP servers, such as apache, overwrite or alter the HTTP date response header.
+   * This should therefore be used and calculated with when the date variable.
+   *
+   * @param int|float|null $timestamp
+   *   (optional) The timestamp to assign to the specified header.
+   *   When NULL, the $request_time timestamp defined by this class is used.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.1.2
+   */
+  public function set_response_date_actual($timestamp = NULL) {
+    if (is_null($timestamp)) {
+      if (is_null($this->request_time)) {
+        $this->request_time = microtime(TRUE);
+      }
+
+      $this->response[self::RESPONSE_DATE_ACTUAL] = $this->request_time;
+      return new c_base_return_true();
+    }
+
+    if (!is_int($timestamp) && !is_float($timestamp)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_DATE_ACTUAL] = $timestamp;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: etag.
+   *
+   * The auto-generated e-tag will be a checksum.
+   * - The checksum will be against the assigned content and should be called before any content encoding is performed.
+   *
+   * @param string|null|bool $entity_tag
+   *   (optional) The entity tag to assign to the specified header.
+   *   If NULL, then the entity tag will be auto-calculated from the content using a full sha256 checksum.
+   *   When NULL, the content must be fully defined or the checksum will be invalid.
+   *   If FALSE, the entity tag will not be changed, only the weak setting will be changed.
+   *   TRUE is not used and is considered invalid.
+   * @param bool $weak
+   *   (optional) Set to TRUE to enable a weak entity-tag.
+   *   When $entity_tag is NULL, the first 9 characters of the generated entity-tag will be used for the 'weak' entity-tag.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232#section-2.3
+   */
+  public function set_response_etag($entity_tag = NULL, $weak = FALSE) {
+    if (!is_null($entity_tag) && !is_string($entity_tag) && $entity_tag !== FALSE) {
+      return c_base_return_error::s_false();
+    }
+
+    $response = array(
+      'tag' => '',
+      'weak' => FALSE,
+    );
+
+    if (is_null($entity_tag)) {
+
+      if ($weak) {
+        $response['tag'] = 'partial:sha256:';
+      }
+      else {
+        $response['tag'] = 'full:sha256:';
+      }
+
+      if ($this->content_is_file) {
+        // @todo: determine how to handle multiple files and the checksum!
+        // this might be inefficient in that the files will likely need to be buffered and then have the string checksumed.
+        // look into: hash_update_file()
+        if (!is_array($this->content)) {
+          unset($response);
+
+          // @todo: report warning about no content file specified.
+          return new c_base_return_false();
+        }
+
+        $hash = hash_init('sha256');
+        foreach ($this->content as $filename) {
+          if (!file_exists($filename)) {
+            unset($filename);
+            unset($hash);
+            unset($response);
+
+            // @todo: report file not found or other related errors.
+            return c_base_return_error::s_false();
+          }
+
+          $success = hash_update_file($hash, $filename);
+          if (!$success) {
+            unset($success);
+            unset($filename);
+            unset($hash);
+            unset($response);
+
+            // @todo: report failure to hash file.
+            return c_base_return_error::s_false();
+          }
+        }
+        unset($filename);
+        unset($success);
+
+        $response['tag'] = hash_final($hash, FALSE);
+        unset($hash);
+
+        if ($weak) {
+          // Keep the first 15 characters for 'partial:sha256:' plus the first 9 characters of the checksum.
+          $response['tag'] = substr($response['tag'], 0, 24);
+        }
+      }
+      else {
+        if (!is_string($this->content)) {
+          unset($response);
+
+          // @todo: report warning about no content specified.
+          return new c_base_return_false();
+        }
+
+        $response['tag'] = hash('sha256', $this->content, FALSE);
+      }
+    }
+    elseif ($entity_tag !== FALSE) {
+      $response['tag'] = $entity_tag;
+    }
+
+    $response['weak'] = $weak;
+
+    $this->response[self::RESPONSE_ETAG] = $response;
+    unset($response);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: expires.
+   *
+   * From the RFC: "The value in Expires is only intended for recipients that have not yet implemented the Cache-Control field.".
+   *
+   * @param int|float $timestamp
+   *   The unix timestamp to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.3
+   */
+  public function set_response_expires($timestamp) {
+    if (!is_int($timestamp) && !is_float($timestamp)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_EXPIRES] = $timestamp;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: last-modified.
+   *
+   * @param int|float $timestamp
+   *   The Unix timestamp to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232#section-2.2
+   */
+  public function set_response_last_modified($timestamp) {
+    if (!is_int($timestamp) && !is_float($timestamp)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_LAST_MODIFIED] = $timestamp;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: link.
+   *
+   * Use self::SCHEME_LOCAL for a local filesystem link.
+   *
+   * @param string $uri
+   *   The URI to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc5988#section-5
+   * @see: https://tools.ietf.org/html/rfc3986
+   */
+  public function set_response_link($uri) {
+    if (!is_string($uri)) {
+      return c_base_return_error::s_false();
+    }
+
+    #$parsed = $this->p_parse_uri($uri);
+
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: location.
+   *
+   * Use self::SCHEME_LOCAL for a local filesystem link.
+   *
+   * @param string $uri
+   *   The URI to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986
+   */
+  public function set_response_location($uri) {
+    if (!is_string($uri)) {
+      return c_base_return_error::s_false();
+    }
+
+    $parsed = $this->p_parse_uri($uri);
+    if ($parsed['invalid']) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    unset($parsed['invalid']);
+
+    $this->response[self::RESPONSE_LOCATION] = $parsed;
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: pragma.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.4
+   */
+  public function set_response_pragma($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: proxy-authenticate.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#section-4.3
+   */
+  public function set_response_proxy_authenticate($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: public-key-pins.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7469
+   */
+  public function set_response_public_key_pins($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: refresh.
+   *
+   * This is not defined in any RFC but is supported by many browser.
+   * It is recommended to never use this.
+   * Instead, try using the HTTP header: location.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: set_response_location()
+   * @see: https://en.wikipedia.org/wiki/Meta_refresh
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  public function set_response_refresh($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: retry-after.
+   *
+   * @param int|float $date
+   *   When $seconds is FALSE, a unix timestamp representing the retry date.
+   *   When $seconds is TRUE, the number of seconds to retry after.
+   *
+   * @param bool $seconds
+   *   (optional) Used to change the interpretation of the $data parameter.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.3
+   */
+  public function set_response_retry_after($date, $seconds = FALSE) {
+    if (!is_int($date) || !is_float($timestamp)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($seconds)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_RETRY_AFTER] = array(
+      'value' => $date,
+      'is_seconds' => $seconds,
+    );
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: server.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.4.2
+   */
+  public function set_response_server($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: set_cookie.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_set_cookie($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: status.
+   *
+   * @param int $code
+   *   The status code to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232#section-4
+   */
+  public function set_response_status($code) {
+    if (!is_int($code)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_STATUS] = $code;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: strict-transport-security.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_strict_transport_security($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: trailer.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_trailer($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: transfer-encoding.
+   *
+   * Read the function comments for self::set_response_transfer_encoding().
+   *
+   * @fixme: this should be an array of options that are essentially identical to content-encoding.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE without error bit set is returned when the Content-Length header field is assigned.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::set_response_content_encoding()
+   * @see: self::set_response_content_length()
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.2
+   */
+  public function set_response_transfer_encoding($value) {
+    // From the RFC: "A sender MUST NOT send a Content-Length header field in any message that contains a Transfer-Encoding header field."
+    if (array_key_exists(self::RESPONSE_CONTENT_LENGTH, $this->response)) {
+      unset($this->response[self::RESPONSE_CONTENT_LENGTH]);
+    }
+
+    // RESPONSE_TRANSFER_ENCODING
+
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: upgrade.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_upgrade($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: vary.
+   *
+   * @param string $header_name
+   *   The name of a header field to assign to the specified header.
+   * @param bool $append
+   *   (optional) Set to TRUE to append values instead of assigning.
+   *   Set to FALSE to assign a new value.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.4
+   */
+  public function set_response_vary($header_name, $append = TRUE) {
+    if (!is_string($header_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    $parsed = $this->p_prepare_token($header_name);
+    if ($parsed === FALSE) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    if ($append) {
+      $this->response[self::RESPONSE_VARY][$parsed] = $parsed;
+    }
+    else {
+      $this->response[self::RESPONSE_VARY] = array($parsed => $parsed);
+    }
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: warning.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.5
+   */
+  public function set_response_warning($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: www_authenticate.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#section-4.1
+   */
+  public function set_response_www_authenticate($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: HTTP Protocol.
+   *
+   * @param string $protocol
+   *   A string representing the HTTP protocol, such as: "HTTP 1.1".
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_protocol($protocol) {
+    if (!is_string($protocol)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_PROTOCOL] = $protocol;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: content-security-policy.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_content_security_policy($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: x-content-type-options.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_x_content_type_options($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: x-ua-compatible.
+   *
+   * @param ?? $value
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_x_ua_compatible($value) {
+    // @todo
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign HTTP response header: checksum_header.
+   *
+   * @param int $action
+   *   (optional) Define how the checksum is to be processed.
+   *   Can only be one of:
+   *   - self::CHECKSUM_ACTION_NONE
+   *   - self::CHECKSUM_ACTION_AUTO
+   *   - self::CHECKSUM_ACTION_MANUAL
+   * @param int|null $what
+   *   (optional) An integer representing the checksum what, can be one of:
+   *   - self::CHECKSUM_WHAT_FULL
+   *   - self::CHECKSUM_WHAT_PARTIAL
+   *   This is only processed when $action is set to CHECKSUM_ACTION_MANUAL.
+   *   Otherwise, this must be NULL.
+   * @param int|null $type
+   *   (optional) An integer representing the checksum algorithm type.
+   *   This is only processed when $action is set to CHECKSUM_ACTION_MANUAL.
+   *   Otherwise, this must be NULL.
+   * @param string|null $checksum
+   *   (optional) A checksum that represents the content.
+   *   This is only processed when $action is set to CHECKSUM_ACTION_MANUAL.
+   *   Otherwise, this must be NULL.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_checksum_header($action = self::CHECKSUM_ACTION_AUTO, $what = NULL, $type = NULL, $checksum = NULL) {
+    if (!is_int($action)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($action != self::CHECKSUM_ACTION_NONE && $action != self::CHECKSUM_ACTION_AUTO && $action != self::CHECKSUM_ACTION_MANUAL) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($action == self::CHECKSUM_ACTION_MANUAL) {
+      if (!is_int($what) || !is_int($type) || !is_string($checksum)) {
+        return c_base_return_error::s_false();
+      }
+
+      if ($what != self::CHECKSUM_WHAT_PARTIAL && $what != self::CHECKSUM_WHAT_FULL) {
+        return c_base_return_error::s_false();
+      }
+
+      switch ($type) {
+        case CHECKSUM_MD2:
+        case CHECKSUM_MD4:
+        case CHECKSUM_MD5:
+        case CHECKSUM_SHA1:
+        case CHECKSUM_SHA224:
+        case CHECKSUM_SHA256:
+        case CHECKSUM_SHA384:
+        case CHECKSUM_SHA512:
+        case CHECKSUM_CRC32:
+        case CHECKSUM_PG:
+          break;
+        default:
+          return c_base_return_error::s_false();
+      }
+
+      $this->response[self::RESPONSE_CHECKSUM_HEADER] = array(
+        'checksum' => $checksum,
+        'action' => $action,
+        'what' => $what,
+        'type' => $type,
+      );
+    }
+    else {
+      if (!is_null($what) || !is_null($type) || !is_null($checksum)) {
+        return c_base_return_error::s_false();
+      }
+
+      $this->response[self::RESPONSE_CHECKSUM_HEADER] = array(
+        'checksum' => NULL,
+        'action' => $action,
+        'what' => self::CHECKSUM_WHAT_FULL,
+        'type' => self::CHECKSUM_SHA256,
+      );
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: checksum_headers.
+   *
+   * @param string $header_name
+   *   The header name to assign to the specified header.
+   * @param bool $append
+   *   (optional) If TRUE, then append the header name.
+   *   If FALSE, then assign the header name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_checksum_headers($header_name, $append = TRUE) {
+    if (!is_string($header_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($append)) {
+      return c_base_return_error::s_false();
+    }
+
+    $parsed = $this->p_prepare_token($header_name);
+    if ($parsed === FALSE) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    if ($append) {
+      $this->response[self::RESPONSE_CHECKSUM_HEADERS][$parsed] = $parsed;
+    }
+    else {
+      $this->response[self::RESPONSE_CHECKSUM_HEADERS] = array($parsed => $parsed);
+    }
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: checksum_content.
+   *
+   * @param int $action
+   *   (optional) Define how the checksum is to be processed.
+   *   Can only be one of:
+   *   - self::CHECKSUM_ACTION_NONE
+   *   - self::CHECKSUM_ACTION_AUTO
+   *   - self::CHECKSUM_ACTION_MANUAL
+   *   When $action is self::CHECKSUM_ACTION_AUTO, the checksum will not be calculated at this point in time.
+   * @param int|null $what
+   *   (optional) An integer representing the checksum what, can be one of:
+   *   - self::CHECKSUM_WHAT_FULL
+   *   - self::CHECKSUM_WHAT_PARTIAL
+   *   This is only processed when $action is set to CHECKSUM_ACTION_MANUAL.
+   *   Otherwise, this must be NULL.
+   * @param int|null $type
+   *   (optional) An integer representing the checksum algorithm type.
+   *   This is only processed when $action is set to CHECKSUM_ACTION_MANUAL.
+   *   Otherwise, this must be NULL.
+   * @param string|null $checksum
+   *   (optional) A checksum that represents the content.
+   *   This is only processed when $action is set to CHECKSUM_ACTION_MANUAL.
+   *   Otherwise, this must be NULL.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_checksum_content($action = self::CHECKSUM_ACTION_AUTO, $what = NULL, $type = NULL, $checksum = NULL) {
+    if (!is_int($action)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($action != self::CHECKSUM_ACTION_NONE && $action != self::CHECKSUM_ACTION_AUTO && $action != self::CHECKSUM_ACTION_MANUAL) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($action == self::CHECKSUM_ACTION_MANUAL) {
+      if (!is_int($what) || !is_int($type) || !is_string($checksum)) {
+        return c_base_return_error::s_false();
+      }
+
+      if ($what != self::CHECKSUM_WHAT_PARTIAL && $what != self::CHECKSUM_WHAT_FULL) {
+        return c_base_return_error::s_false();
+      }
+
+      switch ($type) {
+        case CHECKSUM_MD2:
+        case CHECKSUM_MD4:
+        case CHECKSUM_MD5:
+        case CHECKSUM_SHA1:
+        case CHECKSUM_SHA224:
+        case CHECKSUM_SHA256:
+        case CHECKSUM_SHA384:
+        case CHECKSUM_SHA512:
+        case CHECKSUM_CRC32:
+        case CHECKSUM_PG:
+          break;
+        default:
+          return c_base_return_error::s_false();
+      }
+
+      $this->response[self::RESPONSE_CHECKSUM_CONTENT] = array(
+        'checksum' => $checksum,
+        'action' => $action,
+        'what' => $what,
+        'type' => $type,
+      );
+    }
+    else {
+      if (!is_null($what) || !is_null($type) || !is_null($checksum)) {
+        return c_base_return_error::s_false();
+      }
+
+      $this->response[self::RESPONSE_CHECKSUM_CONTENT] = array(
+        'checksum' => NULL,
+        'action' => $action,
+        'what' => self::CHECKSUM_WHAT_FULL,
+        'type' => self::CHECKSUM_SHA256,
+      );
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Assign HTTP response header: content_revision.
+   *
+   * The content-revision is used to tell clients what revision this document or file is.
+   *
+   * @param int $revision
+   *   The value to assign to the specified header.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_response_content_revision($revision) {
+    if (!is_int($revision)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->response[self::RESPONSE_CONTENT_REVISION] = $revision;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Ensures that a given response value is not assigned.
+   *
+   * @param int $response_id
+   *   The ID representing a given response to unassign
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function unset_response_value($response_id) {
+    if (!is_int($response_id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($response_id, $this->response)) {
+      unset($this->response[$response_id]);
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Obtain HTTP response header: access-control-allow-origin.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @return c_base_return_array|bool
+   *   A decoded uri split into its different parts inside an array.
+   *   This array also contains a key called 'wildcard' which may be either TRUE or FALSE.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function get_response_access_control_allow_origin() {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN]);
+  }
+
+  /**
+   * Obtain HTTP response header: access-control-allow-credentials.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @return c_base_return_bool|c_base_return_status
+   *   A boolean representing whether or not to allow credentials.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function get_response_access_control_allow_credentials() {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_bool::s_new($this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS]);
+  }
+
+  /**
+   * Obtain HTTP response header: access-control-expose-headers.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array of headers to expose.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function get_response_access_control_expose_headers() {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS]);
+  }
+
+  /**
+   * Obtain HTTP response header: access-control-max-age.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   An Unix timestamp representing the specified header.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function get_response_access_control_max_age() {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_MAX_AGE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_ACCESS_CONTROL_MAX_AGE]);
+  }
+
+  /**
+   * Obtain HTTP response header: access-control-allow-methods.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @return c_base_return_array|bool
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  public function get_response_access_control_allow_methods() {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS]);
+  }
+
+  /**
+   * Obtain HTTP response header: access-control-allow-headers.
+   *
+   * @todo: I only glanced at the documentation (which was incomplete on wikipedia).
+   *        More work is likely needed to be done with this and its structure is subject to change.
+   *
+   * @return c_base_return_arrayl|c_base_return_status
+   *   An array of allowed headers is returned.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_access_control_allow_headers() {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS]);
+  }
+
+  /**
+   * Obtain HTTP response header: accept-patch.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing the header values.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc5789#section-3.1
+   * @see: https://tools.ietf.org/html/rfc2616#section-3.7
+   */
+  public function get_response_accept_patch() {
+    if (!array_key_exists(self::RESPONSE_ACCEPT_PATCH, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_ACCEPT_PATCH]);
+  }
+
+  /**
+   * Obtain HTTP response header: accept_ranges.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   A string representing the header value.
+   *
+   *   Common ranges are:
+   *   - bytes
+   *   - none
+   *
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7233#section-2.3
+   * @see: https://tools.ietf.org/html/rfc7233#section-3.1
+   */
+  public function get_response_accept_ranges() {
+    if (!array_key_exists(self::RESPONSE_ACCEPT_RANGES, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($this->response[self::RESPONSE_ACCEPT_RANGES]);
+  }
+
+  /**
+   * Obtain HTTP response header: age.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   A Unix timestamp representing the header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.1
+   */
+  public function get_response_age() {
+    if (!array_key_exists(self::RESPONSE_AGE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_AGE]);
+  }
+
+  /**
+   * Obtain HTTP response header: allow.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array of allow method codes.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.4.1
+   */
+  public function get_response_allow() {
+    if (!array_key_exists(self::RESPONSE_ALLOW, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_ALLOW]);
+  }
+
+  /**
+   * Obtain HTTP response header: cache-control.
+   *
+   * According to the standard, the cache-control directive has the following structure:
+   * -  1*(tchar) *("=" (1*(tchar) / quoted-string))
+   *
+   * It then later describes the use of multiple-cache control directives using commas to separate.
+   * This is very misleading and so the standards own definition of "cache-directive" is inconsistent with itself.
+   *
+   * Based on what I have seen in practice, the cache-control directive should instead be treated as:
+   * 1*(tchar) *("=" 1*(1*(tchar) / quoted-string) *(*(wsp) "," *(wsp) 1*(tchar) *("=" 1*(1*(tchar) / quoted-string))
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing the cache-control directives.
+   *   Each array key is a name and if that directive has no value, then the related directive name will have a NULL value.
+   *   For example, a directive of "no-cache" will have the following array structure:
+   *   - array("no-cache" => NULL)
+   *   For example, a directive of "private, max-age=32" will have the following array structure:
+   *   - array("private" => NULL, "max-age" => 32)
+   *
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.2
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.2.3
+   */
+  public function get_response_cache_control() {
+    if (!array_key_exists(self::RESPONSE_CACHE_CONTROL, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_CACHE_CONTROL]);
+  }
+
+  /**
+   * Obtain HTTP response header: connection.
+   *
+   * @return c_base_return_array|bool
+   *   An array of header names assigned to the connection header.
+   *   The header name format is:
+   *   - 1*(tchar)
+   *
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7230#section-6.1
+   */
+  public function get_response_connection() {
+    if (!array_key_exists(self::RESPONSE_CONNECTION, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_CONNECTION]);
+  }
+
+  /**
+   * Obtain HTTP response header: content-disposition.
+   *
+   * @return ??|c_base_return_status
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc6266#section-4
+   */
+  public function get_response_content_disposition() {
+    if (!array_key_exists(self::RESPONSE_CONTENT_DISPOSITION, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: content-encoding.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   An integer representing the content length value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_content_encoding() {
+    if (!array_key_exists(self::RESPONSE_CONTENT_ENCODING, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_CONTENT_ENCODING]);
+  }
+
+  /**
+   * Obtain HTTP response header: content-language.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   An integer representing the content length value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-3.1.3.2
+   */
+  public function get_response_content_language() {
+    if (!array_key_exists(self::RESPONSE_CONTENT_LANGUAGE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_CONTENT_LANGUAGE]);
+  }
+
+  /**
+   * Obtain HTTP response header: content-length.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   An integer containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_content_length() {
+    if (!array_key_exists(self::RESPONSE_CONTENT_LENGTH, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_CONTENT_LENGTH]);
+  }
+
+  /**
+   * Obtain HTTP response header: content_range.
+   *
+   * @todo: probably an array.
+   *
+   * @return ??|c_base_return_status
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_content_range() {
+    if (!array_key_exists(self::RESPONSE_CONTENT_RANGE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: content_type.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing the following keys:
+   *   - 'type': the content type string, such as 'text/html'.
+   *   - 'charset': the character set integer, such as: c_base_charset::UTF_8.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-3.1.1.5
+   */
+  public function get_response_content_type() {
+    if (!array_key_exists(self::RESPONSE_CONTENT_TYPE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_CONTENT_TYPE]);
+  }
+
+  /**
+   * Obtain HTTP response header: date.
+   *
+   * @return c_base_return_int|c_base_return_float|c_base_return_status
+   *   A unix timestamp integer.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.1.2
+   */
+  public function get_response_date() {
+    if (!array_key_exists(self::RESPONSE_DATE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_float($this->response[self::RESPONSE_DATE])) {
+      return c_base_return_float::s_new($this->response[self::RESPONSE_DATE]);
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_DATE]);
+  }
+
+  /**
+   * Obtain HTTP response header: date_actual.
+   *
+   * This is identical to the HTTP response header: date.
+   * The purpose of this is to allow clients to still receive the correct/actual date when HTTP servers, such as apache, overwrite or alter the HTTP date response header.
+   * This should therefore be used and calculated with when the date variable.
+   *
+   * @return c_base_return_int|c_base_return_float|c_base_return_status
+   *   A unix timestamp integer.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.1.2
+   */
+  public function get_response_date_actual() {
+    if (!array_key_exists(self::RESPONSE_DATE_ACTUAL, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_float($this->response[self::RESPONSE_DATE_ACTUAL])) {
+      return c_base_return_float::s_new($this->response[self::RESPONSE_DATE_ACTUAL]);
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_DATE_ACTUAL]);
+  }
+
+  /**
+   * Obtain HTTP response header: etag.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing the following:
+   *   - tag: The entity tag string (without weakness).
+   *   - weak: A boolean representing whether or not the entity tag is weak.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232#section-2.3
+   */
+  public function get_response_etag() {
+    if (!array_key_exists(self::RESPONSE_ETAG, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_ETAG]);
+  }
+
+  /**
+   * Obtain HTTP response header: expires.
+   *
+   * @return c_base_return_int|c_base_return_float|c_base_return_status
+   *   A unix timestamp integer.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.3
+   */
+  public function get_response_expires() {
+    if (!array_key_exists(self::RESPONSE_EXPIRES, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_float($this->response[self::RESPONSE_EXPIRES])) {
+      return c_base_return_float::s_new($this->response[self::RESPONSE_EXPIRES]);
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_EXPIRES]);
+  }
+
+  /**
+   * Obtain HTTP response header: last-modified.
+   *
+   * @return c_base_return_int|c_base_return_float|c_base_return_status
+   *   A unix timestamp integer.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232#section-2.2
+   */
+  public function get_response_last_modified() {
+    if (!array_key_exists(self::RESPONSE_LAST_MODIFIED, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_float($this->response[self::RESPONSE_LAST_MODIFIED])) {
+      return c_base_return_float::s_new($this->response[self::RESPONSE_LAST_MODIFIED]);
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_LAST_MODIFIED]);
+  }
+
+  /**
+   * Obtain HTTP response header: link.
+   *
+   * @todo: break this into an array of the differnt parts.
+   *
+   * @return ??|c_base_return_status
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc5988#section-5
+   * @see: https://tools.ietf.org/html/rfc3986
+   */
+  public function get_response_link() {
+    if (!array_key_exists(self::RESPONSE_LINK, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: location.
+   *
+   * @todo: consider changing this to an array containing the entire url parts broken into each key.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   A decoded uri split into its different parts inside an array.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986
+   */
+  public function get_response_location() {
+    if (!array_key_exists(self::RESPONSE_LOCATION, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($this->response[self::RESPONSE_LOCATION]);
+  }
+
+  /**
+   * Obtain HTTP response header: pragma.
+   *
+   * @todo: the cache specific options, probably an array.
+   *
+   * @return ??|c_base_return_status
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.4
+   */
+  public function get_response_pragma() {
+    if (!array_key_exists(self::RESPONSE_PRAGMA, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: proxy-authenticate.
+   *
+   * No specific content structure is defined for this response header.
+   * Should a specific structure be defined in the future, then the return value is subject to change.
+   *
+   * @return ??|c_base_return_status
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#section-4.3
+   */
+  public function get_response_proxy_authenticate() {
+    if (!array_key_exists(self::RESPONSE_PROXY_AUTHENTICATE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: public-key-pins.
+   *
+   * No specific content structure is defined for this response header.
+   * Should a specific structure be defined in the future, then the return value is subject to change.
+   *
+   * @return ??|c_base_return_status
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7469
+   */
+  public function get_response_public_key_pins() {
+    if (!array_key_exists(self::RESPONSE_PUBLIC_KEY_PINS, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: refresh.
+   *
+   * This is not defined in any RFC but is supported by many browser.
+   * It is recommended to never use this.
+   * Instead, try using the HTTP header: location.
+   *
+   * @return ??|c_base_return_status
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: get_response_location()
+   * @see: https://en.wikipedia.org/wiki/Meta_refresh
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  public function get_response_refresh() {
+    if (!array_key_exists(self::RESPONSE_REFRESH, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: retry-after.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing the following:
+   *   - value: When 'is_seconds' is FALSE, this is the unix timestamp representing when the page expires.
+   *            When 'is_seconds' is FALSE, this is the relative number of seconds until the content expires.
+   *   - is_seconds: A boolean that when true changes the interpretation of the 'value' key.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.3
+   */
+  public function get_response_retry_after() {
+    if (!array_key_exists(self::RESPONSE_RETRY_AFTER, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_RETRY_AFTER]);
+  }
+
+  /**
+   * Obtain HTTP response header: server.
+   *
+   * @return ??|c_base_return_status
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.4.2
+   */
+  public function get_response_server() {
+    if (!array_key_exists(self::RESPONSE_SERVER, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: get_cookie.
+   *
+   * @todo: cookie might needs special handling because PHP handles it in a different way from other headers.
+   *
+   * @return ??|c_base_return_status
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_cookie() {
+    if (!array_key_exists(self::RESPONSE_COOKIE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: status.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   An integer representing the HTTP status code.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232#section-4
+   */
+  public function get_response_status() {
+    if (!array_key_exists(self::RESPONSE_STATUS, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_STATUS]);
+  }
+
+  /**
+   * Obtain HTTP response header: strict-transport-security.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   A string containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc6797#section-6.1
+   */
+  public function get_response_strict_transport_security() {
+    if (!array_key_exists(self::RESPONSE_STRICT_TRANSPORT_SECURITY, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: trailer.
+   *
+   * @todo: this appears to no longer be directly specified in the headers.
+   *        There is a 'trailer-part' mentioned along with the transfer encoding information.
+   *
+   * @return ???|c_base_return_status
+   *   A string containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc2616#section-14.40
+   * @see: https://tools.ietf.org/html/rfc7230#section-4.1.2
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.1
+   */
+  public function get_response_trailer() {
+    if (!array_key_exists(self::RESPONSE_TRAILER, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: transfer-encoding.
+   *
+   * @return ???|c_base_return_status
+   *   An integer representing the encoding used to transfer this content.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.1
+   */
+  public function get_response_transfer_encoding() {
+    if (!array_key_exists(self::RESPONSE_TRANSFER_ENCODING, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: upgrade.
+   *
+   *
+   * @return ???|c_base_return_status
+   *   A string containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_upgrade() {
+    if (!array_key_exists(self::RESPONSE_UPGRADE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: vary.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing the response header values.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.4
+   */
+  public function get_response_vary() {
+    if (!array_key_exists(self::RESPONSE_VARY, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_VARY]);
+  }
+
+  /**
+   * Obtain HTTP response header: warning.
+   *
+   * @return ???|c_base_return_status
+   *   A string containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.5
+   */
+  public function get_response_warning() {
+    if (!array_key_exists(self::RESPONSE_WARNING, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: www-authenticate.
+   *
+   * @return ???|c_base_return_status
+   *   A string containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#section-4.1
+   */
+  public function get_response_www_authenticate() {
+    if (!array_key_exists(self::RESPONSE_WWW_AUTHENTICATE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: HTTP Protocol.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   A string containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_protocol() {
+    if (!array_key_exists(self::RESPONSE_PROTOCOL, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($this->response[self::RESPONSE_PROTOCOL]);
+  }
+
+  /**
+   * Obtain HTTP response header: content-security-policy.
+   *
+   * @return ???|c_base_return_status
+   *   A string containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_content_security_policy() {
+    if (!array_key_exists(self::RESPONSE_X_CONTENT_SECURITY_POLICY, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: x-content-type-options.
+   *
+   * @return ???|c_base_return_status
+   *   A string containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_x_content_type_options() {
+    if (!array_key_exists(self::RESPONSE_X_CONTENT_TYPE_OPTIONS, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: x-ua-compatible.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   A string containing the response header value.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_x_ua_compatible() {
+    if (!array_key_exists(self::RESPONSE_X_UA_COMPATIBLE, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    // @todo
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Obtain HTTP response header: checksum_header.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing:
+   *   - 'what': A specific way in which to interpret the checksum.
+   *   - 'type': The algorithm type of the checksum.
+   *   - 'checksum': The checksum value after it has been base64 decoded.
+   *   - 'action': An integer representing how this checksum is processed when generating the HTTP response.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_checksum_header() {
+    if (!array_key_exists(self::RESPONSE_CHECKSUM_HEADER, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_CHECKSUM_HEADERS]);
+  }
+
+  /**
+   * Obtain HTTP response header: checksum_headers.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing a list of header field names.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_checksum_headers() {
+    if (!array_key_exists(self::RESPONSE_CHECKSUM_HEADERS, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_CHECKSUM_HEADERS]);
+  }
+
+  /**
+   * Obtain HTTP response header: checksum_content.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing:
+   *   - 'what': A specific way in which to interpret the checksum.
+   *   - 'type': The algorithm type of the checksum.
+   *   - 'checksum': The checksum value after it has been base64 decoded.
+   *   - 'action': An integer representing how this checksum is processed when generating the HTTP response.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_checksum_content() {
+    if (!array_key_exists(self::RESPONSE_CHECKSUM_CONTENT, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($this->response[self::RESPONSE_CHECKSUM_CONTENT]);
+  }
+
+  /**
+   * Obtain HTTP response header: content_revision.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   An integer representing a revision number.
+   *   FALSE with error bit set is returned on error, including when the key is not defined.
+   */
+  public function get_response_content_revision() {
+    if (!array_key_exists(self::RESPONSE_CONTENT_REVISION, $this->response)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($this->response[self::RESPONSE_CONTENT_REVISION]);
+  }
+
+  /**
+   * Return an array for mapping HTTP request header strings to header ids.
+   *
+   * @return c_base_return_array
+   *   An array for mapping HTTP request header strings to header ids.
+   *
+   * @see: p_get_header_request_mapping()
+   */
+  public function get_header_request_mapping() {
+    return c_base_return_array::s_new($this->p_get_header_request_mapping());
+  }
+
+  /**
+   * Return an array for mapping HTTP response header strings to header ids.
+   *
+   * @return c_base_return_array
+   *   An array for mapping HTTP response header strings to header ids.
+   *
+   * @see: p_get_header_response_mapping()
+   */
+  public function get_header_response_mapping() {
+    return c_base_return_array::s_new($this->p_get_header_response_mapping());
+  }
+
+  /**
+   * Return an array for mapping HTTP method strings to ids.
+   *
+   * @return c_base_return_array
+   *   An array for mapping HTTP response header method strings to header method ids.
+   *
+   * @see: p_get_http_method_mapping()
+   */
+  public function get_http_method_mapping() {
+    return c_base_return_array::s_new($this->p_get_http_method_mapping());
+  }
+
+  /**
+   * Send the response HTTP headers.
+   *
+   * This function will not correct incorrect behavior.
+   * The idea is to provide the caller with as much control as possible to handle different situations.
+   *
+   * This sends checksum header fields and any change in the headers or content may result in request checksum failing.
+   *
+   * @param bool $shuffle
+   *   (optional) When TRUE, this will randomize the order in which header fields are defined, except for the status header (which is always first).
+   *    This helps resist fingerprinting techniques thereby helping increase security.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned when headers are sent.
+   *   FALSE is returned when headers have already been sent.
+   *   FALSE with error bit set is returned on error.
+   *
+   *   If headers were already sent, but not by this class, then FALSE with the error bit set is returned.
+   *
+   * @see: self::set_response_checksum_header()
+   * @see: self::set_response_checksum_headers()
+   * @see: self::set_response_checksum_content()
+   * @see: header()
+   * @see: headers_sent()
+   */
+  public function send_response_headers($shuffle = TRUE) {
+    if (!is_bool($shuffle)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($this->headers_sent) {
+      return c_base_return_false;
+    }
+
+    if (headers_sent()) {
+      return c_base_return_error::s_false();
+    }
+
+    $header_id_to_names = $this->p_get_header_response_mapping(TRUE);
+
+    if ($shuffle) {
+      $headers = array();
+
+      // shuffle() alters the array keys, so some additional processing must be done to protect the array keys.
+      $shuffled = array_flip($this->p_get_header_response_mapping());
+      shuffle($shuffled);
+
+      $unshuffled = $this->p_get_header_response_mapping();
+      foreach ($shuffled as $header_code) {
+        $headers[$header_code] = $unshuffled[$header_code];
+      }
+      unset($header_code);
+      unset($unshuffled);
+      unset($shuffled);
+    }
+    else {
+      $headers = $this->p_get_header_response_mapping();
+    }
+
+    // this is used to perform checksums.
+    $header_output = array();
+
+
+    // response status, this must always be first.
+    unset($headers[self::RESPONSE_STATUS]);
+    $status_string = NULL;
+    if (array_key_exists(self::RESPONSE_PROTOCOL, $this->response) && array_key_exists(self::RESPONSE_STATUS, $this->response)) {
+      $status_string = $this->response[self::RESPONSE_PROTOCOL] . ' ';
+
+      $status_text = c_base_http_status::to_text($this->response[self::RESPONSE_STATUS]);
+      if ($status_text instanceof c_base_return_false) {
+        $status_string .= $this->response[self::RESPONSE_STATUS];
+      }
+      else {
+        $status_string .= $this->response[self::RESPONSE_STATUS] . ' ' . $status_text->get_value_exact();
+      }
+      unset($status_text);
+
+      header($status_string, TRUE, $this->response[self::RESPONSE_STATUS]);
+    }
+
+    $this->p_prepare_header_response_access_control_allow_origin($header_output);
+    $this->p_prepare_header_response_simple_value($header_output, $headers[self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS], $header_id_to_names[self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS], self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS);
+    $this->p_prepare_header_response_access_control_expose_headers($header_output);
+    $this->p_prepare_header_response_simple_value($header_output, $headers[self::RESPONSE_ACCESS_CONTROL_MAX_AGE], $header_id_to_names[self::RESPONSE_ACCESS_CONTROL_MAX_AGE], self::RESPONSE_ACCESS_CONTROL_MAX_AGE);
+    $this->p_prepare_header_response_access_control_allow_methods($header_output);
+    $this->p_prepare_header_response_access_control_allow_headers($header_output);
+    $this->p_prepare_header_response_accept_patch($header_output);
+    $this->p_prepare_header_response_simple_value($header_output, $headers[self::RESPONSE_ACCEPT_RANGES], $header_id_to_names[self::RESPONSE_ACCEPT_RANGES], self::RESPONSE_ACCEPT_RANGES);
+    $this->p_prepare_header_response_simple_value($header_output, $headers[self::RESPONSE_AGE], $header_id_to_names[self::RESPONSE_AGE], self::RESPONSE_AGE);
+    $this->p_prepare_header_response_allow($header_output);
+    $this->p_prepare_header_response_cache_control($header_output);
+    $this->p_prepare_header_response_connection($header_output);
+    $this->p_prepare_header_response_content_disposition($header_output);
+    $this->p_prepare_header_response_content_encoding($header_output);
+    $this->p_prepare_header_response_content_language($header_output);
+    // @todo: this is now an array of values.
+    $this->p_prepare_header_response_simple_value($header_output, $headers[self::RESPONSE_CONTENT_LENGTH], $header_id_to_names[self::RESPONSE_CONTENT_LENGTH], self::RESPONSE_CONTENT_LENGTH);
+    $this->p_prepare_header_response_simple_value($header_output, $headers[self::RESPONSE_CONTENT_RANGE], $header_id_to_names[self::RESPONSE_CONTENT_RANGE], self::RESPONSE_CONTENT_RANGE);
+    $this->p_prepare_header_response_content_type($header_output);
+    $this->p_prepare_header_response_timestamp_value($header_output, $headers[self::RESPONSE_DATE], $header_id_to_names[self::RESPONSE_DATE], self::RESPONSE_DATE);
+    $this->p_prepare_header_response_timestamp_value($header_output, $headers[self::RESPONSE_DATE_ACTUAL], $header_id_to_names[self::RESPONSE_DATE_ACTUAL], self::RESPONSE_DATE_ACTUAL);
+    $this->p_prepare_header_response_etag($header_output);
+    $this->p_prepare_header_response_timestamp_value($header_output, $header_id_to_names[self::RESPONSE_EXPIRES], $header_id_to_names[self::RESPONSE_EXPIRES], self::RESPONSE_EXPIRES);
+    $this->p_prepare_header_response_timestamp_value($header_output, $header_id_to_names[self::RESPONSE_LAST_MODIFIED], $header_id_to_names[self::RESPONSE_LAST_MODIFIED], self::RESPONSE_LAST_MODIFIED);
+    $this->p_prepare_header_response_link($header_output);
+    $this->p_prepare_header_response_location($header_output);
+    $this->p_prepare_header_response_pragma($header_output);
+    $this->p_prepare_header_response_proxy_authenticate($header_output);
+    $this->p_prepare_header_response_public_key_pins($header_output);
+    $this->p_prepare_header_response_refresh($header_output);
+    $this->p_prepare_header_response_retry_after($header_output);
+    $this->p_prepare_header_response_server($header_output);
+
+    // @todo: how is cookie going to be handled?
+
+    $this->p_prepare_header_response_strict_transport_security($header_output);
+    $this->p_prepare_header_response_trailer($header_output);
+    $this->p_prepare_header_response_transfer_encoding($header_output);
+    $this->p_prepare_header_response_upgrade($header_output);
+    $this->p_prepare_header_response_vary($header_output);
+    $this->p_prepare_header_response_warning($header_output);
+    $this->p_prepare_header_response_www_authenticate($header_output);
+    $this->p_prepare_header_response_x_content_security_policy($header_output);
+    $this->p_prepare_header_response_x_content_type_options($header_output);
+    $this->p_prepare_header_response_x_ua_compatible($header_output);
+    $this->p_prepare_header_response_simple_value($header_output, $headers[self::RESPONSE_CONTENT_LENGTH], $header_id_to_names[self::RESPONSE_CONTENT_LENGTH], self::RESPONSE_CONTENT_LENGTH);
+    $this->p_prepare_header_response_simple_value($header_output, $headers[self::RESPONSE_CONTENT_REVISION], $header_id_to_names[self::RESPONSE_CONTENT_REVISION], self::RESPONSE_CONTENT_REVISION);
+    $this->p_prepare_header_response_checksum_content($header_output);
+    $this->p_prepare_header_response_checksum_headers($header_output, $status_string);
+    unset($status_string);
+    unset($header_id_to_names);
+
+
+    // send header output.
+    foreach ($headers as $header_id => $header_name) {
+      if (array_key_exists($header_id, $header_output)) {
+        header($header_output[$header_id]);
+      }
+    }
+    unset($output);
+    unset($header_output);
+
+
+    $this->headers_sent = TRUE;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the HTTP response content.
+   *
+   * The term 'content' and 'body' seem to be used interchangably.
+   * The header fields often refer to this as 'content', therefore this will be called 'content' and neither 'body' nor 'data'.
+   *
+   * @return c_base_return_string|c_base_return_array
+   *   A string representing the response content.
+   *   If the content is represented via one or more files, then an array of filenames is returned.
+   */
+  public function get_response_content() {
+    if ($this->content_is_file) {
+      return c_base_return_array::s_new($this->content);
+    }
+
+    return c_base_return_string::s_new($this->content);
+  }
+
+  /**
+   * Encode the HTTP response content.
+   *
+   * This must be performed after all content has been buffered and before the HTTP headers are sent.
+   * This must be called before sending the response content.
+   *
+   * This will alter the 'content-encoding' header.
+   * This will alter the 'content-length' header only if the encoding was altered.
+   * This will alter the 'transfer-encoding' header (if not already defined) and set it to 'chunked', when 'content-length' is not specified.
+   *
+   * If called when the content is a file, then that file will be compressed into memory.
+   * - This allows for the compressed file length to be calculated, but at the cost of additional memory consumption.
+   * - Do not call this when handling large files.
+   *
+   * @fixme: this needs to properly handle content-length when transfer-encoding is specified.
+   *
+   * @param int|null $compression
+   *   (optional) The compression integer.
+   *   This is specific to the algorithm used, but in general could be considered as follows:
+   *   - NULL = use default.
+   *   - -1 = use library default if possible or default if not.
+   *   - 0 = no compression.
+   *   - 1 -> 9 = compress level with 1 being weakest and 9 being strongest.
+   *   Some algorithms, such as lzo, have their own predefined constants that can be specified here, such as LZO1X_999.
+   * @param int|null $max_filesize
+   *   If content is a file and this is some value greater than 0, then files whose filesize is less than this will be loaded and compressed into memory.
+   *   Otherwise, the content-length header will be unset, preventing an incorrect filesize from being transmitted.
+   *   This represents filesizes in number of bytes.
+   *   When NULL, the file size will not be calculated.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE without error bit set is returned if there is no assigned content to send.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::send_response_content()
+   * @see: self::set_response_content_encoding()
+   * @see: self::set_response_content_length()
+   */
+  public function encode_response_content($compression = NULL, $max_filesize = NULL) {
+    if (!is_null($compression) && !is_int($compression)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($max_filesize) && !(is_int($max_filesize) && $max_filesize > 0)) {
+      return c_base_return_error::s_false();
+    }
+
+    $encoding = $this->p_determine_response_encoding();
+
+    if ($this->content_is_file) {
+      if (empty($this->content)) {
+        unset($encoding);
+        return c_base_return_false();
+      }
+
+      if (is_null($max_filesize)) {
+        $content = '';
+        foreach ($this->content as $filename) {
+          if (!file_exists($filename)) {
+            unset($encoding);
+            unset($filename);
+            unset($content);
+            // @todo: provide a warning about missing files.
+            return c_base_return_error::s_false();
+          }
+
+          $content .= file_get_contents($filename);
+        }
+        unset($filename);
+
+        if (empty($content)) {
+          unset($encoding);
+          unset($content);
+          return c_base_return_false();
+        }
+
+        $this->p_encode_content($content, $encoding, $compression, TRUE);
+        unset($content);
+        unset($encoding);
+      }
+      else {
+        $content = '';
+        $content_length = 0;
+        foreach ($this->content as $filename) {
+          if (!file_exists($filename)) {
+            unset($encoding);
+            unset($filename);
+            unset($content);
+            // @todo: provide a warning about missing files.
+            return c_base_return_error::s_false();
+          }
+
+          $content_length += filesize($filename);
+          if ($content_length >= $max_filesize) {
+            break;
+          }
+
+          $content .= file_get_contents($filename);
+        }
+        unset($filename);
+
+        if (empty($content) || $content_length >= $max_filesize) {
+          unset($encoding);
+          unset($content);
+          return c_base_return_false();
+        }
+        unset($content_length);
+
+        $this->p_encode_content($content, $encoding, $compression, $compression, FALSE);
+        unset($content);
+        unset($encoding);
+
+        // the content-length cannot be specified in this case.
+        unset($this->response[self::RESPONSE_CONTENT_LENGTH]);
+      }
+    }
+    else {
+      $this->p_encode_content($this->content, $encoding, $compression, TRUE);
+      unset($encoding);
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Send the HTTP response content.
+   *
+   * The term 'content' and 'body' seem to be used interchangably.
+   * The header fields often refer to this as 'content', therefore this will be called 'content' and neither 'body' nor 'data'.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with the error bit set is returned on error.
+   *
+   * @see: self::encode_response_content()
+   */
+  public function send_response_content() {
+    if ($this->content_is_file) {
+      // @todo: the $this->content may be an array of filenames instead of string data, handle that here.
+      foreach ($this->content as $filename) {
+        if (!file_exists($filename)) {
+          unset($filename);
+          // @todo: provide a warning about missing files.
+          return c_base_return_error::s_false();
+        }
+
+        $opened_file = fopen($filename, 'rb');
+        if ($opened_file === FALSE) {
+          unset($filename);
+          // @todo: provide a warning about unable to open file.
+          return c_base_return_error::s_false();
+        }
+
+        fpassthru($opened_file);
+        fclose($opened_file);
+      }
+      unset($opened_file);
+      unset($filename);
+    }
+    else {
+      print($this->content);
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Calculate the 8-bit length (octet) of the content/body.
+   *
+   * RFC 7230 describes the content-length as referring to the count based on every 8-bits, which is a single octet.
+   * Using strlen() would be incorrect because characters are 7-bit.
+   * Using mb_strlen() would be incorrect because it contains mixed lengths.
+   * Using any string test would be incorrect because the content may already be binary data.
+   *
+   * The solution is to break apart the string into 8-bit chunks and then calculate the length.
+   *
+   * Errata: It turns out this solution is expensive, and so is disabled for now.
+   * - Because this only uses strlen(), there is no way to protect against strlen() becoming mb_strlen() via the mbstring.func_overload setting.
+   * - Therefore, mbstring.func_overload must never be enabled or risk causing security issues.
+   *
+   * @return int
+   *   Total number of octals on success.
+   *
+   * @see: set_response_content_length()
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.2
+   */
+  private function p_calculate_content_length($text) {
+    return strlen($text);
+  }
+
+  /**
+   * Load and process the HTTP request parameter: accept.
+   *
+   * @see: self::pr_rfc_string_is_negotiation()
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.3.2
+   */
+  private function p_load_request_accept() {
+    if (empty($this->headers['accept'])) {
+      $this->request[self::REQUEST_ACCEPT]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['accept']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_ACCEPT]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_ACCEPT]['data'] = $this->pr_rfc_string_is_negotiation($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_ACCEPT]['data']['invalid']) {
+      $this->request[self::REQUEST_ACCEPT]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_ACCEPT]['defined'] = TRUE;
+      $this->request[self::REQUEST_ACCEPT]['data']['accept'] = NULL;
+      $this->request[self::REQUEST_ACCEPT]['data']['category'] = NULL;
+      $this->request[self::REQUEST_ACCEPT]['data']['weight'] = array();
+      $this->request[self::REQUEST_ACCEPT]['data']['types'] = array();
+      #$this->request[self::REQUEST_ACCEPT]['data']['categories'] = array();
+
+      // convert the known values into integers for improved processing.
+      foreach ($this->request[self::REQUEST_ACCEPT]['data']['choices'] as $weight => &$choice) {
+        foreach ($choice as $key => &$c) {
+          $result = c_base_mime::s_identify($c['choice'], TRUE);
+          if ($result instanceof c_base_return_false) {
+            // there is no valid value to process.
+            continue;
+          }
+
+          $identified = $result->get_value_exact();
+          unset($result);
+
+          $c['accept'] = $identified['id_type'];
+          $c['category'] = $identified['id_category'];
+          $this->request[self::REQUEST_ACCEPT]['data']['types'][$weight][$identified['id_type']] = $identified['id_type'];
+          #$this->request[self::REQUEST_ACCEPT]['data']['categories'][$weight][$identified['id_category']] = $identified['id_category'];
+          $this->request[self::REQUEST_ACCEPT]['data']['weight'][$weight][$identified['id_type']] = $identified['name_category'] . '/' . $identified['name_type'];
+
+          // @todo: should this be used or not?
+          #if ($identified['id_category'] == c_base_mime::CATEGORY_UNKNOWN || $identified['id_type'] == c_base_mime::TYPE_UNKNOWN) {
+          #  $c['accept'] = NULL;
+          #}
+
+          unset($identified);
+        }
+      }
+      unset($choice);
+      unset($weight);
+      unset($c);
+      unset($key);
+
+      // sort the weight array.
+      krsort($this->request[self::REQUEST_ACCEPT]['data']['weight']);
+
+      // The NULL key should be the first key in the weight.
+      $this->p_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT]['data']['weight']);
+
+      // rename 'choices' array key to 'accept'.
+      $this->request[self::REQUEST_ACCEPT]['data']['accept'] = $this->request[self::REQUEST_ACCEPT]['data']['choices'];
+      unset($this->request[self::REQUEST_ACCEPT]['data']['choices']);
+    }
+    unset($this->request[self::REQUEST_ACCEPT]['data']['invalid']);
+    unset($this->request[self::REQUEST_ACCEPT]['data']['current']);
+
+    $this->request[self::REQUEST_ACCEPT]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: accept-language.
+   *
+   * @see: self::pr_rfc_string_is_negotiation()
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.3.5
+   */
+  private function p_load_request_accept_language() {
+    if (empty($this->headers['accept_language'])) {
+      $this->request[self::REQUEST_ACCEPT_LANGUAGE]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['accept_language']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_ACCEPT_LANGUAGE]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data'] = $this->pr_rfc_string_is_negotiation($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['invalid']) {
+      $this->request[self::REQUEST_ACCEPT_LANGUAGE]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_ACCEPT_LANGUAGE]['defined'] = TRUE;
+      $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['weight'] = array();
+
+      if (is_null($this->language_class) || !class_exists($this->language_class)) {
+        // PHP does not allow "new self::ACCEPT_LANGUAGE_CLASS_DEFAULT()", but using a variable is allowed.
+        $class = self::ACCEPT_LANGUAGE_CLASS_DEFAULT;
+        $languages = new $class();
+        unset($class);
+      }
+      else {
+        $languages = new $this->language_class();
+      }
+
+      // convert the known values into integers for improved processing.
+      foreach ($this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['choices'] as $weight => &$choice) {
+        foreach ($choice as $key => &$c) {
+          $id = $languages->s_get_id_by_name($c['choice']);
+
+          if ($id instanceof c_base_return_false) {
+            $c['language'] = NULL;
+          }
+          else {
+            $c['language'] = $id->get_value_exact();
+            $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['weight'][$weight][$c['language']] = mb_strtolower($c['choice']);
+            unset($c['choice']);
+          }
+        }
+      }
+      unset($languages);
+
+      // sort the weight array.
+      krsort($this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['weight']);
+
+      // The NULL key should be the first key in the weight.
+      $this->p_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['weight']);
+
+      // rename 'choices' array key to 'language'.
+      $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['language'] = $this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['choices'];
+      unset($this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['choices']);
+    }
+    unset($this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['invalid']);
+    unset($this->request[self::REQUEST_ACCEPT_LANGUAGE]['data']['current']);
+
+    $this->request[self::REQUEST_ACCEPT_LANGUAGE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: accept-encoding.
+   *
+   * @see: self::pr_rfc_string_is_negotiation()
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.3.4
+   */
+  private function p_load_request_accept_encoding() {
+    if (empty($this->headers['accept_encoding'])) {
+      $this->request[self::REQUEST_ACCEPT_ENCODING]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['accept_encoding']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_ACCEPT_ENCODING]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_ACCEPT_ENCODING]['data'] = $this->pr_rfc_string_is_negotiation($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_ACCEPT_ENCODING]['data']['invalid']) {
+      $this->request[self::REQUEST_ACCEPT_ENCODING]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_ACCEPT_ENCODING]['defined'] = TRUE;
+      $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'] = array();
+
+      // convert the known values into integers for improved processing.
+      foreach ($this->request[self::REQUEST_ACCEPT_ENCODING]['data']['choices'] as $weight => &$choice) {
+        foreach ($choice as $key => &$c) {
+          $lowercase = mb_strtolower($c['choice']);
+          if ($lowercase == 'chunked') {
+            $c['encoding'] = self::ENCODING_CHUNKED;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_CHUNKED] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'compress') {
+            $c['encoding'] = self::ENCODING_COMPRESS;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_COMPRESS] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'deflate') {
+            $c['encoding'] = self::ENCODING_DEFLATE;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_DEFLATE] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'gzip') {
+            $c['encoding'] = self::ENCODING_GZIP;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_GZIP] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'bzip') {
+            $c['encoding'] = self::ENCODING_BZIP;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_BZIP] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'lzo') {
+            $c['encoding'] = self::ENCODING_LZO;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_LZO] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'xz') {
+            $c['encoding'] = self::ENCODING_XZ;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_XZ] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'exit') {
+            $c['encoding'] = self::ENCODING_EXI;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_EXI] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'identity') {
+            $c['encoding'] = self::ENCODING_IDENTITY;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_IDENTITY] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'sdch') {
+            $c['encoding'] = self::ENCODING_SDCH;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_SDCH] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'pg') {
+            $c['encoding'] = self::ENCODING_PG;
+            $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'][$weight][self::ENCODING_PG] = $lowercase;
+            unset($c['choice']);
+          }
+          else {
+            $c['encoding'] = NULL;
+          }
+        }
+      }
+      unset($lowercase);
+
+      // sort the weight array.
+      krsort($this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight']);
+
+      // The NULL key should be the first key in the weight.
+      $this->p_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight']);
+
+      // rename 'choices' array key to 'encoding'.
+      $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['encoding'] = $this->request[self::REQUEST_ACCEPT_ENCODING]['data']['choices'];
+      unset($this->request[self::REQUEST_ACCEPT_ENCODING]['data']['choices']);
+    }
+    unset($this->request[self::REQUEST_ACCEPT_ENCODING]['data']['invalid']);
+    unset($this->request[self::REQUEST_ACCEPT_ENCODING]['data']['current']);
+
+    $this->request[self::REQUEST_ACCEPT_ENCODING]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: accept-charset.
+   *
+   * @see: self::pr_rfc_string_is_negotiation()
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.3.3
+   */
+  private function p_load_request_accept_charset() {
+    if (empty($this->headers['accept_charset'])) {
+      $this->request[self::REQUEST_ACCEPT_CHARSET]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['accept_charset']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_ACCEPT_CHARSET]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_ACCEPT_CHARSET]['data'] = $this->pr_rfc_string_is_negotiation($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_ACCEPT_CHARSET]['data']['invalid']) {
+      $this->request[self::REQUEST_ACCEPT_CHARSET]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_ACCEPT_CHARSET]['defined'] = TRUE;
+      $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'] = array();
+
+      // convert the known values into integers for improved processing.
+      foreach ($this->request[self::REQUEST_ACCEPT_CHARSET]['data']['choices'] as $weight => &$choice) {
+        foreach ($choice as $key => &$c) {
+          $lowercase = mb_strtolower($c['choice']);
+          if ($lowercase == 'ascii') {
+            $c['charset'] = c_base_charset::ASCII;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ASCII] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'utf-8') {
+            $c['charset'] = c_base_charset::UTF_8;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::UTF_8] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'utf-16') {
+            $c['charset'] = c_base_charset::UTF_16;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::UTF_16] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'utf-32') {
+            $c['charset'] = c_base_charset::UTF_32;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::UTF_32] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-1') {
+            $c['charset'] = c_base_charset::ISO_8859_1;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_1] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-2') {
+            $c['charset'] = c_base_charset::ISO_8859_2;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_2] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-3') {
+            $c['charset'] = c_base_charset::ISO_8859_3;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_3] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-4') {
+            $c['charset'] = c_base_charset::ISO_8859_4;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_4] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-5') {
+            $c['charset'] = c_base_charset::ISO_8859_5;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_5] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-6') {
+            $c['charset'] = c_base_charset::ISO_8859_6;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_6] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-7') {
+            $c['charset'] = c_base_charset::ISO_8859_7;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_7] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-8') {
+            $c['charset'] = c_base_charset::ISO_8859_8;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_8] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-9') {
+            $c['charset'] = c_base_charset::ISO_8859_9;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_9] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-10') {
+            $c['charset'] = c_base_charset::ISO_8859_10;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_10] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-11') {
+            $c['charset'] = c_base_charset::ISO_8859_11;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_11] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-12') {
+            $c['charset'] = c_base_charset::ISO_8859_12;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_12] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-13') {
+            $c['charset'] = c_base_charset::ISO_8859_13;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_13] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-14') {
+            $c['charset'] = c_base_charset::ISO_8859_14;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_14] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-15') {
+            $c['charset'] = c_base_charset::ISO_8859_15;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_15] = $lowercase;
+            unset($c['choice']);
+          }
+          elseif ($lowercase == 'iso-8859-16') {
+            $c['charset'] = c_base_charset::ISO_8859_16;
+            $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight'][$weight][c_base_charset::ISO_8859_16] = $lowercase;
+            unset($c['choice']);
+          }
+          else {
+            $c['charset'] = NULL;
+          }
+        }
+      }
+      unset($lowercase);
+
+      // sort the weight array.
+      krsort($this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight']);
+
+      // The NULL key should be the first key in the weight.
+      $this->p_prepend_array_value(NULL, $this->request[self::REQUEST_ACCEPT_CHARSET]['data']['weight']);
+    }
+    unset($this->request[self::REQUEST_ACCEPT_CHARSET]['data']['invalid']);
+
+    $this->request[self::REQUEST_ACCEPT_CHARSET]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: accept-datetime.
+   *
+   * This is not part of an official standard, it is provided to support a separate web archive standard.
+   *
+   * @see: http://www.mementoweb.org/guide/rfc/ID/#Accept-Memento-Datetime
+   */
+  private function p_load_request_accept_datetime() {
+    if (p_validate_date_is_valid_rfc($this->headers['accept_datetime']) === FALSE) {
+      $this->request[self::REQUEST_DATE]['invalid'] = TRUE;
+      return;
+    }
+
+    $timestamp = strtotime($this->headers['accept_datetime']);
+    if ($timestamp === FALSE) {
+      $this->request[self::REQUEST_DATE]['invalid'] = TRUE;
+      unset($timestamp);
+      return;
+    }
+
+    $this->request[self::REQUEST_ACCEPT_DATETIME]['defined'] = TRUE;
+    $this->request[self::REQUEST_ACCEPT_DATETIME]['data'] = $timestamp;
+    $this->request[self::REQUEST_ACCEPT_DATETIME]['invalid'] = FALSE;
+
+    unset($timestamp);
+  }
+
+  /**
+   * Load and process the HTTP request parameter: cache-control.
+   *
+   * Only 'no-cache' is supported at this time for requests.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.2
+   */
+  private function p_load_request_cache_control() {
+    $cache_control = $this->headers['cache_control'];
+    if (empty($cache_control)) {
+      $this->request[self::REQUEST_CACHE_CONTROL]['invalid'] = TRUE;
+      unset($cache_control);
+      return;
+    }
+
+    $this->request[self::REQUEST_CACHE_CONTROL]['data'] = array(
+      'methods' => array(),
+    );
+
+    $integer_types = array(
+      SELF::CACHE_CONTROL_MAX_AGE => 'max-age=',
+      SELF::CACHE_CONTROL_MAX_STALE => 'max-stale=',
+      SELF::CACHE_CONTROL_MIN_FRESH => 'min-fresh=',
+    );
+
+    $parts = mb_split(', ', $cache_control);
+    foreach ($parts as $part) {
+      $cleaned_up = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $part));
+      if ($cleaned_up == 'no-cache') {
+        $this->request[self::REQUEST_CACHE_CONTROL]['defined'] = TRUE;
+        $this->request[self::REQUEST_CACHE_CONTROL]['data']['methods'][SELF::CACHE_CONTROL_NO_CACHE] = SELF::CACHE_CONTROL_NO_CACHE;
+      }
+      elseif ($cleaned_up == 'no-store') {
+        $this->request[self::REQUEST_CACHE_CONTROL]['defined'] = TRUE;
+        $this->request[self::REQUEST_CACHE_CONTROL]['data']['methods'][SELF::CACHE_CONTROL_NO_STORE] = SELF::CACHE_CONTROL_NO_STORE;
+      }
+      elseif ($cleaned_up == 'no-transform') {
+        $this->request[self::REQUEST_CACHE_CONTROL]['defined'] = TRUE;
+        $this->request[self::REQUEST_CACHE_CONTROL]['data']['methods'][SELF::CACHE_CONTROL_NO_TRANSFORM] = SELF::CACHE_CONTROL_NO_TRANSFORM;
+      }
+      elseif ($cleaned_up == 'only-if-cached') {
+        $this->request[self::REQUEST_CACHE_CONTROL]['defined'] = TRUE;
+        $this->request[self::REQUEST_CACHE_CONTROL]['data']['methods'][SELF::CACHE_CONTROL_ONLY_IF_CACHED] = SELF::CACHE_CONTROL_ONLY_IF_CACHED;
+      }
+      else {
+        foreach ($integer_types as $type_id => $type_string) {
+          if (mb_strpos($cleaned_up, $type_string) === FALSE) {
+            continue;
+          }
+
+          $pieces = mb_split('=', $cleaned_up);
+          if (!isset($pieces[1]) || !is_numeric($pieces[1]) || count($pieces) > 2) {
+            $this->request[self::REQUEST_CACHE_CONTROL]['invalid'] = TRUE;
+            unset($pieces);
+            continue;
+          }
+
+          $this->request[self::REQUEST_CACHE_CONTROL]['defined'] = TRUE;
+          $this->request[self::REQUEST_CACHE_CONTROL]['data']['methods'][$type_id] = (int) $pieces[1];
+
+          unset($pieces);
+        }
+
+        unset($type_id);
+        unset($type_string);
+      }
+
+      unset($cleaned_up);
+    }
+    unset($part);
+    unset($parts);
+    unset($cache_control);
+
+    $this->request[self::REQUEST_CACHE_CONTROL]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: connection.
+   *
+   * @see: self::pr_rfc_string_is_commad_token()
+   * @see: https://tools.ietf.org/html/rfc7230
+   * @see: https://tools.ietf.org/html/rfc7230#appendix-B
+   */
+  private function p_load_request_connection() {
+    if (empty($this->headers['connection'])) {
+      $this->request[self::REQUEST_CONNECTION]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['connection']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_CONNECTION]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_CONNECTION]['data'] = $this->pr_rfc_string_is_commad_token($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_CONNECTION]['data']['invalid']) {
+      $this->request[self::REQUEST_CONNECTION]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_CONNECTION]['defined'] = TRUE;
+
+      // rename 'tokens' array key to 'connection'.
+      $this->request[self::REQUEST_CONNECTION]['data']['connection'] = $this->request[self::REQUEST_CONNECTION]['data']['tokens'];
+      unset($this->request[self::REQUEST_CONNECTION]['data']['tokens']);
+    }
+    unset($this->request[self::REQUEST_CONNECTION]['data']['invalid']);
+    unset($this->request[self::REQUEST_CONNECTION]['data']['current']);
+
+    $this->request[self::REQUEST_CONNECTION]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: pragma.
+   *
+   * This is an older version of cache_control that supports 'no-cache'.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.4
+   */
+  private function p_load_request_pragma() {
+    if ($this->request[self::REQUEST_CACHE_CONTROL]['defined']) {
+      // this is a conflict, favor 'cache-control' over 'pragma'.
+      return;
+    }
+
+    $pragma = $this->headers['pragma'];
+    if (empty($pragma)) {
+      // silently fail on invalid pragma.
+      unset($pragma);
+      return;
+    }
+
+    $cleaned_up = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $pragma));
+    if ($cleaned_up == 'no-cache') {
+      $this->request[self::REQUEST_CACHE_CONTROL]['defined'] = TRUE;
+      $this->request[self::REQUEST_CACHE_CONTROL]['data'][SELF::CACHE_CONTROL_NO_CACHE] = SELF::CACHE_CONTROL_NO_CACHE;
+    }
+    unset($cleaned_up);
+    unset($pragma);
+
+    $this->request[self::REQUEST_CACHE_CONTROL]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: cookie.
+   *
+   * @see: https://tools.ietf.org/html/rfc6265
+   */
+  private function p_load_request_cookies() {
+    $this->request[self::REQUEST_COOKIE]['data'] = array();
+
+    foreach ($_COOKIE as $cookie_name => $cookie_values) {
+      $cookie = new c_base_cookie();
+      $result = $cookie->set_name($cookie_name);
+
+      if ($result instanceof c_base_return_false) {
+        unset($cookie);
+        unset($result);
+        continue;
+      }
+
+      $cookie->do_pull();
+      $this->request[self::REQUEST_COOKIE]['data'][$cookie_name] = $cookie;
+      $this->request[self::REQUEST_COOKIE]['defined'] = TRUE;
+      unset($cookie);
+    }
+    unset($cookie_name);
+    unset($cookie_values);
+
+    $this->request[self::REQUEST_COOKIE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: content-length.
+   *
+   * This value is represented in octets (8-bit bytes).
+   *
+   * @see: self::pr_rfc_string_is_digit()
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.2
+   */
+  private function p_load_request_content_length() {
+    if (is_int($this->headers['content_length'])) {
+      $this->request[self::REQUEST_CONTENT_LENGTH]['defined'] = TRUE;
+      $this->request[self::REQUEST_CONTENT_LENGTH]['data'] = (int) $this->headers['content_length'];
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['content_length']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_CONTENT_LENGTH]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $parsed = $this->pr_rfc_string_is_digit($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($parsed['invalid']) {
+      $this->request[self::REQUEST_CONTENT_LENGTH]['invalid'] = TRUE;
+      unset($parsed);
+      return;
+    }
+
+    $this->request[self::REQUEST_CONTENT_LENGTH]['defined'] = TRUE;
+    $this->request[self::REQUEST_CONTENT_LENGTH]['data'] = intval($parsed['text']);
+    unset($parsed);
+
+    $this->request[self::REQUEST_CONTENT_LENGTH]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: content-type.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-3.1.1.5
+   */
+  private function p_load_request_content_type() {
+    $content_type = $this->headers['content_type'];
+    if (empty($content_type)) {
+      $this->request[self::REQUEST_CONTENT_TYPE]['invalid'] = TRUE;
+      unset($content_type);
+      return;
+    }
+
+    $content_type_parts = mb_split(';', $content_type);
+    $content_type_part = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $content_type_parts[0]));
+
+    $this->request[self::REQUEST_CONTENT_TYPE]['defined'] = TRUE;
+    $this->request[self::REQUEST_CONTENT_TYPE]['data'] = $content_type_part;
+
+    unset($content_type_part);
+    unset($content_type_parts);
+    unset($content_type);
+
+    $this->request[self::REQUEST_CONTENT_TYPE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: date.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.1.2
+   */
+  private function p_load_request_date() {
+    if (p_validate_date_is_valid_rfc($this->headers['date']) === FALSE) {
+      $this->request[self::REQUEST_DATE]['invalid'] = TRUE;
+      return;
+    }
+
+    $timestamp = strtotime($this->headers['date']);
+    if ($timestamp === FALSE) {
+      $this->request[self::REQUEST_DATE]['invalid'] = TRUE;
+      unset($timestamp);
+      return;
+    }
+
+    $this->request[self::REQUEST_DATE]['defined'] = TRUE;
+    $this->request[self::REQUEST_DATE]['data'] = $timestamp;
+
+    unset($timestamp);
+
+    $this->request[self::REQUEST_DATE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: expect.
+   *
+   * Only '100-continue' is defined by the standard and the server may respond with a 416 (Expectation Failed) as the status code.
+   *
+   * This is essentially a kind way to inform the server that 'hey, I've got a packet coming your way, will you accept it?'.
+   * The server may also respond with 401 (Unauthorized) or 405 (Methid Not Allowed).
+   *
+   * The server should expect header fields like content-type, content-length, and even the operation, such as PUT /some/path.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.1.1
+   */
+  private function p_load_request_expect() {
+    $expect = $this->headers['expect'];
+    if (empty($expect)) {
+      $this->request[self::REQUEST_EXPECT]['invalid'] = TRUE;
+      unset($expect);
+      return;
+    }
+
+    $this->request[self::REQUEST_EXPECT]['defined'] = TRUE;
+    $this->request[self::REQUEST_EXPECT]['data'] = $expect;
+
+    unset($expect);
+
+    $this->request[self::REQUEST_EXPECT]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: from.
+   *
+   * Warning: never use PHP's filter_var('', FILTER_VALIDATE_EMAIL), it is non-compliant and fails to properly validate.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.5.1
+   * @see: https://tools.ietf.org/html/rfc5322#section-3.4
+   */
+  private function p_load_request_from() {
+    if (empty($this->headers['from'])) {
+      $this->request[self::REQUEST_FROM]['invalid'] = TRUE;
+      return;
+    }
+
+    // @todo: write a custom validation to ensure that the from email address is valid.
+    $this->request[self::REQUEST_FROM]['defined'] = TRUE;
+    $this->request[self::REQUEST_FROM]['data'] = $this->headers['from'];
+
+    unset($from);
+
+    $this->request[self::REQUEST_FROM]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: host.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.5.1
+   * @see: https://tools.ietf.org/html/rfc5322#section-3.4
+   */
+  private function p_load_request_host() {
+    if (empty($this->headers['host'])) {
+      $this->request[self::REQUEST_HOST]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_HOST]['data'] = $this->p_parse_uri($this->headers['host']);
+
+    if ($this->request[self::REQUEST_HOST]['data']['invalid']) {
+      $this->request[self::REQUEST_HOST]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_HOST]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_HOST]['data']['invalid']);
+
+    $this->request[self::REQUEST_HOST]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: if-match.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234
+   * @see: https://tools.ietf.org/html/rfc7232#section-3.1
+   * @see: https://tools.ietf.org/html/rfc7232#section-2.3
+   */
+  private function p_load_request_if_match() {
+    if (empty($this->headers['if_match'])) {
+      $this->request[self::REQUEST_IF_MATCH]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_IF_MATCH]['data'] = $this->p_parse_if_entity_tag($this->headers['if_match']);
+
+    if ($this->request[self::REQUEST_IF_MATCH]['data']['invalid']) {
+      $this->request[self::REQUEST_IF_MATCH]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_IF_MATCH]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_IF_MATCH]['data']['current']);
+    unset($this->request[self::REQUEST_IF_MATCH]['data']['invalid']);
+
+    $this->request[self::REQUEST_IF_MATCH]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: if-none-match.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234
+   * @see: https://tools.ietf.org/html/rfc7232#section-3.2
+   * @see: https://tools.ietf.org/html/rfc7232#section-2.3
+   */
+  private function p_load_request_if_none_match() {
+    if (empty($this->headers['if_none_match'])) {
+      $this->request[self::REQUEST_IF_NONE_MATCH]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_IF_NONE_MATCH]['data'] = $this->p_parse_if_entity_tag_and_weak($this->headers['if_none_match']);
+
+    if ($this->request[self::REQUEST_IF_NONE_MATCH]['data']['invalid']) {
+      $this->request[self::REQUEST_IF_NONE_MATCH]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_IF_NONE_MATCH]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_IF_NONE_MATCH]['data']['invalid']);
+
+    $this->request[self::REQUEST_IF_NONE_MATCH]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: if-modified-since.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232#section-3.3
+   */
+  private function p_load_request_if_modified_since() {
+    if ($this->p_validate_date_is_valid_rfc($this->headers['if_modified_since']) === FALSE) {
+      $this->request[self::REQUEST_IF_MODIFIED_SINCE]['invalid'] = TRUE;
+      return;
+    }
+
+    $timestamp = strtotime($this->headers['if_modified_since']);
+    if ($timestamp === FALSE) {
+      $this->request[self::REQUEST_IF_MODIFIED_SINCE]['invalid'] = TRUE;
+      unset($timestamp);
+      return;
+    }
+
+    $this->request[self::REQUEST_IF_MODIFIED_SINCE]['defined'] = TRUE;
+    $this->request[self::REQUEST_IF_MODIFIED_SINCE]['data'] = $timestamp;
+
+    unset($timestamp);
+
+    $this->request[self::REQUEST_IF_MODIFIED_SINCE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: if-unmodified-since.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232#section-3.4
+   */
+  private function p_load_request_if_unmodified_since() {
+    if (p_validate_date_is_valid_rfc($this->headers['if_unmodified_since']) === FALSE) {
+      $this->request[self::REQUEST_IF_UNMODIFIED_SINCE]['invalid'] = TRUE;
+      return;
+    }
+
+    $timestamp = strtotime($this->headers['if_unmodified_since']);
+    if ($timestamp === FALSE) {
+      $this->request[self::REQUEST_IF_UNMODIFIED_SINCE]['invalid'] = TRUE;
+      unset($timestamp);
+      return;
+    }
+
+    $this->request[self::REQUEST_IF_UNMODIFIED_SINCE]['defined'] = TRUE;
+    $this->request[self::REQUEST_IF_UNMODIFIED_SINCE]['data'] = $timestamp;
+
+    unset($timestamp);
+
+    $this->request[self::REQUEST_IF_UNMODIFIED_SINCE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: if-range.
+   *
+   * The range can be either a date or an entity-tag.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232#section-3.5
+   * @see: https://tools.ietf.org/html/rfc7233#section-3.2
+   */
+  private function p_load_request_if_range() {
+    if (p_validate_date_is_valid_rfc($this->headers['if_range'])) {
+      $timestamp = strtotime($this->headers['if_range']);
+      if ($timestamp === FALSE) {
+        $this->request[self::REQUEST_IF_RANGE]['invalid'] = TRUE;
+        $this->request[self::REQUEST_IF_RANGE]['data'] = array(
+          'is_date' => TRUE,
+        );
+        unset($timestamp);
+        return;
+      }
+
+      $this->request[self::REQUEST_IF_RANGE]['defined'] = TRUE;
+      $this->request[self::REQUEST_IF_RANGE]['data'] = array(
+        'range' => $timestamp,
+        'is_date' => TRUE,
+      );
+
+      unset($timestamp);
+      return;
+    }
+
+    // at this point, assume the if-range is an entity tag.
+    $if_range = $this->headers['if_range'];
+    if (empty($if_range)) {
+      $this->request[self::REQUEST_IF_RANGE]['if_range'] = TRUE;
+      $this->request[self::REQUEST_IF_RANGE]['data']['is_date'] = FALSE;
+      unset($if_range);
+      return;
+    }
+
+    $this->request[self::REQUEST_IF_RANGE]['data'] = $this->p_parse_if_entity_tag_and_weak($if_range);
+    $this->request[self::REQUEST_IF_RANGE]['data']['is_date'] = FALSE;
+
+    if ($this->request[self::REQUEST_IF_RANGE]['data']['invalid']) {
+      $this->request[self::REQUEST_IF_RANGE]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_IF_RANGE]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_IF_RANGE]['data']['invalid']);
+
+    unset($if_range);
+
+    $this->request[self::REQUEST_IF_RANGE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: range.
+   *
+   * @see: https://tools.ietf.org/html/rfc7233#section-3.1
+   */
+  private function p_load_request_range() {
+    if (empty($this->headers['range'])) {
+      $this->request[self::REQUEST_RANGE]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['range']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_RANGE]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_RANGE]['data'] = $this->pr_rfc_string_is_range($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_RANGE]['data']['invalid']) {
+      $this->request[self::REQUEST_RANGE]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_RANGE]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_RANGE]['data']['invalid']);
+
+    $this->request[self::REQUEST_RANGE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: max-forwards.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.1.2
+   */
+  private function p_load_request_max_forwards() {
+    if (is_int($this->headers['max_forwards'])) {
+      $this->request[self::REQUEST_MAX_FORWARDS]['defined'] = TRUE;
+      $this->request[self::REQUEST_MAX_FORWARDS]['data'] = (int) $this->headers['max_forwards'];
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['max_forwards']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_MAX_FORWARDS]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $parsed = pr_rfc_string_is_digit($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($parsed['invalid']) {
+      $this->request[self::REQUEST_MAX_FORWARDS]['invalid'] = TRUE;
+      unset($parsed);
+      return;
+    }
+
+    $this->request[self::REQUEST_MAX_FORWARDS]['defined'] = TRUE;
+    $this->request[self::REQUEST_MAX_FORWARDS]['data'] = intval($parsed['text']);
+
+    unset($parsed);
+
+    $this->request[self::REQUEST_MAX_FORWARDS]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: origin.
+   *
+   * Errata: I cannot find Origin specified in the RFC's that I looked at.
+   * - Either I am completely overlooking it or its defined in some other standard.
+   * - I will use wikipedia's notes to define and utilize this field.
+   *
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  private function p_load_request_origin() {
+    if (empty($this->headers['origin'])) {
+      $this->request[self::REQUEST_ORIGIN]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_ORIGIN]['data'] = $this->p_parse_uri($this->headers['origin']);
+
+    if ($this->request[self::REQUEST_ORIGIN]['data']['invalid']) {
+      $this->request[self::REQUEST_ORIGIN]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_ORIGIN]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_ORIGIN]['data']['invalid']);
+
+    $this->request[self::REQUEST_ORIGIN]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: referer.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.5.2
+   */
+  private function p_load_request_referer() {
+    if (empty($this->headers['referer'])) {
+      $this->request[self::REQUEST_REFERER]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_REFERER]['data'] = $this->p_parse_uri($this->headers['referer']);
+
+    if ($this->request[self::REQUEST_REFERER]['data']['invalid']) {
+      $this->request[self::REQUEST_REFERER]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_REFERER]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_REFERER]['data']['invalid']);
+
+    $this->request[self::REQUEST_REFERER]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: te.
+   *
+   * @see: https://tools.ietf.org/html/rfc7230#section-4.3
+   */
+  private function p_load_request_te() {
+    if (empty($this->headers['te'])) {
+      $this->request[self::REQUEST_TE]['te'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['te']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_TE]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    $this->request[self::REQUEST_TE]['data'] = $this->pr_rfc_string_is_negotiation($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_TE]['data']['invalid']) {
+      $this->request[self::REQUEST_TE]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_TE]['defined'] = TRUE;
+
+      // convert the known values into integers for improved processing.
+      foreach ($this->request[self::REQUEST_TE]['data']['choices'] as $weight => &$choice) {
+        foreach ($choice as $key => &$c) {
+          $lowercase = mb_strtolower($c['choice']);
+          if ($c['choice'] == 'compress') {
+            $c['encoding'] = self::ENCODING_COMPRESS;
+          }
+          elseif ($c['choice'] == 'deflate') {
+            $c['encoding'] = self::ENCODING_DEFLATE;
+          }
+          elseif ($c['choice'] == 'gzip') {
+            $c['encoding'] = self::ENCODING_GZIP;
+          }
+          elseif ($c['choice'] == 'bzip') {
+            $c['encoding'] = self::ENCODING_BZIP;
+          }
+          elseif ($c['choice'] == 'lzo') {
+            $c['encoding'] = self::ENCODING_LZO;
+          }
+          elseif ($c['choice'] == 'xz') {
+            $c['encoding'] = self::ENCODING_XZ;
+          }
+          elseif ($c['choice'] == 'exit') {
+            $c['encoding'] = self::ENCODING_EXI;
+          }
+          elseif ($c['choice'] == 'identity') {
+            $c['encoding'] = self::ENCODING_IDENTITY;
+          }
+          elseif ($c['choice'] == 'sdch') {
+            $c['encoding'] = self::ENCODING_SDCH;
+          }
+          elseif ($c['choice'] == 'pg') {
+            $c['encoding'] = self::ENCODING_PG;
+          }
+          else {
+            $c['encoding'] = NULL;
+          }
+        }
+      }
+      unset($lowercase);
+    }
+    unset($this->request[self::REQUEST_TE]['data']['invalid']);
+
+    $this->request[self::REQUEST_TE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: user-agent.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.5.3
+   */
+  private function p_load_request_user_agent() {
+    if (empty($this->headers['user_agent'])) {
+      $this->request[self::REQUEST_USER_AGENT]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['user_agent']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_USER_AGENT]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    // make sure agent is valid text.
+    $agent = $this->pr_rfc_string_is_basic($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($agent['invalid']) {
+      $this->request[self::REQUEST_USER_AGENT]['invalid'] = TRUE;
+      unset($agent);
+      return;
+    }
+
+    $this->request[self::REQUEST_USER_AGENT]['data'] = $this->p_parse_user_agent($agent['text']);
+    unset($agent);
+
+    if ($this->request[self::REQUEST_USER_AGENT]['data']['invalid']) {
+      $this->request[self::REQUEST_USER_AGENT]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_USER_AGENT]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_USER_AGENT]['data']['invalid']);
+
+    $this->request[self::REQUEST_USER_AGENT]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: upgrade.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.5.3
+   */
+  private function p_load_request_upgrade() {
+    if (empty($this->headers['upgrade'])) {
+      $this->request[self::REQUEST_UPGRADE]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['upgrade']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_UPGRADE]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    // @todo: future versions may do something with this, until then just check for text.
+    $this->request[self::REQUEST_UPGRADE]['data'] = $this->pr_rfc_string_is_basic($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_UPGRADE]['data']['invalid']) {
+      $this->request[self::REQUEST_UPGRADE]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_UPGRADE]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_UPGRADE]['data']['invalid']);
+
+    $this->request[self::REQUEST_UPGRADE]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: via.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.5.3
+   */
+  private function p_load_request_via() {
+    if (empty($this->headers['via'])) {
+      $this->request[self::REQUEST_VIA]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['via']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_VIA]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    // @todo: future versions may do something with this, until then just check for text.
+    $this->request[self::REQUEST_VIA]['data'] = $this->pr_rfc_string_is_basic($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_VIA]['data']['invalid']) {
+      $this->request[self::REQUEST_VIA]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_VIA]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_VIA]['data']['invalid']);
+
+    $this->request[self::REQUEST_VIA]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: warning.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.5
+   */
+  private function p_load_request_warning() {
+    if (empty($this->headers['warning'])) {
+      $this->request[self::REQUEST_WARNING]['invalid'] = TRUE;
+      return;
+    }
+
+    $text = $this->pr_rfc_string_prepare($this->headers['warning']);
+    if ($text['invalid']) {
+      $this->request[self::REQUEST_WARNING]['invalid'] = TRUE;
+      unset($text);
+      return;
+    }
+
+    // @todo: future versions may do something with this, until then just check for text.
+    $this->request[self::REQUEST_WARNING]['data'] = $this->pr_rfc_string_is_basic($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($this->request[self::REQUEST_WARNING]['data']['invalid']) {
+      $this->request[self::REQUEST_WARNING]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_WARNING]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_WARNING]['data']['invalid']);
+
+    $this->request[self::REQUEST_WARNING]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: checksum_header.
+   *
+   * The following format is expected:
+   * - 1*(atext):1*(atext):1*(atext)
+   *
+   * The header should have either 'partial:checksum_type:checksum_value' or 'complete:checksum_type:checksum_value'.
+   * The checksum_value is stored in base64.
+   *
+   * The content checksum represents the checksum of the HTTP content (HTTP packet body).
+   * This should already be defined if and when a checksum_header is used.
+   *
+   * The header checksum represents the checksum of the HTTP header when only the checksum_header field is missing.
+   * The checksum_content header should not be removed when creating or validating the checksum_header.
+   *
+   * @see: self::p_parse_checksum()
+   */
+  private function p_load_request_checksum_header() {
+    if (empty($this->headers['checksum_header'])) {
+      $this->request[self::REQUEST_CHECKSUM_HEADER]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_CHECKSUM_HEADER]['data'] = $this->p_parse_checksum($this->headers['checksum_header']);
+
+    if ($this->request[self::REQUEST_CHECKSUM_HEADER]['data']['invalid']) {
+      $this->request[self::REQUEST_CHECKSUM_HEADER]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_CHECKSUM_HEADER]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_CHECKSUM_HEADER]['data']['invalid']);
+
+    $this->request[self::REQUEST_CHECKSUM_HEADER]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: checksum_headers.
+   *
+   * The following format is expected:
+   * - 1*(atext)*(*(wsp) "," *(wsp)1*(atext))
+   *
+   * The headers is a comma separated list of http headers present at the time the header checksum was generated.
+   * This is necessary because anything en-route may add or alter headers and the checksum needs to still validate for the checksum provided.
+   * This also gives the client or server and idea on what was added and what was not.
+   *
+   * @see: self::p_parse_checksum_headers()
+   */
+  private function p_load_request_checksum_headers() {
+    if (empty($this->headers['checksum_header'])) {
+      $this->request[self::REQUEST_CHECKSUM_HEADERS]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_CHECKSUM_HEADERS]['data'] = $this->p_parse_checksum_headers($this->headers['checksum_headers']);
+
+    if ($this->request[self::REQUEST_CHECKSUM_HEADERS]['data']['invalid']) {
+      $this->request[self::REQUEST_CHECKSUM_HEADERS]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_CHECKSUM_HEADERS]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_CHECKSUM_HEADERS]['data']['invalid']);
+
+    $this->request[self::REQUEST_CHECKSUM_HEADERS]['invalid'] = FALSE;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: checksum_content.
+   *
+   * The following format is expected:
+   * - 1*(atext):1*(atext):1*(atext)
+   *
+   * The header should have either 'partial:checksum_type:checksum_value' or 'complete:checksum_type:checksum_value'.
+   * The checksum_value is stored in base64.
+   *
+   * The content checksum represents the checksum of the HTTP content (HTTP packet body).
+   * This should already be defined if and when a checksum_header is used.
+   *
+   * This is not part of any official standard, but is added as an additional feature of this project.
+   * Unlike content_md5, this does not require additional headers.
+   *
+   * @see: self::p_parse_checksum()
+   */
+  private function p_load_request_checksum_content() {
+    if (empty($this->headers['checksum_content'])) {
+      $this->request[self::REQUEST_CHECKSUM_CONTENT]['invalid'] = TRUE;
+      return;
+    }
+
+    $this->request[self::REQUEST_CHECKSUM_CONTENT]['data'] = $this->p_parse_checksum($this->headers['checksum_content']);
+
+    if ($this->request[self::REQUEST_CHECKSUM_CONTENT]['data']['invalid']) {
+      $this->request[self::REQUEST_CHECKSUM_CONTENT]['invalid'] = TRUE;
+    }
+    else {
+      $this->request[self::REQUEST_CHECKSUM_CONTENT]['defined'] = TRUE;
+    }
+    unset($this->request[self::REQUEST_CHECKSUM_CONTENT]['data']['invalid']);
+
+    $this->request[self::REQUEST_CHECKSUM_CONTENT]['invalid'] = FALSE;
+  }
+
+  /**
+   * Store raw values for fields that will not have specific parsing done to them.
+   *
+   * The 'raw' value will be made lower case and then trimmed.
+   *
+   * @param string $field
+   *   The name of the field as it is defined in $this->headers.
+   * @param int $key
+   *   The array key in which to store the data in.
+   * @param int $max_length
+   *   The maximum length that should be allowed in the data.
+   */
+  private function p_load_request_rawish($field, $key, $max_length = NULL) {
+    $raw = $this->headers[$field];
+    if (empty($raw)) {
+      unset($raw);
+      return;
+    }
+
+    $this->request[$key]['defined'] = TRUE;
+    $this->request[$key]['invalid'] = FALSE;
+
+    if (is_null($max_length)) {
+      $this->request[$key]['data'] = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $raw));
+    }
+    else {
+      $this->request[$key]['data'] = mb_substr(mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $raw)), 0, $max_length);
+    }
+    unset($raw);
+  }
+
+  /**
+   * Store raw values for unknown fields
+   *
+   * This is identical to self::p_load_request_rawish() except the results are stored in an array.
+   *
+   * @param string $field
+   *   The name of the field as it is defined in $this->headers.
+   * @param int $key
+   *   The array key in which to store the data in.
+   * @param int $max_length
+   *   The maximum length that should be allowed in the data.
+   */
+  private function p_load_request_unknown($field, $key, $max_length = NULL) {
+    $raw = $this->headers[$field];
+    if (empty($raw)) {
+      unset($raw);
+      return;
+    }
+
+    $this->request[$key]['defined'] = TRUE;
+    $this->request[$key]['invalid'] = FALSE;
+
+    if (is_null($max_length)) {
+      $this->request[$key]['data'][$field] = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $raw));
+    }
+    else {
+      $this->request[$key]['data'][$field] = mb_substr(mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $raw)), 0, $max_length);
+    }
+    unset($raw);
+  }
+
+  /**
+   * Validate that the given date string is in one of the supported rfc formats.
+   *
+   * This function is necessary because simply converting the date string to a timestamp via strtotime() allows to many other possibilities.
+   * To prevent unwanted dates, such as 'now', convert the passed timestamp into an rfd1123 into a date string.
+   * The converted timestamp and the original datestring must match exactly.
+   *
+   * @todo: review this function to see if any utf8 support needs to be added.
+   *
+   * @param string $original
+   *   Tne original, unaltered, date string.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.1.1
+   * @see: https://tools.ietf.org/html/rfc5322#section-3.3
+   */
+  private function p_validate_date_is_valid_rfc($original) {
+    $timestamp = strtotime($original);
+    if ($timestamp === FALSE) {
+      unset($timestamp);
+      return FALSE;
+    }
+
+    $raw = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $original));
+
+    // rfc5322 is the preferred/recommended format.
+    $rfc5322 = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', date(self::TIMESTAMP_RFC_5322, $timestamp)));
+    if ($raw == $rfc5322) {
+      unset($raw);
+      unset($timestamp);
+      unset($rfc5322);
+      return TRUE;
+    }
+    unset($rfc5322);
+
+    $rfc1123 = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', date(self::TIMESTAMP_RFC_1123, $timestamp)));
+    if ($raw == $rfc1123) {
+      unset($raw);
+      unset($timestamp);
+      unset($rfc1123);
+      return TRUE;
+    }
+    unset($rfc1123);
+
+    $rfc850 = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', date(self::TIMESTAMP_RFC_850, $timestamp)));
+    if ($raw == $rfc850) {
+      unset($raw);
+      unset($timestamp);
+      unset($rfc850);
+      return TRUE;
+    }
+    unset($rfc850);
+
+    unset($raw);
+    unset($timestamp);
+    return FALSE;
+  }
+
+  /**
+   * Process the accept header value sub parts.
+   *
+   * @param string $super
+   *   The string that contains the value and the priority.
+   * @param string $value
+   *   The value to be returned.
+   * @param string $priority
+   *   The priority to be returned.
+   */
+  private function p_process_accept_parts_sub($super, &$value, &$priority) {
+    $parts_sub = mb_split(self::DELIMITER_ACCEPT_SUB, $super);
+
+    $part_sub_value = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $parts_sub[0]));
+    $part_sub_priority = NULL;
+    if (count($parts_sub) > 1) {
+      $part_sub_priority = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $parts_sub[1]));
+    }
+
+    if (self::p_length_string($part_sub_value) > 2) {
+      if ($part_sub_value[0] == self::DELIMITER_ACCEPT_SUB_0 && $part_sub_value[1] == self::DELIMITER_ACCEPT_SUB_1) {
+        $value = $part_sub_priority;
+        $parts_sub_priority = $part_sub_value;
+      }
+    }
+    else {
+      $value = $part_sub_value;
+    }
+    unset($part_sub_value);
+
+    if (!is_null($part_sub_priority) && self::p_length_string($part_sub_priority) > 2 && $part_sub_priority == 'q' && $part_sub_priority == '=') {
+      $part = preg_replace('/(^\s+)|(\s+$)/us', '', str_replace(self::DELIMITER_ACCEPT_SUB_0 . self::DELIMITER_ACCEPT_SUB_1, '', $part_sub_priority));
+
+      if (is_numeric($part)) {
+        $priority = sprintf('%.1f', (float) $part);
+      }
+      else {
+        $priority = '1.0';
+      }
+
+      unset($part);
+    }
+    else {
+      $priority = '1.0';
+    }
+
+    unset($part_sub_priority);
+    unset($parts_sub);
+  }
+
+  /**
+   * Decode and check that the given uri is valid.
+   *
+   * This does not decode the uri, it separates it into its individual parts.
+   *
+   * Validation is done according to rfc3986.
+   *
+   *   foo://example.com:8042/over/there?name=ferret#nose
+   *   \_/   \______________/\_________/ \_________/ \__/
+   *    |           |            |            |        |
+   *  scheme    authority       path        query   fragment
+   *    |   _____________________|__
+   *   / \ /                        \
+   *   urn:example:animal:ferret:nose
+   *
+   *
+   * @param string $uri
+   *   The url to validate and decode.
+   *
+   * @return array
+   *   A decoded uri split into its different parts inside an array.
+   *   An array key called 'invalid' exists to designate that the uri is invalid.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986
+   */
+  private function p_parse_uri($uri) {
+    $result = array(
+      'scheme' => array(),
+      'authority' => array(),
+      'path' => array(),
+      'query' => array(),
+      'fragment' => array(),
+      'invalid' => FALSE,
+    );
+
+    // @todo: completely rewrite below.
+    $result['invalid'] = TRUE;
+/*
+    $matches = array();
+    $matched = preg_match('!^((\w[=|\w|\d|\s|\.|-|_|~|%]*)*:)*([^#|?]*)(\?([^#]+))*(#(.+))*$!iu', $uri, $matches);
+
+    if ($matched == FALSE || !array_key_exists(3, $matches)) {
+      unset($address);
+      unset($matches);
+      unset($matched);
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+    unset($matched);
+
+
+    // process scheme.
+    if (array_key_exists(2, $matches) && self::p_length_string($matches[2]) > 0) {
+      $combined = $matches[3];
+      if (array_key_exists(4, $matches)) {
+        $combined .= $matches[4];
+      }
+      if (array_key_exists(6, $matches)) {
+        $combined .= $matches[6];
+      }
+
+      $scheme_string = preg_replace('!:' . $combined . '$!iu', '', $matches[0]);
+      $result['scheme'] = mb_split(':', $scheme_string);
+      unset($scheme_string);
+      unset($combined);
+
+      foreach ($result['scheme'] as &$s) {
+        $s = urldecode($s);
+      }
+      unset($s);
+    }
+
+
+    // process authority.
+    if (self::p_length_string($matches[3]) > 0) {
+      // rfc6854 designates multiple uris, separated by commas.
+      $authority = mb_split(',', $matches[3]);
+      foreach ($authority as $a) {
+        $sub_matches = array();
+        $sub_matched = preg_match('!^(//|/|)(([^@]+)@)*(.*)$!iu', $a, $sub_matches);
+
+        if ($sub_matched === FALSE || !isset($sub_matches[4])) {
+          $result['invalid'] = TRUE;
+
+          unset($sub_matches);
+          unset($sub_matched);
+          continue;
+        }
+
+
+        // process user information.
+        $information_matches = array();
+        if (preg_match('@^([=|!|$|&|\'|(|)|\*|\+|,|;|\w|\d|-|\.|_|~|%|\s]*)(:|)$@iu', $sub_matches[3], $information_matches) === FALSE || !isset($information_matches[1])) {
+          $result['invalid'] = TRUE;
+
+          unset($information_matches);
+          unset($sub_matches);
+          unset($sub_matched);
+          continue;
+        }
+
+        $authority_setting = array(
+          'type_path' => self::URI_PATH_THIS,
+          'type_host' => 0,
+          'user' => urldecode($information_matches[1]),
+          'host' => NULL,
+          'port' => NULL,
+        );
+        unset($information_matches);
+
+
+        // process host information.
+        if ($sub_matches[1] == '//') {
+          $authority_setting['type_path'] = self::URI_PATH_SITE;
+        }
+        elseif ($sub_matches[1] == '/') {
+          $authority_setting['type_path'] = self::URI_PATH_BASE;
+        }
+
+        $ipv6_matches = array();
+        if (preg_match('@^\[([^\]]+)\](:\d+$|$)@iu', $sub_matches[4], $ipv6_matches) !== FALSE && isset($ipv6_matches[1])) {
+          $authority_setting['type_host'] = self::URI_HOST_IPV6;
+
+          $ip = inet_pton($ipv6_matches[1]);
+          if ($ip === FALSE) {
+            $result['invalid'] = TRUE;
+
+            unset($ip);
+            unset($ipv6_matches);
+            continue;
+          }
+
+          $authority_setting['host'] = inet_ntop($ip);
+          unset($ip);
+
+          if (isset($ipv6_matches[2]) && self::p_length_string($ipv6_matches[2]) > 0) {
+            $authority_setting['port'] = (int) $ipv6_matches[2];
+          }
+
+          // @todo: ipvfuture is actually embedded inside of the the double brackets used by ipv6.
+          //        to support this, the ipv6 regex must be modified to check for the ipvfuture parameters.
+          // $authority_setting['type_host'] = self::URI_HOST_IPVX;
+          // '@v[\d|a|b|c|d|e|f]\.([=|!|$|&|\'|(|)|\*|\+|,|;|\w|\d|-|\.|_|~|%|:]*)@i'
+        }
+        unset($ipv6_matches);
+
+        $ipv4_matches = array();
+        if (is_null($authority_setting['host']) && preg_match('@(\d+\.\d+\.d+\.d+)(:(\d+)|)$@iu', $sub_matches[4], $ipv4_matches) !== FALSE && isset($ipv4_matches[1])) {
+          $authority_setting['type_host'] = self::URI_HOST_IPV4;
+
+          $ip = inet_pton($ipv4_matches[1]);
+          if ($ip === FALSE) {
+            $result['invalid'] = TRUE;
+
+            unset($ip);
+            unset($ipv4_matches);
+            continue;
+          }
+
+          $authority_setting['host'] = inet_ntop($ip);
+          unset($ip);
+
+          if (isset($ipv4_matches[3]) && self::p_length_string($ipv4_matches[3]) > 0) {
+            $authority_setting['port'] = (int) $ipv4_matches[3];
+          }
+        }
+        unset($ipv4_matches);
+
+        $ipv4_matches = array();
+        if (is_null($authority_setting['host']) && preg_match('@(\d+\.\d+\.d+\.d+)(:(\d+)|)$@iu', $sub_matches[4], $ipv4_matches) !== FALSE && isset($ipv4_matches[1])) {
+          $authority_setting['type_host'] = self::URI_HOST_IPV4;
+
+          $ip = inet_pton($ipv4_matches[1]);
+          if ($ip === FALSE) {
+            $result['invalid'] = TRUE;
+
+            unset($ip);
+            unset($ipv4_matches);
+            continue;
+          }
+
+          $authority_setting['host'] = inet_ntop($ip);
+          unset($ip);
+
+          if (isset($ipv4_matches[3]) && self::p_length_string($ipv4_matches[3]) > 0) {
+            $authority_setting['port'] = (int) $ipv4_matches[3];
+          }
+        }
+        unset($ipv4_matches);
+
+        $name_matches = array();
+        if (is_null($authority_setting['host']) && preg_match('@((=|\w|\d|-|\.|_|~|\!|$|&|\'|(|)|\*|\+|,|;)+)(:(\d+)|)$@iu', $sub_matches[4], $name_matches) !== FALSE && isset($name_matches[1])) {
+          $authority_setting['type_host'] = self::URI_HOST_NAME;
+          $authority_setting['host'] = $name_matches[2];
+
+          if (isset($name_matches[4]) && self::p_length_string($name_matches[4]) > 0) {
+            $authority_setting['port'] = (int) $name_matches[4];
+          }
+        }
+        unset($name_matches);
+
+        $result['authority'][] = $authority_setting;
+
+        unset($authority_setting);
+        unset($sub_matches);
+        unset($sub_matched);
+      }
+
+      unset($a);
+      unset($authority);
+    }
+
+
+    // process query.
+    if (array_key_exists(5, $matches) && self::p_length_string($matches[5]) > 0) {
+      $query_parts = mb_split(',', $matches[5]);
+
+      foreach ($query_parts as $qp) {
+        $qp_parts = mb_split('=', $qp, 2);
+
+        if (is_array($qp_parts) && isset($qp_parts[0])) {
+          $decoded = urldecode($qp_parts[0]);
+          if (isset($qp_parts[1])) {
+            $result['query'][$decoded] = urldecode($qp_parts[1]);
+          }
+          else {
+            $result['query'][$decoded] = NULL;
+          }
+          unset($decoded);
+        }
+      }
+      unset($qp);
+      unset($query_parts);
+    }
+
+
+    // process fragment.
+    if (array_key_exists(7, $matches) && self::p_length_string($matches[7]) > 0) {
+      $result['fragment'][] = urldecode($matches[7]);
+    }
+
+    unset($matches);
+*/
+    return $result;
+  }
+
+  /**
+   * Decode and check that the given string is a valid entity tag such as with if-match (but do not test for weakness).
+   *
+   * Validation is done according to rfc7232.
+   *
+   * @param string $match
+   *   The string to validate and decode.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'matches': An array of processed entity tags.
+   *   - 'any': A boolean that when TRUE means any matches are allowed and the 'matches' key will be an empty array.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232
+   */
+  private function p_parse_if_entity_tag($match) {
+    $result = array(
+      'matches' => array(),
+      'any' => FALSE,
+      'invalid' => FALSE,
+    );
+
+    $stop = self::p_length_string($match) + 1;
+    if ($stop == 0) {
+      unset($stop);
+
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    $text = $this->pr_rfc_string_prepare($match);
+    if ($text['invalid']) {
+      unset($stop);
+      unset($text);
+
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    $current = 0;
+
+    // The standard specifies the use of a wildcard '*', but only accept wildcard if it is the only character (with no whitespace).
+    if ($stop == 1 && $text['ordinals'][$current] == c_base_ascii::ASTERISK) {
+      unset($stop);
+      unset($text);
+      unset($current);
+
+      $result['any'] = TRUE;
+      return $result;
+    }
+
+    while ($current < $stop) {
+      $parsed = $this->pr_rfc_string_is_quoted_string($text['ordinals'], $text['characters'], $current, self::STOP_AT_CLOSING_CHARACTER);
+      $current = $parsed['current'];
+
+      if ($parsed['invalid']) {
+        $result['invalid'] = TRUE;
+        unset($parsed);
+        break;
+      }
+
+      $result['matches'][] = $parsed['text'];
+
+      // The standard does not immediately define what the rules are for a list of entity tags, but they do imply comma separated.
+      // therefore, until I learn the specifics, I am assuming that only FWS and comma are allowed.
+      $current++;
+      while ($current < $stop) {
+        if ($text['ordinals'][$current] == c_base_ascii::COMMA) {
+          $current++;
+          break;
+        }
+
+        if (!$this->pr_rfc_char_is_fws($text['ordinals'][$current])) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $current++;
+      }
+
+      if ($result['invalid']) {
+        break;
+      }
+    }
+    unset($current);
+    unset($stop);
+    unset($text);
+
+    return $result;
+  }
+
+  /**
+   * Decode and check for weak or strong entity tag matches, such as with: if-none-match or if-range.
+   *
+   * Validation is done according to rfc7232.
+   *
+   * @param string $match
+   *   The string to validate and decode.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'matches': An array of processed entity tags.
+   *   - 'weak': An array of booleans defining whether the corrosponding match in the matches array is weak or not.
+   *   - 'any': A boolean that when TRUE means any matches are allowed and the 'matches' key will be an empty array.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232
+   */
+  private function p_parse_if_entity_tag_and_weak($match) {
+    $result = array(
+      'matches' => array(),
+      'weak' => array(),
+      'any' => FALSE,
+      'invalid' => FALSE,
+    );
+
+    $stop = self::p_length_string($match) + 1;
+    if ($stop == 0) {
+      unset($stop);
+
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    $text = $this->pr_rfc_string_prepare($match);
+    if ($text['invalid']) {
+      unset($stop);
+      unset($text);
+
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    $current = 0;
+
+    // The standard specifies the use of a wildcard '*', but only accept wildcard if it is the only character (with no whitespace).
+    if ($stop == 1 && $text['ordinals'][$current] == c_base_ascii::ASTERISK) {
+      unset($stop);
+      unset($text);
+      unset($current);
+
+      $result['any'] = TRUE;
+      return $result;
+    }
+
+    while ($current < $stop) {
+      $weak = FALSE;
+      if ($text['ordinals'][$current] == c_base_ascii::W) {
+        $current++;
+        if ($current >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        if ($text['ordinals'][$current] == c_base_ascii::SLASH_FORWARD) {
+          $weak = TRUE;
+          $current++;
+          continue;
+        }
+
+        $result['invalid'] = TRUE;
+        break;
+      }
+      elseif ($text['ordinals'][$current] == c_base_ascii::QUOTE_DOUBLE) {
+        $current++;
+        if ($current >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $parsed = $this->pr_rfc_string_is_entity_tag($text['ordinals'], $text['characters'], $current, $stop);
+        $current = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          unset($parsed);
+          break;
+        }
+
+        $result['matches'][] = $parsed['text'];
+        $result['weak'][] = $weak;
+
+        // handle comma separated values.
+        $current++;
+        while ($current < $stop) {
+          if ($text['ordinals'][$current] == c_base_ascii::COMMA) {
+            $current++;
+
+            // seek past all WS.
+            while ($current < $stop) {
+              if (!$this->pr_rfc_char_is_wsp($text['ordinals'][$current])) {
+                break;
+              }
+
+              $current++;
+            }
+
+            break;
+          }
+
+          if (!$this->pr_rfc_char_is_wsp($text['ordinals'][$current])) {
+            $result['invalid'] = TRUE;
+            break;
+          }
+
+          $current++;
+        }
+
+        if ($result['invalid']) {
+          break;
+        }
+      }
+      else {
+        $result['invalid'] = TRUE;
+        break;
+      }
+    }
+    unset($current);
+    unset($stop);
+    unset($text);
+    unset($weak);
+
+    return $result;
+  }
+
+  /**
+   * Decode and check for weak or strong entity tag matches, such as with: user-agent.
+   *
+   * The user agent structure very poorly designed and provides no straight-forward and consistent way to present information.
+   * The approach used here is a quick and simply approach that is intended to be good enough.
+   * Do not expect it to be completely accuarete.
+   *
+   * Being to detailed on agents can cause significant performance penalties that has in the past forced me to implement this particular design.
+   *
+   * @param string $match
+   *   The string to validate and decode.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'full': The entire agent string.
+   *   - 'name_machine': a machine-friendly name for the client, or null if undefined or unknown.
+   *   - 'name_human': a human-friendly name for the client, or null if undefined or unknown.
+   *   - 'engine_name_machine': a machine-friendly name for the client's engine, or null if undefined or unknown.
+   *   - 'engine_name_human': a human-friendly name for the client's engine, or null if undefined or unknown.
+   *   - 'version_major': A major version number of the client, or null if undefined or unknown.
+   *   - 'version_engine': A major version number of the client's engine, or null if undefined or unknown.
+   *   - 'is_ie_edge': a boolean representing whether or not this is IE Edge.
+   *   - 'is_ie_compatibility': a boolean representing whether or not this is IE in "compatibility mode".
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: https://tools.ietf.org/html/rfc7232
+   */
+  private function p_parse_user_agent($agent) {
+    $result = array(
+      'full' => $agent,
+      'name_machine' => NULL,
+      'name_human' => NULL,
+      'engine_name_machine' => NULL,
+      'engine_name_human' => NULL,
+      'version_major' => NULL,
+      'version_engine' => NULL,
+      'is_ie_edge' => FALSE,
+      'is_ie_compatibility' => FALSE,
+      'invalid' => FALSE,
+    );
+
+    $agent_matches = array();
+    $agent_matched = preg_match('/^[^(]*\(([^)]*)\)(.*)$/iu', $agent, $agent_matches);
+
+    if (!$agent_matched) {
+      $result['invalid'] = TRUE;
+      unset($agent_matches);
+      unset($agent_matched);
+      return $result;
+    }
+    unset($agent_matched);
+
+    if (!isset($agent_matches[1])) {
+      unset($agent_matches);
+      return $result;
+    }
+
+
+    // preprocess the agent in an attempt to determine the engine and therefore basic information.
+    $agent_pieces = mb_split(';', $agent_matches[1]);
+
+    if (empty($agent_pieces)) {
+      unset($agent_pieces);
+      unset($agent_matches);
+      return $result;
+    }
+
+    foreach ($agent_pieces as $agent_piece) {
+      $pieces = mb_split('/', $agent_piece);
+
+      // ignore unknown structure.
+      if (count($pieces) > 2) {
+        continue;
+      }
+
+      if (isset($pieces[1])) {
+        $lower_piece_1 = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($pieces[0]));
+        $lower_piece_2 = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($pieces[1]));
+
+        if ($lower_piece_1 == 'trident') {
+          $result['engine_name_machine'] = 'trident';
+          $result['engine_name_human'] = 'Trident';
+          $result['version_engine'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+
+          $result['name_machine'] = 'ie';
+          $result['name_human'] = 'Internet Explorer';
+        }
+        elseif ($lower_piece_1 == 'gecko') {
+          $result['engine_name_machine'] = 'gecko';
+          $result['engine_name_human'] = 'Gecko';
+          $result['version_engine'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+        }
+        elseif ($lower_piece_1 == 'presto') {
+          $result['engine_name_machine'] = 'presto';
+          $result['engine_name_human'] = 'Presto';
+          $result['version_engine'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+        }
+
+        unset($lower_piece_1);
+        unset($lower_piece_2);
+      }
+      elseif (isset($pieces[0])) {
+        $lower_piece_1 = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($pieces[0]));
+
+        if (!empty($lower_piece_1)) {
+          if (preg_match('/^msie \d/iu', $lower_piece_1)) {
+            $lower_piece_2 = preg_replace('/^msie /iu', '', $lower_piece_1);
+
+            $result['name_machine'] = 'ie';
+            $result['name_human'] = 'Internet Explorer';
+
+            if (is_null($result['version_major'])) {
+              $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+            }
+          }
+          elseif (strpos($lower_piece_1, 'midori')) {
+            $result['name_machine'] = 'midori';
+            $result['name_human'] = 'Midori';
+            $result['engine_name_machine'] = 'webkit';
+          }
+          else {
+            // Browsers, such as Internet Explorer, use 'rv:number', such as: 'rv:11'.
+            $revision_parts = explode(':', $lower_piece_1);
+            if (count($revision_parts) == 2 && $revision_parts[0] == 'rv' && is_numeric($revision_parts[1])) {
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) $revision_parts[1];
+              }
+            }
+            unset($revision_parts);
+          }
+        }
+
+        unset($lower_piece_1);
+      }
+    }
+    unset($pieces);
+    unset($agent_pieces);
+
+
+    // determine the client agent information.
+    if (isset($agent_matches[2])) {
+      $agent_pieces = mb_split('\s', $agent_matches[2]);
+
+      if (!empty($agent_pieces)) {
+        foreach ($agent_pieces as $agent_piece) {
+          $pieces = mb_split('/', $agent_piece);
+
+          // ignore unknown structure.
+          if (count($pieces) > 3) {
+            continue;
+          }
+
+          if (isset($pieces[1])) {
+            $lower_piece_1 = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($pieces[0]));
+            $lower_piece_2 = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($pieces[1]));
+
+            if ($lower_piece_1 == 'applewebkit') {
+              $result['engine_name_machine'] = 'webkit';
+              $result['engine_name_human'] = 'Webkit';
+              $result['version_engine'] = (int) $lower_piece_2;
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+            }
+            elseif ($lower_piece_1 == 'safari') {
+              // safari is used in a lot of places that is not safari, so use safari only if it is the only agent detected.
+              if (is_null($result['name_machine'])) {
+                $result['name_machine'] = 'safari';
+                $result['name_human'] = 'Safari';
+
+                if (is_null($result['version_major'])) {
+                  $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+                }
+
+                if (is_null($result['engine_name_machine'])) {
+                  $result['engine_name_machine'] = 'webkit';
+                  $result['engine_name_human'] = 'Webkit';
+                }
+              }
+            }
+            elseif ($lower_piece_1 == 'firefox') {
+              $result['name_machine'] = 'firefox';
+              $result['name_human'] = 'Firefox';
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+
+              $result['engine_name_machine'] = 'gecko';
+              $result['engine_name_human'] = 'Gecko';
+            }
+            elseif ($lower_piece_1 == 'seamonkey') {
+              $result['name_machine'] = 'seamonkey';
+              $result['name_human'] = 'Seamonkey';
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+
+              $result['engine_name_machine'] = 'gecko';
+              $result['engine_name_human'] = 'Gecko';
+            }
+            elseif ($lower_piece_1 == 'gecko') {
+              if (is_null($result['version_engine']) && (is_null($result['engine_name_machine']) || $result['engine_name_machine'] == 'gecko')) {
+                $result['version_engine'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+                $result['engine_name_machine'] = 'gecko';
+                $result['engine_name_human'] = 'Gecko';
+              }
+            }
+            elseif ($lower_piece_1 == 'chrome') {
+              // the newer internet explorer uses safari/webkit based agent names, assign chrome conditionally.
+              if (is_null($result['name_machine']) || $result['name_machine'] == 'safari') {
+                $result['name_machine'] = 'chrome';
+                $result['name_human'] = 'Google Chrome';
+
+                if (is_null($result['version_major'])) {
+                  $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+                }
+              }
+            }
+            elseif ($lower_piece_1 == 'chromium') {
+              $result['name_machine'] = 'chrome';
+              $result['name_human'] = 'Google Chrome';
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+            }
+            elseif ($lower_piece_1 == 'epiphany') {
+              $result['name_machine'] = 'epiphany';
+              $result['name_human'] = 'Ephiphany';
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+
+              if (is_null($result['engine_name_machine'])) {
+                $result['engine_name_machine'] = 'gecko';
+                $result['engine_name_human'] = 'Gecko';
+              }
+            }
+            elseif ($lower_piece_1 == 'konqueror') {
+              $result['name_machine'] = 'konqueror';
+              $result['name_human'] = 'Konqueror';
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+
+              if (is_null($result['engine_name_machine'])) {
+                $result['engine_name_machine'] = 'gecko';
+                $result['engine_name_human'] = 'Gecko';
+              }
+            }
+            elseif ($lower_piece_1 == 'khtml') {
+              $result['name_machine'] = 'konqueror';
+              $result['name_human'] = 'Konqueror';
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+            }
+            elseif ($lower_piece_1 == 'opr') {
+              $result['name_machine'] = 'opera';
+              $result['name_human'] = 'Opera';
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+
+              if (is_null($result['engine_name_machine'])) {
+                $result['engine_name_machine'] = 'presto';
+                $result['engine_name_human'] = 'Presto';
+              }
+            }
+            elseif ($lower_piece_1 == 'edge') {
+              $result['name_machine'] = 'ie';
+              $result['name_human'] = 'Internet Explorer';
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+
+              $result['is_ie_edge'] = TRUE;
+            }
+            elseif ($lower_piece_1 == 'midori') {
+              $result['name_machine'] = 'midori';
+              $result['name_human'] = 'Midori';
+
+              if (is_null($result['version_major'])) {
+                $result['version_major'] = (int) preg_replace('/\..*$/iu', '', $lower_piece_2);
+              }
+            }
+
+            unset($lower_piece_1);
+            unset($lower_piece_2);
+          }
+          elseif (isset($pieces[0])) {
+            $lower_piece_1 = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($pieces[0]));
+
+            if ($lower_piece_1 == 'opera') {
+              $result['name_machine'] = 'opera';
+              $result['name_human'] = 'Opera';
+
+              if (is_null($result['engine_name_machine'])) {
+                $result['engine_name_machine'] = 'presto';
+                $result['engine_name_human'] = 'Presto';
+              }
+            }
+            elseif ($lower_piece_1 == '(khtml,') {
+              // khtml is used in a lot of places that is not safari, so use only when necessary.
+              if (is_null($result['engine_name_machine']) || $result['name_machine'] == 'epiphany' || $result['name_machine'] == 'konqueror') {
+                $result['engine_name_machine'] = 'webkit';
+                $result['engine_name_human'] = 'Webkit';
+              }
+
+              if (is_null($result['name_machine'])) {
+                $result['name_machine'] = 'safari';
+                $result['name_human'] = 'Safari';
+              }
+            }
+            unset($lower_piece_1);
+          }
+        }
+        unset($pieces);
+      }
+      unset($agent_pieces);
+    }
+    unset($agent_matches);
+
+
+    // attempt to determine internet explorer versions if not already found.
+    if ($result['engine_name_machine'] == 'trident' && (is_null($result['name_machine']) || ($result['name_machine'] == 'ie' && is_null($result['version_major'])))) {
+      $result['name_machine'] = 'ie';
+      $result['human_name'] = 'Internet Explorer';
+
+      if (isset($result['is_ie_edge'])) {
+        $result['version_major'] = 12;
+      }
+      elseif ($result['version_engine'] == 7) {
+        $result['version_major'] = 11;
+      }
+      elseif ($result['version_engine'] == 6) {
+        $result['version_major'] = 10;
+      }
+      elseif ($result['version_engine'] == 5) {
+        $result['version_major'] = 9;
+      }
+      elseif ($result['version_engine'] == 4) {
+        $result['version_major'] = 8;
+      }
+    }
+
+
+    // detect internet explorers compatibility mode (for old versions) where possible to allow clients to better handle.
+    if ($result['name_machine'] == 'ie') {
+      if ($result['version_major'] <= 8) {
+        if ($result['version_major'] == 7) {
+          if ($result['engine_name_machine'] == 'trident') {
+            $result['is_ie_compatibility'] = TRUE;
+          }
+        }
+      }
+
+      // alter the (faked) agent version to properly reflect the current browser.
+      if ($result['is_ie_compatibility'] && isset($result['version_engine'])) {
+        if (isset($result['is_ie_edge'])) {
+          $result['version_major'] = 12;
+        }
+        elseif ($result['version_engine'] == 7) {
+          $result['version_major'] = 11;
+        }
+        elseif ($result['version_engine'] == 6) {
+          $result['version_major'] = 10;
+        }
+        elseif ($result['version_engine'] == 5) {
+          $result['version_major'] = 9;
+        }
+        elseif ($result['version_engine'] == 4) {
+          $result['version_major'] = 8;
+        }
+        elseif (preg_match("/; EIE10;/iu", $agent) > 0) {
+          $result['version_major'] = 10;
+        }
+      }
+
+      // added later on to allow for compatibility mode tests to be properly processed.
+      $result['engine_name_machine'] = 'trident';
+      $result['engine_name_human'] = 'Trident';
+    }
+
+
+    // if the agent wasn't identified, check to see if this is a bot or a known command line tool.
+    if (is_null($result['engine_name_machine'])) {
+      $agent_matches = array();
+      preg_match('/^([^(]+)/iu', $agent, $agent_matches);
+
+      if (isset($agent_matches[0])) {
+        $pieces = mb_split('/', $agent_matches[0]);
+        $total_pieces = count($pieces);
+        if ($total_pieces == 2) {
+          $lower_piece_1 = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($pieces[0]));
+          $lower_piece_2 = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($pieces[1]));
+
+          if ($lower_piece_1 == 'curl') {
+            $result['engine_name_machine'] = 'curl';
+            $result['engine_name_human'] = 'Curl';
+
+            if (preg_match('/^(\d|\.)+$/iu', $lower_piece_2) > 0) {
+              $result['version_engine'] = $lower_piece_2;
+            }
+            else {
+              $result['version_engine'] = 0;
+            }
+
+            $result['name_machine'] = 'curl';
+            $result['human_name'] = 'Curl';
+            $result['version_major'] = (int) $lower_piece_2;
+          }
+          elseif ($lower_piece_1 == 'wget') {
+            $result['engine_name_machine'] = 'wget';
+            $result['engine_name_human'] = 'WGet';
+
+            if (preg_match('/^(\d|\.)+$/iu', $lower_piece_2) > 0) {
+              $result['version_engine'] = $lower_piece_2;
+            }
+            else {
+              $result['version_engine'] = 0;
+            }
+
+            $result['name_machine'] = 'wget';
+            $result['human_name'] = 'WGet';
+            $result['version_major'] = (int) $lower_piece_2;
+          }
+          elseif ($lower_piece_1 == 'elinks') {
+            $result['engine_name_machine'] = 'elinks';
+            $result['engine_name_human'] = 'Elimks';
+
+            if (preg_match('/^(\d|\.)+$/iu', $lower_piece_2) > 0) {
+              $result['version_engine'] = $lower_piece_2;
+            }
+            else {
+              $result['version_engine'] = 0;
+            }
+
+            $result['name_machine'] = 'elinks';
+            $result['human_name'] = 'Elimks';
+            $result['version_major'] = (int) $lower_piece_2;
+          }
+          elseif ($lower_piece_1 == 'lynx') {
+            $result['engine_name_machine'] = 'lynx';
+            $result['engine_name_human'] = 'Lynx';
+
+            if (preg_match('/^(\d|\.)+$/iu', $lower_piece_2) > 0) {
+              $result['version_engine'] = $lower_piece_2;
+            }
+            else {
+              $result['version_engine'] = 0;
+            }
+
+            $result['name_machine'] = 'lynx';
+            $result['human_name'] = 'Lynx';
+            $result['version_major'] = (int) $lower_piece_2;
+          }
+
+          unset($lower_piece_1);
+          unset($lower_piece_2);
+        }
+        elseif ($total_pieces == 1) {
+          $lower_piece = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($pieces[0]));
+
+          if ($lower_piece == 'links') {
+            $result['engine_name_machine'] = 'links';
+            $result['engine_name_human'] = 'Links';
+            $result['name_machine'] = 'links';
+            $result['human_name'] = 'Links';
+          }
+
+          unset($lower_piece);
+        }
+        unset($pieces);
+        unset($total_pieces);
+      }
+      unset($agent_matches);
+    }
+
+    return $result;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: checksum_content.
+   *
+   * The following format is expected:
+   * - 1*(atext):1*(atext):1*(atext)
+   *
+   * The header should have either 'partial:checksum_type:checksum_value' or 'complete:checksum_type:checksum_value'.
+   * The checksum_value is stored in base64 and will be decoded by this function.
+   *
+   * This is not part of any official standard, but is added as an additional feature of this project.
+   * Unlike content_md5, this does not require additional headers.
+   *
+   * @param string $checksum
+   *   The checksum string to validate and decode.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'what': A specific way in which to interpret the checksum, currently either: 'partial' or 'full'.
+   *   - 'type': The type of the checksum, such as 'sha256'.
+   *   - 'checksum': The checksum value after it has been base64 decoded.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   */
+  private function p_parse_checksum($checksum) {
+    $result = array(
+      'what' => NULL,
+      'type' => NULL,
+      'checksum' => NULL,
+      'invalid' => FALSE,
+    );
+
+    $fixed_checksum = mb_strtolower(preg_replace('/(^\s+)|(\s+$)/us', '', $checksum));
+    if (empty($fixed_checksum)) {
+      $result['invalid'] = TRUE;
+      unset($fixed_checksum);
+      return $result;
+    }
+
+    $parts = mb_split(':', $fixed_checksum);
+    unset($fixed_checksum);
+    if (count($parts) != 3) {
+      $result['invalid'] = TRUE;
+      unset($parts);
+      return $result;
+    }
+
+
+    // process the partial/complete option.
+    $text = $this->pr_rfc_string_prepare($parts[0]);
+    if ($text['invalid']) {
+      $result['invalid'] = TRUE;
+      unset($text);
+      unset($parts);
+      return $result;
+    }
+
+    $parsed = $this->pr_rfc_string_is_atext($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($parsed['invalid']) {
+      $result['invalid'] = TRUE;
+      unset($parsed);
+      unset($parts);
+      return $result;
+    }
+
+    if ($parsed['text'] == 'partial') {
+      $result['what'] = self::CHECKSUM_WHAT_FULL;
+    }
+    elseif ($parsed['text'] == 'complete') {
+      $result['what'] = self::CHECKSUM_WHAT_COMPLETE;
+    }
+    else {
+      $result['invalid'] = TRUE;
+      unset($parsed);
+      unset($parts);
+      return $result;
+    }
+    unset($parsed);
+
+
+    // process the checksum option.
+    $text = $this->pr_rfc_string_prepare($parts[1]);
+    if ($text['invalid']) {
+      $result['invalid'] = TRUE;
+      unset($text);
+      unset($parts);
+      return $result;
+    }
+
+    $parsed = $this->pr_rfc_string_is_atext($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($parsed['invalid']) {
+      $result['invalid'] = TRUE;
+      unset($parsed);
+      unset($parts);
+      return $result;
+    }
+
+    if ($parsed['text'] == 'md2') {
+      $result['type'] = self::CHECKSUM_MD2;
+    }
+    elseif ($parsed['text'] == 'md4') {
+      $result['type'] = self::CHECKSUM_MD4;
+    }
+    elseif ($parsed['text'] == 'md5') {
+      $result['type'] = self::CHECKSUM_MD5;
+    }
+    elseif ($parsed['text'] == 'sha1') {
+      $result['type'] = self::CHECKSUM_SHA1;
+    }
+    elseif ($parsed['text'] == 'sha224') {
+      $result['type'] = self::CHECKSUM_SHA224;
+    }
+    elseif ($parsed['text'] == 'sha256') {
+      $result['type'] = self::CHECKSUM_SHA256;
+    }
+    elseif ($parsed['text'] == 'sha384') {
+      $result['type'] = self::CHECKSUM_SHA384;
+    }
+    elseif ($parsed['text'] == 'sha512') {
+      $result['type'] = self::CHECKSUM_SHA512;
+    }
+    elseif ($parsed['text'] == 'crc32') {
+      $result['type'] = self::CHECKSUM_CRC32;
+    }
+    else {
+      $result['invalid'] = TRUE;
+      unset($parsed);
+      unset($parts);
+      return $result;
+    }
+    unset($parsed);
+
+
+    // process the checksum value.
+    $text = $this->pr_rfc_string_prepare($parts[1]);
+    unset($parts);
+    if ($text['invalid']) {
+      $result['invalid'] = TRUE;
+      unset($text);
+      return $result;
+    }
+
+    $parsed = $this->pr_rfc_string_is_atext($text['ordinals'], $text['characters']);
+    unset($text);
+
+    $result['checksum'] = base64_decode($parsed['text']);
+    unset($parsed);
+    if ($result['checksum'] === FALSE || empty($result['checksum'])) {
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    return $result;
+  }
+
+  /**
+   * Load and process the HTTP request parameter: checksum_headers.
+   *
+   * The following format is expected:
+   * - 1*(atext)*(*(wsp) "," *(wsp)1*(atext))
+   *
+   * The headers is a comma separated list of http headers present at the time the header checksum was generated.
+   * This is necessary because anything en-route may add or alter headers and the checksum needs to still validate for the checksum provided.
+   * This also gives the client or server and idea on what was added and what was not.
+   *
+   * @param string $checksum_headers
+   *   The checksum headers string to validate and decode.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'headers': An array containing (all checksum header names are forced to become lower case):
+   *     - 'known': An array of checksum header strings whose keys are the header ids.
+   *     - 'unknown': An array of checksum header strings whose keys are numerically sorted.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   */
+  private function p_parse_checksum_headers($checksum_headers) {
+    $result = array(
+      'headers' => array(
+        'known' => array(),
+        'unknown' => array(),
+      ),
+      'invalid' => FALSE,
+    );
+
+    $fixed_checksum = mb_strtolower(preg_replace("/(^( |\t)+)|(( |\t)+$)/us", '', $checksum_headers));
+    if (empty($fixed_checksum)) {
+      $result['invalid'] = TRUE;
+      unset($fixed_checksum);
+      return $result;
+    }
+
+    $parts = mb_split(',', $fixed_checksum);
+    unset($fixed_checksum);
+
+    if (empty($parts)) {
+      // this is not an error, it simply means that no headers are associated with the header checksum (effectively making the header checksum pointless).
+      return $result;
+    }
+
+    $mapping_headers = $this->p_get_header_request_mapping();
+    foreach ($parts as $part) {
+      // strip out leading or trailing whitespace.
+      $sanitized = preg_replace("/(^( |\t)+)|(( |\t)+$)/us", '', $part);
+
+      $text = $this->pr_rfc_string_prepare($sanitized);
+      unset($sanitized);
+      if ($text['invalid']) {
+        $result['invalid'] = TRUE;
+        unset($text);
+        unset($parts);
+        unset($part);
+        unset($mapping_headers);
+        return $result;
+      }
+
+      $parsed = $this->pr_rfc_string_is_atext($text['ordinals'], $text['characters']);
+      unset($text);
+
+      if ($parsed['invalid']) {
+        $result['invalid'] = TRUE;
+        unset($parsed);
+        unset($parts);
+        unset($part);
+        unset($mapping_headers);
+        return $result;
+      }
+
+      if (array_key_exists($parsed['text'], $mapping_headers)) {
+        $result['headers']['known'][$mapping_headers[$parsed['text']]] = $parsed['text'];
+      }
+      else {
+        $result['headers']['unknown'][] = $parsed['text'];
+      }
+      unset($parsed);
+    }
+    unset($parts);
+    unset($part);
+    unset($mapping_headers);
+
+    return $result;
+  }
+
+  /**
+   * Simplify handling of the multibyte string length processing.
+   *
+   * @param string $string
+   *   The string to get the length of.
+   *
+   * @return int
+   *   The string length.
+   *   0 is returned on any error.
+   */
+  private function p_length_string($string) {
+    $length = c_base_utf8::s_length_string($string);
+    if ($length instanceof c_base_return_false) {
+      unset($length);
+      return 0;
+    }
+
+    return $length->get_value_exact();
+  }
+
+  /**
+   * Obtain all headers whether or not apache is used.
+   *
+   * One of the problems is that PHP will clobber the request headers in the following ways:
+   * - make them all uppercase.
+   * - Replace '-' with '_'.
+   *
+   * The uppercase situation is not a problem, they just need to be all lowercased.
+   * The '-' to '_' is a problem because the '_' may be the intended functionality.
+   *
+   * There are also some values stored in $_SERVER that may be useful, so additional fields may be created and used.
+   * - These will be prefixed with 'environment-'.
+   *
+   * @fixme: do something about converting '_' to '-'.
+   */
+  private function p_get_all_headers() {
+    $this->headers = array();
+
+    // this works with apache.
+    if (function_exists('getallheaders')) {
+      $all_headers = getallheaders();
+
+      foreach ($all_headers as $key => $value) {
+        // break the header name so that it is consistent until such time that PHP stops clobbering the header names.
+        $broken = preg_replace('/-/u', '_', $key);
+        $this->headers[mb_strtolower($broken)] = $value;
+        unset($broken);
+      }
+      unset($broken);
+      unset($key);
+      unset($value);
+    }
+    else {
+      // non-apache, or calling php from command line.
+      if (isset($_SERVER) && is_array($_SERVER) && !empty($_SERVER)) {
+        foreach ($_SERVER as $key => $value) {
+          $part = mb_strtolower(mb_substr($key, 0, 5));
+
+          if ($part != 'http_') {
+            continue;
+          }
+
+          $part = mb_strtolower(mb_substr($key, 5));
+          $this->headers[$part] = $value;
+        }
+        unset($part);
+        unset($key);
+        unset($value);
+      }
+    }
+
+    if (isset($_SERVER) && is_array($_SERVER) && !empty($_SERVER)) {
+      // find and process potentially useful additional environment variables.
+      if (array_key_exists('REQUEST_TIME_FLOAT', $_SERVER)) {
+        $this->request_time = $_SERVER['REQUEST_TIME_FLOAT'];
+      }
+      elseif (array_key_exists('REQUEST_TIME', $_SERVER)) {
+        $this->request_time = $_SERVER['REQUEST_TIME'];
+      }
+    }
+
+    if (is_null($this->request_time)) {
+      $this->request_time = microtime(TRUE);
+    }
+  }
+
+  /**
+   * Return an array for mapping HTTP request header strings to header ids.
+   *
+   * @return array
+   *   An array for mapping HTTP request header strings to header ids.
+   */
+  private function p_get_header_request_mapping() {
+    return array(
+      'accept' => self::REQUEST_ACCEPT,
+      'accept-charset' => self::REQUEST_ACCEPT_CHARSET,
+      'accept-encoding' => self::REQUEST_ACCEPT_ENCODING,
+      'accept-language' => self::REQUEST_ACCEPT_LANGUAGE,
+      'accept-datetime' => self::REQUEST_ACCEPT_DATETIME,
+      'access-control-request-method' => self::REQUEST_ACCESS_CONTROL_REQUEST_METHOD,
+      'access-control-request-headers' => self::REQUEST_ACCESS_CONTROL_REQUEST_HEADERS,
+      'authorization' => self::REQUEST_AUTHORIZATION,
+      'cache-control' => self::REQUEST_CACHE_CONTROL,
+      'connection' => self::REQUEST_CONNECTION,
+      'cookie' => self::REQUEST_COOKIE,
+      'content-length' => self::REQUEST_CONTENT_LENGTH,
+      'content-type' => self::REQUEST_CONTENT_TYPE,
+      'date' => self::REQUEST_DATE,
+      'expect' => self::REQUEST_EXPECT,
+      'from' => self::REQUEST_FROM,
+      'host' => self::REQUEST_HOST,
+      'if-match' => self::REQUEST_IF_MATCH,
+      'if-modified-since' => self::REQUEST_IF_MODIFIED_SINCE,
+      'if-none-match' => self::REQUEST_IF_NONE_MATCH,
+      'if-range' => self::REQUEST_IF_RANGE,
+      'if-unmodified-since' => self::REQUEST_IF_UNMODIFIED_SINCE,
+      'max-forwards' => self::REQUEST_MAX_FORWARDS,
+      'origin' => self::REQUEST_ORIGIN,
+      'pragma' => self::REQUEST_PRAGMA,
+      'proxy-authorization' => self::REQUEST_PROXY_AUTHORIZATION,
+      'request-range' => self::REQUEST_RANGE,
+      'referer' => self::REQUEST_REFERER,
+      'te' => self::REQUEST_TE,
+      'user-agent' => self::REQUEST_USER_AGENT,
+      'upgrade' => self::REQUEST_UPGRADE,
+      'via' => self::REQUEST_VIA,
+      'warning' => self::REQUEST_WARNING,
+      'x-requested-with' => self::REQUEST_X_REQUESTED_WITH,
+      'x-forwarded-for' => self::REQUEST_X_FORWARDED_FOR,
+      'x-forwarded-host' => self::REQUEST_X_FORWARDED_HOST,
+      'x-forwarded-proto' => self::REQUEST_X_FORWARDED_PROTO,
+      'checksum_header' => self::REQUEST_CHECKSUM_HEADER,
+      'checksum_headers' => self::REQUEST_CHECKSUM_HEADERS,
+      'checksum_content' => self::REQUEST_CHECKSUM_CONTENT,
+    );
+  }
+
+  /**
+   * Return an array for mapping HTTP response header ids to header strings.
+   *
+   * Note: self::RESPONSE_PROTOCOL is not provided here because it is included in self::RESPONSE_STATUS.
+   *
+   * @param bool $case_first
+   *   (optional) When TRUE, the first character of each word will be capitalized.
+   *   The internal code uses lower case for processing, so in those cases, this should be set to FALSE.
+   *   Many clients do this with HTTP headers, so to reduce the ability to fingerprint this project, one should set this to TRUE for HTTP responses.
+   *
+   * @return array
+   *   An array for mapping HTTP response header strings to header ids.
+   */
+  private function p_get_header_response_mapping($case_first = FALSE) {
+    if ($case_first) {
+      return array(
+        self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN => 'Access-Control-Allow-Origin',
+        self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS => 'Access-Control-Allow-Credentials',
+        self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS => 'Access-Control-Expose-Headers',
+        self::RESPONSE_ACCESS_CONTROL_MAX_AGE => 'Access-Control-Max-Age',
+        self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS => 'Access-Control-Allow-Methods',
+        self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS => 'Access-Control-Allow-Headers',
+        self::RESPONSE_ACCEPT_PATCH => 'Accept-Patch',
+        self::RESPONSE_ACCEPT_RANGES => 'Accept-Ranges',
+        self::RESPONSE_AGE => 'Age',
+        self::RESPONSE_ALLOW => 'Allow',
+        self::RESPONSE_CACHE_CONTROL => 'Cache-Control',
+        self::RESPONSE_CONNECTION => 'Connection',
+        self::RESPONSE_CONTENT_DISPOSITION => 'Content-Disposition',
+        self::RESPONSE_CONTENT_ENCODING => 'Content-Encoding',
+        self::RESPONSE_CONTENT_LANGUAGE => 'Content-Language',
+        self::RESPONSE_CONTENT_LENGTH => 'Content-Length',
+        self::RESPONSE_CONTENT_LOCATION => 'Content-Location',
+        self::RESPONSE_CONTENT_RANGE => 'Content-Range',
+        self::RESPONSE_CONTENT_TYPE => 'Content-Type',
+        self::RESPONSE_DATE => 'Date',
+        self::RESPONSE_DATE_ACTUAL => 'Date_Actual',
+        self::RESPONSE_ETAG => 'Etag',
+        self::RESPONSE_EXPIRES => 'Expires',
+        self::RESPONSE_LAST_MODIFIED => 'Last-Modified',
+        self::RESPONSE_LINK => 'Link',
+        self::RESPONSE_LOCATION => 'Location',
+        self::RESPONSE_PRAGMA => 'Pragma',
+        self::RESPONSE_PROXY_AUTHENTICATE => 'Proxy-Authenticate',
+        self::RESPONSE_PUBLIC_KEY_PINS => 'Public-Key-Pins',
+        self::RESPONSE_REFRESH => 'Refresh',
+        self::RESPONSE_RETRY_AFTER => 'Retry-After',
+        self::RESPONSE_SERVER => 'Server',
+        self::RESPONSE_SET_COOKIE => 'Set-Cookie',
+        self::RESPONSE_STATUS => 'Status',
+        self::RESPONSE_STRICT_TRANSPORT_SECURITY => 'Strict-Transport-Security',
+        self::RESPONSE_TRAILER => 'Trailer',
+        self::RESPONSE_TRANSFER_ENCODING => 'Transfer-Encoding',
+        self::RESPONSE_UPGRADE => 'Upgrade',
+        self::RESPONSE_VARY => 'Vary',
+        self::RESPONSE_WARNING => 'Warning',
+        self::RESPONSE_WWW_AUTHENTICATE => 'Www-Authenticate',
+        self::RESPONSE_X_CONTENT_SECURITY_POLICY => 'X-Content-Security-Policy',
+        self::RESPONSE_X_CONTENT_TYPE_OPTIONS => 'X-Content-Type-Options',
+        self::RESPONSE_X_UA_COMPATIBLE => 'X-UA-Compatible',
+        self::RESPONSE_CHECKSUM_HEADER => 'Checksum_Header',
+        self::RESPONSE_CHECKSUM_HEADERS => 'Checksum_Headers',
+        self::RESPONSE_CHECKSUM_CONTENT => 'Checksum_Content',
+        self::RESPONSE_CONTENT_REVISION => 'Content_Revision',
+      );
+    }
+
+    return array(
+      self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN => 'access-control-allow-origin',
+      self::RESPONSE_ACCESS_CONTROL_ALLOW_CREDENTIALS => 'access-control-allow-credentials',
+      self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS => 'access-control-expose-headers',
+      self::RESPONSE_ACCESS_CONTROL_MAX_AGE => 'access-control-max-age',
+      self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS => 'access-control-allow-methods',
+      self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS => 'access-control-allow-headers',
+      self::RESPONSE_ACCEPT_PATCH => 'accept-patch',
+      self::RESPONSE_ACCEPT_RANGES => 'accept-ranges',
+      self::RESPONSE_AGE => 'age',
+      self::RESPONSE_ALLOW => 'allow',
+      self::RESPONSE_CACHE_CONTROL => 'cache-control',
+      self::RESPONSE_CONNECTION => 'connection',
+      self::RESPONSE_CONTENT_DISPOSITION => 'content-disposition',
+      self::RESPONSE_CONTENT_ENCODING => 'content-encoding',
+      self::RESPONSE_CONTENT_LANGUAGE => 'content-language',
+      self::RESPONSE_CONTENT_LENGTH => 'content-length',
+      self::RESPONSE_CONTENT_LOCATION => 'content-location',
+      self::RESPONSE_CONTENT_RANGE => 'content-range',
+      self::RESPONSE_CONTENT_TYPE => 'content-type',
+      self::RESPONSE_DATE => 'date',
+      self::RESPONSE_DATE_ACTUAL => 'date_actual',
+      self::RESPONSE_ETAG => 'etag',
+      self::RESPONSE_EXPIRES => 'expires',
+      self::RESPONSE_LAST_MODIFIED => 'last-modified',
+      self::RESPONSE_LINK => 'link',
+      self::RESPONSE_LOCATION => 'location',
+      self::RESPONSE_PRAGMA => 'pragma',
+      self::RESPONSE_PROXY_AUTHENTICATE => 'proxy-authenticate',
+      self::RESPONSE_PUBLIC_KEY_PINS => 'public-key-pins',
+      self::RESPONSE_REFRESH => 'refresh',
+      self::RESPONSE_RETRY_AFTER => 'retry-after',
+      self::RESPONSE_SERVER => 'server',
+      self::RESPONSE_SET_COOKIE => 'set-cookie',
+      self::RESPONSE_STATUS => 'status',
+      self::RESPONSE_STRICT_TRANSPORT_SECURITY => 'strict-transport-security',
+      self::RESPONSE_TRAILER => 'trailer',
+      self::RESPONSE_TRANSFER_ENCODING => 'transfer-encoding',
+      self::RESPONSE_UPGRADE => 'upgrade',
+      self::RESPONSE_VARY => 'vary',
+      self::RESPONSE_WARNING => 'warning',
+      self::RESPONSE_WWW_AUTHENTICATE => 'www-authenticate',
+      self::RESPONSE_X_CONTENT_SECURITY_POLICY => 'x-content-security-policy',
+      self::RESPONSE_X_CONTENT_TYPE_OPTIONS => 'x-content-type-options',
+      self::RESPONSE_X_UA_COMPATIBLE => 'x-ua-compatible',
+      self::RESPONSE_CHECKSUM_HEADER => 'checksum_header',
+      self::RESPONSE_CHECKSUM_HEADERS => 'checksum_headers',
+      self::RESPONSE_CHECKSUM_CONTENT => 'checksum_content',
+      self::RESPONSE_CONTENT_REVISION => 'content_revision',
+    );
+  }
+
+  /**
+   * Return an array for mapping HTTP method strings to numeric ids.
+   *
+   * @return array
+   *   An array for mapping HTTP response header strings to header ids.
+   */
+  private function p_get_http_method_mapping() {
+    return array(
+      'get' => self::HTTP_METHOD_GET,
+      'head' => self::HTTP_METHOD_HEAD,
+      'post' => self::HTTP_METHOD_POST,
+      'put' => self::HTTP_METHOD_PUT,
+      'delete' => self::HTTP_METHOD_DELETE,
+      'trace' => self::HTTP_METHOD_TRACE,
+      'options' => self::HTTP_METHOD_OPTIONS,
+      'connect' => self::HTTP_METHOD_CONNECT,
+      'patch' => self::HTTP_METHOD_PATCH,
+      'track' => self::HTTP_METHOD_TRACK,
+    );
+  }
+
+  /**
+   * Sanitize a token field, such as a header name, and make sure it is valid.
+   *
+   * A valid header name has the following structure:
+   * - 1*(tchar)
+   *
+   * @param string $token_name
+   *   The string to sanitize as a token name.
+   *
+   * @return string|bool
+   *   A sanitized string is return on success.
+   *   FALSE is returned on error or if the header name is invalid.
+   */
+  private function p_prepare_token($token_name) {
+    $trimmed = preg_replace('/(^\s+)|(\s+$)/us', '', mb_strtolower($token_name));
+    if ($trimmed === FALSE) {
+      unset($trimmed);
+      return FALSE;
+    }
+
+    $text = $this->pr_rfc_string_prepare($trimmed);
+    unset($trimmed);
+
+    if ($text['invalid']) {
+      unset($text);
+      return FALSE;
+    }
+
+    $sanitized = $this->pr_rfc_string_is_token($text['ordinals'], $text['characters']);
+    unset($text);
+
+    if ($sanitized['invalid']) {
+      unset($sanitized);
+      return FALSE;
+    }
+
+    return $sanitized['text'];
+  }
+
+  /**
+   * Prepare HTTP response header: access-control-allow-origin.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  private function p_prepare_header_response_access_control_allow_origin(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_ORIGIN, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: access-control-expose-headers.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  private function p_prepare_header_response_access_control_expose_headers(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_EXPOSE_HEADERS, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: access-control-allow-methods.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  private function p_prepare_header_response_access_control_allow_methods(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_METHODS, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: access-control-allow-headers.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
+   */
+  private function p_prepare_header_response_access_control_allow_headers(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_ACCESS_CONTROL_ALLOW_HEADERS, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: accept-patch.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: self::pr_rfc_string_is_media_type()
+   * @see: https://tools.ietf.org/html/rfc5789#section-3.1
+   * @see: https://tools.ietf.org/html/rfc2616#section-3.7
+   */
+  private function p_prepare_header_response_accept_patch(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_ACCEPT_PATCH, $this->response)) {
+      return;
+    }
+
+    $header_output[self::RESPONSE_ACCEPT_PATCH] = 'Accept-Patch: ' . array_shift($this->response[self::RESPONSE_ACCEPT_PATCH]);
+
+    if (!empty($this->response[self::RESPONSE_ACCEPT_PATCH])) {
+      foreach ($this->response[self::RESPONSE_ACCEPT_PATCH] as $media_type) {
+        $header_output[self::RESPONSE_ACCEPT_PATCH] .= ', ' . $media_type;
+      }
+      unset($media_type);
+    }
+  }
+
+  /**
+   * Prepare HTTP response header: allow.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.4.1
+   */
+  private function p_prepare_header_response_allow(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_ALLOW, $this->response)) {
+      return;
+    }
+
+    if (array_key_exists(self::HTTP_METHOD_NONE, $this->response[self::RESPONSE_ALLOW])) {
+      // An empty Allow field value indicates that the resource allows no methods, which might occur in a 405 response if the resource has been temporarily disabled by configuration.
+      $header_output[self::RESPONSE_ALLOW] = 'Allow: ';
+      return;
+    }
+
+    $mapping = array_flip($this->p_get_http_method_mapping());
+
+    $header_output[self::RESPONSE_ALLOW] = 'Allow: ';
+
+    $allow = array_shift($this->response[self::RESPONSE_ALLOW]);
+    $header_output[self::RESPONSE_ALLOW] .= $mapping[$allow];
+
+    if (!empty($this->response[self::RESPONSE_ALLOW])) {
+      foreach ($this->response[self::RESPONSE_ALLOW] as $allow) {
+        $header_output[self::RESPONSE_ALLOW] .= ', ' . $mapping[$allow];
+      }
+    }
+    unset($allow);
+    unset($mapping);
+  }
+
+  /**
+   * Prepare HTTP response header: cache-control.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.2
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.2.3
+   */
+  private function p_prepare_header_response_cache_control(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_CACHE_CONTROL, $this->response) || empty($this->response[self::RESPONSE_CACHE_CONTROL])) {
+      return;
+    }
+
+    $header_output[self::RESPONSE_CACHE_CONTROL] = NULL;
+    foreach ($this->response[self::RESPONSE_CACHE_CONTROL] as $cache_control_directive => $cache_control_value) {
+      if (is_null($header_output[self::RESPONSE_CACHE_CONTROL])) {
+        $header_output[self::RESPONSE_CACHE_CONTROL] = 'Cache-Control: ';
+      }
+      else {
+        $header_output[self::RESPONSE_CACHE_CONTROL] .= ', ';
+      }
+
+      switch ($cache_control_directive) {
+        case self::CACHE_CONTROL_NO_CACHE:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'no-cache';
+          break;
+
+        case self::CACHE_CONTROL_NO_STORE:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'no-store';
+          break;
+
+        case self::CACHE_CONTROL_NO_TRANSFORM:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'no-transform';
+          break;
+
+        case self::CACHE_CONTROL_MAX_AGE:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'max-age';
+          break;
+
+        case self::CACHE_CONTROL_MAX_AGE_S:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 's-max-age';
+          break;
+
+        case self::CACHE_CONTROL_MAX_STALE:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'max-statle';
+          break;
+
+        case self::CACHE_CONTROL_MIN_FRESH:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'min-fresh';
+          break;
+
+        case self::CACHE_CONTROL_ONLY_IF_CACHED:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'only-if-cached';
+          break;
+
+        case self::CACHE_CONTROL_PUBLIC:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'public';
+          break;
+
+        case self::CACHE_CONTROL_PRIVATE:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'private';
+          break;
+
+        case self::CACHE_CONTROL_MUST_REVALIDATE:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'must-revalidate';
+          break;
+
+        case self::CACHE_CONTROL_PROXY_REVALIDATE:
+          $header_output[self::RESPONSE_CACHE_CONTROL] .= 'proxy-revalidate';
+          break;
+
+        default:
+          break;
+      }
+
+      if (!is_null($cache_control_value)) {
+        $header_output[self::RESPONSE_CACHE_CONTROL] .= '=' . $cache_control_value;
+      }
+    }
+    unset($cache_control_directive);
+    unset($cache_control_value);
+  }
+
+  /**
+   * Prepare HTTP response header: connection.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7230#section-6.1
+   */
+  private function p_prepare_header_response_connection(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_CONNECTION, $this->response)) {
+      return;
+    }
+
+    $header_output[self::RESPONSE_CONNECTION] = 'Connection: ';
+
+    $connection = array_shift($this->response[self::RESPONSE_CONNECTION]);
+    $header_output[self::RESPONSE_CONNECTION] .= $connection;
+
+    if (!empty($this->response[self::RESPONSE_CONNECTION])) {
+      foreach ($this->response[self::RESPONSE_CONNECTION] as $connection) {
+        $header_output[self::RESPONSE_CONNECTION] .= ', ' . $connection;
+      }
+    }
+    unset($connection);
+  }
+
+  /**
+   * Prepare HTTP response header: content-disposition.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc6266#section-4
+   */
+  private function p_prepare_header_response_content_disposition(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_CONTENT_DISPOSITION, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: content-encoding.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-3.1.2.2
+   */
+  private function p_prepare_header_response_content_encoding(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_CONTENT_ENCODING, $this->response)) {
+      return;
+    }
+
+    $header_output[self::RESPONSE_CONTENT_ENCODING] = 'Content-Encoding: ';
+
+    switch ($this->response[self::RESPONSE_CONTENT_ENCODING]) {
+      case self::ENCODING_CHUNKED:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'chubked';
+        break;
+      case self::ENCODING_COMPRESS:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'compress';
+        break;
+      case self::ENCODING_DEFLATE:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'deflate';
+        break;
+      case self::ENCODING_GZIP:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'gzip';
+        break;
+      case self::ENCODING_BZIP:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'bzip';
+        break;
+      case self::ENCODING_LZO:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'lzo';
+        break;
+      case self::ENCODING_XZ:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'xz';
+        break;
+      case self::ENCODING_EXI:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'exi';
+        break;
+      case self::ENCODING_IDENTITY:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'identity';
+        break;
+      case self::ENCODING_SDCH:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'sdch';
+        break;
+      case self::ENCODING_PG:
+        $header_output[self::RESPONSE_CONTENT_ENCODING] .= 'pg';
+        break;
+      default:
+        unset($header_output[self::RESPONSE_CONTENT_ENCODING]);
+    }
+  }
+
+  /**
+   * Prepare HTTP response header: content-language.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-3.1.3.2
+   */
+  private function p_prepare_header_response_content_language(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_CONTENT_LANGUAGE, $this->response)) {
+      return;
+    }
+
+    $language_array = $this->language_class->s_get_aliases_by_id($this->response[self::RESPONSE_CONTENT_LANGUAGE]);
+    if ($language_array instanceof c_base_return_array) {
+      $language_array = $language_array->get_value_exact();
+
+      if (!empty($language_array[0])) {
+        $header_output[self::RESPONSE_CONTENT_LANGUAGE] = 'Content-Language: ' . $language_array[0];
+      }
+    }
+    unset($language_array);
+  }
+
+  /**
+   * Prepare HTTP response header: content-type.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-3.1.1.5
+   */
+  private function p_prepare_header_response_content_type(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_CONTENT_TYPE, $this->response)) {
+      return;
+    }
+
+    $header_output[self::RESPONSE_CONTENT_TYPE] = 'Content-Type: ' . $this->response[self::RESPONSE_CONTENT_TYPE]['type'] . '; ';
+
+    $encoding_string = c_base_charset::s_to_string($this->response[self::RESPONSE_CONTENT_TYPE]['charset']);
+    if ($encoding_string instanceof c_base_return_string) {
+      $header_output[self::RESPONSE_CONTENT_TYPE] .= $encoding_string->get_value_exact();
+    }
+    unset($encoding_string);
+  }
+
+  /**
+   * Prepare HTTP response header: etag.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc6266#section-4
+   */
+  private function p_prepare_header_response_etag(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_ETAG, $this->response)) {
+      return;
+    }
+
+    if ($this->response[self::RESPONSE_ETAG]['weak']) {
+      $header_output[self::RESPONSE_ETAG] = 'Etag: W/"' . $this->response[self::RESPONSE_ETAG]['tag'] . '"';
+    }
+    else {
+      $header_output[self::RESPONSE_ETAG] = 'Etag: "' . $this->response[self::RESPONSE_ETAG]['tag'] . '"';
+    }
+  }
+
+  /**
+   * Prepare HTTP response header: link.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc5988#section-5
+   * @see: https://tools.ietf.org/html/rfc3986
+   */
+  private function p_prepare_header_response_link(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_LINK, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: location.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986
+   */
+  private function p_prepare_header_response_location(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_LOCATION, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: pragma.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.4
+   */
+  private function p_prepare_header_response_pragma(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_PRAGMA, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: proxy-authenticate.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#section-4.3
+   */
+  private function p_prepare_header_response_proxy_authenticate(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_PROXY_AUTHENTICATE, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: public-key-pins.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7469
+   */
+  private function p_prepare_header_response_public_key_pins(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_PUBLIC_KEY_PINS, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: refresh.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://en.wikipedia.org/wiki/Meta_refresh
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  private function p_prepare_header_response_refresh(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_REFRESH, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: retry-after.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.3
+   */
+  private function p_prepare_header_response_retry_after(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_RETRY_AFTER, $this->response)) {
+      return;
+    }
+
+    $header_output[self::RESPONSE_RETRY_AFTER] = 'Retry-After: ';
+
+    if ($this->response[self::RESPONSE_RETRY_AFTER]['is_seconds']) {
+      $header_output[self::RESPONSE_RETRY_AFTER] .= $this->response[self::RESPONSE_RETRY_AFTER]['is_seconds'];
+    }
+    else {
+      $timezone = date_default_timezone_get();
+      date_default_timezone_set('GMT');
+
+      $header_output[self::RESPONSE_RETRY_AFTER] .= date(self::TIMESTAMP_RFC_5322, $this->response[self::RESPONSE_RETRY_AFTER]['value']);
+
+      date_default_timezone_set($timezone);
+      unset($timezone);
+    }
+  }
+
+  /**
+   * Prepare HTTP response header: server.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.4.2
+   */
+  private function p_prepare_header_response_server(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_SERVER, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: strict-transport-security.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc6797#section-6.1
+   */
+  private function p_prepare_header_response_strict_transport_security(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_STRICT_TRANSPORT_SECURITY, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: trailer.
+   *
+   * @todo: this appears to no longer be directly specified in the headers.
+   *        There is a 'trailer-part' mentioned along with the transfer encoding information.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc2616#section-14.40
+   * @see: https://tools.ietf.org/html/rfc7230#section-4.1.2
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.1
+   */
+  private function p_prepare_header_response_trailer(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_TRAILER, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: transfer-encoding.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.3.1
+   */
+  private function p_prepare_header_response_transfer_encoding(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_TRANSFER_ENCODING, $this->response)) {
+      return;
+    }
+
+    // according to the standard, content-length cannot be specified when transfer-encoding is defined.
+    if (array_key_exists(self::RESPONSE_CONTENT_LENGTH, $header_output)) {
+      unset($header_output[self::RESPONSE_CONTENT_LENGTH]);
+    }
+
+    // @todo
+    // @fixme:  transfer-encoding is now an array of values.
+  }
+
+  /**
+   * Prepare HTTP response header: upgrade.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-5.5.3
+   */
+  private function p_prepare_header_response_upgrade(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_UPGRADE, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: vary.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7231#section-7.1.4
+   */
+  private function p_prepare_header_response_vary(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_VARY, $this->response)) {
+      return;
+    }
+
+    $header_output[self::RESPONSE_VARY] = 'Vary: ';
+
+    $vary = array_shift($this->response[self::RESPONSE_VARY]);
+    $header_output[self::RESPONSE_VARY] .= $vary;
+
+    if (!empty($this->response[self::RESPONSE_VARY])) {
+      foreach ($this->response[self::RESPONSE_VARY] as $vary) {
+        $header_output[self::RESPONSE_VARY] .= ', ' . $vary;
+      }
+    }
+    unset($vary);
+  }
+
+  /**
+   * Prepare HTTP response header: warning.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.5
+   */
+  private function p_prepare_header_response_warning(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_WARNING, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: www-authenticate.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#section-4.1
+   */
+  private function p_prepare_header_response_www_authenticate(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_WWW_AUTHENTICATE, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: x-content-security-policy
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  private function p_prepare_header_response_x_content_security_policy(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_X_CONTENT_SECURITY_POLICY, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: x-content-type-options
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  private function p_prepare_header_response_x_content_type_options(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_X_CONTENT_TYPE_OPTIONS, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response header: x-ua-compatible
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   *
+   * @see: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+   */
+  private function p_prepare_header_response_x_ua_compatible(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_X_UA_COMPATIBLE, $this->response)) {
+      return;
+    }
+
+    // @todo
+  }
+
+  /**
+   * Prepare HTTP response headers for the content checksum fields.
+   *
+   * This will perform a checksum against the content.
+   * Be sure to perform this check before changing the content-encoding.
+   *
+   * This handles the following header fields:
+   * - checksum_content
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   */
+  private function p_prepare_header_response_checksum_content(&$header_output) {
+    if (!array_key_exists(self::RESPONSE_CHECKSUM_CONTENT, $this->response)) {
+      return;
+    }
+
+    $what = NULL;
+    if ($this->response[self::RESPONSE_CHECKSUM_CONTENT]['what'] == self::CHECKSUM_WHAT_FULL) {
+      $what = 'full';
+    }
+    elseif ($this->response[self::RESPONSE_CHECKSUM_CONTENT]['what'] == self::CHECKSUM_WHAT_PARTIAL) {
+      $what = 'partial';
+    }
+    elseif ($this->response[self::RESPONSE_CHECKSUM_CONTENT]['what'] == self::CHECKSUM_WHAT_SIGNED) {
+      $what = 'signed';
+    }
+    elseif ($this->response[self::RESPONSE_CHECKSUM_CONTENT]['what'] == self::CHECKSUM_WHAT_UNSIGNED) {
+      $what = 'unsigned';
+    }
+
+    if (is_null($what)) {
+      unset($what);
+      return;
+    }
+
+    $algorithm = NULL;
+    $use_hash = FALSE;
+    switch ($this->response[self::RESPONSE_CHECKSUM_CONTENT]['type']) {
+      case self::CHECKSUM_MD2:
+        $algorithm = 'md2';
+        $use_hash = TRUE;
+        break;
+      case self::CHECKSUM_MD4:
+        $algorithm = 'md4';
+        $use_hash = TRUE;
+        break;
+      case self::CHECKSUM_MD5:
+        $algorithm = 'md5';
+        $use_hash = TRUE;
+        break;
+      case self::CHECKSUM_SHA1:
+        $algorithm = 'sha1';
+        $use_hash = TRUE;
+        break;
+      case self::CHECKSUM_SHA224:
+        $algorithm = 'sha224';
+        $use_hash = TRUE;
+        break;
+      case self::CHECKSUM_SHA256:
+        $algorithm = 'sha256';
+        $use_hash = TRUE;
+        break;
+      case self::CHECKSUM_SHA384:
+        $algorithm = 'sha384';
+        $use_hash = TRUE;
+        break;
+      case self::CHECKSUM_SHA512:
+        $algorithm = 'sha512';
+        $use_hash = TRUE;
+        break;
+      case self::CHECKSUM_CRC32:
+        $algorithm = 'crc32';
+        $use_hash = TRUE;
+        break;
+      case self::CHECKSUM_PG:
+        $algorithm = 'pg';
+        break;
+    }
+
+    // @todo: handle support for other algorithms.
+    if ($this->response[self::RESPONSE_CHECKSUM_CONTENT]['action'] == self::CHECKSUM_ACTION_AUTO) {
+      if ($this->content_is_file) {
+        if ($use_hash) {
+          $hash = hash_init($algorithm);
+          foreach ($this->content as $filename) {
+            if (!file_exists($filename)) {
+              unset($filename);
+              unset($hash);
+              unset($what);
+              unset($algorithm);
+              unset($use_hash);
+
+              // @todo: report file not found or other related errors.
+              return c_base_return_error::s_false();
+            }
+
+            $success = hash_update_file($hash, $filename);
+            if (!$success) {
+              unset($success);
+              unset($filename);
+              unset($hash);
+              unset($what);
+              unset($algorithm);
+              unset($use_hash);
+
+              // failure
+              return;
+            }
+          }
+          unset($filename);
+          unset($success);
+
+          $header_output[self::RESPONSE_CHECKSUM_CONTENT] = 'Checksum_Content: ' . $what . ':' . $algorithm . ':' . hash_final($hash, FALSE);
+          unset($hash);
+        }
+        else {
+          // @todo: handle CHECKSUM_PG in this case.
+          // if ($this->response[self::RESPONSE_CHECKSUM_CONTENT]['type'] == self::CHECKSUM_PG) {
+          // }
+        }
+      }
+      else {
+        if ($use_hash) {
+          $header_output[self::RESPONSE_CHECKSUM_CONTENT] = 'Checksum_Content: ' . $what . ':' . $algorithm . ':' . hash($algorithm, $this->content);
+        }
+        else {
+          // @todo: handle CHECKSUM_PG in this case.
+          // if ($this->response[self::RESPONSE_CHECKSUM_CONTENT]['type'] == self::CHECKSUM_PG) {
+          // }
+        }
+      }
+    }
+    elseif ($this->response[self::RESPONSE_CHECKSUM_CONTENT]['action'] == self::CHECKSUM_ACTION_MANUAL) {
+      $header_output[self::RESPONSE_CHECKSUM_CONTENT] = 'Checksum_Content: ' . $what . ':' . $algorithm . ':' . $this->response[self::RESPONSE_CHECKSUM_CONTENT]['checksum'];
+    }
+    unset($use_hash);
+    unset($what);
+    unset($algorithm);
+  }
+
+  /**
+   * Prepare HTTP response headers for both the header checksum fields.
+   *
+   * @todo: implement custom functions for setting algorithms, checksum, and even enabled/disabling auto-checksuming for all checksum header fields.
+   *
+   * This handles the following header fields:
+   * - checksum_header
+   * - checksum_headers
+   *
+   * This must be performed after all other header fields have been prepared.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   * @param string|null $status_string
+   *   When not NULL, this is prepended to the start of the header checksum string before the checksum is calculated.
+   *   When NULL, this value is ignored.
+   */
+  private function p_prepare_header_response_checksum_headers(&$header_output, $status_string) {
+    if (!array_key_exists(self::RESPONSE_CHECKSUM_HEADER, $this->response) || !array_key_exists(self::RESPONSE_CHECKSUM_HEADERS, $this->response)) {
+      return;
+    }
+
+    $header_output_copy = $header_output;
+
+    if (array_key_exists('date_actual', $header_output_copy)) {
+      // When date_actual is specified, the date parameter will not be processed, prevent the 'date' parameter from being used to calculate the header checksum.
+      unset($header_output_copy['date']);
+    }
+
+    $header_output_keys = array_keys($header_output_copy);
+    unset($header_output_copy);
+
+    if (!empty($header_output_keys)) {
+      $header_value = array_shift($header_output_keys);
+      if (!empty($header_output_keys)) {
+        foreach ($header_output_keys as $header_name) {
+          $header_value .= ', ' . $header_name;
+        }
+      }
+    }
+    unset($header_output_keys);
+
+    $header_output[self::RESPONSE_CHECKSUM_HEADER] = $header_value;
+    unset($header_value);
+
+    $header_output_copy = $header_output;
+    unset($header_output_copy['checkum_header']);
+
+    $header_string = '';
+    if (!is_null($status_string)) {
+      $header_string .= $status_string . "\n";
+    }
+
+    $header_string .= implode("\n", $header_output_copy);
+    unset($header_output_copy);
+
+    // @todo: allow caller to specifiy which hash code and which settings.
+    $checkum_header = hash('sha256', $header_string);
+    unset($header_string);
+
+    // @todo: handle support for other algorithms.
+    $header_output[self::RESPONSE_CHECKSUM_HEADER] = 'Checksum_Header: full:sha256:' . $checkum_header;
+    unset($checkum_header);
+  }
+
+  /**
+   * Prepare HTTP response headers that are simple values.
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   * @param string $name_lower
+   *   The HTTP header name (all lowercase), such as: 'age'.
+   * @param string $name
+   *   The HTTP header name, such as: 'Age'.
+   * @param int $code
+   *   The HTTP header code, such as:  self::RESPONSE_AGE.
+   */
+  private function p_prepare_header_response_simple_value(&$header_output, $name_lower, $name, $code) {
+    if (!array_key_exists($code, $this->response)) {
+      return;
+    }
+
+    $header_output[$code] = $name . ': ' . $this->response[$code];
+  }
+
+  /**
+   * Prepare HTTP response header: date
+   *
+   * @param array $header_output
+   *   The header output array to make changes to.
+   * @param string $name_lower
+   *   The HTTP header name (all lowercase), such as: 'age'.
+   * @param string $name
+   *   The HTTP header name, such as: 'Age'.
+   * @param int $code
+   *   The HTTP header code, such as:  self::RESPONSE_AGE.
+   */
+  private function p_prepare_header_response_timestamp_value(&$header_output, $name_lower, $name, $code) {
+    if (!array_key_exists($code, $this->response)) {
+      return;
+    }
+
+    $timezone = date_default_timezone_get();
+    date_default_timezone_set('GMT');
+
+    $header_output[$code] = $name . ': ' . date(self::TIMESTAMP_RFC_5322, $this->response[$code]);
+
+    date_default_timezone_set($timezone);
+    unset($timezone);
+  }
+
+  /**
+   * Process the accept-encoding HTTP header to determine which content-encoding to use.
+   *
+   * @return int
+   *   The encoding to use.
+   */
+  private function p_determine_response_encoding() {
+    if (!$this->request[self::REQUEST_ACCEPT_ENCODING]['defined'] || $this->request[self::REQUEST_ACCEPT_ENCODING]['invalid']) {
+      return self::ENCODING_CHUNKED;
+    }
+
+    $encoding = self::ENCODING_CHUNKED;
+    foreach ($this->request[self::REQUEST_ACCEPT_ENCODING]['data']['weight'] as $weight => $choices) {
+      foreach ($choices as $key => $choice) {
+        if ($key == c_base_http::ENCODING_GZIP) {
+          $encoding = $key;
+          break 2;
+        }
+
+        if ($key == self::ENCODING_DEFLATE) {
+          $encoding = $key;
+          break 2;
+        }
+      }
+      unset($key);
+      unset($choice);
+    }
+    unset($weight);
+    unset($choices);
+
+    return $encoding;
+  }
+
+  /**
+   * Encode and store the given content string.
+   *
+   * Don't pass $this->content directly to this function unless you know it is not an array of filenames.
+   * - For an array of filenames, load the files into memory and then pass the loaded content.
+   *
+   * @param string $content
+   *   The content string to encode and store.
+   * @param int $encoding
+   *   The encoding algorithm to perform.
+   * @param bool $calculate_content_length
+   *   (optional) Determine how the content-length HTTP header is to be calculated:
+   *   - FALSE = do not calculate the content length.
+   *   - TRUE = calculate and store the content length.
+   */
+  private function p_encode_content($content, $encoding, $compression = NULL, $calculate_content_length = TRUE) {
+    if ($encoding == self::ENCODING_GZIP) {
+      if (is_null($compression) || $compression < -1) {
+        $compression = -1;
+      }
+      elseif ($compression > 9) {
+        $compression = 9;
+      }
+
+      $this->content = gzencode($this->content, $compression, FORCE_GZIP);
+      $this->content_is_file = FALSE;
+      $this->response[self::RESPONSE_CONTENT_ENCODING] = $encoding;
+
+      if ($calculate_content_length) {
+        $this->response[self::RESPONSE_CONTENT_LENGTH] = strlen($this->content);
+      }
+
+    }
+    elseif ($encoding == self::ENCODING_DEFLATE) {
+      if (is_null($compression) || $compression < -1) {
+        $compression = -1;
+      }
+      elseif ($compression > 9) {
+        $compression = 9;
+      }
+
+      $this->content = gzencode($content, $compression, FORCE_DEFLATE);
+      $this->content_is_file = FALSE;
+      $this->response[self::RESPONSE_CONTENT_ENCODING] = $encoding;
+
+      if ($calculate_content_length) {
+        $this->response[self::RESPONSE_CONTENT_LENGTH] = strlen($this->content);
+      }
+    }
+    elseif ($encoding == self::ENCODING_BZIP) {
+      if (is_null($compression) || $compression < -1) {
+        $compression = 4;
+      }
+      elseif ($compression > 9) {
+        $compression = 9;
+      }
+
+      $this->content = bzcompress($content, $compression);
+      $this->content_is_file = FALSE;
+      $this->response[self::RESPONSE_CONTENT_ENCODING] = $encoding;
+
+      if ($calculate_content_length) {
+        $this->response[self::RESPONSE_CONTENT_LENGTH] = strlen($this->content);
+      }
+    }
+    elseif ($encoding == self::ENCODING_LZO) {
+      switch ($compression) {
+        case LZO1X_1:
+        case LZO1_1:
+        case LZO1_99:
+        case LZO1A_1:
+        case LZO1A_99:
+        case LZO1B_1:
+        case LZO1B_2:
+        case LZO1B_3:
+        case LZO1B_4:
+        case LZO1B_5:
+        case LZO1B_6:
+        case LZO1B_7:
+        case LZO1B_8:
+        case LZO1B_9:
+        case LZO1B_99:
+        case LZO1B_999:
+        case LZO1C_1:
+        case LZO1C_2:
+        case LZO1C_3:
+        case LZO1C_4:
+        case LZO1C_5:
+        case LZO1C_6:
+        case LZO1C_7:
+        case LZO1C_8:
+        case LZO1C_9:
+        case LZO1C_99:
+        case LZO1C_999:
+        case LZO1F_1:
+        case LZO1F_999:
+        case LZO1X_1_11:
+        case LZO1X_1_12:
+        case LZO1X_1_15:
+        case LZO1X_999:
+        case LZO1Y_1:
+        case LZO1Y_999:
+        case LZO1Z_999:
+        case LZO2A_999:
+          break;
+        default:
+          $compression = LZO2A_999;
+          // Note: according to (http://content.gpwiki.org/index.php/LZO) LZO1X tends to be the best (in general) consider that for the default.
+      }
+
+      $this->content = lzo_compress($content, $compression);
+      $this->content_is_file = FALSE;
+      $this->response[self::RESPONSE_CONTENT_ENCODING] = $encoding;
+
+      if ($calculate_content_length) {
+        $this->response[self::RESPONSE_CONTENT_LENGTH] = strlen($this->content);
+      }
+    }
+    elseif ($encoding == self::ENCODING_XZ) {
+      // @todo, see: https://github.com/payden/php-xz
+    }
+    elseif ($encoding == self::ENCODING_EXI) {
+      // @todo, maybe? (cannot seem to find a php library at this time)
+    }
+    elseif ($encoding == self::ENCODING_IDENTITY) {
+      // @todo, maybe? (cannot seem to find a php library at this time)
+    }
+    elseif ($encoding == self::ENCODING_SDCH) {
+      // @todo, maybe? (cannot seem to find a php library at this time)
+    }
+    elseif ($encoding == self::ENCODING_PG) {
+      // @todo, using ascii armor on entire body.
+      //        should be a header field containing the public key.
+    }
+  }
+}
diff --git a/common/base/classes/base_http_status.php b/common/base/classes/base_http_status.php
new file mode 100644 (file)
index 0000000..eb3e43b
--- /dev/null
@@ -0,0 +1,349 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing the HTTP protocol status codes.
+ */
+
+/**
+ * A generic class for managing the HTTP protocol status codes.
+ *
+ * @see: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
+ */
+class c_base_http_status {
+  const UNDEFINED = 0;
+  const INVALID   = 1;
+  const UNKNOWN   = 2;
+
+  const CONTINUE_REQUEST    = 100; // https://tools.ietf.org/html/rfc7231#section-6.2.1 (cannot use "CONTINUE" here because it is reserved by PHP.)
+  const SWITCHING_PROTOCOLS = 101; // https://tools.ietf.org/html/rfc7231#section-6.2.2
+  const PROCESSING          = 102;
+
+  const OK                = 200; // https://tools.ietf.org/html/rfc7231#section-6.3.1
+  const CREATED           = 201; // https://tools.ietf.org/html/rfc7231#section-6.3.2
+  const ACCEPTED          = 202; // https://tools.ietf.org/html/rfc7231#section-6.3.3
+  const NON_AUTHORATATIVE = 203; // https://tools.ietf.org/html/rfc7231#section-6.3.4
+  const NO_CONTENT        = 204; // https://tools.ietf.org/html/rfc7231#section-6.3.5
+  const RESET_CONTENT     = 205; // https://tools.ietf.org/html/rfc7231#section-6.3.6
+  const PARTIAL_CONTENT   = 206; // https://tools.ietf.org/html/rfc7233#section-4.1
+  const MULTI_STATUS      = 207;
+  const ALREADY_REPORTED  = 208;
+  const IM_USED           = 209;
+
+  const MULTIPLE_CHOICES   = 300; // https://tools.ietf.org/html/rfc7231#section-6.4.1
+  const MOVED_PERMANENTLY  = 301; // https://tools.ietf.org/html/rfc7231#section-6.4.2
+  const FOUND              = 302; // https://tools.ietf.org/html/rfc7231#section-6.4.3
+  const SEE_OTHER          = 303; // https://tools.ietf.org/html/rfc7231#section-6.4.4
+  const NOT_MODIFIED       = 304; // https://tools.ietf.org/html/rfc7232#section-4.1
+  const USE_PROXY          = 305; // https://tools.ietf.org/html/rfc7231#section-6.4.5
+  const SWITCH_PROXY       = 306;
+  const TEMPORARY_REDIRECT = 307; // https://tools.ietf.org/html/rfc7231#section-6.4.7
+  const PERMANENT_REDIRECT = 308;
+
+  const BAD_REQUEST                     = 400; // https://tools.ietf.org/html/rfc7231#section-6.5.1
+  const UNAUTHORIZED                    = 401; // https://tools.ietf.org/html/rfc7235#section-3.1
+  const PAYMENT_REQUIRED                = 402; // https://tools.ietf.org/html/rfc7231#section-6.5.2
+  const FORBIDDEN                       = 403; // https://tools.ietf.org/html/rfc7231#section-6.5.3
+  const NOT_FOUND                       = 404; // https://tools.ietf.org/html/rfc7231#section-6.5.4
+  const METHOD_NOT_ALLOWED              = 405; // https://tools.ietf.org/html/rfc7231#section-6.5.5
+  const NOT_ACCEPTABLE                  = 406; // https://tools.ietf.org/html/rfc7231#section-6.5.6
+  const PROXY_AUTHENTICATION_REQUIRED   = 407; // https://tools.ietf.org/html/rfc7235#section-3.2
+  const REQUEST_TIMEOUT                 = 408; // https://tools.ietf.org/html/rfc7231#section-6.5.7
+  const CONFLICT                        = 409; // https://tools.ietf.org/html/rfc7231#section-6.5.8
+  const GONE                            = 410; // https://tools.ietf.org/html/rfc7231#section-6.5.9
+  const LENGTH_REQUIRED                 = 411; // https://tools.ietf.org/html/rfc7231#section-6.5.10
+  const PRECONDITION_FAILED             = 412; // https://tools.ietf.org/html/rfc7232#section-4.2
+  const PAYLOAD_TOO_LARGE               = 413; // https://tools.ietf.org/html/rfc7231#section-6.5.11
+  const REQUEST_URI_TOO_LONG            = 414; // https://tools.ietf.org/html/rfc7231#section-6.5.12
+  const UNSUPPORTED_MEDIA_TYPE          = 415; // https://tools.ietf.org/html/rfc7231#section-6.5.13
+  const REQUESTED_RANGE_NOT_SATISFIABLE = 416; // https://tools.ietf.org/html/rfc7233#section-4.4
+  const EXPECTATION_FAILED              = 417; // https://tools.ietf.org/html/rfc7231#section-6.5.14
+  const MISDIRECTED_REQUEST             = 422;
+  const LOCKED                          = 423;
+  const FAILED_DEPENDENCY               = 424;
+  const UPGRADE_REQUIRED                = 426; // https://tools.ietf.org/html/rfc7231#section-6.5.15
+  const PRECONDITION_REQUIRED           = 428;
+  const TOO_MANY_REQUESTS               = 429;
+  const REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
+  const UNAVAILABLE_FOR_LEGAL_REASONS   = 451;
+
+  const INTERNAL_SERVER_ERROR           = 500; // https://tools.ietf.org/html/rfc7231#section-6.6.1
+  const NOT_IMPLEMENTED                 = 501; // https://tools.ietf.org/html/rfc7231#section-6.6.2
+  const BAD_GATEWAY                     = 502; // https://tools.ietf.org/html/rfc7231#section-6.6.3
+  const SERVICE_UNAVAILABLE             = 503; // https://tools.ietf.org/html/rfc7231#section-6.6.4
+  const GATEWAY_TIMEOUT                 = 504; // https://tools.ietf.org/html/rfc7231#section-6.6.5
+  const HTTP_VERSION_NOT_SUPPORTED      = 505; // https://tools.ietf.org/html/rfc7231#section-6.6.6
+  const VARIANT_ALSO_NEGOTIATES         = 506;
+  const INSUFFICIENT_STORAGE            = 507;
+  const LOOP_DETECTED                   = 508;
+  const NOT_EXTENDED                    = 510;
+  const NETWORK_AUTHENTICATION_REQUIRED = 511;
+
+
+  /**
+   * Convert the given status code into a text statement.
+   *
+   * @param int $status
+   *   The code to convert
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The status text string on success, FALSE otherwise.
+   *   FALSE with the error bit set is returned on error.
+   */
+  public static function to_text($status) {
+    if (!is_int($status)) {
+      return c_base_return_error::s_false();
+    }
+
+    $string = "";
+    switch ($status) {
+      case self::CONTINUE_REQUEST:
+        $string = "Continue Request";
+        break;
+
+      case self::SWITCHING_PROTOCOLS:
+        $string = "Switching Protocols";
+        break;
+
+      case self::PROCESSING:
+        $string = "Processing";
+        break;
+
+      case self::OK:
+        $string = "OK";
+        break;
+
+      case self::CREATED:
+        $string = "Created";
+        break;
+
+      case self::ACCEPTED:
+        $string = "Accepted";
+        break;
+
+      case self::NON_AUTHORATATIVE:
+        $string = "Non-Authoratative";
+        break;
+
+      case self::NO_CONTENT:
+        $string = "No Content";
+        break;
+
+      case self::RESET_CONTENT:
+        $string = "Reset Content";
+        break;
+
+      case self::PARTIAL_CONTENT:
+        $string = "Partial Content";
+        break;
+
+      case self::MULTI_STATUS:
+        $string = "Multi-Status";
+        break;
+
+      case self::ALREADY_REPORTED:
+        $string = "Already Reported";
+        break;
+
+      case self::IM_USED:
+        $string = "IM Used";
+        break;
+
+      case self::MULTIPLE_CHOICES:
+        $string = "Multiple Choices";
+        break;
+
+      case self::MOVED_PERMANENTLY:
+        $string = "Moved Permanently";
+        break;
+
+      case self::FOUND:
+        $string = "Found";
+        break;
+
+      case self::SEE_OTHER:
+        $string = "See Other";
+        break;
+
+      case self::NOT_MODIFIED:
+        $string = "Not Modified";
+        break;
+
+      case self::USE_PROXY:
+        $string = "Use Proxy";
+        break;
+
+      case self::SWITCH_PROXY:
+        $string = "Switch Proxy";
+        break;
+
+      case self::TEMPORARY_REDIRECT:
+        $string = "Temporary Redirect";
+        break;
+
+      case self::PERMANENT_REDIRECT:
+        $string = "Permanent Redirect";
+        break;
+
+      case self::BAD_REQUEST:
+        $string = "Bad Request";
+        break;
+
+      case self::UNAUTHORIZED:
+        $string = "Unauthorized";
+        break;
+
+      case self::PAYMENT_REQUIRED:
+        $string = "Payment Required";
+        break;
+
+      case self::FORBIDDEN:
+        $string = "Forbidden";
+        break;
+
+      case self::NOT_FOUND:
+        $string = "Not Found";
+        break;
+
+      case self::METHOD_NOT_ALLOWED:
+        $string = "Method Not Allowed";
+        break;
+
+      case self::NOT_ACCEPTABLE:
+        $string = "Not Acceptable";
+        break;
+
+      case self::PROXY_AUTHENTICATION_REQUIRED:
+        $string = "Proxy Authentication Required";
+        break;
+
+      case self::REQUEST_TIMEOUT:
+        $string = "Request Timeout";
+        break;
+
+      case self::CONFLICT:
+        $string = "Conflict";
+        break;
+
+      case self::GONE:
+        $string = "Gone";
+        break;
+
+      case self::LENGTH_REQUIRED:
+        $string = "Length Required";
+        break;
+
+      case self::PRECONDITION_FAILED:
+        $string = "Pre-condition Failed";
+        break;
+
+      case self::PAYLOAD_TOO_LARGE:
+        $string = "Payload Too Large";
+        break;
+
+      case self::REQUEST_URI_TOO_LONG:
+        $string = "Request URI Too Long";
+        break;
+
+      case self::UNSUPPORTED_MEDIA_TYPE:
+        $string = "Unsupported Media Type";
+        break;
+
+      case self::REQUESTED_RANGE_NOT_SATISFIABLE:
+        $string = "Requested Range Not Satisfiable";
+        break;
+
+      case self::EXPECTATION_FAILED:
+        $string = "Expectation Failed";
+        break;
+
+      case self::MISDIRECTED_REQUEST:
+        $string = "Misdirected Request";
+        break;
+
+      case self::LOCKED:
+        $string = "Locked";
+        break;
+
+      case self::FAILED_DEPENDENCY:
+        $string = "Failed Dependency";
+        break;
+
+      case self::UPGRADE_REQUIRED:
+        $string = "Upgrade Required";
+        break;
+
+      case self::PRECONDITION_REQUIRED:
+        $string = "Pre-Condition Required";
+        break;
+
+      case self::TOO_MANY_REQUESTS:
+        $string = "Too Many Requests";
+        break;
+
+      case self::REQUEST_HEADER_FIELDS_TOO_LARGE:
+        $string = "Request Header Fields Too Large";
+        break;
+
+      case self::UNAVAILABLE_FOR_LEGAL_REASONS:
+        $string = "Unavailable for Legal Reasons";
+        break;
+
+      case self::INTERNAL_SERVER_ERROR:
+        $string = "Internal Server Error";
+        break;
+
+      case self::NOT_IMPLEMENTED:
+        $string = "Not Implemented";
+        break;
+
+      case self::BAD_GATEWAY:
+        $string = "Bad Gateway";
+        break;
+
+      case self::SERVICE_UNAVAILABLE:
+        $string = "Service Unavailable";
+        break;
+
+      case self::GATEWAY_TIMEOUT:
+        $string = "Gateway Timeout";
+        break;
+
+      case self::HTTP_VERSION_NOT_SUPPORTED:
+        $string = "HTTP Version Not Supported";
+        break;
+
+      case self::VARIANT_ALSO_NEGOTIATES:
+        $string = "Variant Also Negotiates";
+        break;
+
+      case self::INSUFFICIENT_STORAGE:
+        $string = "Unsufficient Storage";
+        break;
+
+      case self::LOOP_DETECTED:
+        $string = "Loop Detected";
+        break;
+
+      case self::NOT_EXTENDED:
+        $string = "Not Extended";
+        break;
+
+      case self::NETWORK_AUTHENTICATION_REQUIRED:
+        $string = "Network Authentication Required";
+        break;
+
+      case self::UNDEFINED:
+      case self::UNKNOWN:
+        $string = "";
+        break;
+
+      case self::INVALID:
+        // invalid will not be processed because it is invalid.
+
+      default:
+        return c_base_return_false();
+    }
+
+    return c_base_return_string::s_new($string);
+  }
+}
diff --git a/common/base/classes/base_languages.php b/common/base/classes/base_languages.php
new file mode 100644 (file)
index 0000000..a5c4d3a
--- /dev/null
@@ -0,0 +1,2821 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing the different supported languages.
+ */
+
+/**
+ * A generic interface for managing the different supported languages.
+ *
+ * Additional known sub-languages, such as en-us, are added even though they do not appear in the iso standard.
+ *
+ * @see: http://www.loc.gov/standards/iso639-2/php/code_list.php
+ */
+interface i_base_language {
+  const AFAR                    = 1;   // aar, aa
+  const ABKHAZIAN               = 2;   // abk, ab
+  const ACHINESE                = 3;   // ace
+  const ACOLI                   = 4;   // ach
+  const ADANGME                 = 5;   // ada
+  const ADYGHE                  = 6;   // ady
+  const AFRO_ASIATIC            = 7;   // afa
+  const AFRIHILI                = 8;   // afh
+  const AFRIKAANS               = 9;   // afr, af
+  const AINU                    = 10;  // ain
+  const AKAN                    = 11;  // aka, ak
+  const AKKADIAN                = 12;  // akk
+  const ALBANIAN                = 13;  // alb (b), sqi (t), sq
+  const ALEUT                   = 14;  // ale
+  const ALGONQUIAN              = 15;  // alg
+  const SOUTHERN_ALTAI          = 16;  // alt
+  const AMHARIC                 = 17;  // amh, am
+  const ENGLISH_OLD             = 18;  // ang
+  const ANGIKA                  = 19;  // anp
+  const APACHE                  = 20;  // apa
+  const ARABIC                  = 21;  // ara, ar
+  const ARAMAIC                 = 22;  // arc
+  const ARAGONESE               = 23;  // arg, an
+  const ARMENIAN                = 24;  // arm (b), hye (t), hy
+  const MAPUDUNGUN              = 25;  // am
+  const ARAPAHO                 = 26;  // arp
+  const ARTIFICIAL              = 27;  // art
+  const ARAWAK                  = 28;  // arw
+  const ASSAMESE                = 29;  // asm, as
+  const ASTURIAN                = 30;  // ast
+  const ATHAPASCAN              = 31;  // ath
+  const AUSTRALIAN              = 32;  // aus
+  const AVARIC                  = 33;  // ava, av
+  const AVESTAN                 = 34;  // ave, ae
+  const AWADHI                  = 35;  // awa
+  const AYMARA                  = 36;  // aym, ay
+  const AZERBAIJANI             = 37;  // aze, az
+  const BANDA                   = 38;  // bad
+  const BAMILEKE                = 39;  // bai
+  const BASHKIR                 = 40;  // bak, ba
+  const BALUCHI                 = 41;  // bal
+  const BAMBARA                 = 42;  // bam, bm
+  const BALINESE                = 43;  // ban
+  const BASQUE                  = 44;  // baq (b), eus (t), eu
+  const BASA                    = 45;  // bas
+  const BALTIC                  = 46;  // bat
+  const BEJA                    = 47;  // bej
+  const BELARUSIAN              = 48;  // bel, be
+  const BEMBA                   = 49;  // bem
+  const BENGALI                 = 50;  // ben, bn
+  const BERBER                  = 51;  // ber
+  const BHOJPURI                = 52;  // bho
+  const BIHARI                  = 53;  // bih, bh
+  const BIKOL                   = 54;  // bik
+  const BINI                    = 55;  // bin
+  const BISLAMA                 = 56;  // bis, bi
+  const SIKSIKA                 = 57;  // bla
+  const BANTU                   = 58;  // bnt
+  const TIBETAN                 = 59;  // tib (b), bod (t), bo
+  const BOSNIAN                 = 60;  // bos, bs
+  const BRAJ                    = 61;  // bra
+  const BRETON                  = 62;  // bre
+  const BATAK                   = 63;  // btk
+  const BURIAT                  = 64;  // bua
+  const BUGINESE                = 65;  // bug
+  const BULGARIAN               = 66;  // bul
+  const BURMESE                 = 67;  // bur (b), mya (t), my
+  const BLIN                    = 68;  // byn
+  const CADDO                   = 69;  // cad
+  const AMERICAN_INDIAN_CENTRAL = 70;  // cai
+  const GALIBI_CARIB            = 71;  // car
+  const CATALAN                 = 72;  // cat, ca
+  const CAUCASIAN               = 73;  // cau
+  const CEBUANO                 = 74;  // ceb
+  const CELTIC                  = 75;  // cel
+  const CZECH                   = 76;  // cze (b), ces (t), cs
+  const CHAMORRO                = 77;  // cha, ch
+  const CHIBCHA                 = 78;  // chb
+  const CHECHEN                 = 79;  // che, ce
+  const CHAGATAI                = 80;  // chg
+  const CHINESE                 = 81;  // chi (b), zho (t), zh
+  const CHUUKESE                = 82;  // chk
+  const MARI                    = 83;  // chm
+  const CHINOOK_JARGON          = 84;  // chn
+  const CHOCTAW                 = 85;  // cho
+  const CHIPEWYAN               = 86;  // chp
+  const CHEROKEE                = 87;  // chr
+  const CHURCH_SLAVIC           = 88;  // chu, cu
+  const CHUVASH                 = 89;  // chv, cv
+  const CHEYENNE                = 90;  // chy
+  const CHAMIC                  = 91;  // cmc
+  const COPTIC                  = 92;  // cop
+  const CORNISH                 = 93;  // cor
+  const CORSICAN                = 94;  // cos, co
+  const CREOLES_ENGLISH         = 95;  // cpe
+  const CREOLES_FRENCH          = 96;  // cpf
+  const CREOLES_PORTUGESE       = 97;  // cpp
+  const CREE                    = 98;  // cre, cr
+  const CRIMEAN_TATAR           = 99;  // crh
+  const CREOLES                 = 100; // crp
+  const KASHUBIAN               = 101; // csb
+  const CUSHITIC                = 102; // cus
+  const WELSH                   = 103; // wel (b), cym (t), cy
+  const DAKOTA                  = 104; // dak
+  const DANISH                  = 105; // dan, da
+  const DARGWA                  = 106; // dar
+  const LAND_DAYAK              = 107; // day
+  const DELAWARE                = 108; // del
+  const SLAVE                   = 109; // den
+  const GERMAN                  = 110; // ger (b), deu (t), de
+  const DOGRIB                  = 111; // dgr
+  const DINKA                   = 112; // din
+  const DIVEHI                  = 113; // div, dv
+  const DOGRI                   = 114; // doi
+  const DRAVIDIAN               = 115; // dra
+  const LOWER_SORBIAN           = 116; // dsb
+  const DUALA                   = 117; // dua
+  const DUTCH_MIDDLE            = 118; // dum
+  const DUTCH_FLEMISH           = 119; // dut (b), nld (t), nl
+  const DYULA                   = 120; // dyu
+  const DZONGKHA                = 121; // dzo, dz
+  const EFIK                    = 122; // efi
+  const EGYPTIAN                = 123; // egy
+  const EKAJUK                  = 124; // eka
+  const GREEK_MODERN            = 125; // gre (b), ell (t), el
+  const ELAMITE                 = 126; // elx
+  const ENGLISH                 = 127; // eng, en
+  const ENGLISH_MIDDLE          = 128; // enm
+  const ESPERANTO               = 129; // epo, eo
+  const ESTONIAN                = 130; // est, et
+  const EWE                     = 131; // ewe, ee
+  const EWONDO                  = 132; // ewo
+  const FANG                    = 133; // fan
+  const FAROESE                 = 134; // fao, fo
+  const PERSIAN                 = 135; // per (b), fas (t), fa
+  const FANTI                   = 136; // fat
+  const FIJIAN                  = 137; // fij, fj
+  const FILIPINO                = 138; // fil
+  const FINNISH                 = 139; // fin, fi
+  const FINNO_UGRIAN            = 140; // fiu
+  const FON                     = 141; // fon
+  const FRENCH                  = 142; // fre (b), fra (t), fr
+  const FRENCH_MIDDLE           = 143; // frm
+  const FRENCH_OLD              = 144; // fro
+  const FRISIAN_NORTHERN        = 145; // frr
+  const FRISIAN_EASTERN         = 146; // frs
+  const FRISIAN_WESTERN         = 147; // fry, fy
+  const FULAH                   = 148; // ful, ff
+  const FRIULIAN                = 149; // fur
+  const GA                      = 150; // gaa
+  const GAYO                    = 151; // gay
+  const GBAYA                   = 152; // gba
+  const GERMANIC                = 153; // gem
+  const GEORGIAN                = 154; // geo (b), kat (t), ka
+  const GEEZ                    = 155; // gez
+  const GILBERTESE              = 156; // gil
+  const GAELIC                  = 157; // gla, gd
+  const IRISH                   = 158; // gle, ga
+  const GALICIAN                = 159; // glg, gl
+  const MANX                    = 160; // glv, gv
+  const GERMAN_MIDDLE_HIGH      = 161; // gmh
+  const GERMAN_OLD_HIGH         = 162; // goh
+  const GONDI                   = 163; // gon
+  const GORONTALO               = 164; // gor
+  const GOTHIC                  = 165; // got
+  const GREBO                   = 166; // grb
+  const GREEK_ANCIENT           = 167; // grc
+  const GUARANI                 = 168; // grm, gn
+  const GERMAN_SWISS            = 169; // gsw
+  const GUJARATI                = 170; // guj, gu
+  const GWICHIN                 = 171; // gwi
+  const HAIDA                   = 172; // hai
+  const HAITIAN                 = 173; // hat, ht
+  const HAUSA                   = 174; // hau, ha
+  const HAWAIIAN                = 175; // haw
+  const HEBREW                  = 176; // heb, he
+  const HERERO                  = 177; // her, hz
+  const HILIGAYNON              = 178; // hil
+  const HIMACHALI               = 179; // him
+  const HINDI                   = 180; // hin, hi
+  const HITTITE                 = 181; // hit
+  const HMONG                   = 182; // hmn
+  const HIRI_MOTU               = 183; // hmo, ho
+  const CROATIAN                = 184; // hrv
+  const SORBIAN_UPPER           = 185; // hsb
+  const HUNGARIAN               = 186; // hun, hu
+  const HUPA                    = 187; // hup
+  const IBAN                    = 188; // iba
+  const IGBO                    = 189; // ibo, ig
+  const ICELANDIC               = 190; // ice (b), isl (t), is
+  const IDO                     = 191; // ido, io
+  const SICHUAN_YI              = 192; // iii, ii
+  const IJO                     = 193; // ijo
+  const INUKTITUT               = 194; // iku, iu
+  const INTERLINGUE             = 195; // ile, ie
+  const ILOKO                   = 196; // ilo
+  const INTERLINGUA             = 197; // ina, ia
+  const INDIC                   = 198; // inc
+  const INDONESIAN              = 199; // ind, id
+  const INDO_EUROPEAN           = 200; // ine
+  const INGUSH                  = 201; // inh
+  const INUPIAQ                 = 202; // ipk, ik
+  const IRANIAN                 = 203; // ira
+  const IROQUOIAN               = 204; // iro
+  const ITALIAN                 = 205; // ita, it
+  const JAVANESE                = 206; // jav, jv
+  const LOJBAN                  = 207; // jbo
+  const JAPANESE                = 208; // jpn, ja
+  const JUDEO_PERSIAN           = 209; // jpr
+  const JUDEO_ARABIC            = 210; // jrb
+  const KARA_KALPAK             = 211; // kaa
+  const KABYLE                  = 212; // kab
+  const KACHIN                  = 213; // kac
+  const KALAALLISUT             = 214; // kal, kl
+  const KAMBA                   = 215; // kam
+  const KANNADA                 = 216; // kan, kn
+  const KAREN                   = 217; // kar
+  const KASHMIRI                = 218; // kas, ks
+  const KANURI                  = 219; // kau, kr
+  const KAWI                    = 220; // kaw
+  const KAZAKH                  = 221; // kaz
+  const KABARDIAN               = 222; // kbd
+  const KHASI                   = 223; // kha
+  const KHOISAN                 = 224; // khi
+  const CENTRAL_KHMER           = 225; // khm, km
+  const KHOTANESE               = 226; // kho
+  const KIKUYU                  = 227; // kik, ki
+  const KINYARWANDA             = 228; // kin, rw
+  const KIRGHIZ                 = 229; // kir, ky
+  const KIMBUNDU                = 230; // kmb
+  const KONKANI                 = 231; // kok
+  const KOMI                    = 232; // kom, kv
+  const KONGO                   = 233; // kon, kg
+  const KOREAN                  = 234; // kor, ko
+  const KOSRAEAN                = 235; // kos
+  const KPELLE                  = 236; // kpe
+  const KARACHAY_BALKAR         = 237; // krc
+  const KARELIAN                = 238; // krl
+  const KRU                     = 239; // kro
+  const KURUKH                  = 240; // kru
+  const KUANYAMA                = 241; // kua, kj
+  const KUMYK                   = 242; // kum
+  const KURDISH                 = 243; // kur, ku
+  const KUTENAI                 = 244; // kut
+  const LADINO                  = 245; // lad
+  const LAHNDA                  = 246; // lah
+  const LAMBA                   = 247; // lam
+  const LAO                     = 248; // lao, lo
+  const LATIN                   = 249; // lat, la
+  const LATVIAN                 = 250; // lav, lv
+  const LEZGHIAN                = 251; // lez
+  const LIMBURGAN               = 252; // lim, li
+  const LINGALA                 = 253; // lin, ln
+  const LITHUANIAN              = 254; // lit, lt
+  const MONGO                   = 255; // lol
+  const LOZI                    = 256; // loz
+  const LUXEMBOURGISH           = 257; // ltz, lb
+  const LUBA_LULUA              = 258; // lua
+  const LUBA_KATANGA            = 259; // lub, lu
+  const GANDA                   = 260; // lug, lg
+  const LUISENO                 = 261; // lui
+  const LUNDA                   = 262; // lun
+  const LUO                     = 263; // luo
+  const LUSHAI                  = 264; // lus
+  const MACEDONIAN              = 265; // mac (b), mkd (t), mk
+  const MADURESE                = 266; // mad
+  const MAGAHI                  = 267; // mag
+  const MARSHALLESE             = 268; // mah
+  const MAITHILI                = 269; // mai
+  const MAKASAR                 = 270; // mak
+  const MALAYALAM               = 271; // mal
+  const MANDINGO                = 272; // man
+  const MAORI                   = 273; // mao (b), mri (t), mi
+  const AUSTRONESIAN            = 274; // map
+  const MARATHI                 = 275; // mar, mr
+  const MASAI                   = 276; // mas
+  const MALAY                   = 277; // may (b), msa (t), ms
+  const MOKSHA                  = 278; // mdf
+  const MANDAR                  = 279; // mdr
+  const MENDE                   = 280; // men
+  const IRISH_MIDDLE            = 281; // mga
+  const MIKMAQ                  = 282; // mic
+  const MINANGKABAU             = 283; // min
+  const UNCODED                 = 284; // mis
+  const MON_KHMER               = 285; // mkh
+  const MALAGASY                = 286; // mlg
+  const MALTESE                 = 287; // mlt
+  const MANCHU                  = 288; // mnc
+  const MANIPURI                = 289; // mni
+  const MANOBO                  = 290; // mno
+  const MOHAWK                  = 291; // moh
+  const MONGOLIAN               = 292; // mon, mn
+  const MOSSI                   = 293; // mos
+  const MULTIPLE                = 294; // mul
+  const MUNDA                   = 295; // mun
+  const CREEK                   = 296; // mus
+  const MIRANDESE               = 297; // mwl
+  const MARWARI                 = 298; // mwr
+  const MAYAN                   = 299; // myn
+  const ERZYA                   = 300; // myv
+  const NAHUATL                 = 301; // nah
+  const AMERICAN_INDIAN_NORTH   = 302; // nai
+  const NEAPOLITAN              = 303; // nap
+  const NAURU                   = 304; // nau, na
+  const NAVAJO                  = 305; // nav, nv
+  const NDEBELE_SOUTH           = 306; // nbl, nr
+  const NDEBELE_NORTH           = 307; // nde, nd
+  const NDONGA                  = 308; // ndo, ng
+  const LOW_GERMAN              = 309; // nds
+  const NEPALI                  = 310; // nep, ne
+  const NEPAL_BHASA             = 311; // new
+  const NIAS                    = 312; // nia
+  const NIGER_KORDOFANIAN       = 313; // nic
+  const NIUEAN                  = 314; // niu
+  const NORWEGIAN_NYNORSK       = 315; // nno, nn
+  const BOKMAL                  = 316; // nob, nb
+  const NOGAI                   = 317; // nog
+  const NORSE_OLD               = 318; // non
+  const NORWEGIAN               = 319; // nor, no
+  const NKO                     = 320; // nqo
+  const PEDI                    = 321; // nso
+  const NUBIAN                  = 322; // nub
+  const CLASSICAL_NEWARI        = 323; // nwc
+  const CHICHEWA                = 324; // nya, ny
+  const NYAMWEZI                = 325; // nym
+  const NYANKOLE                = 326; // nyn
+  const NYORO                   = 327; // nyo
+  const NZIMA                   = 328; // nzi
+  const OCCITAN                 = 329; // oci, oc
+  const OJIBWA                  = 330; // oji, oj
+  const ORIYA                   = 331; // ori, or
+  const OROMO                   = 332; // orm, om
+  const OSAGE                   = 333; // osa
+  const OSSETIAN                = 334; // oss, os
+  const OTTOMAN                 = 335; // ota
+  const OTOMIAN                 = 336; // oto
+  const PAPUAN                  = 337; // paa
+  const PANGASINAN              = 338; // pag
+  const PAHLAVI                 = 339; // pal
+  const PAMPANGA                = 340; // pam
+  const PANJABI                 = 341; // pan, pa
+  const PAPIAMENTO              = 342; // pap
+  const PALAUAN                 = 342; // pau
+  const PERSIAN_OLD             = 343; // peo
+  const PHILIPPINE              = 344; // phi
+  const PHOENICIAN              = 345; // phn
+  const PALI                    = 346; // pli, pi
+  const POLISH                  = 347; // pol, pl
+  const POHNPEIAN               = 348; // pon
+  const PORTUGUESE              = 349; // por, pt
+  const PRAKRIT                 = 350; // pra
+  const PROVENCAL               = 351; // pro
+  const PUSHTO                  = 352; // pus, ps
+  const QUECHUA                 = 353; // que, qu
+  const RAJASTHANI              = 354; // raj
+  const RAPANUI                 = 355; // rap
+  const RAROTONGAN              = 356; // rar
+  const ROMANCE                 = 357; // roa
+  const ROMANSH                 = 358; // roh, rm
+  const ROMANY                  = 359; // rom
+  const ROMANIAN                = 360; // rum (b), ron (t), ro
+  const RUNDI                   = 361; // run, rn
+  const AROMANIAN               = 362; // rup
+  const RUSSIAN                 = 363; // rus, ru
+  const SANDAWE                 = 364; // sad
+  const SANGO                   = 365; // sag, sg
+  const YAKUT                   = 366; // sah
+  const AMERICAN_INDIAN_SOUTH   = 367; // sai
+  const SALISHAN                = 368; // sal
+  const SAMARITAN               = 369; // sam
+  const SANSKRIT                = 370; // san, sa
+  const SASAK                   = 371; // sas
+  const SANTALI                 = 372; // sat
+  const SICILIAN                = 373; // scn
+  const SCOTS                   = 374; // sco
+  const SELKUP                  = 375; // sel
+  const SEMITIC                 = 376; // sem
+  const IRISH_OLD               = 377; // sga
+  const SIGN                    = 378; // sgn
+  const SHAN                    = 379; // shn
+  const SIDAMO                  = 380; // sid
+  const SINHALA                 = 381; // sin, si
+  const SIOUAN                  = 382; // sio
+  const SINO_TIBETAN            = 383; // sit
+  const SLAVIC                  = 384; // sla
+  const SLOVAK                  = 385; // slo (b), slk (t), sk
+  const SLOVENIAN               = 386; // slv, sl
+  const SAMI_SOUTHERN           = 387; // sma
+  const SAMI_NORTHERN           = 388; // sme, se
+  const SAMI                    = 389; // smi
+  const SAMI_LULE               = 390; // smj
+  const SAMI_IRARI              = 391; // smn
+  const SAMOAN                  = 392; // smo, sm
+  const SAMI_SKOLT              = 393; // sms
+  const SHONA                   = 394; // sna, sn
+  const SINDHI                  = 395; // snd, sd
+  const SONINKE                 = 396; // snk
+  const SOGDIAN                 = 397; // sog
+  const SOMALI                  = 398; // som, so
+  const SONGHAI                 = 399; // son
+  const SOTHO_SOUTHERN          = 400; // sot, st
+  const SPANISH                 = 401; // spa, es
+  const SARDINIAN               = 402; // srd, sc
+  const SRANAN_TONGO            = 403; // sm
+  const SERBIAN                 = 404; // srp, sr
+  const SERER                   = 405; // srr
+  const NILO_SAHARAN            = 406; // ssa
+  const SWATI                   = 407; // ssw, ss
+  const SUKUMA                  = 408; // suk
+  const SUNDANESE               = 409; // sun, su
+  const SUSU                    = 410; // sus
+  const SUMERIAN                = 411; // sux
+  const SWAHILI                 = 412; // swa, sw
+  const SWEDISH                 = 413; // swe, sv
+  const SYRIAC_CLASSICAL        = 414; // syc
+  const SYRIAC                  = 415; // syr
+  const TAHITIAN                = 416; // tah, ty
+  const TAI                     = 417; // tai
+  const TAMIL                   = 418; // tam, ta
+  const TATAR                   = 419; // tat, tt
+  const TELUGU                  = 420; // tel, te
+  const TIMNE                   = 421; // tem
+  const TERENO                  = 422; // ter
+  const TETUM                   = 423; // tet
+  const TAJIK                   = 424; // tgk, tg
+  const TAGALOG                 = 425; // tgl, tl
+  const THAI                    = 426; // tha, th
+  const TIGRE                   = 427; // tig
+  const TIGRINYA                = 428; // tir, ti
+  const TIV                     = 429; // tiv
+  const TOKELAU                 = 430; // tkl
+  const KLINGON                 = 431; // tlh
+  const TLINGIT                 = 432; // tli
+  const TAMASHEK                = 433; // tmh
+  const TONGA_NYASA             = 434; // tog
+  const TONGA_ISLANDS           = 435; // ton, to
+  const TOK_PISIN               = 436; // tpi
+  const TSIMSHIAN               = 437; // tsi
+  const TSWANA                  = 438; // tsn, tn
+  const TSONGA                  = 439; // tso, ts
+  const TURKMEN                 = 440; // tuk, tk
+  const TUMBUKA                 = 441; // tum
+  const TUPI                    = 442; // tup
+  const TURKISH                 = 443; // tur, tr
+  const ALTAIC                  = 444; // tut
+  const TUVALU                  = 445; // tvl
+  const TWI                     = 446; // twi, tw
+  const TUVINIAN                = 447; // tyv
+  const UDMURT                  = 448; // udm
+  const UGARITIC                = 449; // uga
+  const UIGHUR                  = 450; // uig, ug
+  const UKRAINIAN               = 451; // ukr, uk
+  const UMBUNDU                 = 452; // umb
+  const UNDETERMINED            = 453; // und
+  const URDU                    = 454; // urd, ur
+  const UZBEK                   = 455; // uzb, uz
+  const VAI                     = 456; // vai
+  const VENDA                   = 457; // ven, ve
+  const VIETNAMESE              = 458; // vie, vi
+  const VOLAPUK                 = 459; // vol, vo
+  const VOTIC                   = 460; // vot
+  const WAKASHAN                = 461; // wak
+  const WOLAITTA                = 462; // wal
+  const WARAY                   = 463; // war
+  const WASHO                   = 464; // was
+  const SORBIAN                 = 465; // wen
+  const WALLOON                 = 466; // wln, wa
+  const WOLOF                   = 467; // wol, wo
+  const KALMYK                  = 468; // xal
+  const XHOSA                   = 469; // xho, xh
+  const YAO                     = 470; // yao
+  const YAPESE                  = 471; // yap
+  const YIDDISH                 = 472; // yid, yi
+  const YORUBA                  = 473; // yor, yo
+  const YUPIK                   = 474; // ypk
+  const ZAPOTEC                 = 475; // zap
+  const BLISSYMBOLS             = 476; // zbl
+  const ZENAGA                  = 477; // zen
+  const MOROCCAN_TAMAZIGHT      = 478; // zgh
+  const ZHUANG                  = 479; // zha, za
+  const ZANDE                   = 480; // znd
+  const ZULU                    = 481; // zul, zu
+  const ZUNI                    = 482; // zun
+  const NOT_APPLICABLE          = 483; // zxx
+  const ZAZA                    = 484; // zza
+  const ENGLISH_CA              = 485; // en-ca
+  const ENGLISH_GB              = 486; // en-gb
+  const ENGLISH_US              = 487; // en-us
+
+
+  /**
+   * Get the language names associated with the id.
+   *
+   * @param int $id
+   *   The id of the names to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of names or FALSE on error.
+   *   FALSE without the error flag means that there are no names associated with the given id.
+   */
+  static function s_get_names_by_id($id);
+
+  /**
+   * Get the language names associated with the alias.
+   *
+   * @param string $alias
+   *   The id of the names to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of names or FALSE on error.
+   *   FALSE without the error flag means that there are no names associated with the given alias.
+   */
+  static function s_get_names_by_alias($alias);
+
+  /**
+   * Get the id associated with the language name.
+   *
+   * @param string $name
+   *   The string associated with the id
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The numeric id or FALSE on error.
+   *   FALSE without the error flag means that there are no ids associated with the given name.
+   */
+  static function s_get_id_by_name($name);
+
+  /**
+   * Get the id associated with the language name.
+   *
+   * @param string $name
+   *   The string associated with the id
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The numeric id or FALSE on error.
+   *   FALSE without the error flag means that there are no ids associated with the given name.
+   */
+  static function s_get_id_by_alias($alias);
+
+  /**
+   * Get the language aliases associated with the id.
+   *
+   * @param int $id
+   *   The id of the aliases to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of aliases or FALSE on error.
+   *   FALSE without the error flag means that there are no aliases associated with the given id.
+   */
+  static function s_get_aliases_by_id($id);
+
+  /**
+   * Get the language aliases associated with the name.
+   *
+   * @param string $name
+   *   The language name of the aliases to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of aliases or FALSE on error.
+   *   FALSE without the error flag means that there are no aliases associated with the given name.
+   */
+  static function s_get_aliases_by_name($name);
+
+  /**
+   * Get the id of the language considered to be default by the implementing class.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the default language.
+   *   FALSE without the error flag means that there are no languages assigned as default.
+   */
+  static function s_get_default_id();
+
+  /**
+   * Get the name of the language considered to be default by the implementing class.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   A string representing the default language.
+   *   FALSE without the error flag means that there are no languages assigned as default.
+   */
+  static function s_get_default_name();
+}
+
+/**
+ * A language class specifically for english only languages.
+ *
+ * @see: http://www.loc.gov/standards/iso639-2/php/code_list.php
+ */
+final class c_base_language_us_only implements i_base_language {
+
+  private static $s_aliases = array(
+    self::ENGLISH_US              => array('en-us'),
+    self::ENGLISH                 => array('eng', 'en'),
+    self::UNDETERMINED            => array('und'),
+    self::NOT_APPLICABLE          => array('zxx'),
+  );
+
+  private static $s_names = array(
+    self::ENGLISH_US              => array('US English'),
+    self::ENGLISH                 => array('English'),
+    self::UNDETERMINED            => array('Undetermined'),
+    self::NOT_APPLICABLE          => array('No Linguistic Content', 'Not Applicable'),
+  );
+
+  private static $s_ids = array(
+    'en-us' => self::ENGLISH_US,
+    'eng'   => self::ENGLISH,
+    'en'    => self::ENGLISH,
+    'und'   => self::UNDETERMINED,
+    'zxx'   => self::NOT_APPLICABLE,
+  );
+
+
+  /**
+   * Get the language names associated with the id.
+   *
+   * @param int $id
+   *   The id of the names to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of names or FALSE on error.
+   *   FALSE without the error flag means that there are no names associated with the given id.
+   */
+  static function s_get_names_by_id($id) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($id, self::$s_names)) {
+      return c_base_return_array::s_new(self::$s_names[$id]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the language names associated with the alias.
+   *
+   * @param string $alias
+   *   The id of the names to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of names or FALSE on error.
+   *   FALSE without the error flag means that there are no names associated with the given alias.
+   */
+  static function s_get_names_by_alias($alias) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($id, self::$s_aliases)) {
+      return c_base_return_array::s_new(self::$s_aliases[$id]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the id associated with the language name.
+   *
+   * @param string $name
+   *   The string associated with the id
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The numeric id or FALSE on error.
+   *   FALSE without the error flag means that there are no ids associated with the given name.
+   */
+  static function s_get_id_by_name($name) {
+    if (!is_string($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($name, self::$s_ids)) {
+      return c_base_return_int::s_new(self::$s_ids[$name]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the id associated with the language name.
+   *
+   * @param string $name
+   *   The string associated with the id
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The numeric id or FALSE on error.
+   *   FALSE without the error flag means that there are no ids associated with the given name.
+   */
+  static function s_get_id_by_alias($alias) {
+    if (!is_string($alias)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($alias, self::$s_ids)) {
+      return c_base_return_int::s_new(self::$s_ids[$alias]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the language aliases associated with the id.
+   *
+   * @param int $id
+   *   The id of the aliases to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of aliases or FALSE on error.
+   *   FALSE without the error flag means that there are no aliases associated with the given id.
+   */
+  static function s_get_aliases_by_id($id) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($id, self::$s_aliases)) {
+      return c_base_return_array::s_new(self::$s_aliases[$id]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the language aliases associated with the name.
+   *
+   * @param string $name
+   *   The language name of the aliases to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of aliases or FALSE on error.
+   *   FALSE without the error flag means that there are no aliases associated with the given name.
+   */
+  static function s_get_aliases_by_name($name) {
+    if (!is_string($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($name, self::$s_aliases)) {
+      return c_base_return_array::s_new(self::$s_aliases[$name]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the id of the language considered to be default by the implementing class.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the default language.
+   *   FALSE without the error flag means that there are no languages assigned as default.
+   */
+  static function s_get_default_id() {
+    return c_base_return_int::s_new(self::ENGLISH_US);
+  }
+
+  /**
+   * Get the name of the language considered to be default by the implementing class.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   A string representing the default language.
+   *   FALSE without the error flag means that there are no languages assigned as default.
+   */
+  static function s_get_default_name() {
+    return c_base_return_string::s_new($this->s_aliases[self::ENGLISH_US]);
+  }
+}
+
+/**
+ * A generic class for managing the different supported languages.
+ *
+ * @see: http://www.loc.gov/standards/iso639-2/php/code_list.php
+ */
+final class c_base_language_us_limited implements i_base_language {
+
+  private static $s_aliases = array(
+    self::ENGLISH_US              => array('en-us'),
+    self::ENGLISH                 => array('eng', 'en'),
+    self::FRENCH                  => array('fre', 'fra', 'fr'),
+    self::SPANISH                 => array('spa', 'es'),
+    self::INDONESIAN              => array('ind', 'id'),
+    self::RUSSIAN                 => array('rus', 'ru'),
+    self::CHINESE                 => array('chi', 'zho', 'zh'),
+    self::UNDETERMINED            => array('und'),
+    self::NOT_APPLICABLE          => array('zxx'),
+  );
+
+  private static $s_names = array(
+    self::ENGLISH_US              => array('US English'),
+    self::ENGLISH                 => array('English'),
+    self::FRENCH                  => array('French'),
+    self::SPANISH                 => array('Spanish', 'Castilian'),
+    self::INDONESIAN              => array('Indonesian'),
+    self::RUSSIAN                 => array('Russian'),
+    self::CHINESE                 => array('Chinese'),
+    self::UNDETERMINED            => array('Undetermined'),
+    self::NOT_APPLICABLE          => array('No Linguistic Content', 'Not Applicable'),
+  );
+
+  private static $s_ids = array(
+    'en-us' => self::ENGLISH_US,
+    'eng'   => self::ENGLISH,
+    'en'    => self::ENGLISH,
+    'fre'   => self::FRENCH,
+    'fra'   => self::FRENCH,
+    'fr'    => self::FRENCH,
+    'spa'   => self::SPANISH,
+    'es'    => self::SPANISH,
+    'ind'   => self::INDONESIAN,
+    'id'    => self::INDONESIAN,
+    'rus'   => self::RUSSIAN,
+    'ru'    => self::RUSSIAN,
+    'chi'   => self::CHINESE,
+    'zho'   => self::CHINESE,
+    'zh'    => self::CHINESE,
+    'und'   => self::UNDETERMINED,
+    'zxx'   => self::NOT_APPLICABLE,
+  );
+
+
+  /**
+   * Get the language names associated with the id.
+   *
+   * @param int $id
+   *   The id of the names to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of names or FALSE on error.
+   *   FALSE without the error flag means that there are no names associated with the given id.
+   */
+  static function s_get_names_by_id($id) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($id, self::$s_names)) {
+      return c_base_return_array::s_new(self::$s_names[$id]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the language names associated with the alias.
+   *
+   * @param string $alias
+   *   The id of the names to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of names or FALSE on error.
+   *   FALSE without the error flag means that there are no names associated with the given alias.
+   */
+  static function s_get_names_by_alias($alias) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($id, self::$s_aliases)) {
+      return c_base_return_array::s_new(self::$s_aliases[$id]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the id associated with the language name.
+   *
+   * @param string $name
+   *   The string associated with the id
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The numeric id or FALSE on error.
+   *   FALSE without the error flag means that there are no ids associated with the given name.
+   */
+  static function s_get_id_by_name($name) {
+    if (!is_string($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($name, self::$s_ids)) {
+      return c_base_return_int::s_new(self::$s_ids[$name]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the id associated with the language name.
+   *
+   * @param string $name
+   *   The string associated with the id
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The numeric id or FALSE on error.
+   *   FALSE without the error flag means that there are no ids associated with the given name.
+   */
+  static function s_get_id_by_alias($alias) {
+    if (!is_string($alias)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($alias, self::$s_ids)) {
+      return c_base_return_int::s_new(self::$s_ids[$alias]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the language aliases associated with the id.
+   *
+   * @param int $id
+   *   The id of the aliases to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of aliases or FALSE on error.
+   *   FALSE without the error flag means that there are no aliases associated with the given id.
+   */
+  static function s_get_aliases_by_id($id) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($id, self::$s_aliases)) {
+      return c_base_return_array::s_new(self::$s_aliases[$id]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the language aliases associated with the name.
+   *
+   * @param string $name
+   *   The language name of the aliases to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of aliases or FALSE on error.
+   *   FALSE without the error flag means that there are no aliases associated with the given name.
+   */
+  static function s_get_aliases_by_name($name) {
+    if (!is_string($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($name, self::$s_aliases)) {
+      return c_base_return_array::s_new(self::$s_aliases[$name]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the id of the language considered to be default by the implementing class.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the default language.
+   *   FALSE without the error flag means that there are no languages assigned as default.
+   */
+  static function s_get_default_id() {
+    return c_base_return_int::s_new(self::ENGLISH_US);
+  }
+
+  /**
+   * Get the name of the language considered to be default by the implementing class.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   A string representing the default language.
+   *   FALSE without the error flag means that there are no languages assigned as default.
+   */
+  static function s_get_default_name() {
+    return c_base_return_string::s_new($this->s_aliases[self::ENGLISH_US]);
+  }
+}
+
+/**
+ * A generic class for managing the different supported languages.
+ *
+ * @see: http://www.loc.gov/standards/iso639-2/php/code_list.php
+ */
+final class c_base_language_us_all implements i_base_language {
+
+  private static $s_aliases = array(
+    self::ENGLISH_US              => array('en-us'),
+    self::ENGLISH                 => array('eng', 'en'),
+    self::ENGLISH_CA              => array('en-ca'),
+    self::ENGLISH_GB              => array('en-gb'),
+    self::AFAR                    => array('aar', 'aa'),
+    self::ABKHAZIAN               => array('abk', 'ab'),
+    self::ACHINESE                => array('ace'),
+    self::ACOLI                   => array('ach'),
+    self::ADANGME                 => array('ada'),
+    self::ADYGHE                  => array('ady'),
+    self::AFRO_ASIATIC            => array('afa'),
+    self::AFRIHILI                => array('afh'),
+    self::AFRIKAANS               => array('afr', 'af'),
+    self::AINU                    => array('ain'),
+    self::AKAN                    => array('aka', 'ak'),
+    self::AKKADIAN                => array('akk'),
+    self::ALBANIAN                => array('alb', 'sqi', 'sq'),
+    self::ALEUT                   => array('ale'),
+    self::ALGONQUIAN              => array('alg'),
+    self::SOUTHERN_ALTAI          => array('alt'),
+    self::AMHARIC                 => array('amh', 'am'),
+    self::ENGLISH_OLD             => array('ang'),
+    self::ANGIKA                  => array('anp'),
+    self::APACHE                  => array('apa'),
+    self::ARABIC                  => array('ara', 'ar'),
+    self::ARAMAIC                 => array('arc'),
+    self::ARAGONESE               => array('arg', 'an'),
+    self::ARMENIAN                => array('arm', 'hye', 'hy'),
+    self::MAPUDUNGUN              => array('am'),
+    self::ARAPAHO                 => array('arp'),
+    self::ARTIFICIAL              => array('art'),
+    self::ARAWAK                  => array('arw'),
+    self::ASSAMESE                => array('asm', 'as'),
+    self::ASTURIAN                => array('ast'),
+    self::ATHAPASCAN              => array('ath'),
+    self::AUSTRALIAN              => array('aus'),
+    self::AVARIC                  => array('ava', 'av'),
+    self::AVESTAN                 => array('ave', 'ae'),
+    self::AWADHI                  => array('awa'),
+    self::AYMARA                  => array('aym', 'ay'),
+    self::AZERBAIJANI             => array('aze', 'az'),
+    self::BANDA                   => array('bad'),
+    self::BAMILEKE                => array('bai'),
+    self::BASHKIR                 => array('bak', 'ba'),
+    self::BALUCHI                 => array('bal'),
+    self::BAMBARA                 => array('bam', 'bm'),
+    self::BALINESE                => array('ban'),
+    self::BASQUE                  => array('baq', 'eus', 'eu'),
+    self::BASA                    => array('bas'),
+    self::BALTIC                  => array('bat'),
+    self::BEJA                    => array('bej'),
+    self::BELARUSIAN              => array('bel', 'be'),
+    self::BEMBA                   => array('bem'),
+    self::BENGALI                 => array('ben', 'bn'),
+    self::BERBER                  => array('ber'),
+    self::BHOJPURI                => array('bho'),
+    self::BIHARI                  => array('bih', 'bh'),
+    self::BIKOL                   => array('bik'),
+    self::BINI                    => array('bin'),
+    self::BISLAMA                 => array('bis', 'bi'),
+    self::SIKSIKA                 => array('bla'),
+    self::BANTU                   => array('bnt'),
+    self::TIBETAN                 => array('tib', 'bod', 'bo'),
+    self::BOSNIAN                 => array('bos', 'bs'),
+    self::BRAJ                    => array('bra'),
+    self::BRETON                  => array('bre'),
+    self::BATAK                   => array('btk'),
+    self::BURIAT                  => array('bua'),
+    self::BUGINESE                => array('bug'),
+    self::BULGARIAN               => array('bul'),
+    self::BURMESE                 => array('bur', 'mya', 'my'),
+    self::BLIN                    => array('byn'),
+    self::CADDO                   => array('cad'),
+    self::AMERICAN_INDIAN_CENTRAL => array('cai'),
+    self::GALIBI_CARIB            => array('car'),
+    self::CATALAN                 => array('cat', 'ca'),
+    self::CAUCASIAN               => array('cau'),
+    self::CEBUANO                 => array('ceb'),
+    self::CELTIC                  => array('cel'),
+    self::CZECH                   => array('cze', 'ces', 'cs'),
+    self::CHAMORRO                => array('cha', 'ch'),
+    self::CHIBCHA                 => array('chb'),
+    self::CHECHEN                 => array('che', 'ce'),
+    self::CHAGATAI                => array('chg'),
+    self::CHINESE                 => array('chi', 'zho', 'zh'),
+    self::CHUUKESE                => array('chk'),
+    self::MARI                    => array('chm'),
+    self::CHINOOK_JARGON          => array('chn'),
+    self::CHOCTAW                 => array('cho'),
+    self::CHIPEWYAN               => array('chp'),
+    self::CHEROKEE                => array('chr'),
+    self::CHURCH_SLAVIC           => array('chu', 'cu'),
+    self::CHUVASH                 => array('chv', 'cv'),
+    self::CHEYENNE                => array('chy'),
+    self::CHAMIC                  => array('cmc'),
+    self::COPTIC                  => array('cop'),
+    self::CORNISH                 => array('cor'),
+    self::CORSICAN                => array('cos', 'co'),
+    self::CREOLES_ENGLISH         => array('cpe'),
+    self::CREOLES_FRENCH          => array('cpf'),
+    self::CREOLES_PORTUGESE       => array('cpp'),
+    self::CREE                    => array('cre', 'cr'),
+    self::CRIMEAN_TATAR           => array('crh'),
+    self::CREOLES                 => array('crp'),
+    self::KASHUBIAN               => array('csb'),
+    self::CUSHITIC                => array('cus'),
+    self::WELSH                   => array('wel', 'cym', 'cy'),
+    self::DAKOTA                  => array('dak'),
+    self::DANISH                  => array('dan', 'da'),
+    self::DARGWA                  => array('dar'),
+    self::LAND_DAYAK              => array('day'),
+    self::DELAWARE                => array('del'),
+    self::SLAVE                   => array('den'),
+    self::GERMAN                  => array('ger', 'deu', 'de'),
+    self::DOGRIB                  => array('dgr'),
+    self::DINKA                   => array('din'),
+    self::DIVEHI                  => array('div', 'dv'),
+    self::DOGRI                   => array('doi'),
+    self::DRAVIDIAN               => array('dra'),
+    self::LOWER_SORBIAN           => array('dsb'),
+    self::DUALA                   => array('dua'),
+    self::DUTCH_MIDDLE            => array('dum'),
+    self::DUTCH_FLEMISH           => array('dut', 'nld', 'nl'),
+    self::DYULA                   => array('dyu'),
+    self::DZONGKHA                => array('dzo', 'dz'),
+    self::EFIK                    => array('efi'),
+    self::EGYPTIAN                => array('egy'),
+    self::EKAJUK                  => array('eka'),
+    self::GREEK_MODERN            => array('gre', 'ell', 'el'),
+    self::ELAMITE                 => array('elx'),
+    self::ENGLISH_MIDDLE          => array('enm'),
+    self::ESPERANTO               => array('epo', 'eo'),
+    self::ESTONIAN                => array('est', 'et'),
+    self::EWE                     => array('ewe', 'ee'),
+    self::EWONDO                  => array('ewo'),
+    self::FANG                    => array('fan'),
+    self::FAROESE                 => array('fao', 'fo'),
+    self::PERSIAN                 => array('per', 'fas', 'fa'),
+    self::FANTI                   => array('fat'),
+    self::FIJIAN                  => array('fij', 'fj'),
+    self::FILIPINO                => array('fil'),
+    self::FINNISH                 => array('fin', 'fi'),
+    self::FINNO_UGRIAN            => array('fiu'),
+    self::FON                     => array('fon'),
+    self::FRENCH                  => array('fre', 'fra', 'fr'),
+    self::FRENCH_MIDDLE           => array('frm'),
+    self::FRENCH_OLD              => array('fro'),
+    self::FRISIAN_NORTHERN        => array('frr'),
+    self::FRISIAN_EASTERN         => array('frs'),
+    self::FRISIAN_WESTERN         => array('fry', 'fy'),
+    self::FULAH                   => array('ful', 'ff'),
+    self::FRIULIAN                => array('fur'),
+    self::GA                      => array('gaa'),
+    self::GAYO                    => array('gay'),
+    self::GBAYA                   => array('gba'),
+    self::GERMANIC                => array('gem'),
+    self::GEORGIAN                => array('geo', 'kat', 'ka'),
+    self::GEEZ                    => array('gez'),
+    self::GILBERTESE              => array('gil'),
+    self::GAELIC                  => array('gla', 'gd'),
+    self::IRISH                   => array('gle', 'ga'),
+    self::GALICIAN                => array('glg', 'gl'),
+    self::MANX                    => array('glv', 'gv'),
+    self::GERMAN_MIDDLE_HIGH      => array('gmh'),
+    self::GERMAN_OLD_HIGH         => array('goh'),
+    self::GONDI                   => array('gon'),
+    self::GORONTALO               => array('gor'),
+    self::GOTHIC                  => array('got'),
+    self::GREBO                   => array('grb'),
+    self::GREEK_ANCIENT           => array('grc'),
+    self::GUARANI                 => array('grm', 'gn'),
+    self::GERMAN_SWISS            => array('gsw'),
+    self::GUJARATI                => array('guj', 'gu'),
+    self::GWICHIN                 => array('gwi'),
+    self::HAIDA                   => array('hai'),
+    self::HAITIAN                 => array('hat', 'ht'),
+    self::HAUSA                   => array('hau', 'ha'),
+    self::HAWAIIAN                => array('haw'),
+    self::HEBREW                  => array('heb', 'he'),
+    self::HERERO                  => array('her', 'hz'),
+    self::HILIGAYNON              => array('hil'),
+    self::HIMACHALI               => array('him'),
+    self::HINDI                   => array('hin', 'hi'),
+    self::HITTITE                 => array('hit'),
+    self::HMONG                   => array('hmn'),
+    self::HIRI_MOTU               => array('hmo', 'ho'),
+    self::CROATIAN                => array('hrv'),
+    self::SORBIAN_UPPER           => array('hsb'),
+    self::HUNGARIAN               => array('hun', 'hu'),
+    self::HUPA                    => array('hup'),
+    self::IBAN                    => array('iba'),
+    self::IGBO                    => array('ibo', 'ig'),
+    self::ICELANDIC               => array('ice', 'isl', 'is'),
+    self::IDO                     => array('ido', 'io'),
+    self::SICHUAN_YI              => array('iii', 'ii'),
+    self::IJO                     => array('ijo'),
+    self::INUKTITUT               => array('iku', 'iu'),
+    self::INTERLINGUE             => array('ile', 'ie'),
+    self::ILOKO                   => array('ilo'),
+    self::INTERLINGUA             => array('ina', 'ia'),
+    self::INDIC                   => array('inc'),
+    self::INDONESIAN              => array('ind', 'id'),
+    self::INDO_EUROPEAN           => array('ine'),
+    self::INGUSH                  => array('inh'),
+    self::INUPIAQ                 => array('ipk', 'ik'),
+    self::IRANIAN                 => array('ira'),
+    self::IROQUOIAN               => array('iro'),
+    self::ITALIAN                 => array('ita', 'it'),
+    self::JAVANESE                => array('jav', 'jv'),
+    self::LOJBAN                  => array('jbo'),
+    self::JAPANESE                => array('jpn', 'ja'),
+    self::JUDEO_PERSIAN           => array('jpr'),
+    self::JUDEO_ARABIC            => array('jrb'),
+    self::KARA_KALPAK             => array('kaa'),
+    self::KABYLE                  => array('kab'),
+    self::KACHIN                  => array('kac'),
+    self::KALAALLISUT             => array('kal', 'kl'),
+    self::KAMBA                   => array('kam'),
+    self::KANNADA                 => array('kan', 'kn'),
+    self::KAREN                   => array('kar'),
+    self::KASHMIRI                => array('kas', 'ks'),
+    self::KANURI                  => array('kau', 'kr'),
+    self::KAWI                    => array('kaw'),
+    self::KAZAKH                  => array('kaz'),
+    self::KABARDIAN               => array('kbd'),
+    self::KHASI                   => array('kha'),
+    self::KHOISAN                 => array('khi'),
+    self::CENTRAL_KHMER           => array('khm', 'km'),
+    self::KHOTANESE               => array('kho'),
+    self::KIKUYU                  => array('kik', 'ki'),
+    self::KINYARWANDA             => array('kin', 'rw'),
+    self::KIRGHIZ                 => array('kir', 'ky'),
+    self::KIMBUNDU                => array('kmb'),
+    self::KONKANI                 => array('kok'),
+    self::KOMI                    => array('kom', 'kv'),
+    self::KONGO                   => array('kon', 'kg'),
+    self::KOREAN                  => array('kor', 'ko'),
+    self::KOSRAEAN                => array('kos'),
+    self::KPELLE                  => array('kpe'),
+    self::KARACHAY_BALKAR         => array('krc'),
+    self::KARELIAN                => array('krl'),
+    self::KRU                     => array('kro'),
+    self::KURUKH                  => array('kru'),
+    self::KUANYAMA                => array('kua', 'kj'),
+    self::KUMYK                   => array('kum'),
+    self::KURDISH                 => array('kur', 'ku'),
+    self::KUTENAI                 => array('kut'),
+    self::LADINO                  => array('lad'),
+    self::LAHNDA                  => array('lah'),
+    self::LAMBA                   => array('lam'),
+    self::LAO                     => array('lao', 'lo'),
+    self::LATIN                   => array('lat', 'la'),
+    self::LATVIAN                 => array('lav', 'lv'),
+    self::LEZGHIAN                => array('lez'),
+    self::LIMBURGAN               => array('lim', 'li'),
+    self::LINGALA                 => array('lin', 'ln'),
+    self::LITHUANIAN              => array('lit', 'lt'),
+    self::MONGO                   => array('lol'),
+    self::LOZI                    => array('loz'),
+    self::LUXEMBOURGISH           => array('ltz', 'lb'),
+    self::LUBA_LULUA              => array('lua'),
+    self::LUBA_KATANGA            => array('lub', 'lu'),
+    self::GANDA                   => array('lug', 'lg'),
+    self::LUISENO                 => array('lui'),
+    self::LUNDA                   => array('lun'),
+    self::LUO                     => array('luo'),
+    self::LUSHAI                  => array('lus'),
+    self::MACEDONIAN              => array('mac', 'mkd', 'mk'),
+    self::MADURESE                => array('mad'),
+    self::MAGAHI                  => array('mag'),
+    self::MARSHALLESE             => array('mah'),
+    self::MAITHILI                => array('mai'),
+    self::MAKASAR                 => array('mak'),
+    self::MALAYALAM               => array('mal'),
+    self::MANDINGO                => array('man'),
+    self::MAORI                   => array('mao', 'mri', 'mi'),
+    self::AUSTRONESIAN            => array('map'),
+    self::MARATHI                 => array('mar', 'mr'),
+    self::MASAI                   => array('mas'),
+    self::MALAY                   => array('may', 'msa', 'ms'),
+    self::MOKSHA                  => array('mdf'),
+    self::MANDAR                  => array('mdr'),
+    self::MENDE                   => array('men'),
+    self::IRISH_MIDDLE            => array('mga'),
+    self::MIKMAQ                  => array('mic'),
+    self::MINANGKABAU             => array('min'),
+    self::UNCODED                 => array('mis'),
+    self::MON_KHMER               => array('mkh'),
+    self::MALAGASY                => array('mlg'),
+    self::MALTESE                 => array('mlt'),
+    self::MANCHU                  => array('mnc'),
+    self::MANIPURI                => array('mni'),
+    self::MANOBO                  => array('mno'),
+    self::MOHAWK                  => array('moh'),
+    self::MONGOLIAN               => array('mon', 'mn'),
+    self::MOSSI                   => array('mos'),
+    self::MULTIPLE                => array('mul'),
+    self::MUNDA                   => array('mun'),
+    self::CREEK                   => array('mus'),
+    self::MIRANDESE               => array('mwl'),
+    self::MARWARI                 => array('mwr'),
+    self::MAYAN                   => array('myn'),
+    self::ERZYA                   => array('myv'),
+    self::NAHUATL                 => array('nah'),
+    self::AMERICAN_INDIAN_NORTH   => array('nai'),
+    self::NEAPOLITAN              => array('nap'),
+    self::NAURU                   => array('nau', 'na'),
+    self::NAVAJO                  => array('nav', 'nv'),
+    self::NDEBELE_SOUTH           => array('nbl', 'nr'),
+    self::NDEBELE_NORTH           => array('nde', 'nd'),
+    self::NDONGA                  => array('ndo', 'ng'),
+    self::LOW_GERMAN              => array('nds'),
+    self::NEPALI                  => array('nep', 'ne'),
+    self::NEPAL_BHASA             => array('new'),
+    self::NIAS                    => array('nia'),
+    self::NIGER_KORDOFANIAN       => array('nic'),
+    self::NIUEAN                  => array('niu'),
+    self::NORWEGIAN_NYNORSK       => array('nno', 'nn'),
+    self::BOKMAL                  => array('nob', 'nb'),
+    self::NOGAI                   => array('nog'),
+    self::NORSE_OLD               => array('non'),
+    self::NORWEGIAN               => array('nor', 'no'),
+    self::NKO                     => array('nqo'),
+    self::PEDI                    => array('nso'),
+    self::NUBIAN                  => array('nub'),
+    self::CLASSICAL_NEWARI        => array('nwc'),
+    self::CHICHEWA                => array('nya', 'ny'),
+    self::NYAMWEZI                => array('nym'),
+    self::NYANKOLE                => array('nyn'),
+    self::NYORO                   => array('nyo'),
+    self::NZIMA                   => array('nzi'),
+    self::OCCITAN                 => array('oci', 'oc'),
+    self::OJIBWA                  => array('oji', 'oj'),
+    self::ORIYA                   => array('ori', 'or'),
+    self::OROMO                   => array('orm', 'om'),
+    self::OSAGE                   => array('osa'),
+    self::OSSETIAN                => array('oss', 'os'),
+    self::OTTOMAN                 => array('ota'),
+    self::OTOMIAN                 => array('oto'),
+    self::PAPUAN                  => array('paa'),
+    self::PANGASINAN              => array('pag'),
+    self::PAHLAVI                 => array('pal'),
+    self::PAMPANGA                => array('pam'),
+    self::PANJABI                 => array('pan', 'pa'),
+    self::PAPIAMENTO              => array('pap'),
+    self::PALAUAN                 => array('pau'),
+    self::PERSIAN_OLD             => array('peo'),
+    self::PHILIPPINE              => array('phi'),
+    self::PHOENICIAN              => array('phn'),
+    self::PALI                    => array('pli', 'pi'),
+    self::POLISH                  => array('pol', 'pl'),
+    self::POHNPEIAN               => array('pon'),
+    self::PORTUGUESE              => array('por', 'pt'),
+    self::PRAKRIT                 => array('pra'),
+    self::PROVENCAL               => array('pro'),
+    self::PUSHTO                  => array('pus', 'ps'),
+    self::QUECHUA                 => array('que', 'qu'),
+    self::RAJASTHANI              => array('raj'),
+    self::RAPANUI                 => array('rap'),
+    self::RAROTONGAN              => array('rar'),
+    self::ROMANCE                 => array('roa'),
+    self::ROMANSH                 => array('roh', 'rm'),
+    self::ROMANY                  => array('rom'),
+    self::ROMANIAN                => array('rum', 'ron', 'ro'),
+    self::RUNDI                   => array('run', 'rn'),
+    self::AROMANIAN               => array('rup'),
+    self::RUSSIAN                 => array('rus', 'ru'),
+    self::SANDAWE                 => array('sad'),
+    self::SANGO                   => array('sag', 'sg'),
+    self::YAKUT                   => array('sah'),
+    self::AMERICAN_INDIAN_SOUTH   => array('sai'),
+    self::SALISHAN                => array('sal'),
+    self::SAMARITAN               => array('sam'),
+    self::SANSKRIT                => array('san', 'sa'),
+    self::SASAK                   => array('sas'),
+    self::SANTALI                 => array('sat'),
+    self::SICILIAN                => array('scn'),
+    self::SCOTS                   => array('sco'),
+    self::SELKUP                  => array('sel'),
+    self::SEMITIC                 => array('sem'),
+    self::IRISH_OLD               => array('sga'),
+    self::SIGN                    => array('sgn'),
+    self::SHAN                    => array('shn'),
+    self::SIDAMO                  => array('sid'),
+    self::SINHALA                 => array('sin', 'si'),
+    self::SIOUAN                  => array('sio'),
+    self::SINO_TIBETAN            => array('sit'),
+    self::SLAVIC                  => array('sla'),
+    self::SLOVAK                  => array('slo', 'slk', 'sk'),
+    self::SLOVENIAN               => array('slv', 'sl'),
+    self::SAMI_SOUTHERN           => array('sma'),
+    self::SAMI_NORTHERN           => array('sme', 'se'),
+    self::SAMI                    => array('smi'),
+    self::SAMI_LULE               => array('smj'),
+    self::SAMI_IRARI              => array('smn'),
+    self::SAMOAN                  => array('smo', 'sm'),
+    self::SAMI_SKOLT              => array('sms'),
+    self::SHONA                   => array('sna', 'sn'),
+    self::SINDHI                  => array('snd', 'sd'),
+    self::SONINKE                 => array('snk'),
+    self::SOGDIAN                 => array('sog'),
+    self::SOMALI                  => array('som', 'so'),
+    self::SONGHAI                 => array('son'),
+    self::SOTHO_SOUTHERN          => array('sot', 'st'),
+    self::SPANISH                 => array('spa', 'es'),
+    self::SARDINIAN               => array('srd', 'sc'),
+    self::SRANAN_TONGO            => array('sm'),
+    self::SERBIAN                 => array('srp', 'sr'),
+    self::SERER                   => array('srr'),
+    self::NILO_SAHARAN            => array('ssa'),
+    self::SWATI                   => array('ssw', 'ss'),
+    self::SUKUMA                  => array('suk'),
+    self::SUNDANESE               => array('sun', 'su'),
+    self::SUSU                    => array('sus'),
+    self::SUMERIAN                => array('sux'),
+    self::SWAHILI                 => array('swa', 'sw'),
+    self::SWEDISH                 => array('swe', 'sv'),
+    self::SYRIAC_CLASSICAL        => array('syc'),
+    self::SYRIAC                  => array('syr'),
+    self::TAHITIAN                => array('tah', 'ty'),
+    self::TAI                     => array('tai'),
+    self::TAMIL                   => array('tam', 'ta'),
+    self::TATAR                   => array('tat', 'tt'),
+    self::TELUGU                  => array('tel', 'te'),
+    self::TIMNE                   => array('tem'),
+    self::TERENO                  => array('ter'),
+    self::TETUM                   => array('tet'),
+    self::TAJIK                   => array('tgk', 'tg'),
+    self::TAGALOG                 => array('tgl', 'tl'),
+    self::THAI                    => array('tha', 'th'),
+    self::TIGRE                   => array('tig'),
+    self::TIGRINYA                => array('tir', 'ti'),
+    self::TIV                     => array('tiv'),
+    self::TOKELAU                 => array('tkl'),
+    self::KLINGON                 => array('tlh'),
+    self::TLINGIT                 => array('tli'),
+    self::TAMASHEK                => array('tmh'),
+    self::TONGA_NYASA             => array('tog'),
+    self::TONGA_ISLANDS           => array('ton', 'to'),
+    self::TOK_PISIN               => array('tpi'),
+    self::TSIMSHIAN               => array('tsi'),
+    self::TSWANA                  => array('tsn', 'tn'),
+    self::TSONGA                  => array('tso', 'ts'),
+    self::TURKMEN                 => array('tuk', 'tk'),
+    self::TUMBUKA                 => array('tum'),
+    self::TUPI                    => array('tup'),
+    self::TURKISH                 => array('tur', 'tr'),
+    self::ALTAIC                  => array('tut'),
+    self::TUVALU                  => array('tvl'),
+    self::TWI                     => array('twi', 'tw'),
+    self::TUVINIAN                => array('tyv'),
+    self::UDMURT                  => array('udm'),
+    self::UGARITIC                => array('uga'),
+    self::UIGHUR                  => array('uig', 'ug'),
+    self::UKRAINIAN               => array('ukr', 'uk'),
+    self::UMBUNDU                 => array('umb'),
+    self::UNDETERMINED            => array('und'),
+    self::URDU                    => array('urd', 'ur'),
+    self::UZBEK                   => array('uzb', 'uz'),
+    self::VAI                     => array('vai'),
+    self::VENDA                   => array('ven', 've'),
+    self::VIETNAMESE              => array('vie', 'vi'),
+    self::VOLAPUK                 => array('vol', 'vo'),
+    self::VOTIC                   => array('vot'),
+    self::WAKASHAN                => array('wak'),
+    self::WOLAITTA                => array('wal'),
+    self::WARAY                   => array('war'),
+    self::WASHO                   => array('was'),
+    self::SORBIAN                 => array('wen'),
+    self::WALLOON                 => array('wln', 'wa'),
+    self::WOLOF                   => array('wol', 'wo'),
+    self::KALMYK                  => array('xal'),
+    self::XHOSA                   => array('xho', 'xh'),
+    self::YAO                     => array('yao'),
+    self::YAPESE                  => array('yap'),
+    self::YIDDISH                 => array('yid', 'yi'),
+    self::YORUBA                  => array('yor', 'yo'),
+    self::YUPIK                   => array('ypk'),
+    self::ZAPOTEC                 => array('zap'),
+    self::BLISSYMBOLS             => array('zbl'),
+    self::ZENAGA                  => array('zen'),
+    self::MOROCCAN_TAMAZIGHT      => array('zgh'),
+    self::ZHUANG                  => array('zha', 'za'),
+    self::ZANDE                   => array('znd'),
+    self::ZULU                    => array('zul', 'zu'),
+    self::ZUNI                    => array('zun'),
+    self::NOT_APPLICABLE          => array('zxx'),
+    self::ZAZA                    => array('zza'),
+  );
+
+  private static $s_names = array(
+    self::ENGLISH_US              => array('US English'),
+    self::ENGLISH                 => array('English'),
+    self::ENGLISH_CA              => array('Canadian English'),
+    self::ENGLISH_GB              => array('British English'),
+    self::AFAR                    => array('Afar'),
+    self::ABKHAZIAN               => array('Abkhazian'),
+    self::ACHINESE                => array('Achinese'),
+    self::ACOLI                   => array('Acoli'),
+    self::ADANGME                 => array('Adangme'),
+    self::ADYGHE                  => array('Adyghe'),
+    self::AFRO_ASIATIC            => array('Afro-Asiatic', 'Adygei'),
+    self::AFRIHILI                => array('Afrihili'),
+    self::AFRIKAANS               => array('Afrikaans'),
+    self::AINU                    => array('Ainu'),
+    self::AKAN                    => array('Akan'),
+    self::AKKADIAN                => array('Akkadian'),
+    self::ALBANIAN                => array('Albanian'),
+    self::ALEUT                   => array('Aleut'),
+    self::ALGONQUIAN              => array('Algonquian'),
+    self::SOUTHERN_ALTAI          => array('Southern Altai'),
+    self::AMHARIC                 => array('Amharic'),
+    self::ENGLISH_OLD             => array('Old English'),
+    self::ANGIKA                  => array('Angika'),
+    self::APACHE                  => array('Apache'),
+    self::ARABIC                  => array('Arabic'),
+    self::ARAMAIC                 => array('Official Aramaic', 'Imperial Aramaic'),
+    self::ARAGONESE               => array('Aragonese'),
+    self::ARMENIAN                => array('Armenian'),
+    self::MAPUDUNGUN              => array('Mapudungun', 'Mapuche'),
+    self::ARAPAHO                 => array('Arapaho'),
+    self::ARTIFICIAL              => array('Artificial'),
+    self::ARAWAK                  => array('Arawak'),
+    self::ASSAMESE                => array('Assamese'),
+    self::ASTURIAN                => array('Asturian', 'Bable', 'Leonese', 'Asturleonese'),
+    self::ATHAPASCAN              => array('Athapascan'),
+    self::AUSTRALIAN              => array('Australian'),
+    self::AVARIC                  => array('Avaric'),
+    self::AVESTAN                 => array('Avestan'),
+    self::AWADHI                  => array('Awadhi'),
+    self::AYMARA                  => array('Aymara'),
+    self::AZERBAIJANI             => array('Azerbaijani'),
+    self::BANDA                   => array('Banda'),
+    self::BAMILEKE                => array('Bamileke'),
+    self::BASHKIR                 => array('Bashkir'),
+    self::BALUCHI                 => array('Baluchi'),
+    self::BAMBARA                 => array('Bambara'),
+    self::BALINESE                => array('Balinese'),
+    self::BASQUE                  => array('Basque'),
+    self::BASA                    => array('Basa'),
+    self::BALTIC                  => array('Baltic'),
+    self::BEJA                    => array('Beja'),
+    self::BELARUSIAN              => array('Belarusian'),
+    self::BEMBA                   => array('Bemba'),
+    self::BENGALI                 => array('Bengali'),
+    self::BERBER                  => array('Berber'),
+    self::BHOJPURI                => array('Bhojpuri'),
+    self::BIHARI                  => array('Bihari'),
+    self::BIKOL                   => array('Bikol'),
+    self::BINI                    => array('Bini', 'Edo'),
+    self::BISLAMA                 => array('Bislama'),
+    self::SIKSIKA                 => array('Siksika'),
+    self::BANTU                   => array('Bantu'),
+    self::TIBETAN                 => array('Tibetan'),
+    self::BOSNIAN                 => array('Bosnian'),
+    self::BRAJ                    => array('Braj'),
+    self::BRETON                  => array('Breton'),
+    self::BATAK                   => array('Batak'),
+    self::BURIAT                  => array('Buriat'),
+    self::BUGINESE                => array('Buginese'),
+    self::BULGARIAN               => array('Bulgarian'),
+    self::BURMESE                 => array('Burmese'),
+    self::BLIN                    => array('Blin', 'Bilin'),
+    self::CADDO                   => array('Caddo'),
+    self::AMERICAN_INDIAN_CENTRAL => array('Central American Indian'),
+    self::GALIBI_CARIB            => array('Galibi Carib'),
+    self::CATALAN                 => array('Catalan', 'Valencian'),
+    self::CAUCASIAN               => array('Caucasian'),
+    self::CEBUANO                 => array('Cebuano'),
+    self::CELTIC                  => array('Celtic'),
+    self::CZECH                   => array('Czech'),
+    self::CHAMORRO                => array('Chamorro'),
+    self::CHIBCHA                 => array('Chibcha'),
+    self::CHECHEN                 => array('Chechen'),
+    self::CHAGATAI                => array('Chagatai'),
+    self::CHINESE                 => array('Chinese'),
+    self::CHUUKESE                => array('Chuukese'),
+    self::MARI                    => array('Mari'),
+    self::CHINOOK_JARGON          => array('Chinook jargon'),
+    self::CHOCTAW                 => array('Choctaw'),
+    self::CHIPEWYAN               => array('Chipewyan', 'Dene Suline'),
+    self::CHEROKEE                => array('Cherokee'),
+    self::CHURCH_SLAVIC           => array('Church Slavic', 'Old Slavonic', 'Church Slavonic', 'Old Bulgarian', 'Old Church Slavonic'),
+    self::CHUVASH                 => array('Chuvash'),
+    self::CHEYENNE                => array('Cheyenne'),
+    self::CHAMIC                  => array('Chamic'),
+    self::COPTIC                  => array('Coptic'),
+    self::CORNISH                 => array('Cornish'),
+    self::CORSICAN                => array('Corsican'),
+    self::CREOLES_ENGLISH         => array('Creoles and Pidgins, English Based'),
+    self::CREOLES_FRENCH          => array('Creoles and Pidgins, French Based'),
+    self::CREOLES_PORTUGESE       => array('Creoles and Pidgins, Portugese Based'),
+    self::CREE                    => array('Cree'),
+    self::CRIMEAN_TATAR           => array('Crimean Tatar', 'Crimean Turkish'),
+    self::CREOLES                 => array('Creoles and Pidgins'),
+    self::KASHUBIAN               => array('Kashubian'),
+    self::CUSHITIC                => array('Cushitic'),
+    self::WELSH                   => array('Welsh'),
+    self::DAKOTA                  => array('Dakota'),
+    self::DANISH                  => array('Danish'),
+    self::DARGWA                  => array('Dargwa'),
+    self::LAND_DAYAK              => array('Land Dayak'),
+    self::DELAWARE                => array('Delaware'),
+    self::SLAVE                   => array('Athapascan Slave'),
+    self::GERMAN                  => array('German'),
+    self::DOGRIB                  => array('Dogrib'),
+    self::DINKA                   => array('Dinka'),
+    self::DIVEHI                  => array('Divehi', 'Dhivehi', 'Maldivian'),
+    self::DOGRI                   => array('Dogri'),
+    self::DRAVIDIAN               => array('Dravidian'),
+    self::LOWER_SORBIAN           => array('Lower Sorbian'),
+    self::DUALA                   => array('Duala'),
+    self::DUTCH_MIDDLE            => array('Middle Dutch'),
+    self::DUTCH_FLEMISH           => array('Dutch', 'Flemish'),
+    self::DYULA                   => array('Dyula'),
+    self::DZONGKHA                => array('Dzongkha'),
+    self::EFIK                    => array('Efik'),
+    self::EGYPTIAN                => array('Ancient Egyptian'),
+    self::EKAJUK                  => array('Ekajuk'),
+    self::GREEK_MODERN            => array('Modern Greek'),
+    self::ELAMITE                 => array('Elamite'),
+    self::ENGLISH_MIDDLE          => array('Middle English'),
+    self::ESPERANTO               => array('Esperanto'),
+    self::ESTONIAN                => array('Estonian'),
+    self::EWE                     => array('Ewe'),
+    self::EWONDO                  => array('Ewondo'),
+    self::FANG                    => array('Fang'),
+    self::FAROESE                 => array('Faroese'),
+    self::PERSIAN                 => array('Persian'),
+    self::FANTI                   => array('Fanti'),
+    self::FIJIAN                  => array('Fijian'),
+    self::FILIPINO                => array('Filipino', 'Pilipino'),
+    self::FINNISH                 => array('Finnish'),
+    self::FINNO_UGRIAN            => array('Finno-Ugrian '),
+    self::FON                     => array('Fon'),
+    self::FRENCH                  => array('French'),
+    self::FRENCH_MIDDLE           => array('Middle French'),
+    self::FRENCH_OLD              => array('Old French'),
+    self::FRISIAN_NORTHERN        => array('Northern Frisian'),
+    self::FRISIAN_EASTERN         => array('Eastern Frisian'),
+    self::FRISIAN_WESTERN         => array('Southern Frisian'),
+    self::FULAH                   => array('Fulah'),
+    self::FRIULIAN                => array('Friulian'),
+    self::GA                      => array('Ga'),
+    self::GAYO                    => array('Gayo'),
+    self::GBAYA                   => array('Gbaya'),
+    self::GERMANIC                => array('Germanic'),
+    self::GEORGIAN                => array('Georgian'),
+    self::GEEZ                    => array('Geez'),
+    self::GILBERTESE              => array('Gilbertese'),
+    self::GAELIC                  => array('Gaelic', 'Scottish Gaelic'),
+    self::IRISH                   => array('Irish'),
+    self::GALICIAN                => array('Galician'),
+    self::MANX                    => array('Manx'),
+    self::GERMAN_MIDDLE_HIGH      => array('Middle High German'),
+    self::GERMAN_OLD_HIGH         => array('Old High German'),
+    self::GONDI                   => array('Gondi'),
+    self::GORONTALO               => array('Gorontalo'),
+    self::GOTHIC                  => array('Gothic'),
+    self::GREBO                   => array('Grebo'),
+    self::GREEK_ANCIENT           => array('Ancient Greek'),
+    self::GUARANI                 => array('Guarani'),
+    self::GERMAN_SWISS            => array('Swiss German', 'Alemannic', 'Alsatian'),
+    self::GUJARATI                => array('Gujarati'),
+    self::GWICHIN                 => array('Gwich\'in'),
+    self::HAIDA                   => array('Haida'),
+    self::HAITIAN                 => array('Haitian', 'Haitian Creole'),
+    self::HAUSA                   => array('Hausa'),
+    self::HAWAIIAN                => array('Hawaiian'),
+    self::HEBREW                  => array('Hebrew'),
+    self::HERERO                  => array('Herero'),
+    self::HILIGAYNON              => array('Hiligaynon'),
+    self::HIMACHALI               => array('Himachali', 'Western Pahari'),
+    self::HINDI                   => array('Hindi'),
+    self::HITTITE                 => array('Hittite'),
+    self::HMONG                   => array('Hmong', 'Mong'),
+    self::HIRI_MOTU               => array('Hiri Motu'),
+    self::CROATIAN                => array('Croatian'),
+    self::SORBIAN_UPPER           => array('Upper Sorbian'),
+    self::HUNGARIAN               => array('Hungarian'),
+    self::HUPA                    => array('Hupa'),
+    self::IBAN                    => array('Iban'),
+    self::IGBO                    => array('Igbo'),
+    self::ICELANDIC               => array('Icelandic'),
+    self::IDO                     => array('Ido'),
+    self::SICHUAN_YI              => array('Sichuan Yi', 'Nuosu'),
+    self::IJO                     => array('Ijo'),
+    self::INUKTITUT               => array('Inuktitut'),
+    self::INTERLINGUE             => array('Interlingue'),
+    self::ILOKO                   => array('Iloko'),
+    self::INTERLINGUA             => array('Interlingua'),
+    self::INDIC                   => array('Indic'),
+    self::INDONESIAN              => array('Indonesian'),
+    self::INDO_EUROPEAN           => array('Indo-European'),
+    self::INGUSH                  => array('Ingush'),
+    self::INUPIAQ                 => array('Inupiaq'),
+    self::IRANIAN                 => array('Iranian'),
+    self::IROQUOIAN               => array('Iroquoian'),
+    self::ITALIAN                 => array('Italian'),
+    self::JAVANESE                => array('Javanese'),
+    self::LOJBAN                  => array('Lojban'),
+    self::JAPANESE                => array('Japanese'),
+    self::JUDEO_PERSIAN           => array('Judeo-Persian'),
+    self::JUDEO_ARABIC            => array('Judeo-Arabic'),
+    self::KARA_KALPAK             => array('Kara-Kalpak'),
+    self::KABYLE                  => array('Kabyle'),
+    self::KACHIN                  => array('Kachin', 'Jingpho'),
+    self::KALAALLISUT             => array('Kalaallisut', 'Greenlandic'),
+    self::KAMBA                   => array('Kamba'),
+    self::KANNADA                 => array('Kannada'),
+    self::KAREN                   => array('Karen'),
+    self::KASHMIRI                => array('Kashmiri'),
+    self::KANURI                  => array('Kanuri'),
+    self::KAWI                    => array('Kawi'),
+    self::KAZAKH                  => array('Kazakh'),
+    self::KABARDIAN               => array('Kabardian'),
+    self::KHASI                   => array('Khasi'),
+    self::KHOISAN                 => array('Khoisan'),
+    self::CENTRAL_KHMER           => array('Central Khmer'),
+    self::KHOTANESE               => array('Khotanese', 'Sakan'),
+    self::KIKUYU                  => array('Kikuyu', 'Gikuyu'),
+    self::KINYARWANDA             => array('Kinyarwanda'),
+    self::KIRGHIZ                 => array('Kirghiz', 'Kyrgyz'),
+    self::KIMBUNDU                => array('Kimbundu'),
+    self::KONKANI                 => array('Konkani'),
+    self::KOMI                    => array('Komi'),
+    self::KONGO                   => array('Kongo'),
+    self::KOREAN                  => array('Korean'),
+    self::KOSRAEAN                => array('Kosraean'),
+    self::KPELLE                  => array('Kpelle'),
+    self::KARACHAY_BALKAR         => array('Karachay-Balkar'),
+    self::KARELIAN                => array('Karelian'),
+    self::KRU                     => array('Kru'),
+    self::KURUKH                  => array('Kurukh'),
+    self::KUANYAMA                => array('Kuanyama', 'Kwanyama'),
+    self::KUMYK                   => array('Kumyk'),
+    self::KURDISH                 => array('Kurdish'),
+    self::KUTENAI                 => array('Kutenai'),
+    self::LADINO                  => array('Ladino'),
+    self::LAHNDA                  => array('Lahnda'),
+    self::LAMBA                   => array('Lamba'),
+    self::LAO                     => array('Lao'),
+    self::LATIN                   => array('Latin'),
+    self::LATVIAN                 => array('Latvian'),
+    self::LEZGHIAN                => array('Lezghian'),
+    self::LIMBURGAN               => array('Limburgan', 'Limburger', 'Limburgish'),
+    self::LINGALA                 => array('Lingala'),
+    self::LITHUANIAN              => array('Lithuanian'),
+    self::MONGO                   => array('Mongo'),
+    self::LOZI                    => array('Lozi'),
+    self::LUXEMBOURGISH           => array('Luxembourgish', 'Letzeburgesch'),
+    self::LUBA_LULUA              => array('Luba-Lulua'),
+    self::LUBA_KATANGA            => array('Luba-Katanga'),
+    self::GANDA                   => array('Ganda'),
+    self::LUISENO                 => array('Luiseno'),
+    self::LUNDA                   => array('Lunda'),
+    self::LUO                     => array('Luo'),
+    self::LUSHAI                  => array('Lushai'),
+    self::MACEDONIAN              => array('Macedonian'),
+    self::MADURESE                => array('Madurese'),
+    self::MAGAHI                  => array('Magahi'),
+    self::MARSHALLESE             => array('Marshallese'),
+    self::MAITHILI                => array('Maithili'),
+    self::MAKASAR                 => array('Makasar'),
+    self::MALAYALAM               => array('Malayalam'),
+    self::MANDINGO                => array('Mandingo'),
+    self::MAORI                   => array('Maori'),
+    self::AUSTRONESIAN            => array('Austronesian'),
+    self::MARATHI                 => array('Marathi'),
+    self::MASAI                   => array('Masai'),
+    self::MALAY                   => array('Malay'),
+    self::MOKSHA                  => array('Moksha'),
+    self::MANDAR                  => array('Mandar'),
+    self::MENDE                   => array('Mende'),
+    self::IRISH_MIDDLE            => array('Middle Irish'),
+    self::MIKMAQ                  => array('Mi\'kmaq', 'Micmac'),
+    self::MINANGKABAU             => array('Minangkabau'),
+    self::UNCODED                 => array('Uncoded'),
+    self::MON_KHMER               => array('Mon-Khmer'),
+    self::MALAGASY                => array('Malagasy'),
+    self::MALTESE                 => array('Maltese'),
+    self::MANCHU                  => array('Manchu'),
+    self::MANIPURI                => array('Manipuri'),
+    self::MANOBO                  => array('Manobo'),
+    self::MOHAWK                  => array('Mohawk'),
+    self::MONGOLIAN               => array('Mongolian'),
+    self::MOSSI                   => array('Mossi'),
+    self::MULTIPLE                => array('Multiple'),
+    self::MUNDA                   => array('Munda'),
+    self::CREEK                   => array('Creek'),
+    self::MIRANDESE               => array('Mirandese'),
+    self::MARWARI                 => array('Marwari'),
+    self::MAYAN                   => array('Mayan'),
+    self::ERZYA                   => array('Erzya'),
+    self::NAHUATL                 => array('Nahuatl'),
+    self::AMERICAN_INDIAN_NORTH   => array('North American Indian'),
+    self::NEAPOLITAN              => array('Neapolitan'),
+    self::NAURU                   => array('Nauru'),
+    self::NAVAJO                  => array('Navajo', 'Navaho'),
+    self::NDEBELE_SOUTH           => array('South Ndebele'),
+    self::NDEBELE_NORTH           => array('North Ndebele'),
+    self::NDONGA                  => array('Ndonga'),
+    self::LOW_GERMAN              => array('Low German', 'Low Saxon'),
+    self::NEPALI                  => array('Nepali'),
+    self::NEPAL_BHASA             => array('Nepal Bhasa', 'Newari'),
+    self::NIAS                    => array('Nias'),
+    self::NIGER_KORDOFANIAN       => array('Niger-Kordofanian'),
+    self::NIUEAN                  => array('Niuean'),
+    self::NORWEGIAN_NYNORSK       => array('Norwegian Nynorsk'),
+    self::BOKMAL                  => array('BokmÃ¥l', 'Norwegian BokmÃ¥l'),
+    self::NOGAI                   => array('Nogai'),
+    self::NORSE_OLD               => array('Old Norse'),
+    self::NORWEGIAN               => array('Norwegian'),
+    self::NKO                     => array('N\'Ko'),
+    self::PEDI                    => array('Pedi', 'Sepedi', 'Northern Sotho'),
+    self::NUBIAN                  => array('Nubian'),
+    self::CLASSICAL_NEWARI        => array('Classical Newari', 'Old Newari', 'Classical Nepal Bhasa'),
+    self::CHICHEWA                => array('Chichewa', 'Chewa', 'Nyanja'),
+    self::NYAMWEZI                => array('Nyamwezi'),
+    self::NYANKOLE                => array('Nyankole'),
+    self::NYORO                   => array('Nyoro'),
+    self::NZIMA                   => array('Nzima'),
+    self::OCCITAN                 => array('Occitan'),
+    self::OJIBWA                  => array('Ojibwa'),
+    self::ORIYA                   => array('Oriya'),
+    self::OROMO                   => array('Oromo'),
+    self::OSAGE                   => array('Osage'),
+    self::OSSETIAN                => array('Ossetian', 'Ossetic'),
+    self::OTTOMAN                 => array('Ottoman Turkish'),
+    self::OTOMIAN                 => array('Otomian'),
+    self::PAPUAN                  => array('Papuan'),
+    self::PANGASINAN              => array('Pangasinan'),
+    self::PAHLAVI                 => array('Pahlavi'),
+    self::PAMPANGA                => array('Pampanga', 'Kapampangan'),
+    self::PANJABI                 => array('Panjabi', 'Punjabi'),
+    self::PAPIAMENTO              => array('Papiamento'),
+    self::PALAUAN                 => array('Palauan'),
+    self::PERSIAN_OLD             => array('Old Persian'),
+    self::PHILIPPINE              => array('Philippine'),
+    self::PHOENICIAN              => array('Phoenician'),
+    self::PALI                    => array('Pali'),
+    self::POLISH                  => array('Polish'),
+    self::POHNPEIAN               => array('Pohnpeian'),
+    self::PORTUGUESE              => array('Portuguese'),
+    self::PRAKRIT                 => array('Prakrit'),
+    self::PROVENCAL               => array('Old Provençal', 'Old Occitan'),
+    self::PUSHTO                  => array('Pushto', 'Pashto'),
+    self::QUECHUA                 => array('Quechua'),
+    self::RAJASTHANI              => array('Rajasthani'),
+    self::RAPANUI                 => array('Rapanui'),
+    self::RAROTONGAN              => array('Rarotongan', 'Cook Islands Maori'),
+    self::ROMANCE                 => array('Romance'),
+    self::ROMANSH                 => array('Romansh'),
+    self::ROMANY                  => array('Romany'),
+    self::ROMANIAN                => array('Romanian', 'Moldavian', 'Moldovan'),
+    self::RUNDI                   => array('Rundi'),
+    self::AROMANIAN               => array('Aromanian', 'Arumanian', 'Macedo-Romanian'),
+    self::RUSSIAN                 => array('Russian'),
+    self::SANDAWE                 => array('Sandawe'),
+    self::SANGO                   => array('Sango'),
+    self::YAKUT                   => array('Yakut'),
+    self::AMERICAN_INDIAN_SOUTH   => array('South American Indian'),
+    self::SALISHAN                => array('Salishan'),
+    self::SAMARITAN               => array('Samaritan'),
+    self::SANSKRIT                => array('Sanskrit'),
+    self::SASAK                   => array('Sasak'),
+    self::SANTALI                 => array('Santali'),
+    self::SICILIAN                => array('Sicilian'),
+    self::SCOTS                   => array('Scots'),
+    self::SELKUP                  => array('Selkup'),
+    self::SEMITIC                 => array('Semitic'),
+    self::IRISH_OLD               => array('Old Irish'),
+    self::SIGN                    => array('Sign Language'),
+    self::SHAN                    => array('Shan'),
+    self::SIDAMO                  => array('Sidamo'),
+    self::SINHALA                 => array('Sinhala', 'Sinhalese'),
+    self::SIOUAN                  => array('Siouan'),
+    self::SINO_TIBETAN            => array('Sino-Tibetan'),
+    self::SLAVIC                  => array('Slavic'),
+    self::SLOVAK                  => array('Slovak'),
+    self::SLOVENIAN               => array('Slovenian'),
+    self::SAMI_SOUTHERN           => array('Southern Sami'),
+    self::SAMI_NORTHERN           => array('Northern Sami'),
+    self::SAMI                    => array('Sami'),
+    self::SAMI_LULE               => array('Lule Sami'),
+    self::SAMI_IRARI              => array('Inari Sami'),
+    self::SAMOAN                  => array('Samoan'),
+    self::SAMI_SKOLT              => array('Skolt Sami'),
+    self::SHONA                   => array('Shona'),
+    self::SINDHI                  => array('Sindhi'),
+    self::SONINKE                 => array('Soninke'),
+    self::SOGDIAN                 => array('Sogdian'),
+    self::SOMALI                  => array('Somali'),
+    self::SONGHAI                 => array('Songhai'),
+    self::SOTHO_SOUTHERN          => array('Southern Sotho'),
+    self::SPANISH                 => array('Spanish', 'Castilian'),
+    self::SARDINIAN               => array('Sardinian'),
+    self::SRANAN_TONGO            => array('Sranan Tongo'),
+    self::SERBIAN                 => array('Serbian'),
+    self::SERER                   => array('Serer'),
+    self::NILO_SAHARAN            => array('Nilo-Saharan'),
+    self::SWATI                   => array('Swati'),
+    self::SUKUMA                  => array('Sukuma'),
+    self::SUNDANESE               => array('Sundanese'),
+    self::SUSU                    => array('Susu'),
+    self::SUMERIAN                => array('Sumerian'),
+    self::SWAHILI                 => array('Swahili'),
+    self::SWEDISH                 => array('Swedish'),
+    self::SYRIAC_CLASSICAL        => array('Classical Syriac'),
+    self::SYRIAC                  => array('Syriac'),
+    self::TAHITIAN                => array('Tahitian'),
+    self::TAI                     => array('Tai'),
+    self::TAMIL                   => array('Tamil'),
+    self::TATAR                   => array('Tatar'),
+    self::TELUGU                  => array('Telugu'),
+    self::TIMNE                   => array('Timne'),
+    self::TERENO                  => array('Tereno'),
+    self::TETUM                   => array('Tetum'),
+    self::TAJIK                   => array('Tajik'),
+    self::TAGALOG                 => array('Tagalog'),
+    self::THAI                    => array('Thai'),
+    self::TIGRE                   => array('Tigre'),
+    self::TIGRINYA                => array('Tigrinya'),
+    self::TIV                     => array('Tiv'),
+    self::TOKELAU                 => array('Tokelau'),
+    self::KLINGON                 => array('Klingon', 'tlhIngan-Hol'),
+    self::TLINGIT                 => array('Tlingit'),
+    self::TAMASHEK                => array('Tamashek'),
+    self::TONGA_NYASA             => array('Nyasa Tonga'),
+    self::TONGA_ISLANDS           => array('Tonga Islands Tonga', 'to'),
+    self::TOK_PISIN               => array('Tok Pisin'),
+    self::TSIMSHIAN               => array('Tsimshian'),
+    self::TSWANA                  => array('Tswana'),
+    self::TSONGA                  => array('Tsonga'),
+    self::TURKMEN                 => array('Turkmen'),
+    self::TUMBUKA                 => array('Tumbuka'),
+    self::TUPI                    => array('Tupi'),
+    self::TURKISH                 => array('Turkish'),
+    self::ALTAIC                  => array('Altaic'),
+    self::TUVALU                  => array('Tuvalu'),
+    self::TWI                     => array('Twi'),
+    self::TUVINIAN                => array('Tuvinian'),
+    self::UDMURT                  => array('Udmurt'),
+    self::UGARITIC                => array('Ugaritic'),
+    self::UIGHUR                  => array('Uighur', 'Uyghur'),
+    self::UKRAINIAN               => array('Ukrainian'),
+    self::UMBUNDU                 => array('Umbundu'),
+    self::UNDETERMINED            => array('Undetermined'),
+    self::URDU                    => array('Urdu'),
+    self::UZBEK                   => array('Uzbek'),
+    self::VAI                     => array('Vai'),
+    self::VENDA                   => array('Venda'),
+    self::VIETNAMESE              => array('Vietnamese'),
+    self::VOLAPUK                 => array('Volapük'),
+    self::VOTIC                   => array('Votic'),
+    self::WAKASHAN                => array('Wakashan'),
+    self::WOLAITTA                => array('Wolaitta', 'Wolaytta'),
+    self::WARAY                   => array('Waray'),
+    self::WASHO                   => array('Washo'),
+    self::SORBIAN                 => array('Sorbian'),
+    self::WALLOON                 => array('Walloon'),
+    self::WOLOF                   => array('Wolof'),
+    self::KALMYK                  => array('Kalmyk', 'Oirat'),
+    self::XHOSA                   => array('Xhosa'),
+    self::YAO                     => array('Yao'),
+    self::YAPESE                  => array('Yapese'),
+    self::YIDDISH                 => array('Yiddish'),
+    self::YORUBA                  => array('Yoruba'),
+    self::YUPIK                   => array('Yupik'),
+    self::ZAPOTEC                 => array('Zapotec'),
+    self::BLISSYMBOLS             => array('Blissymbols', 'Blissymbolics', 'Bliss'),
+    self::ZENAGA                  => array('Zenaga'),
+    self::MOROCCAN_TAMAZIGHT      => array('Standard Moroccan Tamazight'),
+    self::ZHUANG                  => array('Zhuang', 'Chuang'),
+    self::ZANDE                   => array('Zande'),
+    self::ZULU                    => array('Zulu'),
+    self::ZUNI                    => array('Zuni'),
+    self::NOT_APPLICABLE          => array('No Linguistic Content', 'Not Applicable'),
+    self::ZAZA                    => array('Zaza', 'Dimili', 'Dimli', 'Kirdki', 'Kirmanjki', 'Zazaki'),
+  );
+
+  private static $s_ids = array(
+    'en-us' => self::ENGLISH_US,
+    'en'    => self::ENGLISH,
+    'eng'   => self::ENGLISH,
+    'en-ca' => self::ENGLISH_CA,
+    'en-gb' => self::ENGLISH_GB,
+    'aar'   => self::AFAR,
+    'aa'    => self::AFAR,
+    'abk'   => self::ABKHAZIAN,
+    'ab'    => self::ABKHAZIAN,
+    'ace'   => self::ACHINESE,
+    'ach'   => self::ACOLI,
+    'ada'   => self::ADANGME,
+    'ady'   => self::ADYGHE,
+    'afa'   => self::AFRO_ASIATIC,
+    'afh'   => self::AFRIHILI,
+    'afr'   => self::AFRIKAANS,
+    'af'    => self::AFRIKAANS,
+    'ain'   => self::AINU,
+    'aka'   => self::AKAN,
+    'ak'    => self::AKAN,
+    'akk'   => self::AKKADIAN,
+    'alb'   => self::ALBANIAN,
+    'sqi'   => self::ALBANIAN,
+    'sq'    => self::ALBANIAN,
+    'ale'   => self::ALEUT,
+    'alg'   => self::ALGONQUIAN,
+    'alt'   => self::SOUTHERN_ALTAI,
+    'amh'   => self::AMHARIC,
+    'am'    => self::AMHARIC,
+    'ang'   => self::ENGLISH_OLD,
+    'anp'   => self::ANGIKA,
+    'apa'   => self::APACHE,
+    'ara'   => self::ARABIC,
+    'arc'   => self::ARAMAIC,
+    'arg'   => self::ARAGONESE,
+    'arm'   => self::ARMENIAN,
+    'hye'   => self::ARMENIAN,
+    'hy'    => self::ARMENIAN,
+    'am'    => self::MAPUDUNGUN,
+    'arp'   => self::ARAPAHO,
+    'art'   => self::ARTIFICIAL,
+    'arw'   => self::ARAWAK,
+    'asm'   => self::ASSAMESE,
+    'as'    => self::ASSAMESE,
+    'ast'   => self::ASTURIAN,
+    'ath'   => self::ATHAPASCAN,
+    'aus'   => self::AUSTRALIAN,
+    'ava'   => self::AVARIC,
+    'av'    => self::AVARIC,
+    'ave'   => self::AVESTAN,
+    'ae'    => self::AVESTAN,
+    'awa'   => self::AWADHI,
+    'aym'   => self::AYMARA,
+    'ay'    => self::AYMARA,
+    'aze'   => self::AZERBAIJANI,
+    'az'    => self::AZERBAIJANI,
+    'bad'   => self::BANDA,
+    'bai'   => self::BAMILEKE,
+    'bak'   => self::BASHKIR,
+    'ba'    => self::BASHKIR,
+    'bal'   => self::BALUCHI,
+    'bam'   => self::BAMBARA,
+    'ba'    => self::BAMBARA,
+    'ban'   => self::BALINESE,
+    'baq'   => self::BASQUE,
+    'eus'   => self::BASQUE,
+    'eu'    => self::BASQUE,
+    'bas'   => self::BASA,
+    'bat'   => self::BALTIC,
+    'bej'   => self::BEJA,
+    'bel'   => self::BELARUSIAN,
+    'be'    => self::BELARUSIAN,
+    'bem'   => self::BEMBA,
+    'ben'   => self::BENGALI,
+    'bn'    => self::BENGALI,
+    'ber'   => self::BERBER,
+    'bho'   => self::BHOJPURI,
+    'bih'   => self::BIHARI,
+    'bh'    => self::BIHARI,
+    'bik'   => self::BIKOL,
+    'bin'   => self::BINI,
+    'bis'   => self::BISLAMA,
+    'bi'    => self::BISLAMA,
+    'bla'   => self::SIKSIKA,
+    'bnt'   => self::BANTU,
+    'tib'   => self::TIBETAN,
+    'bod'   => self::TIBETAN,
+    'bo'    => self::TIBETAN,
+    'bos'   => self::BOSNIAN,
+    'bs'    => self::BOSNIAN,
+    'bra'   => self::BRAJ,
+    'bre'   => self::BRETON,
+    'btk'   => self::BATAK,
+    'bua'   => self::BURIAT,
+    'bug'   => self::BUGINESE,
+    'bul'   => self::BULGARIAN,
+    'bur'   => self::BURMESE,
+    'mya'   => self::BURMESE,
+    'my'    => self::BURMESE,
+    'byn'   => self::BLIN,
+    'cad'   => self::CADDO,
+    'cai'   => self::AMERICAN_INDIAN_CENTRAL,
+    'car'   => self::GALIBI_CARIB,
+    'cat'   => self::CATALAN,
+    'ca'    => self::CATALAN,
+    'cau'   => self::CAUCASIAN,
+    'ceb'   => self::CEBUANO,
+    'cel'   => self::CELTIC,
+    'cze'   => self::CZECH,
+    'ces'   => self::CZECH,
+    'cs'    => self::CZECH,
+    'cha'   => self::CHAMORRO,
+    'ch'    => self::CHAMORRO,
+    'chb'   => self::CHIBCHA,
+    'che'   => self::CHECHEN,
+    'ce'    => self::CHECHEN,
+    'chg'   => self::CHAGATAI,
+    'chi'   => self::CHINESE,
+    'zho'   => self::CHINESE,
+    'zh'    => self::CHINESE,
+    'chk'   => self::CHUUKESE,
+    'chm'   => self::MARI,
+    'chn'   => self::CHINOOK_JARGON,
+    'cho'   => self::CHOCTAW,
+    'chp'   => self::CHIPEWYAN,
+    'chr'   => self::CHEROKEE,
+    'chu'   => self::CHURCH_SLAVIC,
+    'cu'    => self::SLAVIC,
+    'chv'   => self::CHUVASH,
+    'cv'    => self::CHUVASH,
+    'chy'   => self::CHEYENNE,
+    'cmc'   => self::CHAMIC,
+    'cop'   => self::COPTIC,
+    'cor'   => self::CORNISH,
+    'cos'   => self::CORSICAN,
+    'co'    => self::CORSICAN,
+    'cpe'   => self::CREOLES_ENGLISH,
+    'cpf'   => self::CREOLES_FRENCH,
+    'cpp'   => self::CREOLES_PORTUGESE,
+    'cre'   => self::CREE,
+    'cr'    => self::CREE,
+    'crh'   => self::CRIMEAN_TATAR,
+    'crp'   => self::CREOLES,
+    'csb'   => self::KASHUBIAN,
+    'cus'   => self::CUSHITIC,
+    'wel'   => self::WELSH,
+    'cym'   => self::WELSH,
+    'cy'    => self::WELSH,
+    'dak'   => self::DAKOTA,
+    'dan'   => self::DANISH,
+    'da'    => self::DANISH,
+    'dar'   => self::DARGWA,
+    'day'   => self::LAND_DAYAK,
+    'del'   => self::DELAWARE,
+    'den'   => self::SLAVE,
+    'ger'   => self::GERMAN,
+    'deu'   => self::GERMAN,
+    'de'    => self::GERMAN,
+    'dgr'   => self::DOGRIB,
+    'din'   => self::DINKA,
+    'div'   => self::DIVEHI,
+    'dv'    => self::DIVEHI,
+    'doi'   => self::DOGRI,
+    'dra'   => self::DRAVIDIAN,
+    'dsb'   => self::LOWER_SORBIAN,
+    'dua'   => self::DUALA,
+    'dum'   => self::DUTCH_MIDDLE,
+    'dut'   => self::DUTCH_FLEMISH,
+    'nld'   => self::DUTCH_FLEMISH,
+    'nl'    => self::DUTCH_FLEMISH,
+    'dyu'   => self::DYULA,
+    'dzo'   => self::DZONGKHA,
+    'dz'    => self::DZONGKHA,
+    'efi'   => self::EFIK,
+    'egy'   => self::EGYPTIAN,
+    'eka'   => self::EKAJUK,
+    'gre'   => self::GREEK_MODERN,
+    'ell'   => self::GREEK_MODERN,
+    'el'    => self::GREEK_MODERN,
+    'elx'   => self::ELAMITE,
+    'enm'   => self::ENGLISH_MIDDLE,
+    'epo'   => self::ESPERANTO,
+    'eo'    => self::ESPERANTO,
+    'est'   => self::ESTONIAN,
+    'et'    => self::ESTONIAN,
+    'ewe'   => self::EWE,
+    'ew'    => self::EWE,
+    'ewo'   => self::EWONDO,
+    'fan'   => self::FANG,
+    'fao'   => self::FAROESE,
+    'fo'    => self::FAROESE,
+    'per'   => self::PERSIAN,
+    'fas'   => self::PERSIAN,
+    'fa'    => self::PERSIAN,
+    'fat'   => self::FANTI,
+    'fij'   => self::FIJIAN,
+    'fj'    => self::FIJIAN,
+    'fil'   => self::FILIPINO,
+    'fin'   => self::FINNISH,
+    'fi'    => self::FINNISH,
+    'fiu'   => self::FINNO_UGRIAN,
+    'fon'   => self::FON,
+    'fre'   => self::FRENCH,
+    'fra'   => self::FRENCH,
+    'fr'    => self::FRENCH,
+    'frm'   => self::FRENCH_MIDDLE,
+    'fro'   => self::FRENCH_OLD,
+    'frr'   => self::FRISIAN_NORTHERN,
+    'frs'   => self::FRISIAN_EASTERN,
+    'fry'   => self::FRISIAN_WESTERN,
+    'fy'    => self::FRISIAN_WESTERN,
+    'ful'   => self::FULAH,
+    'ff'    => self::FULAH,
+    'fur'   => self::FRIULIAN,
+    'gaa'   => self::GA,
+    'gay'   => self::GAYO,
+    'gba'   => self::GBAYA,
+    'gem'   => self::GERMANIC,
+    'geo'   => self::GEORGIAN,
+    'kat'   => self::GEORGIAN,
+    'ka'    => self::GEORGIAN,
+    'gez'   => self::GEEZ,
+    'gil'   => self::GILBERTESE,
+    'gla'   => self::GAELIC,
+    'ga'    => self::GAELIC,
+    'gle'   => self::IRISH,
+    'ga'    => self::IRISH,
+    'glg'   => self::GALICIAN,
+    'gl'    => self::GALICIAN,
+    'glv'   => self::MANX,
+    'gv'    => self::MANX,
+    'gmh'   => self::GERMAN_MIDDLE_HIGH,
+    'goh'   => self::GERMAN_OLD_HIGH,
+    'gon'   => self::GONDI,
+    'gor'   => self::GORONTALO,
+    'got'   => self::GOTHIC,
+    'grb'   => self::GREBO,
+    'grc'   => self::GREEK_ANCIENT,
+    'grm'   => self::GUARANI,
+    'gn'    => self::GUARANI,
+    'gsw'   => self::GERMAN_SWISS,
+    'guj'   => self::GUJARATI,
+    'gu'    => self::GUJARATI,
+    'gwi'   => self::GWICHIN,
+    'hai'   => self::HAIDA,
+    'hat'   => self::HAITIAN,
+    'ht'    => self::HAITIAN,
+    'hau'   => self::HAUSA,
+    'ha'    => self::HAUSA,
+    'haw'   => self::HAWAIIAN,
+    'heb'   => self::HEBREW,
+    'he'    => self::HEBREW,
+    'her'   => self::HERERO,
+    'hz'    => self::HERERO,
+    'hil'   => self::HILIGAYNON,
+    'him'   => self::HIMACHALI,
+    'hin'   => self::HINDI,
+    'hi'    => self::HINDI,
+    'hit'   => self::HITTITE,
+    'hmn'   => self::HMONG,
+    'hmo'   => self::HIRI_MOTU,
+    'ho'    => self::HIRI_MOTU,
+    'hrv'   => self::CROATIAN,
+    'hr'    => self::CROATIAN,
+    'hsb'   => self::SORBIAN_UPPER,
+    'hun'   => self::HUNGARIAN,
+    'hu'    => self::HUNGARIAN,
+    'hup'   => self::HUPA,
+    'iba'   => self::IBAN,
+    'ibo'   => self::IGBO,
+    'ig'    => self::IGBO,
+    'ice'   => self::ICELANDIC,
+    'isl'   => self::ICELANDIC,
+    'is'    => self::ICELANDIC,
+    'ido'   => self::IDO,
+    'io'    => self::IDO,
+    'iii'   => self::SICHUAN_YI,
+    'ii'    => self::SICHUAN_YI,
+    'ijo'   => self::IJO,
+    'iku'   => self::INUKTITUT,
+    'iu'    => self::INUKTITUT,
+    'ile'   => self::INTERLINGUE,
+    'ie'    => self::INTERLINGUE,
+    'ilo'   => self::ILOKO,
+    'ina'   => self::INTERLINGUA,
+    'ia'    => self::INTERLINGUA,
+    'inc'   => self::INDIC,
+    'ind'   => self::INDONESIAN,
+    'id'    => self::INDONESIAN,
+    'ine'   => self::INDO_EUROPEAN,
+    'inh'   => self::INGUSH,
+    'ipk'   => self::INUPIAQ,
+    'ik'    => self::INUPIAQ,
+    'ira'   => self::IRANIAN,
+    'iro'   => self::IROQUOIAN,
+    'ita'   => self::ITALIAN,
+    'it'    => self::ITALIAN,
+    'jav'   => self::JAVANESE,
+    'jv'    => self::JAVANESE,
+    'jbo'   => self::LOJBAN,
+    'jpn'   => self::JAPANESE,
+    'ja'    => self::JAPANESE,
+    'jpr'   => self::JUDEO_PERSIAN,
+    'jrb'   => self::JUDEO_ARABIC,
+    'kaa'   => self::KARA_KALPAK,
+    'kab'   => self::KABYLE,
+    'kac'   => self::KACHIN,
+    'kal'   => self::KALAALLISUT,
+    'kl'    => self::KALAALLISUT,
+    'kam'   => self::KAMBA,
+    'kan'   => self::KANNADA,
+    'kn'    => self::KANNADA,
+    'kar'   => self::KAREN,
+    'kas'   => self::KASHMIRI,
+    'ks'    => self::KASHMIRI,
+    'kau'   => self::KANURI,
+    'kk'    => self::KANURI,
+    'kaw'   => self::KAWI,
+    'kaz'   => self::KAZAKH,
+    'kz'    => self::KAZAKH,
+    'kbd'   => self::KABARDIAN,
+    'kha'   => self::KHASI,
+    'khi'   => self::KHOISAN,
+    'khm'   => self::CENTRAL_KHMER,
+    'km'    => self::CENTRAL_KHMER,
+    'kho'   => self::KHOTANESE,
+    'kik'   => self::KIKUYU,
+    'ki'    => self::KIKUYU,
+    'kin'   => self::KINYARWANDA,
+    'rw'    => self::KINYARWANDA,
+    'kir'   => self::KIRGHIZ,
+    'ky'    => self::KIRGHIZ,
+    'kmb'   => self::KIMBUNDU,
+    'kok'   => self::KONKANI,
+    'kom'   => self::KOMI,
+    'kv'    => self::KOMI,
+    'kon'   => self::KONGO,
+    'kg'    => self::KONGO,
+    'kor'   => self::KOREAN,
+    'ko'    => self::KOREAN,
+    'kos'   => self::KOSRAEAN,
+    'kpe'   => self::KPELLE,
+    'krc'   => self::KARACHAY_BALKAR,
+    'krl'   => self::KARELIAN,
+    'kro'   => self::KRU,
+    'kru'   => self::KURUKH,
+    'kua'   => self::KUANYAMA,
+    'kj'    => self::KUANYAMA,
+    'kum'   => self::KUMYK,
+    'kur'   => self::KURDISH,
+    'ku'    => self::KURDISH,
+    'kut'   => self::KUTENAI,
+    'lad'   => self::LADINO,
+    'lah'   => self::LAHNDA,
+    'lam'   => self::LAMBA,
+    'lao'   => self::LAO,
+    'lo'    => self::LAO,
+    'lat'   => self::LATIN,
+    'la'    => self::LATIN,
+    'lav'   => self::LATVIAN,
+    'la'    => self::LATVIAN,
+    'lez'   => self::LEZGHIAN,
+    'lv'    => self::LEZGHIAN,
+    'lim'   => self::LIMBURGAN,
+    'li'    => self::LIMBURGAN,
+    'lin'   => self::LINGALA,
+    'li'    => self::LINGALA,
+    'lit'   => self::LITHUANIAN,
+    'lt'    => self::LITHUANIAN,
+    'lol'   => self::MONGO,
+    'loz'   => self::LOZI,
+    'ltz'   => self::LUXEMBOURGISH,
+    'lb'    => self::LUXEMBOURGISH,
+    'lua'   => self::LUBA_LULUA,
+    'lub'   => self::LUBA_KATANGA,
+    'lu'    => self::LUBA_KATANGA,
+    'lug'   => self::GANDA,
+    'lg'    => self::GANDA,
+    'lui'   => self::LUISENO,
+    'lun'   => self::LUNDA,
+    'luo'   => self::LUO,
+    'lus'   => self::LUSHAI,
+    'mac'   => self::MACEDONIAN,
+    'mkd'   => self::MACEDONIAN,
+    'mk'    => self::MACEDONIAN,
+    'mad'   => self::MADURESE,
+    'mag'   => self::MAGAHI,
+    'mah'   => self::MARSHALLESE,
+    'mh'    => self::MARSHALLESE,
+    'mai'   => self::MAITHILI,
+    'mak'   => self::MAKASAR,
+    'mal'   => self::MALAYALAM,
+    'ml'    => self::MALAYALAM,
+    'man'   => self::MANDINGO,
+    'mao'   => self::MAORI,
+    'mri'   => self::MAORI,
+    'mi'    => self::MAORI,
+    'map'   => self::AUSTRONESIAN,
+    'mar'   => self::MARATHI,
+    'mr'    => self::MARATHI,
+    'mas'   => self::MASAI,
+    'may'   => self::MALAY,
+    'msa'   => self::MALAY,
+    'ms'    => self::MALAY,
+    'mdf'   => self::MOKSHA,
+    'mdr'   => self::MANDAR,
+    'men'   => self::MENDE,
+    'mga'   => self::IRISH_MIDDLE,
+    'mic'   => self::MIKMAQ,
+    'min'   => self::MINANGKABAU,
+    'mis'   => self::UNCODED,
+    'mkh'   => self::MON_KHMER,
+    'mlg'   => self::MALAGASY,
+    'mg'    => self::MALAGASY,
+    'mlt'   => self::MALTESE,
+    'mt'    => self::MALTESE,
+    'mnc'   => self::MANCHU,
+    'mni'   => self::MANIPURI,
+    'mno'   => self::MANOBO,
+    'moh'   => self::MOHAWK,
+    'mon'   => self::MONGOLIAN,
+    'mn'    => self::MONGOLIAN,
+    'mos'   => self::MOSSI,
+    'mul'   => self::MULTIPLE,
+    'mun'   => self::MUNDA,
+    'mus'   => self::CREEK,
+    'mwl'   => self::MIRANDESE,
+    'mwr'   => self::MARWARI,
+    'myn'   => self::MAYAN,
+    'myv'   => self::ERZYA,
+    'nah'   => self::NAHUATL,
+    'nai'   => self::AMERICAN_INDIAN_NORTH,
+    'nap'   => self::NEAPOLITAN,
+    'nau'   => self::NAURU,
+    'na'    => self::NAURU,
+    'nav'   => self::NAVAJO,
+    'nv'    => self::NAVAJO,
+    'nbl'   => self::NDEBELE_SOUTH,
+    'nr'    => self::NDEBELE_SOUTH,
+    'nde'   => self::NDEBELE_NORTH,
+    'nd'    => self::NDEBELE_NORTH,
+    'ndo'   => self::NDONGA,
+    'ng'    => self::NDONGA,
+    'nds'   => self::LOW_GERMAN,
+    'nep'   => self::NEPALI,
+    'ne'    => self::NEPALI,
+    'new'   => self::NEPAL_BHASA,
+    'nia'   => self::NIAS,
+    'nic'   => self::NIGER_KORDOFANIAN,
+    'niu'   => self::NIUEAN,
+    'nno'   => self::NORWEGIAN_NYNORSK,
+    'nn'    => self::NORWEGIAN_NYNORSK,
+    'nob'   => self::BOKMAL,
+    'nb'    => self::BOKMAL,
+    'nog'   => self::NOGAI,
+    'non'   => self::NORSE_OLD,
+    'nor'   => self::NORWEGIAN,
+    'no'    => self::NORWEGIAN,
+    'nqo'   => self::NKO,
+    'nso'   => self::PEDI,
+    'nub'   => self::NUBIAN,
+    'nwc'   => self::CLASSICAL_NEWARI,
+    'nya'   => self::CHICHEWA,
+    'nym'   => self::NYAMWEZI,
+    'nyn'   => self::NYANKOLE,
+    'nyo'   => self::NYORO,
+    'nzi'   => self::NZIMA,
+    'oci'   => self::OCCITAN,
+    'oc'    => self::OCCITAN,
+    'oji'   => self::OJIBWA,
+    'oj'    => self::OJIBWA,
+    'ori'   => self::ORIYA,
+    'or'    => self::ORIYA,
+    'orm'   => self::OROMO,
+    'om'    => self::OROMO,
+    'osa'   => self::OSAGE,
+    'oss'   => self::OSSETIAN,
+    'os'    => self::OSSETIAN,
+    'ota'   => self::OTTOMAN,
+    'oto'   => self::OTOMIAN,
+    'paa'   => self::PAPUAN,
+    'pag'   => self::PANGASINAN,
+    'pal'   => self::PAHLAVI,
+    'pam'   => self::PAMPANGA,
+    'pan'   => self::PANJABI,
+    'pa'    => self::PANJABI,
+    'pap'   => self::PAPIAMENTO,
+    'pau'   => self::PALAUAN,
+    'peo'   => self::PERSIAN_OLD,
+    'phi'   => self::PHILIPPINE,
+    'phn'   => self::PHOENICIAN,
+    'pli'   => self::PALI,
+    'pi'    => self::PALI,
+    'pol'   => self::POLISH,
+    'pl'    => self::POLISH,
+    'pon'   => self::POHNPEIAN,
+    'por'   => self::PORTUGUESE,
+    'pt'    => self::PORTUGUESE,
+    'pra'   => self::PRAKRIT,
+    'pro'   => self::PROVENCAL,
+    'pus'   => self::PUSHTO,
+    'ps'    => self::PUSHTO,
+    'que'   => self::QUECHUA,
+    'qu'    => self::QUECHUA,
+    'raj'   => self::RAJASTHANI,
+    'rap'   => self::RAPANUI,
+    'rar'   => self::RAROTONGAN,
+    'roa'   => self::ROMANCE,
+    'roh'   => self::ROMANSH,
+    'rm'    => self::ROMANSH,
+    'rom'   => self::ROMANY,
+    'rum'   => self::ROMANIAN,
+    'ron'   => self::ROMANIAN,
+    'ro'    => self::ROMANIAN,
+    'run'   => self::RUNDI,
+    'rn'    => self::RUNDI,
+    'rup'   => self::AROMANIAN,
+    'rus'   => self::RUSSIAN,
+    'ru'    => self::RUSSIAN,
+    'sad'   => self::SANDAWE,
+    'sag'   => self::SANGO,
+    'sg'    => self::SANGO,
+    'sah'   => self::YAKUT,
+    'sai'   => self::AMERICAN_INDIAN_SOUTH,
+    'sal'   => self::SALISHAN,
+    'sam'   => self::SAMARITAN,
+    'san'   => self::SANSKRIT,
+    'sa'    => self::SANSKRIT,
+    'sas'   => self::SASAK,
+    'sat'   => self::SANTALI,
+    'scn'   => self::SICILIAN,
+    'sco'   => self::SCOTS,
+    'sel'   => self::SELKUP,
+    'sem'   => self::SEMITIC,
+    'sga'   => self::IRISH_OLD,
+    'sgn'   => self::SIGN,
+    'shn'   => self::SHAN,
+    'sid'   => self::SIDAMO,
+    'sin'   => self::SINHALA,
+    'si'    => self::SINHALA,
+    'sio'   => self::SIOUAN,
+    'sit'   => self::SINO_TIBETAN,
+    'sla'   => self::SLAVIC,
+    'slo'   => self::SLOVAK,
+    'slk'   => self::SLOVAK,
+    'sk'    => self::SLOVAK,
+    'slv'   => self::SLOVENIAN,
+    'sl'    => self::SLOVENIAN,
+    'sma'   => self::SAMI_SOUTHERN,
+    'sme'   => self::SAMI_NORTHERN,
+    'se'    => self::SAMI_NORTHERN,
+    'smi'   => self::SAMI,
+    'smj'   => self::SAMI_LULE,
+    'smn'   => self::SAMI_IRARI,
+    'smo'   => self::SAMOAN,
+    'sm'    => self::SAMOAN,
+    'sms'   => self::SAMI_SKOLT,
+    'sna'   => self::SHONA,
+    'sn'    => self::SHONA,
+    'snd'   => self::SINDHI,
+    'sd'    => self::SINDHI,
+    'snk'   => self::SONINKE,
+    'sog'   => self::SOGDIAN,
+    'som'   => self::SOMALI,
+    'so'    => self::SOMALI,
+    'son'   => self::SONGHAI,
+    'sot'   => self::SOTHO_SOUTHERN,
+    'st'    => self::SOTHO_SOUTHERN,
+    'spa'   => self::SPANISH,
+    'es'    => self::SPANISH,
+    'srd'   => self::SARDINIAN,
+    'sc'    => self::SARDINIAN,
+    'sm'    => self::SRANAN_TONGO,
+    'srp'   => self::SERBIAN,
+    'sr'    => self::SERBIAN,
+    'srr'   => self::SERER,
+    'ssa'   => self::NILO_SAHARAN,
+    'ssw'   => self::SWATI,
+    'ss'    => self::SWATI,
+    'suk'   => self::SUKUMA,
+    'sun'   => self::SUNDANESE,
+    'su'    => self::SUNDANESE,
+    'sus'   => self::SUSU,
+    'sux'   => self::SUMERIAN,
+    'swa'   => self::SWAHILI,
+    'sw'    => self::SWAHILI,
+    'swe'   => self::SWEDISH,
+    'sv'    => self::SWEDISH,
+    'syc'   => self::SYRIAC_CLASSICAL,
+    'syr'   => self::SYRIAC,
+    'tah'   => self::TAHITIAN,
+    'ty'    => self::TAHITIAN,
+    'tai'   => self::TAI,
+    'tam'   => self::TAMIL,
+    'ta'    => self::TAMIL,
+    'tat'   => self::TATAR,
+    'tt'    => self::TATAR,
+    'tel'   => self::TELUGU,
+    'te'    => self::TELUGU,
+    'tem'   => self::TIMNE,
+    'ter'   => self::TERENO,
+    'tet'   => self::TETUM,
+    'tgk'   => self::TAJIK,
+    'tg'    => self::TAJIK,
+    'tgl'   => self::TAGALOG,
+    'tl'    => self::TAGALOG,
+    'tha'   => self::THAI,
+    'th'    => self::THAI,
+    'tig'   => self::TIGRE,
+    'tir'   => self::TIGRINYA,
+    'ti'    => self::TIGRINYA,
+    'tiv'   => self::TIV,
+    'tkl'   => self::TOKELAU,
+    'tlh'   => self::KLINGON,
+    'tli'   => self::TLINGIT,
+    'tmh'   => self::TAMASHEK,
+    'tog'   => self::TONGA_NYASA,
+    'ton'   => self::TONGA_ISLANDS,
+    'to'    => self::TONGA_ISLANDS,
+    'tpi'   => self::TOK_PISIN,
+    'tsi'   => self::TSIMSHIAN,
+    'tsn'   => self::TSWANA,
+    'tn'    => self::TSWANA,
+    'tso'   => self::TSONGA,
+    'ts'    => self::TSONGA,
+    'tuk'   => self::TURKMEN,
+    'tk'    => self::TURKMEN,
+    'tum'   => self::TUMBUKA,
+    'tup'   => self::TUPI,
+    'tur'   => self::TURKISH,
+    'tr'    => self::TURKISH,
+    'tut'   => self::ALTAIC,
+    'tvl'   => self::TUVALU,
+    'twi'   => self::TWI,
+    'tw'    => self::TWI,
+    'tyv'   => self::TUVINIAN,
+    'udm'   => self::UDMURT,
+    'uga'   => self::UGARITIC,
+    'uig'   => self::UIGHUR,
+    'ug'    => self::UIGHUR,
+    'ukr'   => self::UKRAINIAN,
+    'uk'    => self::UKRAINIAN,
+    'umb'   => self::UMBUNDU,
+    'und'   => self::UNDETERMINED,
+    'urd'   => self::URDU,
+    'ur'    => self::URDU,
+    'uzb'   => self::UZBEK,
+    'uz'    => self::UZBEK,
+    'vai'   => self::VAI,
+    'ven'   => self::VENDA,
+    've'    => self::VENDA,
+    'vie'   => self::VIETNAMESE,
+    'vi'    => self::VIETNAMESE,
+    'vol'   => self::VOLAPUK,
+    'vo'    => self::VOLAPUK,
+    'vot'   => self::VOTIC,
+    'wak'   => self::WAKASHAN,
+    'wal'   => self::WOLAITTA,
+    'war'   => self::WARAY,
+    'was'   => self::WASHO,
+    'wen'   => self::SORBIAN,
+    'wln'   => self::WALLOON,
+    'wa'    => self::WALLOON,
+    'wol'   => self::WOLOF,
+    'wo'    => self::WOLOF,
+    'xal'   => self::KALMYK,
+    'xho'   => self::XHOSA,
+    'xh'    => self::XHOSA,
+    'yao'   => self::YAO,
+    'yap'   => self::YAPESE,
+    'yid'   => self::YIDDISH,
+    'yi'    => self::YIDDISH,
+    'yor'   => self::YORUBA,
+    'yo'    => self::YORUBA,
+    'ypk'   => self::YUPIK,
+    'zap'   => self::ZAPOTEC,
+    'zbl'   => self::BLISSYMBOLS,
+    'zen'   => self::ZENAGA,
+    'zgh'   => self::MOROCCAN_TAMAZIGHT,
+    'zha'   => self::ZHUANG,
+    'za'    => self::ZHUANG,
+    'znd'   => self::ZANDE,
+    'zul'   => self::ZULU,
+    'zu'    => self::ZULU,
+    'zun'   => self::ZUNI,
+    'zxx'   => self::NOT_APPLICABLE,
+    'zza'   => self::ZAZA,
+  );
+
+
+  /**
+   * Get the language names associated with the id.
+   *
+   * @param int $id
+   *   The id of the names to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of names or FALSE on error.
+   *   FALSE without the error flag means that there are no names associated with the given id.
+   */
+  static function s_get_names_by_id($id) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($id, self::$s_names)) {
+      return c_base_return_array::s_new(self::$s_names[$id]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the language names associated with the alias.
+   *
+   * @param string $alias
+   *   The id of the names to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of names or FALSE on error.
+   *   FALSE without the error flag means that there are no names associated with the given alias.
+   */
+  static function s_get_names_by_alias($alias) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($id, self::$s_aliases)) {
+      return c_base_return_array::s_new(self::$s_aliases[$id]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the id associated with the language name.
+   *
+   * @param string $name
+   *   The string associated with the id
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The numeric id or FALSE on error.
+   *   FALSE without the error flag means that there are no ids associated with the given name.
+   */
+  static function s_get_id_by_name($name) {
+    if (!is_string($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($name, self::$s_ids)) {
+      return c_base_return_int::s_new(self::$s_ids[$name]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the id associated with the language name.
+   *
+   * @param string $name
+   *   The string associated with the id
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The numeric id or FALSE on error.
+   *   FALSE without the error flag means that there are no ids associated with the given name.
+   */
+  static function s_get_id_by_alias($alias) {
+    if (!is_string($alias)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($alias, self::$s_ids)) {
+      return c_base_return_int::s_new(self::$s_ids[$alias]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the language aliases associated with the id.
+   *
+   * @param int $id
+   *   The id of the aliases to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of aliases or FALSE on error.
+   *   FALSE without the error flag means that there are no aliases associated with the given id.
+   */
+  static function s_get_aliases_by_id($id) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($id, self::$s_aliases)) {
+      return c_base_return_array::s_new(self::$s_aliases[$id]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the language aliases associated with the name.
+   *
+   * @param string $name
+   *   The language name of the aliases to return.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of aliases or FALSE on error.
+   *   FALSE without the error flag means that there are no aliases associated with the given name.
+   */
+  static function s_get_aliases_by_name($name) {
+    if (!is_string($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (array_key_exists($name, self::$s_aliases)) {
+      return c_base_return_array::s_new(self::$s_aliases[$name]);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Get the id of the language considered to be default by the implementing class.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the default language.
+   *   FALSE without the error flag means that there are no languages assigned as default.
+   */
+  static function s_get_default_id() {
+    return c_base_return_int::s_new(self::ENGLISH_US);
+  }
+
+  /**
+   * Get the name of the language considered to be default by the implementing class.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   A string representing the default language.
+   *   FALSE without the error flag means that there are no languages assigned as default.
+   */
+  static function s_get_default_name() {
+    return c_base_return_string::s_new($this->s_aliases[self::ENGLISH_US]);
+  }
+}
diff --git a/common/base/classes/base_ldap.php b/common/base/classes/base_ldap.php
new file mode 100644 (file)
index 0000000..3a16fb2
--- /dev/null
@@ -0,0 +1,945 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing ldap connections.
+ *
+ * This is initially designed just to select/read from the ldap and not meant to modify or manage ldap databases.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+/**
+ * A class for managing ldap connections.
+ */
+class c_base_ldap {
+  private $ldap;
+  private $name;
+
+  private $bind_name;
+  private $bind_password;
+
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->ldap = NULL;
+    $this->name = NULL;
+
+    $this->bind_name = NULL;
+    $this->bind_password = NULL;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->ldap);
+    unset($this->name);
+
+    unset($this->bind_name);
+    unset($this->bind_password);
+  }
+
+  /**
+   * Assigns the host name url of the server to connect to.
+   *
+   * @param string $name
+   *   The host name url, such as: ldaps://ldap.example.com/ .
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_name($name) {
+    if (!is_string($name) || empty($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    // sanitize the name string.
+    $parsed = parse_url($name, PHP_URL_HOST);
+    if ($parsed === FALSE) {
+      unset($parsed);
+      return c_base_return_error::s_false();
+    }
+
+    $this->name = preg_replace('/(^\s+)|(\s+$)/us', '', $parsed);
+    unset($parsed);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored ldap host name url.
+   *
+   * @return c_base_return_string
+   *   The user name string.
+   */
+  public function get_name() {
+    return c_base_return_string::s_new($this->name);
+  }
+
+  /**
+   * Assigns the bind name used to connect to the ldap server.
+   *
+   * @param string $name
+   *   The bind name. Often referred to as the bind_rdn.
+   *   Set to NULL to disble bind username.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_bind_name($name) {
+    if (!is_null($name) && (!is_string($name) || empty($name))) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->bind_name = $name;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the bind user name.
+   *
+   * @return c_base_return_string
+   *   The bind name. Often referred to as the bind_rdn.
+   */
+  public function get_bind_name() {
+    return c_base_return_string::s_new($this->bind_name);
+  }
+
+  /**
+   * Assigns the bind password used to connect to the ldap server.
+   *
+   * @param string|null $password
+   *   The bind password.
+   *   Set to NULL to disble bind password.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_bind_password($password) {
+    if (!is_null($password) && (!is_string($password) || empty($password))) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->bind_password = $password;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the bind password.
+   *
+   * @return c_base_return_string
+   *   The password string.
+   */
+  public function get_bind_password() {
+    return c_base_return_string::s_new($this->bind_password);
+  }
+
+  /**
+   * Binds/Connects to the ldap server.
+   *
+   * This performs both ldap_connect() and ldap_bind().
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: ldap_connect()
+   * @see: ldap_bind()
+   * @see: self::do_prepare()
+   */
+  public function do_connect() {
+    if (is_null($this->name)) {
+      return c_base_return_error::s_false();
+    }
+
+    // already prepared, just return true.
+    if (is_resource($this->ldap)) {
+      return new c_base_return_true();
+    }
+
+    $this->ldap = ldap_connect($this->name);
+    if (!is_resource($this->ldap)) {
+      $this->ldap = NULL;
+      return c_base_return_error::s_false();
+    }
+
+    $bound = ldap_bind($this->ldap, $this->bind_name, $this->bind_password);
+    if ($bound) {
+      unset($bound);
+      return new c_base_return_true();
+    }
+    unset($bound);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Unbinds/Disconnects from the ldap server.
+   *
+   * The ldap connection must be prepared before this function can be used.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: ldap_unbind()
+   * @see: self::do_connect()
+   */
+  public function do_disconnect() {
+    if (!is_resource($this->ldap)) {
+      return new c_base_return_false();
+    }
+
+    $unbound = ldap_unbind($this->ldap);
+    if ($unbound) {
+      unset($unbound);
+      return new c_base_return_true();
+    }
+    unset($unbound);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Search the ldap database.
+   *
+   * @param string $directory_name
+   *   The is the base directory name (DN).
+   * @param string $filter
+   *   A simple or advanced filter string.
+   * @param array $attributes
+   *   An array of required attributes.
+   *   The PHP documentation recommends supplying this for efficiency reasons.
+   * @param bool $attributes_only
+   *   (optional) If TRUE, then only the attribute types.
+   *   Otherwise, both attribute types and values are loaded.
+   * @param int $entry_limit
+   *   (optional) Limit the number of entries fetched by this limit.
+   *   PHP calls this the size limit. 0 means no limit.
+   * @param int $time_limit
+   *   (optional) Limit the number of seconds the query is allowed to operate.
+   *   0 means no limit.
+   * @param int $dereference
+   *   Specify how aliases should be handled during the search.
+   *   One of: LDAP_DEREF_NEVER, LDAP_DEREF_SEARCHING, LDAP_DEREF_FINDING, LDAP_DEREF_ALWAYS.
+   *
+   * @return c_base_return_status|c_base_ldap_entries
+   *   The search identifier is returned
+   *
+   * @see: ldap_search()
+   */
+  public function do_search($directory_name, $filter, $attributes, $attributes_only = FALSE, $entry_limit = 0, $time_limit = 0, $dereference = LDAP_DEREF_NEVER) {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($directory_name) || !is_string($filter)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($attributes)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($entry_limit) || $entry_limit < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($time_limit) || $time_limit < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($dereference)) {
+      return c_base_return_error::s_false();
+    }
+
+    // To prevent flooding the logs, prepend @ to prevent errors from being printed as described by the PHP documentation.
+    // Any errors can still be obtained via the self::get_error_message() and self::get_error_number() functions.
+    $found = ldap_search($this->ldap, $directory_name, $filter, $attributes, $attributes_only, $entry_limit, $time_limit, $dereference);
+    if (is_resource($found)) {
+      $result = c_base_ldap_result::s_new($found);
+      $result->set_ldap($this->ldap);
+
+      unset($found);
+      return $result;
+    }
+    unset($found);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Search the ldap database, but only as far as the base directory name (single-depth).
+   *
+   * @param string $directory_name
+   *   The is the base directory name (DN).
+   * @param string $filter
+   *   A simple or advanced filter string.
+   * @param array $attributes
+   *   An array of required attributes.
+   *   The PHP documentation recommends supplying this for efficiency reasons.
+   * @param bool $attributes_only
+   *   (optional) If TRUE, then only the attribute types.
+   *   Otherwise, both attribute types and values are loaded.
+   * @param int $entry_limit
+   *   (optional) Limit the number of entries fetched by this limit.
+   *   PHP calls this the size limit. 0 means no limit.
+   * @param int $time_limit
+   *   (optional) Limit the number of seconds the query is allowed to operate.
+   *   0 means no limit.
+   * @param int $dereference
+   *   Specify how aliases should be handled during the search.
+   *   One of: LDAP_DEREF_NEVER, LDAP_DEREF_SEARCHING, LDAP_DEREF_FINDING, LDAP_DEREF_ALWAYS.
+   *
+   * @return c_base_return_status|c_base_ldap_entries
+   *   The search identifier is returned
+   *
+   * @see: ldap_list()
+   */
+  public function do_list($directory_name, $filter, $attributes, $attributes_only = FALSE, $entry_limit = 0, $time_limit = 0, $dereference = LDAP_DEREF_NEVER) {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($directory_name) || !is_string($filter)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($attributes)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($entry_limit) || $entry_limit < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($time_limit) || $time_limit < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($dereference)) {
+      return c_base_return_error::s_false();
+    }
+
+    // To prevent flooding the logs, prepend @ to prevent errors from being printed as described by the PHP documentation.
+    // Any errors can still be obtained via the self::get_error_message() and self::get_error_number() functions.
+    $found = ldap_list($this->ldap, $directory_name, $filter, $attributes, $attributes_only, $entry_limit, $time_limit, $dereference);
+    if (is_resource($found)) {
+      $result = c_base_ldap_result::s_new($found);
+      $result->set_ldap($this->ldap);
+
+      unset($found);
+      return $result;
+    }
+    unset($found);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Read the ldap database, but only for a single entry.
+   *
+   * @param string $directory_name
+   *   The is the base directory name (DN).
+   * @param string $filter
+   *   A simple or advanced filter string.
+   * @param array $attributes
+   *   An array of required attributes.
+   *   The PHP documentation recommends supplying this for efficiency reasons.
+   * @param bool $attributes_only
+   *   (optional) If TRUE, then only the attribute types.
+   *   Otherwise, both attribute types and values are loaded.
+   * @param int $entry_limit
+   *   (optional) Limit the number of entries fetched by this limit.
+   *   PHP calls this the size limit. 0 means no limit.
+   * @param int $time_limit
+   *   (optional) Limit the number of seconds the query is allowed to operate.
+   *   0 means no limit.
+   * @param int $dereference
+   *   Specify how aliases should be handled during the search.
+   *   One of: LDAP_DEREF_NEVER, LDAP_DEREF_SEARCHING, LDAP_DEREF_FINDING, LDAP_DEREF_ALWAYS.
+   *
+   * @return c_base_return_status|c_base_ldap_entries
+   *   The search identifier is returned
+   *
+   * @see: ldap_read()
+   */
+  public function do_read($directory_name, $filter, $attributes, $attributes_only = FALSE, $entry_limit = 0, $time_limit = 0, $dereference = LDAP_DEREF_NEVER) {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($directory_name) || !is_string($filter)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($attributes)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($entry_limit) || $entry_limit < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($time_limit) || $time_limit < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($dereference)) {
+      return c_base_return_error::s_false();
+    }
+
+    // To prevent flooding the logs, prepend @ to prevent errors from being printed as described by the PHP documentation.
+    // Any errors can still be obtained via the self::get_error_message() and self::get_error_number() functions.
+    $found = ldap_read($this->ldap, $directory_name, $filter, $attributes, $attributes_only, $entry_limit, $time_limit, $dereference);
+    if (is_resource($found)) {
+      $result = c_base_ldap_result::s_new($found);
+      $result->set_ldap($this->ldap);
+
+      unset($found);
+      return $result;
+    }
+    unset($found);
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Compare value of an attribute specified within a directory.
+   *
+   * This does not recurse down to sub-entries.
+   *
+   * @param string $directory_name
+   *   The diretory or dn.
+   * @param string $attribute
+   *   The attribute to compare.
+   * @param string $value
+   *   The value to compare against.
+   *
+   * @return c_return_status
+   *   TRUE on match, FALSE on no-match (no error bit set), and FALSE with error bit set for error.
+   *
+   * @see: ldap_compare()
+   */
+  public function do_compare($directory_name, $attribute, $value) {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($directory_name) || !is_string($attribute) || !is_string($value)) {
+      return c_base_return_error::s_false();
+    }
+
+    $result = ldap_compare($this->ldap, $domain, $attribute, $value);
+    if ($result === -1) {
+      unset($result);
+      return c_base_return_error::s_false();
+    }
+
+    if ($result === TRUE) {
+      unset($result);
+      return new c_base_return_true();
+    }
+    unset($result);
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Returns the ldap error message, if there is an error.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   The error message.
+   *
+   * @see: ldap_error()
+   */
+  public function get_error_message() {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new(ldap_error($this->ldap));
+  }
+
+  /**
+   * Returns the ldap error number, if there is an error.
+   *
+   * Call ldap_err2str() with this number at any point to determine the message associated with it.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The error number.
+   *
+   * @see: ldap_errno()
+   * @see: ldap_err2str()
+   */
+  public function get_error_number() {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new(ldap_errno($this->ldap));
+  }
+
+  /**
+   * Get the current value for a given option in the ldap connection.
+   *
+   * @param int $option
+   *   A number representing the option.
+   *
+   * @return c_base_return_status|c_base_return_value
+   *   FALSE with the error bit set is returned on failure.
+   *   Anything else is returned on success.
+   *   The return type is depending on the value for option, see:
+   *   - LDAP_OPT_DEREF: c_base_return_int
+   *   - LDAP_OPT_SIZELIMIT: c_base_return_int
+   *   - LDAP_OPT_TIMELIMIT: c_base_return_int
+   *   - LDAP_OPT_NETWORK_TIMEOUT: c_base_return_int
+   *   - LDAP_OPT_PROTOCOL_VERSION: c_base_return_int
+   *   - LDAP_OPT_ERROR_NUMBER: c_base_return_int
+   *   - LDAP_OPT_REFERRALS: c_base_return_status
+   *   - LDAP_OPT_RESTART: c_base_return_status
+   *   - LDAP_OPT_HOST_NAME: c_base_return_string
+   *   - LDAP_OPT_ERROR_STRING: c_base_return_string
+   *   - LDAP_OPT_MATCHED_DN: c_base_return_string
+   *   - LDAP_OPT_SERVER_CONTROLS: c_base_return_array
+   *   - LDAP_OPT_CLIENT_CONTROLS: c_base_return_array
+   *   - Anything else: c_base_return_value.
+   *
+   * @see: ldap_get_option()
+   */
+  public function get_option($option) {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($option)) {
+      return c_base_return_error::s_false();
+    }
+
+    $value = NULL;
+    if (ldap_get_option($this->ldap, $option, $value) === FALSE) {
+      unset($value);
+      return c_base_return_error::s_false();
+    }
+
+    if ($option == LDAP_OPT_DEREF || $option == LDAP_OPT_SIZELIMIT || $option == LDAP_OPT_TIMELIMIT || $option == LDAP_OPT_NETWORK_TIMEOUT || $option == LDAP_OPT_PROTOCOL_VERSION || $option == LDAP_OPT_ERROR_NUMBER) {
+      return c_base_return_int::s_new($value);
+    }
+
+    if ($option == LDAP_OPT_REFERRALS || $option == LDAP_OPT_RESTART) {
+      if ($value === TRUE) {
+        unset($value);
+        return new c_base_return_true();
+      }
+      unset($value);
+
+      return new c_base_return_false();
+    }
+
+    if ($option == LDAP_OPT_HOST_NAME || $option == LDAP_OPT_ERROR_STRING || $option == LDAP_OPT_MATCHED_DN) {
+      return c_base_return_string::s_new($value);
+    }
+
+    if ($option == LDAP_OPT_SERVER_CONTROLS || $option == LDAP_OPT_CLIENT_CONTROLS) {
+      return c_base_return_array::s_new($value);
+    }
+
+    return c_base_return_value::s_new($value);
+  }
+}
+
+/**
+ * A generic class for processing ldap search results.
+ *
+ * This should be returned by c_base_ldap::search().
+ */
+class c_base_ldap_result extends c_base_return_resource {
+  private $ldap;
+  private $entry;
+
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->ldap = NULL;
+    $this->entry = NULL;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    if (is_resource($this->value)) {
+      ldap_free_result($this->value);
+    }
+
+    unset($this->ldap);
+    unset($this->entry);
+
+    parent::__destruct();
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * Assigns the ldap resource to this class.
+   *
+   * @param resource $ldap
+   *   The ldap resource to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_ldap($ldap) {
+    if (!is_resource($ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->ldap = $ldap;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the total number of entries.
+   *
+   * This appears to be capped by the directory limit.
+   * Therefore, this may not actually be the max number of entries.
+   * Instead, consider this the total number of entries retrieved in a single request.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *
+   * @see: ldap_count_entries()
+   */
+  public function get_count() {
+    if (!is_resource($ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    $total = ldap_count_entries($this->ldap, $this->value);
+    if ($total === FALSE) {
+      unset($total);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($total);
+  }
+
+  /**
+   * Loads the first entry.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: ldap_first_entry()
+   */
+  public function load_entry_first() {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    $first = ldap_first_entry($this->ldap, $this->value);
+    if ($first === FALSE) {
+      unset($first);
+      return c_base_return_error::s_false();
+    }
+
+    $this->entry = $first;
+    unset($first);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the next ldap result entry.
+   *
+   * The first entry must be loaded before calling this function.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE on error.
+   *   FALSE (without error bit set) is returned when there are no remaining entries.
+   *
+   * @see: ldap_first_entry()
+   * @see: self::load_entry_first()
+   */
+  public function load_entry_next() {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    // the entry is false when there are no entries remaining.
+    if ($this->entry === FALSE) {
+      return new c_base_return_false();
+    }
+
+    // the entry must first be loaded by self::load_entry_first().
+    if (!is_null($this->entry)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->entry = ldap_next_entry($this->ldap, $this->value);
+    if ($this->entry === FALSE) {
+      return new c_base_return_false();
+    }
+
+    return $entry;
+  }
+
+  /**
+   * Returns all entries.
+   *
+   * This requires that the entries be loaded first.
+   * Call self::load_entry_first() or the subsequent self::load_entry_next() before calling this.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   The an array of attribute strings on success, FALSE otherwise.
+   *
+   * @see: ldap_get_entries()
+   * @see: self::load_entry_first()
+   * @see: self::load_entry_next()
+   */
+  public function get_entry_all() {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    $entries = ldap_get_entries($this->ldap, $this->value);
+    if ($entries === FALSE) {
+      unset($entries);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($entries);
+  }
+
+  /**
+   * Returns the first attribute for a given entry.
+   *
+   * This requires that the entries be loaded first.
+   * Call self::load_entry_first() or the subsequent self::load_entry_next() before calling this.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   The attribute string on success, FALSE otherwise.
+   *
+   * @see: ldap_first_attribute()
+   * @see: self::load_entry_first()
+   * @see: self::load_entry_next()
+   */
+  public function get_attribute_first() {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->entry)) {
+      return c_base_return_error::s_false();
+    }
+
+    $attribute = ldap_first_attribute($this->ldap, $this->entry);
+    if ($attribute === FALSE) {
+      unset($attribute);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($attribute);
+  }
+
+  /**
+   * Returns the next attribute for a given entry.
+   *
+   * This requires that the entries be loaded first.
+   * Call self::load_entry_first() or the subsequent self::load_entry_next() before calling this.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   The attribute string on success, FALSE otherwise.
+   *
+   * @see: ldap_next_attribute()
+   * @see: self::load_attribute_first()
+   * @see: self::load_entry_first()
+   * @see: self::load_entry_next()
+   */
+  public function get_attribute_next() {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->entry)) {
+      return c_base_return_error::s_false();
+    }
+
+    $attribute = ldap_next_attribute($this->ldap, $this->entry);
+    if ($attribute === FALSE) {
+      unset($attribute);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($attribute);
+  }
+
+  /**
+   * Loads the all attributes for a given entry.
+   *
+   * This requires that the entries be loaded first.
+   * Call self::load_entry_first() or the subsequent self::load_entry_next() before calling this.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   The an array of attribute strings on success, FALSE otherwise.
+   *
+   * @see: ldap_next_attribute()
+   * @see: self::load_attribute_first()
+   * @see: self::load_entry_first()
+   * @see: self::load_entry_next()
+   */
+  public function get_attribute_all() {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->entry)) {
+      return c_base_return_error::s_false();
+    }
+
+    $attributes = ldap_get_attributes($this->ldap, $this->entry);
+    if ($attributes === FALSE) {
+      unset($attributes);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($attributes);
+  }
+
+  /**
+   * Returns the directory name for a given entry.
+   *
+   * This requires that the entries be loaded first.
+   * Call self::load_entry_first() or the subsequent self::load_entry_next() before calling this.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   The attribute string on success, FALSE otherwise.
+   *
+   * @see: ldap_get_dn()
+   * @see: self::load_attribute_first()
+   * @see: self::load_entry_first()
+   * @see: self::load_entry_next()
+   */
+  public function get_directory_name() {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->entry)) {
+      return c_base_return_error::s_false();
+    }
+
+    $directory_name = ldap_get_dn($this->ldap, $this->entry);
+    if ($directory_name === FALSE) {
+      unset($directory_name);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($directory_name);
+  }
+
+  /**
+   * Returns the values for a given attribute.
+   *
+   * @param string $attribute
+   *   The attribute to get the values for.
+   * @param bool $binary
+   *   (optional) When TRUE, returns binary data. When FALSE, returns string data.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array on success, FALSE otherwise.
+   *
+   * @see: ldap_get_values()
+   * @see: ldap_get_values_len()
+   */
+  public function get_values($attribute, $binary = FALSE) {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->entry)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($attribute)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($binary) {
+      $values = ldap_get_values_len($this->ldap, $this->entry, $attribute);
+    }
+    else {
+      $values = ldap_get_values($this->ldap, $this->entry, $attribute);
+    }
+
+    if (!is_array($values)) {
+      unset($values);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($values);
+  }
+
+  /**
+   * Sorts all loaded entries by the given filter.
+   *
+   * @param string $attribute
+   *   The attribute to perform the sort against.
+   *
+   * @see: ldap_sort()
+   */
+  public function do_sort($attribute) {
+    if (!is_resource($this->ldap)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($attribute)) {
+      return c_base_return_error::s_false();
+    }
+
+    $status = ldap_sort($this->ldap, $this->value, $attribute);
+    if ($status === FALSE) {
+      unset($status);
+      return c_base_return_error::s_false();
+    }
+    unset($status);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Free's memory allocated to the class.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned on success, FALSE is returned if nothing to free.
+   *   FALSE with the error flag set is returned on error.
+   *
+   * @see: ldap_free_result()
+   */
+  public function free_result() {
+    if (!is_resource($this->value)) {
+      return new c_base_return_false();
+    }
+
+    if (ldap_free_result($this->value)) {
+      return new c_base_return_true();
+    }
+
+    return c_base_return_error::s_false();
+  }
+}
diff --git a/common/base/classes/base_log.php b/common/base/classes/base_log.php
new file mode 100644 (file)
index 0000000..a1140a7
--- /dev/null
@@ -0,0 +1,220 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing the logs.
+ */
+
+/**
+ * A generic class for managing the logs.
+ */
+class c_base_log {
+  const TYPE_NONE       = 0;
+  const TYPE_BASE       = 1; // for low-level entries.
+  const TYPE_REQUEST    = 2; // accessing the site (generally page requests).
+  const TYPE_INTERPET   = 3; // interpretting (such as a PHP-related).
+  const TYPE_DATABASE   = 4; // the database.
+  const TYPE_USER       = 5; // related to users.
+  const TYPE_PROXY      = 6; // proxying as some other user.
+  const TYPE_ACCESS     = 7; // access control.
+  const TYPE_CONTENT    = 8; // content itself.
+  const TYPE_THEME      = 9; // theme (such as renderring a theme).
+  const TYPE_RESPONSE   = 10; // response to requests.
+  const TYPE_CONNECT    = 11; // relating connecting and disconnecting from the site.
+  const TYPE_CLIENT     = 12; // client information.
+  const TYPE_SERVER     = 13; // server information.
+  const TYPE_LEGAL      = 14; // legal or law-based information.
+  const TYPE_AUDIT      = 15; // legal or law-based information.
+  const TYPE_CACHE      = 16; // caching.
+  const TYPE_SYSTEM     = 17; // system.
+  const TYPE_FILE       = 18; // files.
+  const TYPE_TIME       = 19; // time-related matters (such as cron jobs).
+  const TYPE_EVENT      = 20; // time and place related matters.
+  const TYPE_SESSION    = 21; // sessions.
+  const TYPE_MAIL       = 22; // e-mails.
+  const TYPE_SIGN       = 23; // signatures, such as PGP/GPG.
+  const TYPE_SYNC       = 24; // synchronization of information.
+  const TYPE_WORKFLOW   = 25; // workflow.
+  const TYPE_REQUEST    = 26; // workflow: requesting.
+  const TYPE_COMMENT    = 27; // workflow: commenting.
+  const TYPE_DRAFT      = 28; // workflow: drafting.
+  const TYPE_REVIEW     = 29; // workflow: reviewing.
+  const TYPE_EDIT       = 30; // workflow: editting.
+  const TYPE_AMEND      = 31; // workflow: ammending.
+  const TYPE_UNDO       = 32; // workflow: undoing an edit.
+  const TYPE_APPROVE    = 33; // workflow: approving.
+  const TYPE_DISPROVE   = 34; // workflow: disproving.
+  const TYPE_PUBLISH    = 35; // workflow: publushing.
+  const TYPE_UNPUBLISH  = 36; // workflow: publushing.
+  const TYPE_ACCEPT     = 37; // workflow: accepting.
+  const TYPE_DENY       = 38; // workflow: denying.
+  const TYPE_CANCEL     = 39; // workflow: cancelling.
+  const TYPE_UNCANCEL   = 40; // workflow: cancelling.
+  const TYPE_AUDIT      = 41; // workflow: auditing.
+  const TYPE_TRANSITION = 42; // workflow: transitioning.
+  const TYPE_REVERT     = 43; // workflow: revert.
+  const TYPE_DELETE     = 44; // workflow: delete.
+  const TYPE_RESTORE    = 45; // workflow: restore (undelete).
+  const TYPE_UPGRADE    = 46; // upgrade.
+  const TYPE_DOWNGRADE  = 47; // downgrade.
+
+  // severity defines how important or the context of the log entry.
+  const SEVERITY_NONE        = 0;
+  const SEVERITY_DEBUG       = 1;
+  const SEVERITY_INFORMATION = 2; // regular logging information.
+  const SEVERITY_NOTICE      = 3; // information worth noting.
+  const SEVERITY_WARNING     = 4; // this could be a problem.
+  const SEVERITY_ERROR       = 5; // this is a problem.
+  const SEVERITY_CRITICAL    = 6; // this is a big problem.
+  const SEVERITY_EMERGENCY   = 7; // this is the most serious type of problem.
+
+  private $type;
+  private $data;
+
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->type = self::TYPE_NONE;
+    $this->data = array();
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->type);
+    unset($this->data);
+  }
+
+  /**
+   * Assigns the type code for this log entry.
+   *
+   * @param int $tyoe
+   *   The type code.
+   *
+   * @return c_base_return_status
+   *   TRUE on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_type($type) {
+    if (!is_int($type) || $type < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->type = $type;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the type code for this log entry.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   The type integer on success.
+   *   FALSE with error bit set on error.
+   */
+  public function get_type() {
+    return c_base_return_int::s_new($this->type);
+  }
+
+  /**
+   * Returns the data as a serialized array string.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array representing the data array on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_data_serialized() {
+    return c_base_return_array::s_new($this->data);
+  }
+
+  /**
+   * Returns the data as a serialized array string.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   A serialized string representing the data array on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_data_serialized() {
+    return c_base_return_string::s_new(serialize($this->data));
+  }
+
+  /**
+   * Returns the data as a json-serialized array string.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   A json-serialized string representing the data array on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_data_jsonized($options = 0, $depth = 512) {
+    if (!is_int($options)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($depth) || $depth < 1) {
+      return c_base_return_error::s_false();
+    }
+
+    $encoded = json_encode($this->data, $options, $depth)
+    if ($encoded === FALSE) {
+      unset($encoded);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($encoded);
+  }
+
+  /**
+   * Assigns data to a specific key in the data array.
+   *
+   * @param string|int $key
+   *   The key name string or integer.
+   * @param $value
+   *   The value to assign.
+   *   There is no enforcement on the data type.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  protected function pr_set_data($key, $value) {
+    if (is_int($key)) {
+      if ($key < 0) {
+        return c_base_return_error::s_false();
+      }
+    }
+    elseif (!is_string($key)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->data[$key] = $value;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the data assigned at the specified key.
+   *
+   * @param string|int $key
+   *   The key name string or integer.
+   *
+   * @return c_base_return_status|c_base_return_value
+   *   The array value is returned on success.
+   *   FALSE with error bit set is returned on invalid key.
+   *   FALSE with error bit set is returned on error.
+   */
+  protected function pr_get_data($key) {
+    if (is_int($key)) {
+      if ($key < 0) {
+        return c_base_return_error::s_false();
+      }
+    }
+    elseif (!is_string($key)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!array_key_exists($key, $this->data)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_value::s_new($this->data[$key]);
+  }
+}
diff --git a/common/base/classes/base_markup.php b/common/base/classes/base_markup.php
new file mode 100644 (file)
index 0000000..0ffa6b5
--- /dev/null
@@ -0,0 +1,157 @@
+<?php
+/**
+ * @file
+ * Provides a class for htmo markup.
+ *
+ * This is currently a draft/brainstorm and is subject to be completely rewritten/redesigned.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+/**
+ * A generic class for html tags.
+ *
+ * The structure and attributes may be used to communicate information, therefore the attributes extend to both input and output (theme).
+ * This class is not intended to be used for generate the theme but is instead intended to be used as a base class for both the input and the output classes for their respective purposes.
+ *
+ * Each tag has an internal id that is expected to be processed.
+ * This is not the same as the HTML 'id' attribute but can be the same.
+ *
+ * Many of the attributes are defined from HTML forms because of the number of forms.
+ *
+ * @see: https://www.w3.org/TR/html5/forms.html#forms
+ */
+class c_base_markup_tag {
+  private $id;
+  private $attributes;
+  private $tags;
+  private $tags_total;
+  private $parent;
+  private $children;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->id = NULL;
+    $this->attributes = array();
+    $this->tags = NULL;
+    $this->tags_total = 0;
+    $this->parent = NULL;
+    $this->children = array();
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->id);
+    unset($this->attributes);
+    unset($this->tags);
+    unset($this->tags_total);
+    unset($this->parent);
+    unset($this->children);
+  }
+
+  /**
+   * Assign the internal unique numeric tag id.
+   *
+   * @param int $id
+   *   The internal numeric id to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_id($id) {
+    if (!is_int($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->id = $id;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the unique numeric tag id assigned to this object.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   The tag type assigned to this class.
+   *   FALSE is returned if the unique numeric tag id is not set.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_id() {
+    if (!isset($this->id)) {
+      return new c_base_return_false();
+    }
+
+    return new c_base_return_int($this->id);
+  }
+
+  /**
+   * Add or append a given tag to the object.
+   *
+   * If the tag is not assigned a unique internal id, then one is generated.
+   *
+   * @param c_base_markup_tag $tag
+   *   The tag to assign.
+   * @param int|null $index
+   *   (optional) A position within the children array to assign the tag.
+   *   If NULL, then the tag is appended to the end of the children array.
+   *
+   * @return c_base_return_int|$c_base_return_status
+   *   The position in which the tag was added is returned on success.
+   *   FALSE is reeturned if an tag at the specified index already exists.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_tag($tag, $index = NULL) {
+    if (!($tag instanceof c_base_markup_tag)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($index) && (!is_int($index) && $index < 0)) {
+      return c_base_return_error::s_false();
+    }
+
+    $tag_id = $tag->get_id();
+    if (!($tag_id instanceof c_base_return_int)) {
+      // PHP fails to provide an end() equivalent to get the end key.
+      // This performs a less efficient process of generating an array of keys and then calling end() on that array.
+      // This is then used to get the last key so that the end key can be used as the tag id.
+      $keys = array_keys($this->tags);
+      $tag_id = end($keys);
+      unset($keys);
+
+      $tag->set_id($tag_id);
+    }
+
+    if (!array_key_exists($tag_id, $this->tags)) {
+      $this->tags[$tag_id] = $tag;
+    }
+
+    if (is_null($index)) {
+      $this->children[] = $tag;
+
+      // PHP fails to provide an end() equivalent to get the end key.
+      // This performs a less efficient process of generating an array of keys and then calling end() on that array.
+      // This is then used to get the last key so that the end key can be used as the tag children position.
+      $keys = array_keys($this->tags);
+      $index = end($keys);
+      unset($keys);
+    }
+    else {
+      if (array_key_exists($index, $this->children)) {
+        return new c_base_return_false();
+      }
+
+      $this->children[$index] = $tag;
+      ksort($this->children);
+    }
+    unset($tag_id);
+
+    $this->tags_total++;
+    return new c_base_return_int::s_new($index);
+  }
+}
diff --git a/common/base/classes/base_mime.php b/common/base/classes/base_mime.php
new file mode 100644 (file)
index 0000000..ea63e21
--- /dev/null
@@ -0,0 +1,534 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing mime-type information.
+ */
+
+/**
+ * A generic class for managing mime-type information.
+ */
+class c_base_mime {
+  const CATEGORY_UNKNOWN     = 0;
+  const CATEGORY_PROVIDED    = 1;
+  const CATEGORY_STREAM      = 2;
+  const CATEGORY_TEXT        = 1000;
+  const CATEGORY_IMAGE       = 2000;
+  const CATEGORY_AUDIO       = 3000;
+  const CATEGORY_VIDEO       = 4000;
+  const CATEGORY_DOCUMENT    = 5000;
+  const CATEGORY_CONTAINER   = 6000;
+  const CATEGORY_APPLICATION = 7000; // only for application/* that values not covered by any other category.
+
+
+  const TYPE_UNKNOWN  = 0;
+  const TYPE_PROVIDED = 1;
+  const TYPE_STREAM   = 2;
+
+  const TYPE_TEXT_PLAIN = 1001;
+  const TYPE_TEXT_HTML  = 1002;
+  const TYPE_TEXT_RSS   = 1003;
+  const TYPE_TEXT_ICAL  = 1004;
+  const TYPE_TEXT_CSV   = 1005;
+  const TYPE_TEXT_XML   = 1006;
+  const TYPE_TEXT_CSS   = 1007;
+  const TYPE_TEXT_JS    = 1008;
+  const TYPE_TEXT_JSON  = 1009;
+  const TYPE_TEXT_RICH  = 1010;
+  const TYPE_TEXT_XHTML = 1011;
+  const TYPE_TEXT_PS    = 1012;
+
+  const TYPE_IMAGE_PNG  = 2001;
+  const TYPE_IMAGE_GIF  = 2002;
+  const TYPE_IMAGE_JPEG = 2003;
+  const TYPE_IMAGE_BMP  = 2004;
+  const TYPE_IMAGE_SVG  = 2005;
+  const TYPE_IMAGE_TIFF = 2006;
+
+  const TYPE_AUDIO_WAV  = 3001;
+  const TYPE_AUDIO_OGG  = 3002;
+  const TYPE_AUDIO_MP3  = 3003;
+  const TYPE_AUDIO_MP4  = 3004;
+  const TYPE_AUDIO_MIDI = 3005;
+
+  const TYPE_VIDEO_MPEG      = 4001;
+  const TYPE_VIDEO_OGG       = 4002;
+  const TYPE_VIDEO_H264      = 4003;
+  const TYPE_VIDEO_QUICKTIME = 4004;
+  const TYPE_VIDEO_DV        = 4005;
+  const TYPE_VIDEO_JPEG      = 4006;
+  const TYPE_VIDEO_WEBM      = 4007;
+
+  const TYPE_DOCUMENT_LIBRECHART        = 5001;
+  const TYPE_DOCUMENT_LIBREFORMULA      = 5002;
+  const TYPE_DOCUMENT_LIBREGRAPHIC      = 5003;
+  const TYPE_DOCUMENT_LIBREPRESENTATION = 5004;
+  const TYPE_DOCUMENT_LIBRESPREADSHEET  = 5005;
+  const TYPE_DOCUMENT_LIBRETEXT         = 5006;
+  const TYPE_DOCUMENT_LIBREHTML         = 5007;
+  const TYPE_DOCUMENT_PDF               = 5008;
+  const TYPE_DOCUMENT_ABIWORD           = 5009;
+  const TYPE_DOCUMENT_MSWORD            = 5010;
+  const TYPE_DOCUMENT_MSEXCEL           = 5011;
+  const TYPE_DOCUMENT_MSPOWERPOINT      = 5012;
+
+  const TYPE_CONTAINER_TAR  = 6001;
+  const TYPE_CONTAINER_CPIO = 6002;
+  const TYPE_CONTAINER_JAVA = 6003;
+
+
+  private static $s_names_provided = array(
+    self::TYPE_PROVIDED => array('*/*', 'text/*', 'image/*', 'audio/*', 'video/*', 'application/*'),
+    self::TYPE_STREAM   => array('application/octet-stream'),
+  );
+
+  private static $s_names_text = array(
+    self::TYPE_TEXT_PLAIN   => array('text/plain'),
+    self::TYPE_TEXT_HTML    => array('text/html'),
+    self::TYPE_TEXT_RSS     => array('application/rss', 'application/rss+xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml'),
+    self::TYPE_TEXT_ICAL    => array('text/calendar'),
+    self::TYPE_TEXT_CSV     => array('text/csv'),
+    self::TYPE_TEXT_XML     => array('application/xml'),
+    self::TYPE_TEXT_CSS     => array('text/css'),
+    self::TYPE_TEXT_JS      => array('text/javascript', 'application/javascript'),
+    self::TYPE_TEXT_JSON    => array('text/json', 'application/json'),
+    self::TYPE_TEXT_RICH    => array('text/rtf'),
+    self::TYPE_TEXT_XHTML   => array('application/xhtml', 'application/xhtml+xml'),
+    self::TYPE_TEXT_PS      => array('text/ps'),
+  );
+
+  private static $s_names_image = array(
+    self::TYPE_IMAGE_PNG  => array('image/png'),
+    self::TYPE_IMAGE_GIF  => array('image/gif'),
+    self::TYPE_IMAGE_JPEG => array('image/jpeg', 'image/jpg', 'image/jpx'),
+    self::TYPE_IMAGE_BMP  => array('image/bmp'),
+    self::TYPE_IMAGE_SVG  => array('image/svg'),
+    self::TYPE_IMAGE_TIFF => array('image/tiff', 'image/tiff-fx'),
+  );
+
+  private static $s_names_audio = array(
+    self::TYPE_AUDIO_WAV  => array('audio/wav'),
+    self::TYPE_AUDIO_OGG  => array('audio/ogg'),
+    self::TYPE_AUDIO_MP3  => array('audio/mpeg'),
+    self::TYPE_AUDIO_MP4  => array('audio/mp4'),
+    self::TYPE_AUDIO_MIDI => array('audio/midi'),
+  );
+
+  private static $s_names_video = array(
+    self::TYPE_VIDEO_MPEG      => array('video/mp4', 'video/mpeg'),
+    self::TYPE_VIDEO_OGG       => array('video/ogg'),
+    self::TYPE_VIDEO_H264      => array('video/h264'),
+    self::TYPE_VIDEO_QUICKTIME => array('video/qt'),
+    self::TYPE_VIDEO_DV        => array('video/dv'),
+    self::TYPE_VIDEO_JPEG      => array('video/jpeg', 'video/jpeg2000'),
+    self::TYPE_VIDEO_WEBM      => array('video/webm'),
+  );
+
+  private static $s_names_document = array(
+    self::TYPE_DOCUMENT_PDF               => array('application/pdf'),
+    self::TYPE_DOCUMENT_LIBRECHART        => array('application/vnd.oasis.opendocument.chart'),
+    self::TYPE_DOCUMENT_LIBREFORMULA      => array('application/vnd.oasis.opendocument.formula'),
+    self::TYPE_DOCUMENT_LIBREGRAPHIC      => array('application/vnd.oasis.opendocument.graphics'),
+    self::TYPE_DOCUMENT_LIBREPRESENTATION => array('application/vnd.oasis.opendocument.presentation'),
+    self::TYPE_DOCUMENT_LIBRESPREADSHEET  => array('application/vnd.oasis.opendocument.spreadsheet'),
+    self::TYPE_DOCUMENT_LIBRETEXT         => array('application/vnd.oasis.opendocument.text'),
+    self::TYPE_DOCUMENT_LIBREHTML         => array('application/vnd.oasis.opendocument.text-web'),
+    self::TYPE_DOCUMENT_ABIWORD           => array('application/abiword', 'application/abiword-compressed'),
+    self::TYPE_DOCUMENT_MSWORD            => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/msword'),
+    self::TYPE_DOCUMENT_MSEXCEL           => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/ms-excel'),
+    self::TYPE_DOCUMENT_MSPOWERPOINT      => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/ms-powerpoint'),
+  );
+
+  private static $s_names_container = array(
+    self::TYPE_CONTAINER_TAR  => array('application/tar'),
+    self::TYPE_CONTAINER_CPIO => array('application/cpio'),
+    self::TYPE_CONTAINER_JAVA => array('application/java'),
+  );
+
+  private static $s_names_application = array(
+  );
+
+
+  /**
+   * Get the mime-types associated with the id.
+   *
+   * @param int $id
+   *   The id of the mime type to return.
+   * @param int|null $category
+   *   (optional) search a limited sub-set of ids based on the category.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of mime-types or FALSE on error.
+   *   FALSE without the error flag means that there are no names associated with the given id.
+   */
+  static function s_get_names_by_id($id, $category = NULL) {
+    if (!is_int($id) && !is_numeric($id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_null($category)) {
+      $result = NULL;
+
+      if (array_key_exists($id, self::$s_names_basic)) {
+        return c_base_return_array::s_new(self::$s_names_basic[$id]);
+      }
+
+      if (array_key_exists($id, self::$s_names_text)) {
+        return c_base_return_array::s_new(self::$s_names_text[$id]);
+      }
+
+      if (array_key_exists($id, self::$s_names_audio)) {
+        return c_base_return_array::s_new(self::$s_names_audio[$id]);
+      }
+
+      if (array_key_exists($id, self::$s_names_video)) {
+        return c_base_return_array::s_new(self::$s_names_video[$id]);
+      }
+
+      if (array_key_exists($id, self::$s_names_document)) {
+        return c_base_return_array::s_new(self::$s_names_document[$id]);
+      }
+
+      if (array_key_exists($id, self::$s_names_container)) {
+        return c_base_return_array::s_new(self::$s_names_container[$id]);
+      }
+
+      if (array_key_exists($id, self::$s_names_application)) {
+        return c_base_return_array::s_new(self::$s_names_application[$id]);
+      }
+    }
+    else {
+      if (!is_int($category)) {
+        return c_base_return_error::s_false();
+      }
+
+      if ($category == self::CATEGORY_PROVIDED) {
+        if (array_key_exists($id, self::$s_names_basic)) {
+          return c_base_return_array::s_new(self::$s_names_basic[$id]);
+        }
+      }
+      elseif ($category == self::CATEGORY_TEXT) {
+        if (array_key_exists($id, self::$s_names_text)) {
+          return c_base_return_array::s_new(self::$s_names_text[$id]);
+        }
+      }
+      elseif ($category == self::CATEGORY_IMAGE) {
+        if (array_key_exists($id, self::$s_names_text)) {
+          return c_base_return_array::s_new(self::$s_names_text[$id]);
+        }
+      }
+      elseif ($category == self::CATEGORY_AUDIO) {
+        if (array_key_exists($id, self::$s_names_audio)) {
+          return c_base_return_array::s_new(self::$s_names_audio[$id]);
+        }
+      }
+      elseif ($category == self::CATEGORY_VIDEO) {
+        if (array_key_exists($id, self::$s_names_video)) {
+          return c_base_return_array::s_new(self::$s_names_video[$id]);
+        }
+      }
+      elseif ($category == self::CATEGORY_DOCUMENT) {
+        if (array_key_exists($id, self::$s_names_document)) {
+          return c_base_return_array::s_new(self::$s_names_document[$id]);
+        }
+      }
+      elseif ($category == self::CATEGORY_CONTAINER) {
+        if (array_key_exists($id, self::$s_names_container)) {
+          return c_base_return_array::s_new(self::$s_names_container[$id]);
+        }
+      }
+      elseif ($category == self::CATEGORY_APPLICATION) {
+        if (array_key_exists($id, self::$s_names_application)) {
+          return c_base_return_array::s_new(self::$s_names_application[$id]);
+        }
+      }
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Identifies the mime-type and returns relevant information.
+   *
+   * @param string $mime
+   *   The mime-type string to identify.
+   * @param bool $lowercase
+   *   (optional) When TRUE, the passed string will be auto-forced to be lower-case.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array of containing the following:
+   *   - 'id_category': An integer representing the mime-type category.
+   *   - 'id_type': An integer representing the mime-type.
+   *   - 'name_category': A (lowercase) string representing the category part of the mime-type.
+   *   - 'name_type': A (lowercase) string representing the type part of the mime-type.
+   *   Both category and type ids may have appropriate UNKNOWN values to for valid mime-type formats of unknown mime-type strings.
+   *   FALSE with the error bit set is returned on error.
+   *   FALSE without the error flag means for an invalid mime-type string.
+   *
+   * @see: mb_strtolower()
+   */
+  static function s_identify($mime, $lowercase = FALSE) {
+    if (!is_string($mime)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($lowercase)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($lowercase) {
+      $lower_mime = mb_strtolower($mime);
+    }
+    else {
+      $lower_mime = $mime;
+    }
+
+    $information = array(
+      'id_category' => self::CATEGORY_PROVIDED,
+      'id_type' => self::TYPE_PROVIDED,
+      'name_category' => '*',
+      'name_type' => '*',
+    );
+
+    if ($mime == '*/*') {
+      return c_base_return_array::s_new($information);
+    }
+
+    $parts = mb_split('/', $lower_mime);
+
+    if (count($parts) != 2) {
+      // there is no valid value to process.
+      unset($parts);
+      unset($lower_mime);
+      unset($information);
+      return new c_base_return_false();
+    }
+
+    $information['name_category'] = $parts[0];
+    $information['name_type'] = $parts[1];
+
+    if ($parts[0] == 'text') {
+      $information['id_category'] = self::CATEGORY_TEXT;
+
+      if ($parts[1] == '*') {
+        // nothing to change.
+      }
+      elseif ($parts[1] == 'html') {
+        $information['id_type'] = self::TYPE_TEXT_HTML;
+      }
+      elseif ($parts[1] == 'plain') {
+        $information['id_type'] = self::TYPE_TEXT_PLAIN;
+      }
+      elseif ($parts[1] == 'calendar') {
+        $information['id_type'] = self::TYPE_TEXT_ICAL;
+      }
+      elseif ($parts[1] == 'csv') {
+        $information['id_type'] = self::TYPE_TEXT_CSV;
+      }
+      elseif ($parts[1] == 'xml') {
+        $information['id_type'] = self::TYPE_TEXT_XML;
+      }
+      elseif ($parts[1] == 'css') {
+        $information['id_type'] = self::TYPE_TEXT_CSS;
+      }
+      elseif ($parts[1] == 'rtf') {
+        $information['id_type'] = self::TYPE_TEXT_RICH;
+      }
+      elseif ($parts[1] == 'javascript') {
+        $information['id_type'] = self::TYPE_TEXT_JS;
+      }
+      else {
+        $information['id_type'] = self::TYPE_UNKNOWN;
+      }
+    }
+    elseif ($parts[0] == 'application') {
+      $information['id_category'] = self::CATEGORY_APPLICATION;
+
+      if ($parts[1] == '*') {
+        // nothing to change.
+      }
+      elseif ($parts[1] == 'octet-stream') {
+        $information['id_category'] = self::CATEGORY_STREAM;
+        $information['id_type'] = self::TYPE_STREAM;
+      }
+      elseif ($parts[1] == 'pdf') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_APPLICATION_PDF;
+      }
+      elseif ($parts[1] == 'rss' || $parts[1] == 'rss+xml' || $parts[1] == 'rdf+xml' || $parts[1] == 'atom+xml') {
+        $information['id_category'] = self::CATEGORY_TEXT;
+        $information['id_type'] = self::TYPE_TEXT_RSS;
+      }
+      elseif ($parts[1] == 'xml') {
+        $information['id_category'] = self::CATEGORY_TEXT;
+        $information['id_type'] = self::TYPE_TEXT_XML;
+      }
+      elseif ($parts[1] == 'javascript') {
+        $information['id_category'] = self::CATEGORY_TEXT;
+        $information['id_type'] = self::TYPE_TEXT_JS;
+      }
+      elseif ($parts[1] == 'json') {
+        $information['id_category'] = self::CATEGORY_TEXT;
+        $information['id_type'] = self::TYPE_TEXT_JSON;
+      }
+      elseif ($parts[1] == 'xhtml' || $parts[1] == 'xhtml+xml') {
+        $information['id_category'] = self::CATEGORY_TEXT;
+        $information['id_type'] = self::TYPE_TEXT_XHTML;
+      }
+      elseif ($parts[1] == 'ps') {
+        $information['id_category'] = self::CATEGORY_TEXT;
+        $information['id_type'] = self::TYPE_TEXT_PS;
+      }
+      elseif ($parts[1] == 'tar') {
+        $information['id_category'] = self::CATEGORY_CONTAINER;
+        $information['id_type'] = self::TYPE_CONTAINER_TAR;
+      }
+      elseif ($parts[1] == 'cpio') {
+        $information['id_category'] = self::CATEGORY_CONTAINER;
+        $information['id_type'] = self::TYPE_CONTAINER_CPIO;
+      }
+      elseif ($parts[1] == 'vnd.oasis.opendocument.chart') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_LIBRECHART;
+      }
+      elseif ($parts[1] == 'vnd.oasis.opendocument.formula') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_LIBREFORMULA;
+      }
+      elseif ($parts[1] == 'vnd.oasis.opendocument.graphics') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_LIBREGRAPHIC;
+      }
+      elseif ($parts[1] == 'vnd.oasis.opendocument.presentation') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_LIBREPRESENTATION;
+      }
+      elseif ($parts[1] == 'vnd.oasis.opendocument.spreadsheet') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_LIBRESPREADSHEET;
+      }
+      elseif ($parts[1] == 'vnd.oasis.opendocument.text') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_LIBRETEXT;
+      }
+      elseif ($parts[1] == 'vnd.oasis.opendocument.text-web') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_LIBREHTML;
+      }
+      elseif ($parts[1] == 'abiword' || $parts[1] == 'abiword-compressed') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_ABIWORD;
+      }
+      elseif ($parts[1] == 'msword' || $parts[1] == 'vnd.openxmlformats-officedocument.wordprocessingml.document') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_MSWORD;
+      }
+      elseif ($parts[1] == 'ms-excel' || $parts[1] == 'vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_MSEXCEL;
+      }
+      elseif ($parts[1] == 'ms-powerpoint' || $parts[1] == 'vnd.openxmlformats-officedocument.presentationml.presentation') {
+        $information['id_category'] = self::CATEGORY_DOCUMENT;
+        $information['id_type'] = self::TYPE_DOCUMENT_MSPOWERPOINT;
+      }
+      elseif ($parts[1] == 'java') {
+        $information['id_category'] = self::CATEGORY_CONTAINER;
+        $information['id_type'] = self::TYPE_CONTAINER_JAVA;
+      }
+      else {
+        $information['id_type'] = self::TYPE_UNKNOWN;
+      }
+    }
+    elseif ($parts[0] == 'image') {
+      $information['id_category'] = self::CATEGORY_IMAGE;
+
+      if ($parts[1] == '*') {
+        // nothing to change.
+      }
+      elseif ($parts[1] == 'png') {
+        $information['id_type'] = self::TYPE_IMAGE_PNG;
+      }
+      elseif ($parts[1] == 'jpeg' || $parts[1] == 'jpg' || $parts[1] == 'jpx') {
+        $information['id_type'] = self::TYPE_IMAGE_JPEG;
+      }
+      elseif ($parts[1] == 'gif') {
+        $information['id_type'] = self::TYPE_IMAGE_GIF;
+      }
+      elseif ($parts[1] == 'bmp') {
+        $information['id_type'] = self::TYPE_IMAGE_BMP;
+      }
+      elseif ($parts[1] == 'svg') {
+        $information['id_type'] = self::TYPE_IMAGE_SVG;
+      }
+      elseif ($parts[1] == 'tiff' || $parts[1] == 'tiff-fx') {
+        $information['id_type'] = self::TYPE_IMAGE_TIFF;
+      }
+      else {
+        $information['id_type'] = self::TYPE_UNKNOWN;
+      }
+    }
+    elseif ($parts[0] == 'audio') {
+      $information['id_category'] = self::CATEGORY_AUDIO;
+
+      if ($parts[1] == '*') {
+        // nothing to change.
+      }
+      elseif ($parts[1] == 'ogg') {
+        $information['id_type'] = self::TYPE_AUDIO_OGG;
+      }
+      elseif ($parts[1] == 'mpeg') {
+        $information['id_type'] = self::TYPE_AUDIO_MP3;
+      }
+      elseif ($parts[1] == 'mp4') {
+        $information['id_type'] = self::TYPE_AUDIO_MP4;
+      }
+      elseif ($parts[1] == 'wav') {
+        $information['id_type'] = self::TYPE_AUDIO_WAV;
+      }
+      elseif ($parts[1] == 'midi') {
+        $information['id_type'] = self::TYPE_AUDIO_MIDI;
+      }
+      else {
+        $information['id_type'] = self::TYPE_UNKNOWN;
+      }
+    }
+    elseif ($parts[0] == 'video') {
+      $information['id_category'] = self::CATEGORY_VIDEO;
+
+      if ($parts[1] == '*') {
+        // nothing to change.
+      }
+      elseif ($parts[1] == 'mp4' || $parts[1] == 'mpeg') {
+        $information['id_type'] = self::TYPE_VIDEO_MPEG;
+      }
+      elseif ($parts[1] == 'ogg') {
+        $information['id_type'] = self::TYPE_VIDEO_OGG;
+      }
+      elseif ($parts[1] == 'h264') {
+        $information['id_type'] = self::TYPE_VIDEO_H264;
+      }
+      elseif ($parts[1] == 'quicktime') {
+        $information['id_type'] = self::TYPE_VIDEO_QUICKTIME;
+      }
+      elseif ($parts[1] == 'dv') {
+        $information['id_type'] = self::TYPE_VIDEO_DV;
+      }
+      elseif ($parts[1] == 'jpeg' || $parts[1] == 'jpeg2000') {
+        $information['id_type'] = self::TYPE_VIDEO_JPEG;
+      }
+      elseif ($parts[1] == 'webm') {
+        $information['id_type'] = self::TYPE_VIDEO_WEBM;
+      }
+      else {
+        $information['id_type'] = self::TYPE_UNKNOWN;
+      }
+    }
+    else {
+      $information['id_category'] = self::CATEGORY_UNKNOWN;
+      $information['id_type'] = self::TYPE_UNKNOWN;
+    }
+    unset($parts);
+
+    unset($lower_mime);
+    return c_base_return_array::s_new($information);
+  }
+}
diff --git a/common/base/classes/base_return.php b/common/base/classes/base_return.php
new file mode 100644 (file)
index 0000000..010ed71
--- /dev/null
@@ -0,0 +1,1311 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing return values.
+ *
+ * Each function needs to return TRUE/FALSE to represent success or failure.
+ * However, if TRUE/FALSE could also represent a specific value, then TRUE/FALSE can have different contexts.
+ *
+ * This is a problem and the return result needs to be as context free as possible (aka: only 1 context).
+ * Having custom error codes can also be useful.
+ *
+ * Each class should instead return a type of base_return that can be easily tested for success or failure.
+ *
+ * The downside of this is that returning an object can waste more resources than a simple TRUE/FALSE.
+ * For consistency purposes, TRUE/FALSE should never be directly returned.
+ *
+ * Functions defined in this class will return the normal TRUE/FALSE and not the class-based TRUE/FALSE as an exception to this rule.
+ * - This is done because this class defines those objects.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+
+/**
+ * A generic trait for a return value class to have some value data.
+ *
+ * Return classes can be defined without this.
+ * - An example would be a boolean class whose name defines its state.
+ *
+ * Most return classes will use this trait.
+ *
+ * @require class base_error
+ */
+trait t_base_return_value {
+  protected $value = NULL;
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->value);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param $value
+   *   This can be anything that is to be considered a return value.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return $value
+   *   This can be anything that is to be considered a return value.
+   */
+  public function get_value() {
+    return $this->value;
+  }
+
+  /**
+   * Determine if this class has a value assigned to it.
+   *
+   * @return bool
+   *   TRUE if a value is assigned, FALSE otherwise.
+   */
+  public function has_value() {
+    return !is_null($this->value);
+  }
+
+  /**
+   * Creates a new return __class__ type.
+   *
+   * This is used to simplify the returning of a new class value.
+   *
+   * Errata: My theory was that by using __CLASS__ child classes could call this static function and __CLASS__ would be replaced with the child class type.
+   * This is not the case, so I am left with using an abstract static class, which is also not supported by PHP.
+   * Therefore, API-wise, this is an abstract static class and child classes are expected to implement this function, even if PHP does not enforce this.
+   *
+   * @param $value
+   *   The value to assign.
+   *   Validation is performed by the current class.
+   *   No error will be set if an invalid parameter is provided.
+   *   Child classes are expected to assign a value of NULL for invalid parameters.
+   *
+   * @return __class__
+   *   A newly created __class__ type, without an error set.
+   */
+  //public abstract static function s_new($value);
+
+  /**
+   * Private implemntation of s_new().
+   *
+   * @param $value
+   *   The value to assign.
+   *   Validation is performed by the current class.
+   *   No error will be set if an invalid parameter is provided.
+   *   Child classes are expected to assign a value of NULL for invalid parameters.
+   * @param string $class
+   *   This is the class name of the type to create.
+   *   Essentially just pass __CLASS__ to this argument.
+   *
+   * @see: t_base_return_value::s_new()
+   */
+  final protected static function p_s_new($value, $class) {
+    $object_return = new $class();
+    unset($class);
+
+    // allow for NULL to be passed without generating any errors.
+    // the default value, when undefined is always null.
+    if (!is_null($value)) {
+      $object_return->set_value($value);
+    }
+
+    return $object_return;
+  }
+
+  /**
+   * Perform a very basic, safe, value retrieval.
+   *
+   * PHP allows for things like $account->get_password()->get_value().
+   * If get_password() returns an non-object or an object without the get_value() function, a PHP error happens.
+   * This provides a simple way to obtain the value of a specific class that supports this trait without generating errors.
+   *
+   * Errata: My theory was that by using __CLASS__ child classes could call this static function and __CLASS__ would be replaced with the child class type.
+   * This is not the case, so I am left with using an abstract static class, which is also not supported by PHP.
+   * Therefore, API-wise, this is an abstract static class and child classes are expected to implement this function, even if PHP does not enforce this.
+   *
+   * @return
+   *   The value is returned or NULL is returned if value retrieval is not possible.
+   */
+  //public abstract static function s_value($return);
+
+  /**
+   * Private implementation of s_value().
+   *
+   * @param object $return
+   *   The appropriate c_base_return class that supports the t_base_return_value_exact trait.
+   * @param string $class
+   *   The class name to expect.
+   *
+   * @return
+   *   The value is returned or a generated expected type is returned if value retrieval is not possible.
+   *
+   * @see: t_base_return_value::p_s_value()
+   */
+  final protected static function p_s_value($return, $class) {
+    if (!is_object($return) || !($return instanceof $class)) {
+      return NULL;
+    }
+
+    return $return->get_value();
+  }
+}
+
+/**
+ * A generic trait for a getting a return value that is an exact type.
+ *
+ * No NULL values are allowed.
+ */
+trait t_base_return_value_exact {
+  // PHP does not support this. This project's API does require this trait to be used even if its not enforced by PHP.
+  //use t_base_return_value;
+
+  /**
+   * Perform a very basic, safe, value retrieval of the expected type.
+   *
+   * This guarantees that a specific type is returned.
+   *
+   * PHP allows for things like $account->get_password()->get_value().
+   * If get_password() returns an non-object or an object without the get_value() function, a PHP error happens.
+   * This provides a simple way to obtain the value of a specific class that supports this trait without generating errors.
+   *
+   * Errata: API-wise, this is an abstract static class and child classes are expected to implement this function, even if PHP does not enforce this.
+   *
+   * @param object $return
+   *   The appropriate c_base_return class that supports the t_base_return_value_exact trait.
+   *
+   * @return
+   *   The value is returned or a generated expected type is returned if value retrieval is not possible.
+   */
+  //abstract public static function s_value_exact($return);
+
+  /**
+   * Private implementation of s_value_exact().
+   *
+   * @param object $return
+   *   The appropriate c_base_return class that supports the t_base_return_value_exact trait.
+   * @param string $class
+   *   The class name to expect.
+   * @param $failsafe
+   *   The variable to return in case $return is invalid in some way.
+   *   This is used to guarantee that the return value is of the exact expected type.
+   *
+   * @return
+   *   The value is returned or a generated expected type is returned if value retrieval is not possible.
+   *
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  final protected static function p_s_value_exact($return, $class, $failsafe) {
+    if (!is_object($return) || !($return instanceof $class)) {
+      return $failsafe;
+    }
+
+    return $return->get_value_exact();
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * This guarantees that a specific type is returned.
+   *
+   * @return $value
+   *   The value of a specific type is returned.
+   */
+  abstract public function get_value_exact();
+}
+
+/**
+ * A generic trait for a message associated with a return value.
+ *
+ * This is added as a consideration and may be removed it ends up being unused.
+ */
+trait t_base_return_message {
+  protected $message = NULL;
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->message);
+  }
+
+  /**
+   * Assign the message.
+   */
+  public function set_message($message) {
+    $this->message = $message;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return $value
+   *   This can be anything that is to be considered a return value.
+   */
+  public function get_message() {
+    return $this->value;
+  }
+}
+
+/**
+ * A generic class for managing return values.
+ *
+ * This is the base template class used for specific return classes.
+ *
+ * All base returns will have an error variable.
+ *
+ * @require class c_base_error
+ */
+class c_base_return {
+  private $error;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->error = NULL;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->error);
+  }
+
+  /**
+   * Provide a simple way to check for error in a single step.
+   *
+   * This is intended to help clean up code and make code more readable.
+   *
+   * @return bool
+   *   return TRUE if the passed argument is an object class of type __CLASS__ and has an error flag set.
+   */
+  public static function s_has_error($return) {
+    return is_object($return) && $return instanceof c_base_return && $return->has_error();
+  }
+
+  /**
+   * Assign the error code.
+   *
+   * @param null|c_base_error $error
+   *   The error code class defining what the error is.
+   *   Setting this to NULL will clear the error flag.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_error($error) {
+    if (!is_null($error) && !($error instanceof c_base_error)) {
+      return FALSE;
+    }
+
+    $this->error = $error;
+    return TRUE;
+  }
+
+  /**
+   * Return the error code.
+   *
+   * @return null|c_base_error
+   *   @todo: finish this once c_base_error is implemented.
+   */
+  public function get_error() {
+    if (!($this->error instanceof c_base_error)) {
+      $this->error = NULL;
+    }
+
+    return $this->error;
+  }
+
+  /**
+   * Return the error state in a simple TRUE/FALSE manner.
+   *
+   * This is similar to get_error(), but should instead be used to to check to see if there is an error and not check what the error is set to.
+   *
+   * @return bool
+   *   TRUE if an error is assigned and FALSE if no error is assigned.
+   *
+   * @see: get_error()
+   */
+  public function has_error() {
+    // when there is no error flag assigned, its value should be NULL so a simple existence check should be all that is needed.
+    return $this->error instanceof c_base_error;
+  }
+}
+
+/**
+ * A boolean return class representing a return value of either TRUE or FALSE.
+ *
+ * This is used as a return status for a function that does not return any expected valid values.
+ * This class does not have any values of its own.
+ */
+class c_base_return_status extends c_base_return {
+}
+
+/**
+ * A boolean return class representing a return value of TRUE.
+ *
+ * This class will not have any values.
+ */
+class c_base_return_true extends c_base_return_status {
+}
+
+/**
+ * A boolean return class representing a return value of FALSE.
+ *
+ * This class will not have any values.
+ */
+class c_base_return_false extends c_base_return_status {
+}
+
+/**
+ * A return class representing a return value of NULL.
+ *
+ * This class will not have any values.
+ */
+class c_base_return_null extends c_base_return_status {
+}
+
+/**
+ * A return class representing a return value with a specific value.
+ */
+class c_base_return_value extends c_base_return {
+  use t_base_return_value;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+}
+
+/**
+ * A return class whose value is represented as a bool.
+ *
+ * Do not use this class for returning status or errors for functions.
+ * Instead use anything of type c_base_return_status().
+ */
+class c_base_return_bool extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, FALSE);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param bool $value
+   *   Any value so long as it is a bool.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_bool($value)) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return bool|null $value
+   *   The value bool stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !is_bool($this->value)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return bool $value
+   *   The value bool stored within this class.
+   */
+  public function get_value_exact() {
+    if (!is_bool($this->value)) {
+      $this->value = FALSE;
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a string.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ */
+class c_base_return_string extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, '');
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param string $value
+   *   Any value so long as it is a string.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_string($value)) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return string|null $value
+   *   The value array stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !is_string($this->value)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return string $value
+   *   The value string stored within this class.
+   */
+  public function get_value_exact() {
+    if (!is_string($this->value)) {
+      $this->value = '';
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a int.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ * Numeric types are converted to integers and stored as an integer.
+ */
+class c_base_return_int extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, 0);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param int|numeric $value
+   *   Any value so long as it is an integer or a numeric.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_int($value) && !is_numeric($value)) {
+      return FALSE;
+    }
+
+    if (is_numeric($value)) {
+      $this->value = (int) $value;
+    }
+    else {
+      $this->value = $value;
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return int|null $value
+   *   The value integer stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !is_int($this->value)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return int $value
+   *   The value int stored within this class.
+   */
+  public function get_value_exact() {
+    if (!is_int($this->value)) {
+      $this->value = 0;
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a float.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ * Numeric types are converted to float and stored as a float.
+ */
+class c_base_return_float extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, 0.0);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param float|numeric $value
+   *   Any value so long as it is an integer or a numeric.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_float($value) && !is_numeric($value)) {
+      return FALSE;
+    }
+
+    if (is_numeric($value)) {
+      $this->value = (float) $value;
+    }
+    else {
+      $this->value = $value;
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return float|null $value
+   *   The value float stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !is_int($this->value)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return float $value
+   *   The value float stored within this class.
+   */
+  public function get_value_exact() {
+    if (!is_float($this->value)) {
+      $this->value = 0.0;
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as an array.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ * This provides basic functionality for managing keys and values in the class.
+ */
+class c_base_return_array extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, array());
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param array $value
+   *   Any value so long as it is an array.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_array($value)) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Assign the value at a specific index in the array.
+   *
+   * @param array $value
+   *   Any value so long as it is an array.
+   *   NULL is not allowed.
+   * @param string $key
+   *   A key to assign a specific value to.
+   * @param string $type
+   *   (optional) When key is not NULL, a specific known type to assign.
+   *   This does nothing if $key is not provided.
+   *   This is used for validation purposes.
+   *
+   *   Supported known types:
+   *     'bool': a boolean value.
+   *     'int': an integer value.
+   *     'string': a string value.
+   *     'array': an array value.
+   *     'object': an object value.
+   *     'resource': a generic resource value.
+   *     'stream': a stream resource value.
+   *     'socket': a socket resource value.
+   *     'numeric': a string value that represents either an int or float.
+   *     'null': A null value.
+   *     NULL: no specific type requirements.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value_at($value, $key, $type = NULL) {
+    if (!is_string($key) || empty($key)) {
+      return FALSE;
+    }
+
+    // when type is not supplied, return a generic type.
+    if (is_null($type)) {
+      $this->value[$key] = $value;
+      return TRUE;
+    }
+
+    // if type is supplied, it must be string.
+    if (!is_string($type) || empty($type)) {
+      return FALSE;
+    }
+
+    if ($type == 'bool' && !is_bool($value)) {
+      return FALSE;
+    }
+    elseif ($type == 'int' && !is_int($value)) {
+      return FALSE;
+    }
+    elseif ($type == 'float' && !is_float($value)) {
+      return FALSE;
+    }
+    elseif ($type == 'numeric' && !is_numeric($value)) {
+      return FALSE;
+    }
+    elseif ($type == 'string' && !is_string($value)) {
+      return FALSE;
+    }
+    elseif ($type == 'array' && !is_array($value)) {
+      return FALSE;
+    }
+    elseif ($type == 'object' && !is_object($value)) {
+      return FALSE;
+    }
+    elseif ($type == 'resource' && !is_resource($value)) {
+      return FALSE;
+    }
+    elseif ($type == 'stream') {
+      if (!is_resource($value) || get_resource_type($value) != 'stream') {
+        return FALSE;
+      }
+    }
+    elseif ($type == 'socket') {
+      if (!is_resource($value) || get_resource_type($value) != 'socket') {
+        return FALSE;
+      }
+    }
+    elseif ($type == 'null' && !is_null($value)) {
+      return FALSE;
+    }
+
+    $this->value[$key] = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return array|null $value
+   *   The value array stored within this class.
+   *   NULL may be returned if there is no defined valid array.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !is_array($this->value)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return array $value
+   *   The value array stored within this class.
+   */
+  public function get_value_exact() {
+    if (!is_array($this->value)) {
+      $this->value = array();
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value at a specific index in the array.
+   *
+   * Note: ideally, this should return specific c_base_return_* types.
+   *   The problem is that this would then make this class dependent on those type, which I am trying to avoid.
+   *   No c_base_return_* type should use another c_base_return_* as their return values for their non-static functions.
+   *   @todo: This design might be reviewed and changed before this project is finalized.
+   *
+   * @param string $key
+   *   A key to assign a specific value to.
+   * @param string $type
+   *   (optional) When key is not NULL, a specific known type to assign.
+   *   This does nothing if $key is not provided.
+   *   This is used for validation purposes.
+   *
+   *   Supported known types:
+   *     'bool': a boolean value.
+   *     'int': an integer value.
+   *     'string': a string value.
+   *     'array': an array value.
+   *     'object': an object value.
+   *     'resource': a generic resource value.
+   *     'stream': a stream resource value.
+   *     'socket': a socket resource value.
+   *     'numeric': a string value that represents either an int or float.
+   *     'null': A null value.
+   *     NULL: no specific type requirements.
+   *
+   * @return
+   *   Value on success, FALSE otherwise.
+   *   Warning: There is no way to distinguish a return value of FALSE for an error to a valid FALSE when $type is set to 'bool'.
+   */
+  public function get_value_at($key, $type = NULL) {
+    if (!is_string($key) || empty($key)) {
+      return FALSE;
+    }
+
+    // if type is supplied, it must be string.
+    if (!is_null($type) && (!is_string($type) || empty($type))) {
+      return FALSE;
+    }
+
+    if (!is_array($this->value)) {
+      $this->value = array();
+    }
+
+    if (!array_key_exists($key, $this->value)) {
+      return FALSE;
+    }
+
+    if (!is_null($type)) {
+      if ($type == 'bool') {
+        if (!is_bool($this->value[$key])) {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'int') {
+        if (!is_int($this->value[$key])) {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'float') {
+        if (!is_float($this->value[$key])) {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'numeric') {
+        if (!is_numeric($this->value[$key])) {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'string') {
+        if (!is_string($this->value[$key])) {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'array') {
+        if (!is_array($this->value[$key])) {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'object') {
+        if (!is_object($this->value[$key])) {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'resource') {
+        if (!is_resource($this->value[$key])) {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'stream') {
+        if (!is_resource($this->value[$key]) || get_resource_type($this->value[$key]) != 'stream') {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'socket') {
+        if (!is_resource($this->value[$key]) || get_resource_type($this->value[$key]) != 'socket') {
+          return FALSE;
+        }
+      }
+      elseif ($type == 'null') {
+        if (!is_null($this->value[$key])) {
+          return FALSE;
+        }
+      }
+      else {
+        return FALSE;
+      }
+    }
+
+    return $this->value[$key];
+  }
+}
+
+/**
+ * A return class whose value is represented as a generic object.
+ *
+ * This should be used to return either a generic object or used as a base class for extending to a specific object type.
+ * All specific object types should extend this class so that instanceof tests against c_base_return_object are always accurate.
+ */
+class c_base_return_object extends c_base_return_value {
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param object $value
+   *   Any value so long as it is an object.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_object($value)) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return object|null $value
+   *   The value object stored within this class.
+   *   NULL may be returned if there is no defined valid resource.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !is_object($this->value)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a generic resource.
+ *
+ * This should be used to return either a generic resource or used as a base class for extending to a specific resource type.
+ * All specific resource types should extend this class so that instanceof tests against c_base_return_resource are always accurate.
+ */
+class c_base_return_resource extends c_base_return_value {
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param resource $value
+   *   Any value so long as it is an resource.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_resource($value)) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return resource|null $value
+   *   The value resource stored within this class.
+   *   NULL may be returned if there is no defined valid resource.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !is_resource($this->value)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a stream resource.
+ */
+class c_base_return_resource_stream extends c_base_return_resource {
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param stream $value
+   *   Any value so long as it is an resource.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_resource($value) || get_resource_type($value) != 'stream') {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return resource|null $value
+   *   The value resource stored within this class.
+   *   NULL may be returned if there is no defined valid resource.
+   */
+  public function get_value() {
+    if (!is_resource($this->value) || get_resource_type($this->value) != 'stream') {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a socket resource.
+ */
+class c_base_return_resource_socket extends c_base_return_resource {
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param stream $value
+   *   Any value so long as it is an resource.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!is_resource($value) || get_resource_type($value) != 'socket') {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return resource|null $value
+   *   The value resource stored within this class.
+   *   NULL may be returned if there is no defined valid resource.
+   */
+  public function get_value() {
+    if (!is_resource($this->value) || get_resource_type($this->value) != 'socket') {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A generic class for pre-populating specific error return codes.
+ *
+ * This is used to simplify the code by pre-setting the error code.
+ *
+ * @require class c_base_error
+ */
+class c_base_return_error {
+
+  /**
+   * Creates a return boolean TRUE with the error value populated.
+   *
+   * @todo: this is incomplete because the base_error class is not yet written.
+   *
+   * @param todo|null $error
+   *   (optional) a custom error setting.
+   *
+   * @return c_base_return_true
+   *   A c_base_return_true object with the error value populated.
+   */
+  public static function s_true($error = NULL) {
+    $object_error = new c_base_error();
+
+    $object_return = new c_base_return_true();
+    $object_return->set_error($object_error);
+
+    if (!is_null($error)) {
+      // @todo: do something with the code.
+    }
+
+    return $object_return;
+  }
+
+  /**
+   * Creates a return boolean TRUE with the error value populated.
+   *
+   * @todo: this is incomplete because the base_error class is not yet written.
+   *
+   * @param todo|null $error
+   *   (optional) a custom error setting.
+   *
+   * @return c_base_return_false
+   *   A c_base_return_true object with the error value populated.
+   */
+  public static function s_false($error = NULL) {
+    $object_error = new c_base_error();
+
+    $object_return = new c_base_return_false();
+    $object_return->set_error($object_error);
+
+    if (!is_null($error)) {
+      // @todo: do something with the code.
+    }
+
+    return $object_return;
+  }
+
+  /**
+   * Creates a return boolean TRUE with the error value populated.
+   *
+   * @todo: this is incomplete because the base_error class is not yet written.
+   *
+   * @param $value
+   *   A value to provide
+   * @param $class
+   *   A custom class name.
+   * @param todo|null $error
+   *   (optional) a custom error setting.
+   *
+   * @return c_base_return_false|c_base_return_value
+   *   A c_base_return_value object is returned with the error value populated
+   *   If the passed class is invalid or not of type c_base_return_value, then a c_base_return_true object with the error value populated.
+   */
+  public static function s_value($value, $class, $error = NULL) {
+    if (!class_exists($class) || !($class instanceof c_base_return_value)) {
+      return self::s_false($error);
+    }
+
+    $object_error = new c_base_error();
+
+    $object_return = new $class();
+    $object_return->set_error($object_error);
+    $object_return->set_value($value);
+
+    if (!is_null($error)) {
+      // @todo: do something with the code.
+    }
+
+    return $object_return;
+  }
+}
diff --git a/common/base/classes/base_rfc_char.php b/common/base/classes/base_rfc_char.php
new file mode 100644 (file)
index 0000000..99e682b
--- /dev/null
@@ -0,0 +1,940 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing common rfc character testing cases.
+ */
+
+// include required files.
+require_once('common/base/classes/base_ascii.php');
+require_once('common/base/classes/base_utf8.php');
+
+
+/**
+ * A class for managing common rfc character testing cases.
+ *
+ * This currently utilizes some of the rules defined in the following rfcs:
+ * - rfc 4234
+ * - rfc 5234
+ * - rfc 6532
+ *
+ * Most of the rules reference the following rfcs as a guideline (but many of the rules defined therein are replaced by rules in the later rfcs):
+ * - rfc 822
+ *
+ * This checks a single character, therefore:
+ * - This does not perform tests that require more than a single character to identify what the text represents.
+ * - This expects UTF_8 characters to be provided as a single 32-bit ordinal integer (as opposed to two distinct integers).
+ *
+ * WARNING: I am very new to UTF_8 coding and may be interpreting the rfc documentation incorrectly.
+ *
+ * Note: rfc 6532 does not mention whitespace of any kind and so non-ASCII "whitespace" is _not_ considered whitespace by this standard.
+ *
+ * @see: https://tools.ietf.org/html/rfc822
+ * @see: https://tools.ietf.org/html/rfc4234
+ * @see: https://tools.ietf.org/html/rfc5234
+ * @see: https://tools.ietf.org/html/rfc5335
+ * @see: https://tools.ietf.org/html/rfc6530
+ * @see: https://tools.ietf.org/html/rfc6531
+ * @see: https://tools.ietf.org/html/rfc6532
+ * @see: https://tools.ietf.org/html/rfc6533
+ * @see: http://www.herongyang.com/Unicode/UTF-8-UTF-8-Encoding.html
+ *
+ * @require class c_base_ascii
+ * @require class c_base_utf8
+ */
+abstract class c_base_rfc_char {
+
+  /**
+   * Check to see if character is: text.
+   *
+   * dtext = ASCII character codes 33, 35->91 and 93-126.
+   * dtext = UTF_8-2, UTF_8-3, UTF_8-4.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc5234
+   * @see: https://tools.ietf.org/html/rfc6532
+   */
+  protected function pr_rfc_char_is_text($ordinal) {
+    if (($ordinal > c_base_ascii::SPACE && $ordinal < c_base_ascii::BRACKET_OPEN) || ($ordinal > c_base_ascii::BRACKET_CLOSE && $ordinal < c_base_ascii::DELETE)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_1($ordinal)) {
+      // UTF_8-1/ASCII characters have already been checked, so return FALSE without doing additional pointless processing.
+      return FALSE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_2($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_3($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_4($ordinal)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: ctext.
+   *
+   * ctext = ASCII character codes 33->39, 42->91, and 93-126.
+   * ctext = UTF_8-2, UTF_8-3, UTF_8-4.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc5234
+   * @see: https://tools.ietf.org/html/rfc6532
+   */
+  protected function pr_rfc_char_is_ctext($ordinal) {
+    if (($ordinal > c_base_ascii::SPACE && $ordinal < c_base_ascii::PARENTHESIS_OPEN) || ($ordinal > c_base_ascii::PARENTHESIS_CLOSE && $ordinal < c_base_ascii::SLASH_BACKWARD) || ($ordinal > c_base_ascii::SLASH_BACKWARD && $ordinal < c_base_ascii::DELETE)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_1($ordinal)) {
+      // UTF_8-1/ASCII characters have already been checked, so return FALSE without doing additional pointless processing.
+      return FALSE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_2($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_3($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_4($ordinal)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: dtext.
+   *
+   * dtext = ASCII character codes 33->90 and 94-126.
+   * dtext = UTF_8-2, UTF_8-3, UTF_8-4.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc5234
+   * @see: https://tools.ietf.org/html/rfc6532
+   */
+  protected function pr_rfc_char_is_dtext($ordinal) {
+    if (($ordinal > c_base_ascii::SPACE && $ordinal < c_base_ascii::BRACKET_OPEN) || ($ordinal > c_base_ascii::BRACKET_CLOSE && $ordinal < c_base_ascii::DELETE)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_1($ordinal)) {
+      // UTF_8-1/ASCII characters have already been checked, so return FALSE without doing additional pointless processing.
+      return FALSE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_2($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_3($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_4($ordinal)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: qtext.
+   *
+   * qtext = ASCII character codes 33, 35->91 and 93-126.
+   * qtext = UTF_8-2, UTF_8-3, UTF_8-4.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc5234
+   * @see: https://tools.ietf.org/html/rfc6532
+   */
+  protected function pr_rfc_char_is_qtext($ordinal) {
+    if ($ordinal == c_base_ascii::SPACE) {
+      return TRUE;
+    }
+
+    if (($ordinal > c_base_ascii::QUOTE_DOUBLE && $ordinal < c_base_ascii::SLASH_BACKWARD) || ($ordinal > c_base_ascii::SLASH_BACKWARD && $ordinal < c_base_ascii::DELETE)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_1($ordinal)) {
+      // UTF_8-1/ASCII characters have already been checked, so return FALSE without doing additional pointless processing.
+      return FALSE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_2($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_3($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_4($ordinal)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: vchar.
+   *
+   * vchar = Visible ASCII character codes 33->126.
+   * vchar = UTF_8-2, UTF_8-3, UTF_8-4.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc5234
+   * @see: https://tools.ietf.org/html/rfc6532
+   */
+  protected function pr_rfc_char_is_vchar($ordinal) {
+    if ($ordinal > c_base_ascii::SPACE && $ordinal < c_base_ascii::DELETE) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_1($ordinal)) {
+      // UTF_8-1/ASCII characters have already been checked, so return FALSE without doing additional pointless processing.
+      return FALSE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_2($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_3($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_4($ordinal)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: tchar.
+   *
+   * tchar = Visible ASCII character codes 33->126, excluding: DQUOTE and "(),/:;<=>?@[\]{}".
+   * tchar = UTF_8-2, UTF_8-3, UTF_8-4.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.2.6
+   */
+  protected function pr_rfc_char_is_tchar($ordinal) {
+    if ($ordinal > c_base_ascii::SPACE && $ordinal < c_base_ascii::DELETE) {
+      if ($ordinal == c_base_ascii::QUOTE_DOUBLE || $ordinal == c_base_ascii::PARENTHESIS_OPEN || $ordinal == c_base_ascii::PARENTHESIS_CLOSE ) {
+        return FALSE;
+      }
+
+      if ($ordinal == c_base_ascii::COMMA || $ordinal == c_base_ascii::SLASH_FORWARD) {
+        return FALSE;
+      }
+
+      if (($ordinal > c_base_ascii::NINE && $ordinal < c_base_ascii::UPPER_A) || ($ordinal > c_base_ascii::UPPER_Z && $ordinal < c_base_ascii::CARET)) {
+        return FALSE;
+      }
+
+      if ($ordinal == c_base_ascii::BRACE_OPEN || $ordinal == c_base_ascii::BRACE_CLOSE) {
+        return FALSE;
+      }
+
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_1($ordinal)) {
+      // UTF_8-1/ASCII characters have already been checked, so return FALSE without doing additional pointless processing.
+      return FALSE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_2($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_3($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_4($ordinal)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: tchar68.
+   *
+   * This doesn't directly exist, but is supplied for use with token68 in the same way tchar is used by token.
+   *
+   * tchar = Visible ASCII character codes: 43, 45->57, 65->90, 95, 97->122, 126
+   * tchar = UTF_8-2, UTF_8-3, UTF_8-4.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#appendix-C
+   */
+  protected function pr_rfc_char_is_tchar68($ordinal) {
+    if ($ordinal > c_base_ascii::COMMA && $ordinal < c_base_ascii::COLON) {
+      return TRUE;
+    }
+
+    if (($ordinal > c_base_ascii::AT && $ordinal < c_base_ascii::BRACKET_OPEN) || ($ordinal > c_base_ascii::GRAVE && $ordinal < c_base_ascii::BRACE_OPEN)) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::UNDERSCORE || $ordinal == c_base_ascii::TILDE) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_1($ordinal)) {
+      // UTF_8-1/ASCII characters have already been checked, so return FALSE without doing additional pointless processing.
+      return FALSE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_2($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_3($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_4($ordinal)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: CRLF.
+   *
+   * CRLF = carraige return or line feed (new line), codes 10, 13.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc5234#appendix-B
+   */
+  protected function pr_rfc_char_is_crlf($ordinal) {
+    return $ordinal == c_base_ascii::NEW_LINE || $ordinal == c_base_ascii::CARRIAGE_RETURN;
+  }
+
+  /**
+   * Check to see if character is: WSP.
+   *
+   * WSP = space or horizontal tab, codes 9, 32.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   * @see: https://tools.ietf.org/html/rfc5234#appendix-B
+   */
+  protected function pr_rfc_char_is_wsp($ordinal) {
+    return $ordinal == c_base_ascii::TAB_HORIZONTAL || $ordinal == c_base_ascii::SPACE;
+  }
+
+  /**
+   * Check to see if character is: SP.
+   *
+   * SP = space, codes 32.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   */
+  protected function pr_rfc_char_is_sp($ordinal) {
+    return $ordinal == c_base_ascii::SPACE;
+  }
+
+  /**
+   * Check to see if character is: special.
+   *
+   * special = special/reserved visible characters, codes 34, 40, 41, 44, 46, 58, 59, 60, 62, 64, 91, 92, 93
+   * - which are the characters: '(', ')', '<', '>', '[', ']', ':', ';', '@', '\', ',', '.', '"'.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc5234
+   */
+  protected function pr_rfc_char_is_special($ordinal) {
+    switch ($ordinal) {
+      case c_base_ascii::PARENTHESIS_OPEN:
+      case c_base_ascii::PARENTHESIS_CLOSE:
+      case c_base_ascii::LESS_THAN:
+      case c_base_ascii::GREATER_THAN:
+      case c_base_ascii::BRACKET_OPEN:
+      case c_base_ascii::BRACKET_CLOSE:
+      case c_base_ascii::COLON:
+      case c_base_ascii::COLON_SEMI:
+      case c_base_ascii::AT:
+      case c_base_ascii::SLASH_BACKWARD:
+      case c_base_ascii::COMMA:
+      case c_base_ascii::PERIOD:
+      case c_base_ascii::QUOTE_DOUBLE:
+        return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: atext.
+   *
+   * atext = Visible ASCII characters, discluding special.
+   * atext = UTF_8-2, UTF_8-3, UTF_8-4.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc5234
+   * @see: https://tools.ietf.org/html/rfc6532
+   */
+  protected function pr_rfc_char_is_atext($ordinal) {
+    return !$this->pr_rfc_char_is_special($ordinal) && $this->pr_rfc_char_is_vchar($ordinal);
+  }
+
+  /**
+   * Check to see if character is: FWS or LWSP.
+   *
+   * FWS = space, tab, or CRLF, codes 9, 10, 13, 32.
+   * LWSP = identical to FWS (defined in rfc 4234).
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   * @see: https://tools.ietf.org/html/rfc5234
+   */
+  protected function pr_rfc_char_is_fws($ordinal) {
+    return $this->pr_rfc_char_is_crlf($ordinal) || $this->pr_rfc_char_is_wsp($ordinal);
+  }
+
+  /**
+   * Check to see if character is: ALPHA.
+   *
+   * ALPHA = Visible ASCII character codes 65->90, 97->122.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   */
+  protected function pr_rfc_char_is_alpha($ordinal) {
+    if (($ordinal > c_base_ascii::AT && $ordinal < c_base_ascii::BRACKET_OPEN) || ($ordinal > c_base_ascii::GRAVE && $ordinal < c_base_ascii::BRACE_OPEN)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: BIT.
+   *
+   * BIT = ASCII character codes 48, 49.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   */
+  protected function pr_rfc_char_is_bit($ordinal) {
+    if ($ordinal == c_base_ascii::ZERO || $ordinal == c_base_ascii::ONE) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: CHAR.
+   *
+   * CHAR = ASCII character codes 1->127.
+   * CHAR = UTF_8-2, UTF_8-3, or UTF_8-4.
+   *
+   * Another name for this is UTF_8-OCTETS, except that based on the standard UTF_8-OCTETS refers to 1 or more UTF_8-chars.
+   * Given that this is a test against a single character, UTF_8-char and UTF_8-octets are synonymous.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: $this->pr_rfc_char_is_octet()
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   * @see: https://tools.ietf.org/html/rfc3629
+   * @see: https://tools.ietf.org/html/rfc6532
+   */
+  protected function pr_rfc_char_is_char($ordinal) {
+    // null is not allowed, otherwise this is identical to UTF_8-octet.
+    if ($ordinal == c_base_ascii::NULL) {
+      return FALSE;
+    }
+
+    return $this->pr_rfc_char_is_octet($ordinal);
+  }
+
+  /**
+   * Check to see if character is: UTF_8-CHAR or UTF_8-OCTET.
+   *
+   * UTF_8-CHAR = ASCII character codes 0->127.
+   * UTF_8-CHAR = UTF_8-1, UTF_8-2, UTF_8-3, or UTF_8-4.
+   *
+   * Another name for this is UTF_8-OCTETS, except that based on the standard UTF_8-OCTETS refers to 1 or more UTF_8-chars.
+   * Given that this is a test against a single character, UTF_8-char and UTF_8-octets are synonymous.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   * @see: https://tools.ietf.org/html/rfc3629
+   * @see: https://tools.ietf.org/html/rfc6532
+   */
+  protected function pr_rfc_char_is_octet($ordinal) {
+    if ($this->pr_rfc_char_is_utf8_1($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_2($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_3($ordinal)) {
+      return TRUE;
+    }
+
+    if ($this->pr_rfc_char_is_utf8_4($ordinal)) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: CR.
+   *
+   * CR = Carriage Return, codes 13.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   */
+  protected function pr_rfc_char_is_cr($ordinal) {
+    if ($ordinal == c_base_ascii::CARRIAGE_RETURN) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: LF.
+   *
+   * LF = line feed (new line), codes 10.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   */
+  protected function pr_rfc_char_is_lf($ordinal) {
+    return $ordinal == c_base_ascii::NEW_LINE;
+  }
+
+  /**
+   * Check to see if character is: CTL.
+   *
+   * CTL = ASCII control character codes 1->31, 127.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   */
+  protected function pr_rfc_char_is_ctl($ordinal) {
+    if (($ordinal > c_base_ascii::NULL && $ordinal < c_base_ascii::SPACE) || $ordinal == c_base_ascii::DELETE) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: DIGIT.
+   *
+   * DIGIT = ASCII character codes 48->57.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   */
+  protected function pr_rfc_char_is_digit($ordinal) {
+    if ($ordinal > c_base_ascii::SLASH_FORWARD && $ordinal < c_base_ascii::COLON) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: HEXDIG.
+   *
+   * HEXDIG = ASCII character codes 48->57, 65-70, 97->103.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   * @param bool $lower_case
+   *   (optional) When TRUE, lower case a-f will also be supported (ascii codes 97-103).
+   *   Using lower case might be a violation of the standard, but this is unclear to me.
+   *   Most things are forced to lower case for simplicity during tests anyway so this might be a necessary functionality.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   */
+  protected function pr_rfc_char_is_hexdigit($ordinal, $lower_case = FALSE) {
+    if ($ordinal > c_base_ascii::SLASH_FORWARD && $ordinal < c_base_ascii::COLON) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::GRAVE && $ordinal < c_base_ascii::LOWER_G) {
+      return TRUE;
+    }
+
+    if ($lower_case) {
+      return FALSE;
+    }
+
+    if ($ordinal > c_base_ascii::AT && $ordinal < c_base_ascii::UPPER_G) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: DQUOTE.
+   *
+   * DQUOTE = ASCII character codes 34.
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc4234
+   */
+  protected function pr_rfc_char_is_dquote($ordinal) {
+    if ($ordinal == c_base_ascii::QUOTE_DOUBLE) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: unreserved.
+   *
+   * unreserved = ASCII character codes 45, 46, 48->57, 65->90, 95, 97->122, 126
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * https://tools.ietf.org/html/rfc3986#appendix-A
+   */
+  protected function pr_rfc_char_is_unreserved($ordinal) {
+    if (self::pr_rfc_char_is_alpha($ordinal) || self::pr_rfc_char_is_digit($ordinal) ) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::MINUS || $ordinal == c_base_ascii::PERIOD || $ordinal == c_base_ascii::UNDERSCORE || $ordinal == c_base_ascii::TILDE) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: unreserved.
+   *
+   * reserved = ASCII character codes 33, 35, 36, 38->44, 47, 58, 59, 61, 63, 64, 91, 93
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986#appendix-A
+   */
+  protected function pr_rfc_char_is_reserved($ordinal) {
+    if ($ordinal == c_base_ascii::EXCLAMATION || $ordinal == c_base_ascii::HASH || $ordinal == c_base_ascii::DOLLAR) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::PERCENT && $ordinal < c_base_ascii::MINUS) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::SLASH_FORWARD || $ordinal == c_base_ascii::COLON || $ordinal == c_base_ascii::COLON_SEMI || $ordinal == c_base_ascii::EQUAL) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::QUESTION_MARK || $ordinal == c_base_ascii::AT || $ordinal == c_base_ascii::BRACKET_OPEN || $ordinal == c_base_ascii::BRACKET_CLOSE) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: gen-delims.
+   *
+   * gen-delims = ASCII character codes 35, 47, 58, 63, 64, 91, 93
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986#appendix-A
+   */
+  protected function pr_rfc_char_is_gen_delims($ordinal) {
+    if ($ordinal == c_base_ascii::COLON || $ordinal == c_base_ascii::SLASH_FORWARD || $ordinal == c_base_ascii::QUESTION_MARK || $ordinal == c_base_ascii::HASH) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::BRACKET_OPEN || $ordinal == c_base_ascii::BRACKET_CLOSE || $ordinal == c_base_ascii::AT) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: sub-delims.
+   *
+   * sub-delims = ASCII character codes 33, 36, 38->44, 59, 61
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE on match, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc3986#appendix-A
+   */
+  protected function pr_rfc_char_is_sub_delims($ordinal) {
+    if ($ordinal == c_base_ascii::EXCLAMATION || $ordinal == c_base_ascii::DOLLAR) {
+      return TRUE;
+    }
+
+    if ($ordinal > c_base_ascii::PERCENT && $ordinal < c_base_ascii::MINUS) {
+      return TRUE;
+    }
+
+    if ($ordinal == c_base_ascii::COLON_SEMI || $ordinal == c_base_ascii::EQUAL) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Check to see if character is: UTF_8-1 (ASCII).
+   *
+   * The standard claims the following ranges:
+   * - [0x00 -> 0x7f]
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE when UTF_8-1 or ASCII, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc3629
+   */
+  protected function pr_rfc_char_is_utf8_1($ordinal) {
+    return ($ordinal & c_base_utf8::MASK_1) == 0;
+  }
+
+  /**
+   * Check to see if character is: UTF_8-2 (ASCII).
+   *
+   * The standard claims the following ranges:
+   * - [0xc2 -> 0xdf] [0x80 -> 0xbf]
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE when UTF_8-1 or ASCII, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc3629
+   */
+  protected function pr_rfc_char_is_utf8_2($ordinal) {
+    return ($ordinal & c_base_utf8::MASK_2) == 0 && ($ordinal & c_base_utf8::MARK_2) == c_base_utf8::MARK_2;
+  }
+
+  /**
+   * Check to see if character is: UTF_8-3 (ASCII).
+   *
+   * The standard claims the following ranges:
+   * - [0xe0] [0xa0 -> 0xbf] [0x80 -> 0xbf]
+   * - [0xe1 -> 0xec] [0x80 -> 0xbf] [0x80 -> 0xbf]
+   * - [0xed] [0x80 -> 0x9f] [0x80 -> 0xbf]
+   * - [0xee -> 0xef] [0x80 -> 0xbf] [0x80 -> 0xbf]
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE when UTF_8-1 or ASCII, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc3629
+   */
+  protected function pr_rfc_char_is_utf8_3($ordinal) {
+    return ($ordinal & c_base_utf8::MASK_3) == 0 && ($ordinal & c_base_utf8::MARK_3) == c_base_utf8::MARK_3;
+  }
+
+  /**
+   * Check to see if character is: UTF_8-4 (ASCII).
+   *
+   * The standard claims the following ranges:
+   * - [0xf0] [0x90 -> 0xbf] [0x80 -> 0xbf] [0x80 -> 0xbf]
+   * - [0xf1 -> 0xf3] [0x80 -> 0xbf] [0x80 -> 0xbf] [0x80 -> 0xbf]
+   * - [0xf4] [0x80 -> 0x8f] [0x80 -> 0xbf] [0x80 -> 0xbf]
+   *
+   * @param int $ordinal
+   *   A code representing a single character to test.
+   *
+   * @return bool
+   *   TRUE when UTF_8-1 or ASCII, FALSE otherwise.
+   *
+   * @see: https://tools.ietf.org/html/rfc822
+   * @see: https://tools.ietf.org/html/rfc3629
+   */
+  protected function pr_rfc_char_is_utf8_4($ordinal) {
+    return ($ordinal & c_base_utf8::MARK_4) == c_base_utf8::MARK_4;
+  }
+}
diff --git a/common/base/classes/base_rfc_string.php b/common/base/classes/base_rfc_string.php
new file mode 100644 (file)
index 0000000..61adedf
--- /dev/null
@@ -0,0 +1,2444 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing common rfc string testing cases.
+ */
+
+// include required files.
+require_once('common/base/classes/base_ascii.php');
+require_once('common/base/classes/base_utf8.php');
+require_once('common/base/classes/base_rfc_char.php');
+
+/**
+ * A class for managing common rfc string testing cases.
+ *
+ * This checks a a string of characters.
+ * The c_base_rf_string_is_* functions that require specific characters to start, such as DQUOTE, assume that the start position is after the initial special character, such as DQUOTE.
+ *
+ * This currently utilizes some of the rules defined in the following rfcs:
+ * - rfc 4234
+ * - rfc 5234
+ * - rfc 6532
+ *
+ * Most of the rules reference the following rfcs as a guideline (but many of the rules defined therein are replaced by rules in the later rfcs):
+ * - rfc 822
+ *
+ * WARNING: I am very new to utf8 coding and may be interpreting the rfc documentation incorrectly.
+ *
+ * Note: rfc 6532 does not mention whitespace of any kind and so non-ASCII "whitespace" is _not_ considered whitespace by this standard.
+ *
+ * @see: https://tools.ietf.org/html/rfc822
+ * @see: https://tools.ietf.org/html/rfc4234
+ * @see: https://tools.ietf.org/html/rfc5234
+ * @see: https://tools.ietf.org/html/rfc5335
+ * @see: https://tools.ietf.org/html/rfc6530
+ * @see: https://tools.ietf.org/html/rfc6531
+ * @see: https://tools.ietf.org/html/rfc6532
+ * @see: https://tools.ietf.org/html/rfc6533
+ * @see: http://www.herongyang.com/Unicode/UTF-8-UTF-8-Encoding.html
+ *
+ * @require class c_base_ascii
+ * @require class c_base_utf8
+ * @require class c_base_rfc_char
+ */
+abstract class c_base_rfc_string extends c_base_rfc_char {
+  const STOP_AT_CLOSING_CHARACTER = -1;
+
+  /**
+   * Converts a string into a ordinals array and a characters array.
+   *
+   * The ordinals and characters returned by this are utf8-friendly such that the caller need only use a counter to navigate.
+   * - This eliminates the need for constant checking for utf8 or non-utf8 while traversing the string.
+   *
+   * @param string $text
+   *   The string to convert into ordinals array and characters array.
+   *
+   * @return array
+   *   An array containing:
+   *     ordinals: an array of ordinal values representing the string.
+   *     characters: an array of characters representing the string (utf8-safe).
+   *     invalid: FALSE on success, TRUE otherwise.
+   *
+   * @see: c_base_utf8::s_string_to_ordinals()
+   * @see: c_base_utf8::s_ordinals_to_string_array()
+   */
+  protected function pr_rfc_string_prepare($text) {
+    $result = array(
+      'ordinals' => array(),
+      'characters' => array(),
+      'invalid' => FALSE,
+    );
+
+    $ordinals = c_base_utf8::s_string_to_ordinals($text);
+    if ($ordinals instanceof c_base_return_false) {
+      unset($ordinals);
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    $result['ordinals'] = $ordinals->get_value_exact();
+    unset($ordinals);
+
+    $characters = c_base_utf8::s_ordinals_to_string_array($result['ordinals']);
+    if ($characters instanceof c_base_return_error) {
+      unset($characters);
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    $result['characters'] = $characters->get_value_exact();
+    unset($characters);
+
+    return $result;
+  }
+
+  /**
+   * Converts a string into a ordinals array but not a characters array.
+   *
+   * This should be somewhat faster if characters are not intended to be used.
+   *
+   * @param string $text
+   *   The string to convert into ordinals array and characters array.
+   *
+   * @return array
+   *   An array containing:
+   *     ordinals: an array of ordinal values representing the string.
+   *     characters: an empty array.
+   *     invalid: FALSE on success, TRUE otherwise.
+   *
+   * @see: c_base_utf8::s_string_to_ordinals()
+   * @see: c_base_utf8::s_ordinals_to_string_array()
+   */
+  protected function pr_rfc_string_prepare_ordinals($text) {
+    $result = array(
+      'ordinals' => array(),
+      'characters' => array(),
+      'invalid' => FALSE,
+    );
+
+    $ordinals = c_base_utf8::s_string_to_ordinals($text);
+    if ($ordinals instanceof c_base_return_error) {
+      unset($ordinals);
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    $result['ordinals'] = $ordinals->get_value_exact();
+    unset($ordinals);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: quoted-string.
+   *
+   * This assumes that the start position is immediately after the initial DQUOTE.
+   * This will stop at the closing DQUOTE or if there is trailing valid CFWS, then it will stop at the end of the CFWS.
+   *
+   * A quoted-string has the following syntax:
+   * - [CFWS] DQUOTE *([FWS] qtext / "\"DQUOTE) [FWS] DQUOTE [CFWS]
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *   If self::STOP_AT_CLOSING_CHARACTER, then stop at the end of a double quote and do no further processing.
+   *
+   * @return array
+   *   The processed information, with comments separated:
+   *   - 'comments': an array containg the comment before and comment after, if found.
+   *   - 'text': A string containing the processed quoted string.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_qtext()
+   */
+  protected function pr_rfc_string_is_quoted_string($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'comments' => array(
+        'before' => NULL,
+        'after' => NULL,
+      ),
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    $stop_at_closing_quote = FALSE;
+    if ($stop < 0) {
+      if ($stop == self::STOP_AT_CLOSING_CHARACTER) {
+        $stop_at_closing_quote = TRUE;
+      }
+
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    $comment_first = FALSE;
+    $comment_last = FALSE;
+    $quote_closed = FALSE;
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::SLASH_BACKWARD) {
+        if ($quote_closed) {
+          // only comments and FWS are allowed after $closing_quote is reached.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        if (!$comment_first) {
+          // anything that is not a comment and not FWS means that comments are no longer allowed at this point until end of quoted string.
+          $comment_first = TRUE;
+        }
+
+        // check for and handle delimiters.
+        $result['current']++;
+
+        if ($ordinals[$result['current']] == c_base_ascii::QUOTE_DOUBLE) {
+          $result['text'] .= $characters[$result['current']];
+          continue;
+        }
+
+        if ($ordinals[$result['current']] == c_base_ascii::SLASH_BACKWARD) {
+          $result['text'] .= $characters[$result['current']];
+          continue;
+        }
+
+        $result['current']--;
+      }
+      elseif ($code == c_base_ascii::QUOTE_DOUBLE) {
+        if ($quote_closed) {
+          // double quote may be supplied only once.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        if ($stop_at_closing_quote) {
+          break;
+        }
+
+        $quote_closed = TRUE;
+        continue;
+      }
+      elseif ($code == c_base_ascii::PARENTHESIS_OPEN) {
+        if ($comment_first || $comment_last) {
+          // there may be only 1 comment at the start and only 1 comment at the end.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $parsed = $this->pr_rfc_string_is_comment($ordinals, $characters, $result['current'], $stop);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          unset($parsed);
+          break;
+        }
+
+        if ($quote_closed) {
+          $comment_last = TRUE;
+          $results['comments']['after'] = $parsed['comment'];
+        }
+        else {
+          $comment_first = TRUE;
+          $results['comments']['before'] = $parsed['comment'];
+        }
+        unset($parsed);
+      }
+      elseif ($code == c_base_ascii::PARENTHESIS_CLOSE) {
+        // an isolated parenthesis is invald.
+        $result['invalid'] = TRUE;
+        break;
+      }
+      elseif ($quote_closed) {
+        if ($this->pr_rfc_char_is_fws($code)) {
+          continue;
+        }
+
+        // only comments and FWS are allowed after $closing_quote is reached.
+        $result['invalid'] = TRUE;
+        break;
+      }
+      else {
+        if (!$this->pr_rfc_char_is_ctext($code) && !$this->pr_rfc_char_is_fws($code)) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        if (!$comment_first) {
+          // anything that is not a comment and not FWS means that comments are no longer allowed at this point until end of quoted string.
+          $comment_first = TRUE;
+        }
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+    unset($comment_first);
+    unset($comment_last);
+    unset($quote_closed);
+    unset($stop_at_closing_quote);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: entity-tag.
+   *
+   * This assumes that the start position is immediately after the initial DQUOTE.
+   * This will stop at the closing DQUOTE.
+   *
+   * Because the start position after the initial DQUOTE, the calling function must handle the "W/" characters.
+   *
+   * An entity-tag has the following syntax:
+   * - 1*(W/) DQUOTE (vchar, except double qoutes) DQUOTE
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed or the closing character is found.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed entity tag.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_qtext()
+   */
+  protected function pr_rfc_string_is_entity_tag($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::QUOTE_DOUBLE) {
+        break;
+      }
+      elseif (!$this->pr_rfc_char_is_vchar($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: comment.
+   *
+   * This assumes that the start position is immediately after the initial "(".
+   *
+   * Comments allow for delimited text as well as other comments to be embedded.
+   * - This function does not recurse embedded comments.
+   * - Embedded comments are still processed, so an open parenthesis must be followed by a closing parenthesis or the string is considered invalid.
+   *
+   * A comment has the following syntax:
+   * - "(" *([FWS] ctext / "\"DQUOTE / comment) [FWS] ")"
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param null|int $start
+   *   (optional) Specify a starting point in which to begin processing.
+   * @param null|int $stop
+   *   (optional) Specify a stopping point in which to end processing.
+   *
+   * @return array
+   *   The processed information, with comments separated:
+   *   - 'comment': a string containing the comment or NULL if no comments defined (such as an empty comment).
+   *   - 'current': an integer representing the position the counter where processing stopped.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   */
+  protected function pr_rfc_string_is_comment($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'comment' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    $comment_depth = 0;
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::SLASH_BACKWARD) {
+        // check for and handle delimiters.
+        $result['current']++;
+
+        if ($ordinals[$result['current']] == c_base_ascii::QUOTE_DOUBLE) {
+          $result['comment'] .= $characters[$result['current']];
+
+          continue;
+        }
+
+        if ($ordinals[$result['current']] == c_base_ascii::SLASH_BACKWARD) {
+          $result['comment'] .= $characters[$result['current']];
+
+          continue;
+        }
+
+        $result['current']--;
+      }
+      elseif ($code == c_base_ascii::PARENTHESIS_OPEN) {
+        // look for open-parenthesis to handle comments within a comment.
+        $comment_depth++;
+      }
+      elseif ($code == c_base_ascii::PARENTHESIS_CLOSE) {
+        // handle end of comment.
+        if ($comment_depth == 0) {
+          // the current position will remain on the closing ')'.
+          // use -1 to designate that the comment has been properly closed.
+          $comment_depth = -1;
+          break;
+        }
+        else {
+          $comment_depth--;
+        }
+      }
+      elseif (!$this->pr_rfc_char_is_ctext($code) && !$this->pr_rfc_char_is_fws($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['comment'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    if ($comment_depth > -1) {
+      $result['invalid'] = TRUE;
+    }
+    unset($comment_depth);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: digit.
+   *
+   * A string that is a digit has the following syntax:
+   * - 1*(digit)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed entity tag.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_is_digit()
+   */
+  protected function pr_rfc_string_is_digit($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_digit($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: token.
+   *
+   * A string that is a digit has the following syntax:
+   * - 1*(tchar)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed entity tag (the entire string, not broken down into individual tokens).
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_is_tchar()
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.2.6
+   */
+  protected function pr_rfc_string_is_token($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_tchar($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: token68.
+   *
+   * A string that is a digit has the following syntax:
+   * - 1*(tchar68)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed token (the entire string, not broken down into individual tokens).
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_is_tchar()
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.2.6
+   */
+  protected function pr_rfc_string_is_token68($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_tchar68($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: token_quoted.
+   *
+   * This is not a literal standard, but is implied in things such as 'cache-control'.
+   *
+   * A string that is a digit has the following syntax:
+   * - 1*(1*(tchar) / quoted-string)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed entity tag (the entire string, not broken down into individual tokens).
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_is_tchar()
+   * @see: https://tools.ietf.org/html/rfc7230#section-3.2.6
+   * @see: https://tools.ietf.org/html/rfc7234#section-5.2.3
+   */
+  protected function pr_rfc_string_is_token_quoted($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    $not_quoted = FALSE;
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::QUOTE_DOUBLE) {
+        if ($not_quoted) {
+          // if the first, non-whitespace, character is not a quote, then a quote anywhere else is invalid.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $parsed = $this->pr_rfc_string_is_quoted_string($ordinals, $characters, $result['current'], self::STOP_AT_CLOSING_CHARACTER);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        // the closing quote must be the last value, so if the stop point was not reached at the closing quote position, then fail.
+        if ($stop != ($result['current'] + 1)) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $result['text'] = $parsed['text'];
+        unset($parsed);
+
+        break;
+      }
+      elseif (!$this->pr_rfc_char_is_tchar($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $not_quoted = TRUE;
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+    unset($not_quoted);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: credentials.
+   *
+   * A string that is a digit has the following syntax:
+   * - token [ 1*(ws) ( token68 / [ ( "," / token *(ws) "=" *(ws) ( token / quoted-string ) ) *( *(ws) "," [ *(ws) token *(ws) "=" *(ws) ( token / quoted-string ) ] ) ] ) ]
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed entity tag.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: https://tools.ietf.org/html/rfc7235#appendix-C
+   */
+  protected function pr_rfc_string_is_credentials($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    // @todo: this looks like a lot of work, so deal with this at some point in the future because this is a very low priority function.
+    $result['invalid'] = TRUE;
+/*
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_digit($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+*/
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: range.
+   *
+   * A string that is a digit has the following syntax:
+   * - ???
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed entity tag.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: https://tools.ietf.org/html/rfc7233#appendix-D
+   */
+  protected function pr_rfc_string_is_range($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    // @todo: this looks like a lot of work, so deal with this at some point in the future because this is a low to moderate priority function.
+    $result['invalid'] = TRUE;
+/*
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_digit($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+*/
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: vchar, wsp.
+   *
+   * A string that has the following syntax:
+   * - 1*(vchar / wsp)
+   *
+   * This is for generic text that is any basic vchar or whitespace.
+   * This will often be used as the default when no particular syntax is specified.
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed text.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_is_vchar()
+   * @see: base_rfc_char::pr_rfc_char_is_wsp()
+   */
+  protected function pr_rfc_string_is_basic($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_vchar($code) && !$this->pr_rfc_char_is_wsp($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: text.
+   *
+   * A string that has the following syntax:
+   * - 1*(text)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed text.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_is_text()
+   */
+  protected function pr_rfc_string_is_text($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_text($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: atext.
+   *
+   * A string that has the following syntax:
+   * - 1*(atext)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed text.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_is_atext()
+   */
+  protected function pr_rfc_string_is_atext($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_atext($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: dtext.
+   *
+   * A string that has the following syntax:
+   * - 1*(dtext)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed text.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_is_dtext()
+   */
+  protected function pr_rfc_string_is_dtext($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_dtext($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: qtext.
+   *
+   * A string that has the following syntax:
+   * - 1*(qtext)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'text': A string containing the processed text.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_is_qtext()
+   */
+  protected function pr_rfc_string_is_qtext($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'text' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if (!$this->pr_rfc_char_is_qtext($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['text'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: negotiation.
+   *
+   * There is no literal "negotiation" rfc syntax, but there are a number of different syntax that utilize this design.
+   * This is being considered an implicit standard.
+   * Negotation can be referred to as priority, or ranking, where the string is a list of preferred option based on some rank (specified by q=).
+   * The structure contradicts normal syntax logic used by most system in that the different groups are broken up by commas and the settings broken up by semicolons.
+   *
+   * For example, normal syntax logic would have the string "text/html; q=1.0, text/*; q=0.8" be broken up into the following structure:
+   * - text/html
+   * - q=1.0, text/*
+   * - q=0.8
+   *
+   * The syntax logic used by this standard should instead have the string "text/html; q=1.0, text/*; q=0.8" be broken up into the following structure:
+   * - text/html; q=1.0
+   * - text/*; q=0.8
+   *
+   * Which should be read as the following:
+   * - text/html, q=1.0
+   * - text/*, q=0.8
+   *
+   * Another bizarre behavior of this standard is the use of decimals (perhaps they are considering values a percentage?).
+   * This behavior is rather wasteful because decimals tend to get processed differently, but is logically irrelevant.
+   *
+   * For example, the string "text/html; q=1.0, text/*; q=0.8" could simply be rewritten:
+   * - "text/html; q=10, text/*; q=8"
+   *
+   * To avoid encoding issues of floating points, all decimal values will be treated as integers and stores as such by this function.
+   * - All decimal values will be multiplied by 1000 and then the remaining valid integers will be truncated.
+   *
+   * The default behavior would be to assume case-sensitive, but just in case, this will accept uppercase "Q" and save it as a lowercase "q".
+   *
+   * A string that has the following syntax:
+   * - 1*(atext) *(wsp) *(";" *(wsp) q=1*(digit / 1*(digit) "." 1*(digit))) *(*(wsp) "," *(wsp) 1*(atext) *(wsp) *(";" *(wsp) 1*(digit / 1*(digit) "." 1*(digit))))
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'choices': An array of negotiation choices.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   */
+  protected function pr_rfc_string_is_negotiation($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'choices' => array(),
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    $choice = array(
+      'choice' => NULL,
+      'weight' => NULL,
+    );
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::COLON_SEMI) {
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+
+        // search for the "q" character.
+        for (; $result['current'] < $stop; $result['current']++) {
+          // allow uppercase "Q" but force it to become lowercase "q".
+          if ($ordinals[$result['current']] == c_base_ascii::UPPER_Q) {
+            $ordinals[$result['current']] = c_base_ascii::LOWER_Q;
+            $characters[$result['current']] = c_base_ascii::LOWER_Q;
+          }
+
+          if ($ordinals[$result['current']] == c_base_ascii::LOWER_Q) {
+            break;
+          }
+
+          if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+            $result['invalid'] = TRUE;
+            break;
+          }
+        }
+
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+        }
+
+        if ($result['invalid']) {
+          break;
+        }
+
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+
+        // search for the "=" character.
+        for (; $result['current'] < $stop; $result['current']++) {
+          if ($ordinals[$result['current']] == c_base_ascii::EQUAL) {
+            break;
+          }
+
+          if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+            $result['invalid'] = TRUE;
+            break;
+          }
+        }
+
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+        }
+
+        if ($result['invalid']) {
+          break;
+        }
+
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+
+        // Skip past whitespace until the first digit is found.
+        for (; $result['current'] < $stop; $result['current']++) {
+          if ($this->pr_rfc_char_is_digit($ordinals[$result['current']])) {
+            $result['current']--;
+            break;
+          }
+
+          if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+            $result['invalid'] = TRUE;
+            break;
+          }
+        }
+
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+        }
+
+        if ($result['invalid']) {
+          break;
+        }
+
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+
+        // Process the weight value, removing decimal numbers and multiplying by 1000.
+        $weight = 0;
+        $multiple = 1000;
+        $period = FALSE;
+        $base = 1;
+        for (; $result['current'] < $stop; $result['current']++) {
+          if ($ordinals[$result['current']] == c_base_ascii::PERIOD) {
+            if ($period) {
+              $result['invalid'] = TRUE;
+              break;
+            }
+
+            $multiple /= 10;
+            $period = TRUE;
+            continue;
+          }
+
+          if ($this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+            break;
+          }
+
+          if (!$this->pr_rfc_char_is_digit($ordinals[$result['current']])) {
+            if ($ordinals[$result['current']] == c_base_ascii::COMMA) {
+              break;
+            }
+
+            $result['invalid'] = TRUE;
+            break;
+          }
+
+          if ($period) {
+            $weight += intval($characters[$result['current']]) * $multiple;
+            $multiple /= 10;
+          }
+          else {
+            $weight *= $base;
+            $weight += (intval($characters[$result['current']]) * $multiple);
+            $base *= 10;
+          }
+        }
+        unset($multiple);
+        unset($period);
+        unset($base);
+
+        if ($result['invalid']) {
+          unset($weight);
+          break;
+        }
+
+
+        // the weight has been identified, so store its value and prepare for another run.
+        $choice['weight'] = $weight;
+        if (!isset($result['choices'][$weight])) {
+          $result['choices'][$weight] = array();
+        }
+
+        // strip out leading and trailing whitespace.
+        $choice['choice'] = preg_replace('/(^\s+)|(\s+$)/us', '', $choice['choice']);
+
+        $result['choices'][$weight][$choice['choice']] = $choice;
+        unset($weight);
+
+        $choice = array(
+          'choice' => NULL,
+          'weight' => NULL,
+        );
+
+        if ($result['current'] >= $stop) {
+          break;
+        }
+
+
+        // skip past trailing whitespace.
+        if ($this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+          for (; $result['current'] < $stop; $result['current']++) {
+            if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+              break;
+            }
+          }
+        }
+
+        // if stop is reached at this point, then a valid end of string has been reached.
+        if ($result['current'] >= $stop) {
+          break;
+        }
+
+
+        // look for comma, which will designate that another pass is allowed, otherwise the string is invalid.
+        if ($ordinals[$result['current']] == c_base_ascii::COMMA) {
+          continue;
+        }
+
+        $result['invalid'] = TRUE;
+        break;
+      }
+      elseif ($code == c_base_ascii::COMMA) {
+        // this is an unweighted choice.
+        $choice['weight'] = NULL;
+        if (!isset($result['choices'][NULL])) {
+          $result['choices'][NULL] = array();
+        }
+
+        // strip out leading and trailing whitespace.
+        $choice['choice'] = preg_replace('/(^\s+)|(\s+$)/us', '', $choice['choice']);
+
+        $result['choices'][NULL][$choice['choice']] = $choice;
+
+        $choice = array(
+          'choice' => NULL,
+          'weight' => NULL,
+        );
+
+        continue;
+      }
+      elseif (!$this->pr_rfc_char_is_atext($code) && !$this->pr_rfc_char_is_wsp($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $choice['choice'] .= $characters[$result['current']];
+    }
+    unset($code);
+
+    // If there were no commas or semi-colons, then this is a single, unweighted, choice (which is valid).
+    if ($choice['choice'] != NULL) {
+      $choice['weight'] = NULL;
+      if (!isset($result['choices'][NULL])) {
+        $result['choices'][NULL] = array();
+      }
+
+      // strip out leading and trailing whitespace.
+      $choice['choice'] = preg_replace('/(^\s+)|(\s+$)/us', '', $choice['choice']);
+
+      $result['choices'][NULL][$choice['choice']] = $choice;
+    }
+    unset($choice);
+
+    if ($result['invalid']) {
+      return $result;
+    }
+
+    // sort the choices array.
+    krsort($result['choices']);
+
+    // The NULL key should be the first key in the weight.
+    $this->p_prepend_array_value(NULL, $result['choices']);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: media-type.
+   *
+   * The standard does not explicitly state that there is whitespace, but it instead implies it.
+   * Therefore optional whitespace are added.
+   *
+   * An media-type has the following syntax:
+   * - 1*(tchar) "/" 1*(tchar) *(*(wsp) ";" *(wsp) 1*(1*(tchar) *(wsp) "=" *(wsp) 1*(tchar) / (quoted-string)))
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed or the closing character is found.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'media': A string containing the processed media type (without parameters).
+   *   - 'parameters': An array of strings representing the parameters.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_tchar()
+   * @see: https://tools.ietf.org/html/rfc2616#section-3.7
+   */
+  protected function pr_rfc_string_is_media_type($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'media' => NULL,
+      'parameters' => array(),
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    $found_slash = FALSE;
+    $process_parameters = FALSE;
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($code == c_base_ascii::SLASH_FORWARD) {
+        if (!$process_parameters) {
+          if ($found_slash) {
+            $result['invalid'] = TRUE;
+            break;
+          }
+
+          $found_slash = TRUE;
+          $result['media'] .= $characters[$result['current']];
+          continue;
+        }
+      }
+      elseif ($code == c_base_ascii::COLON_SEMI || $found_slash && $this->pr_rfc_char_is_wsp($code)) {
+        if ($found_slash && $this->pr_rfc_char_is_wsp($code)) {
+          // in this case, the semi-colon has yet to be found, so seek until a semi-colon is found.
+          // any and all non-semi-colon and non-whitespace means that the string is invalid.
+          for (; $result['current'] < $stop; $result['current']++) {
+            if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+              if ($ordinals[$result['current']] == c_base_ascii::COLON_SEMI) {
+                break;
+              }
+
+              $result['invalid'] = TRUE;
+              break;
+            }
+          }
+
+          if ($result['invalid']) {
+            break;
+          }
+        }
+
+        // begin processing the set of media type parameters, first skipping past the semi-colon.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        // Check for: *(token "=" (token / quoted-string)).
+        $processed_token = NULL;
+        $found_equal = FALSE;
+        $parameter_name = NULL;
+        $parameter_value = NULL;
+        for (; $result['current'] < $stop; $result['current']++) {
+          $subcode = $ordinals[$result['current']];
+
+          if ($this->pr_rfc_char_is_wsp($subcode)) {
+            if (is_null($processed_token)) {
+              // skip past leading whitespace.
+              continue;
+            }
+
+            $processed_token = TRUE;
+            continue;
+          }
+          elseif ($subcode == c_base_ascii::EQUAL) {
+            if ($found_equal || $process_whitespace) {
+              // it cannot start with an equal sign, so if $process_whitespace is TRUE, then this is an invalid equal sign.
+              $result['invalid'] = TRUE;
+              break;
+            }
+
+            // skip past all whitespace.
+            $result['current']++;
+            if ($result['current'] >= $stop) {
+              $result['invalid'] = TRUE;
+              break;
+            }
+
+            for (; $result['current'] < $stop; $result['current']++) {
+              if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+                break;
+              }
+            }
+
+            if ($result['current'] >= $stop) {
+              $result['invalid'] = TRUE;
+              break;
+            }
+
+            // check for quoted_string, which must begin with a double quote.
+            if ($subcode == c_base_ascii::QUOTE_DOUBLE) {
+              // skip past the initial double quote.
+              $result['current']++;
+              if ($result['current'] >= $stop) {
+                $result['invalid'] = TRUE;
+                break;
+              }
+
+              $parsed = $this->pr_rfc_string_is_quoted_string($ordinals, $characters, $result['current'], self::STOP_AT_CLOSING_CHARACTER);
+              $result['current'] = $parsed['current'];
+
+              if ($parsed['invalid']) {
+                $result['invalid'] = TRUE;
+                break;
+              }
+
+              $parameter_value = $parsed['text'];
+              unset($parsed);
+
+              if ($result['current'] >= $stop) {
+                // must break now so that the 'current' counter remains at the stop point.
+                break;
+              }
+
+              // check for semi-colon, if one is found then continue, otherwise end if at stop point.
+              $result['current']++;
+              if ($result['current'] >= $stop) {
+                $result['parameters'][$parameter_name] = $parameter_value;
+                break;
+              }
+
+              // skip past any whitespace to see if there is a semi-colon.
+              for (; $result['current'] < $stop; $result['current']++) {
+                if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+                  break;
+                }
+              }
+
+              if ($result['current'] >= $stop || $ordinals[$result['current']] != c_base_ascii::COLON_SEMI) {
+                $result['invalid'] = TRUE;
+                break;
+              }
+
+              $result['parameters'][$parameter_name] = $parameter_value;
+            }
+            else {
+              for (; $result['current'] < $stop; $result['current']++) {
+                if (!$this->pr_rfc_char_is_tchar($ordinals[$result['current']])) {
+                  $result['invalid'] = TRUE;
+                  break;
+                }
+
+                $parameter_value .= $characters[$result['current']];
+              }
+
+              if ($result['invalid']) {
+                break;
+              }
+
+              $result['parameters'][$parameter_name] = $parameter_value;
+            }
+
+            $parameter_name = NULL;
+            $parameter_value = NULL;
+            $found_equal = FALSE;
+            $processed_token = NULL;
+
+            continue;
+          }
+          elseif (!$this->pr_rfc_char_is_tchar($subcode)) {
+            if ($found_equal) {
+              if ($subcode == c_base_ascii::COLON_SEMI) {
+                // save parameter and value and continue.
+                $result['parameters'][$parameter_name] = $parameter_value;
+
+                $parameter_name = NULL;
+                $parameter_value = NULL;
+                $found_equal = FALSE;
+                $processed_token = NULL;
+
+                continue;
+              }
+            }
+
+            $result['invalid'] = TRUE;
+            break;
+          }
+          elseif ($processed_token) {
+            // processed token is set to TRUE after the first space following the token is found.
+            // spaces are not allowed inside an unqouted token and is therefore invalid.
+            $result['invalid'] = TRUE;
+            break;
+          }
+
+          if ($found_equal) {
+            $parameter_value .= $characters[$subcode];
+          }
+          else {
+            $parameter_name .= $characters[$subcode];
+          }
+
+          $processed_token = FALSE;
+        }
+        unset($subcode);
+        unset($process_whitespace);
+
+        if ($found_equal) {
+          if (!is_null($parameter_name)) {
+            $result['parameters'][$parameter_name] = $parameter_value;
+          }
+        }
+        else {
+          // a parameter name without an equal sign to designate a parameter value is invalid.
+          $result['invalid'] = TRUE;
+        }
+        unset($processed_name);
+        unset($processed_value);
+        unset($found_equal);
+
+        // all parameters have been processed, should be on or after $stop point or there is an invalid character.
+        if (!$result['invalid'] && $result['current'] < $stop) {
+          $result['invalid'] = TRUE;
+        }
+
+        break;
+      }
+      elseif (!$this->pr_rfc_char_is_tchar($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $result['media'] .= $characters[$result['current']];
+      $process_parameters = TRUE;
+    }
+    unset($code);
+    unset($found_slash);
+    unset($process_parameters);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: valued_token.
+   *
+   * There is no explicit "valued_token" standard, but it is implied and used in many ways.
+   * One such example is with the HTTP header "content-disposition".
+   *
+   * The standard does not seem to specify *(wsp) in all cases.
+   * Given that this is not a literal standard, strictness will be loosened a little to allow optional whitespace between the ";" and the "=".
+   *
+   * The standard does not explicitly state that there is whitespace, but in certain cases it implies whitespace usage.
+   * Therefore optional whitespace are added.
+   *
+   * A valued_token has the following syntax:
+   * - *(wsp) 1*(tchar) *(*(wsp) ";" *(wsp) 1*(1*(tchar) *(wsp) "=" *(wsp) 1*(tchar) / (quoted-string))) *(wsp)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed or the closing character is found.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'tokens': An array of strings representing the parameters, with the keys being the valued token name and the values being the valued token value.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: self::pr_rfc_string_is_valued_token_comma()
+   * @see: base_rfc_char::pr_rfc_char_tchar()
+   * @see: base_rfc_char::pr_rfc_string_is_media_type()
+   * @see: https://tools.ietf.org/html/rfc2616#section-3.7
+   */
+  protected function pr_rfc_string_is_valued_token($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'tokens' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    $token_name = NULL;
+    $token_value = NULL;
+    $processed_name = FALSE;
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($this->pr_rfc_char_is_wsp($code)) {
+        // @todo: handle whitespace between '='.
+        if (is_null($token_name)) {
+          continue;
+        }
+
+        // if the whitespace is not leading whitespace, then the end of the token has been reached.
+        // A stop point, an equal sign, or a semi-colon must be reached for the token and value pair to be valid.
+        for (; $result['current'] < $stop; $result['current']++) {
+          if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+            break;
+          }
+        }
+
+        if ($result['current'] >= $stop) {
+          // must break now so that the 'current' counter remains at the stop point.
+          break;
+        }
+
+        if ($ordinals[$result['current']] == c_base_ascii::COLON_SEMI) {
+          $result['tokens'][$token_name] = $token_value;
+
+          $token_name = NULL;
+          $token_value = NULL;
+          $processed_name = FALSE;
+
+          // skip past all whitespace following the semi-colon.
+          for (; $result['current'] < $stop; $result['current']++) {
+            if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+              break;
+            }
+          }
+
+          if ($result['current'] >= $stop) {
+            // must break now so that the 'current' counter remains at the stop point.
+            break;
+          }
+
+          continue;
+        }
+        elseif ($ordinals[$result['current']] == c_base_ascii::EQUAL && !$processed_name) {
+          $processed_name = TRUE;
+
+          // skip past all whitespace following the equal.
+          for (; $result['current'] < $stop; $result['current']++) {
+            if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+              break;
+            }
+          }
+
+          if ($result['current'] >= $stop) {
+            // must break now so that the 'current' counter remains at the stop point.
+            break;
+          }
+
+          continue;
+        }
+
+        $result['invalid'] = TRUE;
+        break;
+      }
+      elseif ($code == c_base_ascii::COLON_SEMI) {
+        $result['tokens'][$token_name] = $token_value;
+        $token_name = NULL;
+        $token_value = NULL;
+        $processed_name = FALSE;
+
+        // skip past all whitespace following the semi-colon.
+        for (; $result['current'] < $stop; $result['current']++) {
+          if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+            break;
+          }
+        }
+
+        if ($result['current'] >= $stop) {
+          // must break now so that the 'current' counter remains at the stop point.
+          break;
+        }
+
+        continue;
+      }
+      elseif ($code == c_base_ascii::QUOTE_DOUBLE) {
+        if (!$processed_name) {
+          // the token name is not allowed to be a quoted string.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        // skip past the initial double quote.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $parsed = $this->pr_rfc_string_is_quoted_string($ordinals, $characters, $result['current'], self::STOP_AT_CLOSING_CHARACTER);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $token_value = $parsed['text'];
+        unset($parsed);
+
+        if ($result['current'] >= $stop) {
+          // must break now so that the 'current' counter remains at the stop point.
+          break;
+        }
+
+        $result['tokens'][$token_name] = $token_value;
+        $token_name = NULL;
+        $token_value = NULL;
+        $processed_name = FALSE;
+
+        continue;
+      }
+      elseif (!$this->pr_rfc_char_is_tchar($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      if ($processed_name) {
+        $token_value .= $characters[$result['current']];
+      }
+      else {
+        $token_name .= $characters[$result['current']];
+      }
+    }
+    unset($code);
+    unset($processed_name);
+
+    if (!is_null($token_name) && $result['current'] >= $stop) {
+      // the stop point was reached, make sure to the token_name and token_value that were last being processed.
+      $result['tokens'][$token_name] = $token_value;
+    }
+    unset($token_name);
+    unset($token_value);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: valued_token_comma.
+   *
+   * This is similar to valued_token, expect that instead of using ';', a ',' is used as a separator.
+   *
+   * There is no explicit "valued_token" standard, but it is implied and used in many ways.
+   * One such example is with the HTTP header "cache-control".
+   *
+   * The standard does not seem to specify *(wsp) in all cases.
+   * Given that this is not a literal standard, strictness will be loosened a little to allow optional whitespace between the ";" and the "=".
+   *
+   * The standard does not explicitly state that there is whitespace, but in certain cases it implies whitespace usage.
+   * Therefore optional whitespace are added.
+   *
+   * A valued_token has the following syntax:
+   * - *(wsp) 1*(tchar) *(*(wsp) "=" *(wsp) 1*(tchar) / (quoted-string)) *(*(wsp) "," *(wsp) 1*(1*(tchar) *(*(wsp) "=" *(wsp) 1*(tchar) / (quoted-string)))) *(wsp)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed or the closing character is found.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'tokens': An array of strings representing the parameters, with the keys being the valued token name and the values being the valued token value.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: self::pr_rfc_string_is_valued_token()
+   * @see: base_rfc_char::pr_rfc_char_tchar()
+   * @see: base_rfc_char::pr_rfc_string_is_media_type()
+   * @see: https://tools.ietf.org/html/rfc2616#section-3.7
+   */
+  protected function pr_rfc_string_is_valued_token_comma($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'tokens' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    $token_name = NULL;
+    $token_value = NULL;
+    $processed_name = FALSE;
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($this->pr_rfc_char_is_wsp($code)) {
+        // @todo: handle whitespace between '='.
+        if (is_null($token_name)) {
+          continue;
+        }
+
+        // if the whitespace is not leading whitespace, then the end of the token has been reached.
+        // A stop point, an equal sign, or a semi-colon must be reached for the token and value pair to be valid.
+        for (; $result['current'] < $stop; $result['current']++) {
+          if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+            break;
+          }
+        }
+
+        if ($result['current'] >= $stop) {
+          // must break now so that the 'current' counter remains at the stop point.
+          break;
+        }
+
+        if ($ordinals[$result['current']] == c_base_ascii::COLON_SEMI) {
+          $result['tokens'][$token_name] = $token_value;
+
+          $token_name = NULL;
+          $token_value = NULL;
+          $processed_name = FALSE;
+
+          // skip past all whitespace following the semi-colon.
+          for (; $result['current'] < $stop; $result['current']++) {
+            if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+              break;
+            }
+          }
+
+          if ($result['current'] >= $stop) {
+            // must break now so that the 'current' counter remains at the stop point.
+            break;
+          }
+
+          continue;
+        }
+        elseif ($ordinals[$result['current']] == c_base_ascii::EQUAL && !$processed_name) {
+          $processed_name = TRUE;
+
+          // skip past all whitespace following the equal.
+          for (; $result['current'] < $stop; $result['current']++) {
+            if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+              break;
+            }
+          }
+
+          if ($result['current'] >= $stop) {
+            // must break now so that the 'current' counter remains at the stop point.
+            break;
+          }
+
+          continue;
+        }
+
+        $result['invalid'] = TRUE;
+        break;
+      }
+      elseif ($code == c_base_ascii::COMMA) {
+        $result['tokens'][$token_name] = $token_value;
+        $token_name = NULL;
+        $token_value = NULL;
+        $processed_name = FALSE;
+
+        // skip past all whitespace following the semi-colon.
+        for (; $result['current'] < $stop; $result['current']++) {
+          if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+            break;
+          }
+        }
+
+        if ($result['current'] >= $stop) {
+          // must break now so that the 'current' counter remains at the stop point.
+          break;
+        }
+
+        continue;
+      }
+      elseif ($code == c_base_ascii::QUOTE_DOUBLE) {
+        if (!$processed_name) {
+          // the token name is not allowed to be a quoted string.
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        // skip past the initial double quote.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $parsed = $this->pr_rfc_string_is_quoted_string($ordinals, $characters, $result['current'], self::STOP_AT_CLOSING_CHARACTER);
+        $result['current'] = $parsed['current'];
+
+        if ($parsed['invalid']) {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $token_value = $parsed['text'];
+        unset($parsed);
+
+        if ($result['current'] >= $stop) {
+          // must break now so that the 'current' counter remains at the stop point.
+          break;
+        }
+
+        $result['tokens'][$token_name] = $token_value;
+        $token_name = NULL;
+        $token_value = NULL;
+        $processed_name = FALSE;
+
+        continue;
+      }
+      elseif (!$this->pr_rfc_char_is_tchar($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      if ($processed_name) {
+        $token_value .= $characters[$result['current']];
+      }
+      else {
+        $token_name .= $characters[$result['current']];
+      }
+    }
+    unset($code);
+    unset($processed_name);
+
+    if (!is_null($token_name) && $result['current'] >= $stop) {
+      // the stop point was reached, make sure to the token_name and token_value that were last being processed.
+      $result['tokens'][$token_name] = $token_value;
+    }
+    unset($token_name);
+    unset($token_value);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: commad_token.
+   *
+   * This is a bit bizarre in that the standard allows for leading commas with nothing defined.
+   * - This function will accept that syntax, but those commas will be ignored.
+   * - Even more oddly, leading white space is not supported, but this too is bizarre and inconsistent.
+   * - A simpler syntax will therefore be used.
+   *
+   * A valued_token has the following syntax:
+   * - 1*(*(ws) "," *(ws) token)
+   *
+   * Original valued_token standard syntax:
+   * - *("," *(ws)) token *(*(ws) "," *(ws) token)
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed or the closing character is found.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'tokens': An array of strings representing the tokens.
+   *               The array keys will represent the order in which they were processed.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_tchar()
+   * @see: https://tools.ietf.org/html/rfc7230#appendix-B
+   */
+  protected function pr_rfc_string_is_commad_token($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'tokens' => NULL,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    $token_value = NULL;
+    for (; $result['current'] < $stop; $result['current']++) {
+      $code = $ordinals[$result['current']];
+
+      if ($this->pr_rfc_char_is_wsp($code)) {
+        if (is_null($token_value)) {
+          continue;
+        }
+
+        // End of token reached, now seek whitespace to end of line or a comma.
+        $result['current']++;
+        if ($result['current'] >= $stop) {
+          break;
+        }
+
+        for (; $result['current'] < $stop; $result['current']++) {
+          if (!$this->pr_rfc_char_is_wsp($ordinals[$result['current']])) {
+            break;
+          }
+        }
+
+        if ($ordinals[$result['current']] == c_base_ascii::COMMA) {
+          if (is_null($token_value)) {
+            // empty values separated by commas are to be ignored.
+            continue;
+          }
+
+          $result['tokens'][] = $token_value;
+          $tokan_value = NULL;
+          continue;
+        }
+
+        $result['invalid'] = TRUE;
+        break;
+      }
+      elseif ($code == c_base_ascii::COMMA) {
+        if (is_null($token_value)) {
+          // empty values separated by commas are to be ignored.
+          continue;
+        }
+
+        $result['tokens'][] = $token_value;
+        $tokan_value = NULL;
+        continue;
+      }
+      elseif (!$this->pr_rfc_char_is_tchar($code)) {
+        $result['invalid'] = TRUE;
+        break;
+      }
+
+      $token_value .= $characters[$result['current']];
+    }
+    unset($code);
+
+    if (!is_null($token_value) && $result['current'] >= $stop) {
+      // the stop point was reached, make sure the token_value was last being processed.
+      $result['tokens'][] = $token_value;
+    }
+    unset($token_value);
+
+    return $result;
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: query.
+   *
+   * This is also used by the rfc syntax: fragment.
+   *
+   * A query has the following syntax:
+   * - *(ALPHA / DIGIT / "-" / "." / "_" / "~" / "%" HEXDIG HEXDIG / "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" / ":" / "@" / "/" / "?")
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed or the closing character is found.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'address': A string containing the processed ip address.
+   *                When is_future is TRUE, this is an array containing:
+   *                - 'version': The ipfuture version.
+   *                - 'ip': The ip address.
+   *   - 'is_future': A boolean that when TRUE represents an ipvfuture address and when FALSE represents an ipv6 address.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_tchar()
+   * @see: https://tools.ietf.org/html/rfc2616#section-3.7
+   */
+  protected function pr_rfc_string_is_query($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'address' => NULL,
+      'is_future' => FALSE,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+  }
+
+  /**
+   * Processes a string based on the rfc syntax: ip-literal.
+   *
+   * This assumes that the start position is immediately after the initial "[".
+   * This will stop at the closing "]".
+   *
+   * An ip-literal has the following syntax:
+   * - "[" (IPv6address / "v" 1*HEXDIG "." 1*(unreserved / sub-delims / ":")) "]"
+   *
+   * @param array $ordinals
+   *   An array of integers representing each character of the string.
+   * @param array $characters
+   *   An array of characters representing the string.
+   * @param int $start
+   *   (optional) The position in the arrays to start checking.
+   * @param int|null $stop
+   *   (optional) The position in the arrays to stop checking.
+   *   If NULL, then the entire string is processed or the closing character is found.
+   *
+   * @return array
+   *   The processed information:
+   *   - 'address': A string containing the processed ip address.
+   *                When is_future is TRUE, this is an array containing:
+   *                - 'version': The ipfuture version.
+   *                - 'ip': The ip address.
+   *   - 'is_future': A boolean that when TRUE represents an ipvfuture address and when FALSE represents an ipv6 address.
+   *   - 'current': an integer representing the position the counter stopped at.
+   *   - 'invalid': a boolean representing whether or not this string is valid or if an error occurred.
+   *
+   * @see: base_rfc_char::pr_rfc_char_tchar()
+   * @see: https://tools.ietf.org/html/rfc2616#section-3.7
+   */
+  protected function pr_rfc_string_is_ip_literal($ordinals, $characters, $start = 0, $stop = NULL) {
+    $result = array(
+      'address' => NULL,
+      'is_future' => FALSE,
+      'current' => $start,
+      'invalid' => FALSE,
+    );
+
+    if (is_null($stop)) {
+      $stop = count($ordinals);
+    }
+
+    if ($start >= $stop) {
+      return $result;
+    }
+
+    // this first character must be either a "v" or a hex digit.
+    if ($ordinals[$result['current']] == c_base_ascii::LOWER_V || $ordinals[$result['current']] == c_base_ascii::UPPER_V) {
+      $result['is_future'] = TRUE;
+    }
+    elseif (!self::pr_rfc_char_is_hexdigit($ordinals[$result['current']])) {
+      $result['invalid'] = TRUE;
+      return $result;
+    }
+
+    if ($result['is_future']) {
+      $result['address'] = array(
+        'version' => NULL,
+        'ip' => NULL,
+      );
+
+      $result['current']++;
+      if ($result['current'] >= $stop) {
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      // store all hexdigits until a non-hexdigit is found as the version number.
+      for (; $result['current'] < $stop; $result['current']++) {
+        $code = $ordinals[$result['current']];
+
+        if (!self::pr_rfc_char_is_hexdigit($code)) {
+          break;
+        }
+
+        $result['address']['version'] .= $characters[$result['current']];
+      }
+      unset($code);
+
+      if ($result['current'] >= $stop) {
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      if ($ordinals[$result['current']] != c_base_ascii::PERIOD) {
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      $result['current']++;
+      if ($result['current'] >= $stop) {
+        $result['invalid'] = TRUE;
+        return $result;
+      }
+
+      // record all valid values as the ip address until the stop point or ']' is reached.
+      for (; $result['current'] < $stop; $result['current']++) {
+        $code = $ordinals[$result['current']];
+
+        if (self::pr_rfc_char_is_digit($code)) {
+          // do nothing, valid
+        }
+        elseif (self::pr_rfc_char_is_alpha($code)) {
+          // do nothing, valid
+        }
+        elseif (self::pr_rfc_char_is_unreserved($code)) {
+          // do nothing, valid
+        }
+        elseif (self::pr_rfc_char_is_sub_delims($code)) {
+          // do nothing, valid
+        }
+        elseif ($code == c_base_ascii::COLON) {
+          // do nothing, valid
+        }
+        elseif ($code == c_base_ascii::BRACKET_CLOSE) {
+          break;
+        }
+        else {
+          $result['invalid'] = TRUE;
+          break;
+        }
+
+        $result['address']['ip'] .= $characters[$result['current']];
+      }
+      unset($code);
+    }
+    else {
+      for (; $result['current'] < $stop; $result['current']++) {
+        $code = $ordinals[$result['current']];
+
+        if (self::pr_rfc_char_is_hexdigit($code)) {
+          $result['address'] .= $characters[$result['current']];
+        }
+        elseif ($code == c_base_ascii::COLON) {
+          $result['address'] .= $characters[$result['current']];
+        }
+        elseif ($code == c_base_ascii::BRACKET_CLOSE) {
+          break;
+        }
+        else {
+          $result['invalid'] = TRUE;
+          break;
+        }
+      }
+      unset($code);
+
+      if (!$result['invalid'] && inet_pton($result['address']) === FALSE) {
+        $result['invalid'] = TRUE;
+      }
+    }
+
+    return $result;
+  }
+
+  /**
+   * Effectively unshift a value onto a given array with a specified index.
+   *
+   * The NULL key should be the first key in the weight.
+   * PHP does not provide a way to preserve keys when merging arrays nor does PHP provide a way to unshift a value onto an array with a specific key.
+   *
+   * @param $key
+   *   The index name to unshift the value onto the array as.
+   * @param array $array
+   *   The array to unshift onto.
+   */
+  protected function p_prepend_array_value($key, &$array) {
+    if (!array_key_exists($key, $array)) {
+      return;
+    }
+
+    $value = $array[$key];
+    unset($array[$key]);
+
+    $new_array = array(
+      $key => $value,
+    );
+    unset($value);
+
+    foreach ($array as $key => $value) {
+      $new_array[$key] = $value;
+    }
+
+    $array = $new_array;
+    unset($new_array);
+  }
+}
diff --git a/common/base/classes/base_session.php b/common/base/classes/base_session.php
new file mode 100644 (file)
index 0000000..b261a09
--- /dev/null
@@ -0,0 +1,957 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing sessions.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+/**
+ * A class for managing sessions.
+ *
+ * This utilizes the custom session project called 'sessionize_accounts' and does not use PHP's session management.
+ * The current design does not store session variables, only the session key, username, ip address, and password.
+ * This session key can be used to retrieve a password between requests and to access the database.
+ * The database can then be used to retrieve any session variables.
+ */
+class c_base_session {
+  const PACKET_MAX_LENGTH = 8192;
+  const SOCKET_PATH_PREFIX = '/var/www/sockets/sessionize_accounts/';
+  const SOCKET_PATH_SUFFIX = '/sessions.socket';
+  const PASSWORD_CLEAR_TEXT_LENGTH = 2048;
+
+  private $socket;
+  private $socket_path;
+  private $socket_timeout;
+
+  private $system_name;
+
+  private $name;
+  private $id_user;
+  private $ip;
+  private $password;
+  private $session_id;
+  private $settings;
+
+  private $error;
+
+  private $timeout_expire;
+  private $timeout_max;
+
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->socket = NULL;
+    $this->socket_path = NULL;
+    $this->socket_timeout = NULL;
+
+    $this->system_name = NULL;
+
+    $this->name = NULL;
+    $this->id_user = NULL;
+    $this->ip = NULL;
+    $this->password = NULL;
+    $this->session_id = NULL;
+    $this->settings = NULL;
+
+    $this->error = NULL;
+
+    $this->timeout_expire = NULL;
+    $this->timeout_max = NULL;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    $this->clear_password();
+
+    if (is_resource($this->socket)) {
+      @socket_close($this->socket);
+    }
+
+    unset($this->socket);
+    unset($this->socket_path);
+    unset($this->socket_timeout);
+
+    unset($this->system_name);
+
+    unset($this->name);
+    unset($this->id_user);
+    unset($this->ip);
+    unset($this->password);
+    unset($this->session_id);
+    unset($this->special);
+    unset($this->settings);
+
+    unset($this->error);
+
+    unset($this->timeout_expire);
+    unset($this->timeout_max);
+  }
+
+  /**
+   * Assigns the system name, which is used to create the socket path.
+   *
+   * @param string $system_name
+   *   A system name string.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_system_name($system_name) {
+    if (!is_string($system_name) || empty($system_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->system_name = basename($system_name);
+    $this->socket_path = self::SOCKET_PATH_PREFIX . $this->system_name . self::SOCKET_PATH_SUFFIX;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored system name.
+   *
+   * @return c_base_return_string
+   *   The system name string or NULL if undefined.
+   */
+  public function get_system_name() {
+    return c_base_return_string::s_new($this->system_name);
+  }
+
+  /**
+   * Assigns the user name associated with the session.
+   *
+   * @param string $name
+   *   The user name.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_name($name) {
+    if (!is_string($name) || empty($name)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (mb_strlen($name) == 0 || preg_match('/^(\w|-)+$/i', $name) != 1) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->name = $name;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored user name.
+   *
+   * @return c_base_return_string
+   *   The user name string.
+   */
+  public function get_name() {
+    return c_base_return_string::s_new($this->name);
+  }
+
+  /**
+   * Assigns the user id associated with the session.
+   *
+   * @param int $id_user
+   *   The user id.
+   *   This must be greater than or equal to 0.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_id_user($id_user) {
+    if ((is_int($id_user) && $id_user < 0) || !is_int($id_user) && (!is_string($id_user) || !(is_numeric($id_user) && (int) $id_user >= 0))) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->id_user = (int) $id_user;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored user id.
+   *
+   * @return c_base_return_int
+   *   The user id_user integer.
+   */
+  public function get_id_user() {
+    return c_base_return_int::s_new($this->id_user);
+  }
+
+  /**
+   * Assigns the ip address associated with the session.
+   *
+   * @param string $ip
+   *   The ip address.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_ip($ip) {
+    if (!is_string($ip) || empty($ip)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (mb_strlen($ip) == 0 || ip2long($ip) === FALSE) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->ip = $ip;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored ip address.
+   *
+   * @return c_base_return_string
+   *   The ip address string.
+   */
+  public function get_ip() {
+    return c_base_return_string::s_new($this->ip);
+  }
+
+  /**
+   * Assigns the password associated with a user name.
+   *
+   * Manually assign this only for new sessions only.
+   * When existing sessions are loaded, this will be auto-populated.
+   *
+   * @param string|null $password
+   *   The password.
+   *   Assigning null disable the password.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: c_base_session::load()
+   */
+  public function set_password($password) {
+    if (!is_null($password) && (!is_string($password) || empty($password))) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_null($password)) {
+      $this->password = NULL;
+      return new c_base_return_true();
+    }
+
+    // deny 0-length passwords.
+    if (mb_strlen($password) == 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->password = $password;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored password.
+   *
+   * @return c_base_return_string
+   *   The password string.
+   */
+  public function get_password() {
+    return c_base_return_string::s_new($this->password);
+  }
+
+  /**
+   * Assigns the settings associated with the session.
+   *
+   * The settings provides optional information that a service may want to store with a particular session.
+   *
+   * @param array $settings
+   *   The settings array to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_settings($settings) {
+    if (!is_array($settings)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->settings = $settings;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored settings.
+   *
+   * @return c_base_return_array
+   *   The settings array.
+   */
+  public function get_settings() {
+    return c_base_return_array::s_new($this->settings);
+  }
+
+  /**
+   * Uses an unproven technique in an attempt to 'delete' a password from memory and then unallocating the resource.
+   *
+   * The password will be set to a hopefully large enough string of whitespaces.
+   * The password variable will then be unset.
+   *
+   * This does not perform the garbage collection, but it is suggested that the caller consider calling gc_collect_cycles().
+   *
+   * @see: gc_collect_cycles()
+   */
+  public function clear_password() {
+    $this->password = str_repeat(' ', self::PASSWORD_CLEAR_TEXT_LENGTH);
+    unset($this->password);
+  }
+
+  /**
+   * Assigns the session id associated with a session.
+   *
+   * Manually assign this for existing sessions only.
+   * This should be auto-populated when a new session is saved.
+   *
+   * @param string $session_id
+   *   The session id string.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: c_base_session::save()
+   */
+  public function set_session_id($session_id) {
+    if (!is_string($session_id) || empty($session_id)) {
+      return c_base_return_error::s_false();
+    }
+
+    // deny 0-length session_id.
+    if (mb_strlen($session_id) == 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->session_id = $session_id;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the stored session id.
+   *
+   * @return c_base_return_string
+   *   The session id string.
+   */
+  public function get_session_id() {
+    return c_base_return_string::s_new($this->session_id);
+  }
+
+  /**
+   * Assigns the session expiration timeout.
+   *
+   * @param int $timeout_expire
+   *   The unix timestamp for the expiration timeout.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: c_base_session::save()
+   */
+  public function set_timeout_expire($timeout_expire) {
+    if (!is_int($timeout_expire)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->timeout_expire = $timeout_expire;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the unix timestamp for the session expiration timeout.
+   *
+   * @return c_base_return_int
+   *   The unix timestamp for the session expiration timeout.
+   */
+  public function get_timeout_expire() {
+    return c_base_return_int::s_new($this->timeout_expire);
+  }
+
+  /**
+   * Assigns the max session timeout.
+   *
+   * @param int $timeout_max
+   *   The unix timestamp for the max session timeout.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: c_base_session::save()
+   */
+  public function set_timeout_max($timeout_max) {
+    if (!is_int($timeout_max)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->timeout_max = $timeout_max;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the unix timestamp for the max timeout.
+   *
+   * @return c_base_return_int
+   *   The unix timestamp for the max timeout.
+   */
+  public function get_timeout_max() {
+    return c_base_return_int::s_new($this->timeout_max);
+  }
+
+  /**
+   * Assigns the max session timeout.
+   *
+   * @param int $seconds
+   *   Number of seconds until timeout is reached.
+   * @param int $microseconds
+   *   (optional) Number of microseconds until timeout is reached.
+   * @param bool $receive
+   *   (optional) When TRUE, the receive timeout is assigned.
+   *   When FALSE, the send timeout is assigned.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: socket_set_option()
+   */
+  public function set_socket_timeout($seconds, $microseconds = 0, $receive = TRUE) {
+    if (!is_int($seconds) || $seconds < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($microseconds) || $microseconds < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($receive)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_array($this->socket_timeout)) {
+      $this->socket_timeout = array(
+        'send' => NULL,
+        'receive' => NULL,
+      );
+    }
+
+    if ($receive) {
+      $this->socket_timeout['receive'] = array('seconds' => $seconds, 'microseconds' => $microseconds);
+      if (is_resource($this->socket)) {
+        socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, $seconds, $microseconds);
+      }
+    }
+    else {
+      $this->socket_timeout['send'] = array('seconds' => $seconds, 'microseconds' => $microseconds);
+      if (is_resource($this->socket)) {
+        socket_set_option($this->socket, SOL_SOCKET, SO_SNDTIMEO, $seconds, $microseconds);
+      }
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the unix timestamp for the max timeout.
+   *
+   * @return c_base_return_int
+   *   The unix timestamp for the max timeout.
+   *
+   * @see: socket_get_option()
+   */
+  public function get_socket_timeout() {
+    return c_base_return_array::s_new($this->socket_timeout);
+  }
+
+  /**
+   * Returns the stored error array.
+   *
+   * This should be called after a load() or a save() command to check to see if the socket returned any error.
+   *
+   * This does not return the socket error, for that use self::get_error_socket()
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   The error array or boolean returned by the socket when transferring data or NULL if there are no socket errors.
+   *   A value of FALSE means that no error was returned by the socket.
+   *   A value of an array() for both load() and save() would contain the socket error message.
+   *
+   * @see: self::get_error_socket()
+   */
+  public function get_error() {
+    if (is_bool($this->error)) {
+      c_base_return_bool::s_new($this->error);
+    }
+
+    return c_base_return_array::s_new($this->error);
+  }
+
+  /**
+   * This returns the error code reported by the socket itself.
+   *
+   * Use self::get_error() to get the error reported in the packet and not the socket.
+   *
+   * @return c_base_return_int
+   *   Number representing the socket error.
+   *
+   * @see: self::get_error()
+   * @see: socket_last_error()
+   */
+  public function get_error_socket() {
+    if (!is_resource($this->socket)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new(@socket_last_error($this->socket));
+  }
+
+  /**
+   * This clears the error on the socket if any exist.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: self::get_error_socket()
+   * @see: socket_clear_error()
+   */
+  public function clear_error_socket() {
+    if (!is_resource($this->socket)) {
+      return c_base_return_error::s_false();
+    }
+
+    @socket_clear_error($this->socket);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Opens a socket connection for later loading.
+   *
+   * The system name must be defined before this call to ensure a valid socket path exists.
+   * The socket should be closed with c_base_session::do_disconnect() when finished.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see: c_base_session::set_system_name()
+   * @see: c_base_session::do_disconnect()
+   */
+  function do_connect() {
+    if (is_resource($this->socket) || is_null($this->system_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    $this->socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
+    if ($this->socket === FALSE) {
+      $this->do_disconnect();
+      return c_base_return_error::s_false();
+    }
+
+    $connected = socket_connect($this->socket, $this->socket_path, 0);
+    if ($connected === FALSE) {
+      unset($connected);
+
+      $this->do_disconnect();
+      return c_base_return_error::s_false();
+    }
+    unset($connected);
+
+
+    // assign any pre-defined timeouts.
+    if (isset($this->socket_timeout['receive']['seconds'])) {
+      socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, $this->socket_timeout['receive']['seconds'], $this->socket_timeout['receive']['microseconds']);
+    }
+
+    if (isset($this->socket_timeout['send']['seconds'])) {
+      socket_set_option($this->socket, SOL_SOCKET, SO_SNDTIMEO, $this->socket_timeout['send']['seconds'], $this->socket_timeout['send']['microseconds']);
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Close an opened socket.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function do_disconnect() {
+    if (!is_resource($this->socket)) {
+      return c_base_return_error::s_false();
+    }
+
+    @socket_close($this->socket);
+
+    $this->socket = NULL;
+    return new c_base_return_true();
+  }
+
+  /**
+   * Returns the connected status.
+   *
+   * This represents whether or not the self::do_connect() function was successfully called.
+   * The state of the connection should still be checked.
+   *
+   * @return c_base_return_status
+   *   TRUE when connected, FALSE otherwise.
+   */
+  public function is_connected() {
+    if (is_resource($this->socket)) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Loads the session information from an open socket.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see: c_base_session::do_connect()
+   * @see: c_base_session::p_transfer()
+   */
+  public function do_pull() {
+    if (is_null($this->ip) || is_null($this->session_id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->socket) || @socket_last_error($this->socket) != 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $response = $this->p_transfer(array('ip' => $this->ip, 'session_id' => $this->session_id));
+    if (c_base_return::s_has_error($response)) {
+      return $response->get_error();
+    }
+
+    $response = c_base_return_array::s_value_exact($response);
+    if (empty($response['result']) || !is_array($response['result'])) {
+      unset($response);
+
+      return c_base_return_error::s_false();
+    }
+
+    $this->name = NULL;
+    if (isset($response['result']['name']) && is_string($response['result']['name'])) {
+      $this->name = $response['result']['name'];
+    }
+
+    $this->id_user = NULL;
+    if (isset($response['result']['id_user']) && is_int($response['result']['id_user'])) {
+      $this->id_user = $response['result']['id_user'];
+    }
+
+    if (!is_null($this->password)) {
+      $this->password = str_repeat(' ', self::PASSWORD_CLEAR_TEXT_LENGTH);
+    }
+
+    $this->password = NULL;
+    if (isset($response['result']['password']) && is_string($response['result']['password'])) {
+      $this->password = $response['result']['password'];
+    }
+
+    $this->timeout_expire = NULL;
+    if (isset($response['result']['expire']) && is_int($response['result']['expire'])) {
+      $this->timeout_expire = $response['result']['expire'];
+    }
+
+    $this->timeout_max = NULL;
+    if (isset($response['result']['max']) && is_int($response['result']['max'])) {
+      $this->timeout_max = $response['result']['max'];
+    }
+
+    $this->settings = NULL;
+    if (isset($response['result']['settings']) && is_array($response['result']['settings'])) {
+      $this->settings = $response['result']['settings'];
+    }
+
+    unset($response);
+    return new c_base_return_true();
+  }
+
+  /**
+   * Saves the session information to an open socket.
+   *
+   * This function accepts interval expire and max, but these should be considered soft limits.
+   * The server is allowed to impose its own hard limits that prevent expire and max from being set longer than.
+   *
+   * @param int|null $interval_expire
+   *   (optional) Number of seconds a session should wait while idling before expiring the session.
+   * @param int|null $interval_max
+   *   (optional) The maximum time a session is allowed to exist.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see: c_base_session::set_name()
+   * @see: c_base_session::set_ip()
+   * @see: c_base_session::set_password()
+   * @see: c_base_session::do_connect()
+   * @see: c_base_session::p_transfer()
+   */
+  public function do_push($interval_expire = NULL, $interval_max = NULL) {
+    if (is_null($this->name) || is_null($this->id_user) || is_null($this->ip) || is_null($this->password)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->socket) || @socket_last_error($this->socket) != 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($interval_expire) && (!is_int($interval_expire) || $interval_expire < 1)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($interval_max) && (!is_int($interval_max) || $interval_max < 1)) {
+      return c_base_return_error::s_false();
+    }
+
+    // settings is allowed to be undefined, so send it as an empty array.
+    if (is_null($this->settings)) {
+      $this->settings = array();
+    }
+
+    $response = $this->p_transfer(array('name' => $this->name, 'id_user' => $this->id_user, 'ip' => $this->ip, 'password' => $this->password, 'expire' => $interval_expire, 'max' => $interval_max, 'settings' => $this->settings));
+    if (c_base_return::s_has_error($response)) {
+      return c_base_return_error::s_false();
+    }
+
+    $response = c_base_return_array::s_value_exact($response);
+    if (empty($response['result']) || !is_array($response['result'])) {
+      unset($response);
+
+      return c_base_return_error::s_false();
+    }
+
+    $this->session_id = NULL;
+    if (isset($response['result']['session_id'])) {
+      $this->session_id = $response['result']['session_id'];
+    }
+
+    $this->timeout_expire = NULL;
+    if (isset($response['result']['expire'])) {
+      $this->timeout_expire = $response['result']['expire'];
+    }
+
+    $this->timeout_max = NULL;
+    if (isset($response['result']['max'])) {
+      $this->timeout_max = $response['result']['max'];
+    }
+
+    unset($response);
+    return new c_base_return_true();
+  }
+
+  /**
+   * Terminates a session from an open socket.
+   *
+   * Unlike self::do_disconnect(), this does not close the connection to the socket, it closes the session itself.
+   *
+   * This is used to terminate a session before the expiration date and time is reached.
+   * Use this on logout operations.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see: self::do_connect()
+   * @see: self::p_transfer()
+   */
+  public function do_terminate() {
+    if (is_null($this->ip) || is_null($this->session_id)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_resource($this->socket) || @socket_last_error($this->socket) != 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $response = $this->p_transfer(array('ip' => $this->ip, 'session_id' => $this->session_id, 'close' => TRUE));
+    if (c_base_return::s_has_error($response)) {
+      return $response->get_error();
+    }
+
+    $response = c_base_return_array::s_value_exact($response);
+    if (empty($response['result']) || !is_array($response['result'])) {
+      unset($response);
+
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($this->password)) {
+      $this->password = str_repeat(' ', self::PASSWORD_CLEAR_TEXT_LENGTH);
+    }
+
+    $this->name = NULL;
+    $this->password = NULL;
+    $this->timeout_expire = NULL;
+    $this->timeout_max = NULL;
+
+    unset($response);
+    return new c_base_return_true();
+  }
+
+  /**
+   * Closes (terminates) a session from an open socket.
+   *
+   * Unlike self::do_disconnect(), this does not close the connection to the socket, it closes the session itself.
+   *
+   * This is used to terminate a session before the expiration date and time is reached.
+   * Use this on logout operations.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE on failure.
+   *
+   * @see: self::do_connect()
+   * @see: self::p_transfer()
+   */
+  public function do_flush() {
+    if (!is_resource($this->socket) || @socket_last_error($this->socket) != 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $response = $this->p_transfer(array('flush' => TRUE));
+    if (c_base_return::s_has_error($response)) {
+      return $response->get_error();
+    }
+
+    $response = c_base_return_array::s_value_exact($response);
+    if (empty($response['result']) || !is_array($response['result'])) {
+      unset($response);
+
+      return c_base_return_error::s_false();
+    }
+
+    unset($response);
+    return new c_base_return_true();
+  }
+
+  /**
+   * Transfer a request packet through the socket.
+   *
+   * @param array $request
+   *   A request array defined as required by the socket.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array is returned on success.
+   *   FALSE is returned otherwise.
+   *
+   * @see: c_base_session::do_connect()
+   */
+  private function p_transfer($request) {
+    unset($this->error);
+    $this->error = NULL;
+
+    $json = json_encode($request);
+
+    $written = socket_write($this->socket, $json);
+    unset($json);
+
+    if ($written === FALSE || $written == 0) {
+      unset($written);
+
+      return c_base_return_error::s_false();
+    }
+    unset($written);
+
+    $json = socket_read($this->socket, self::PACKET_MAX_LENGTH);
+    if (!is_string($json) || mb_strlen($json) == 0) {
+      unset($json);
+
+      return c_base_return_error::s_false();
+    }
+
+    $response = json_decode($json, TRUE);
+    unset($json);
+
+    if (isset($response['error'])) {
+      $this->error = $response['error'];
+    }
+
+    if ($response === FALSE) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($response);
+  }
+}
+
+/**
+ * A return class whose value is represented as a c_base_session.
+ */
+class c_base_session_return extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, FALSE);
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param c_base_session $value
+   *   Any value so long as it is a c_base_session object.
+   *   NULL is not allowed.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!($value instanceof c_base_session)) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return c_base_session $value
+   *   The value array stored within this class.
+   */
+  public function get_value() {
+    if (!($this->value instanceof c_base_session)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return c_base_session $value
+   *   The value c_base_session stored within this class.
+   */
+  public function get_value_exact() {
+    if (!($this->value instanceof c_base_session)) {
+      $this->value = new c_base_session();
+    }
+
+    return $this->value;
+  }
+}
diff --git a/common/base/classes/base_utf8.php b/common/base/classes/base_utf8.php
new file mode 100644 (file)
index 0000000..fff5ca0
--- /dev/null
@@ -0,0 +1,997 @@
+<?php
+/**
+ * @file
+ * Provides UTF-8 support.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+/**
+ * A class for managing and processing utf-8.
+ */
+class c_base_utf8 {
+  const BOM = "\xef\xbb\xbf";
+  const UTF_8 = 'UTF-8';
+
+  const SPACE_NO_BREAK        = 160;
+  const SPACE_NO_BREAK_NARROW = 8239;
+  const SPACE_OGHAM_MARK      = 5760;
+  const SPACE_MONGOLIAN_VOWEL = 6158;
+  const SPACE_EN              = 8194;
+  const SPACE_EN_QUAD         = 8192;
+  const SPACE_EM              = 8195;
+  const SPACE_EM_QUAD         = 8193;
+  const SPACE_EM_THREE_PER    = 8196;
+  const SPACE_EM_FOUR_PER     = 8197;
+  const SPACE_EM_SIX_PER      = 8198;
+  const SPACE_FIGURE          = 8199;
+  const SPACE_PUNCTUTION      = 8200;
+  const SPACE_THIN            = 8201;
+  const SPACE_HAIR            = 8202;
+  const SPACE_LINE            = 8232;
+  const SPACE_PARAGRAPH       = 8233;
+  const SPACE_MATHEMATICAL    = 8287;
+  const SPACE_IDEOGRAPHIC     = 12288;
+
+  /**
+   * UTF_8-X masks and marks.
+   * - UTF_8-1: 0bxxxxxxxx (0b0xxxxxxx) (0000 0000)
+   * - UTF_8-2: 0byyyyyxxxxxx (0b110yyyyy, 0b10xxxxxx) (1100 0000 1000 0000)
+   * - UTF_8-3: 0bzzzzyyyyyyxxxxxx (0b1110zzzz, 0b10yyyyyy, 0b10xxxxxx) (1110 0000 1000 0000 1000 0000)
+   * - UTF_8-4: 0bvvvzzzzzzyyyyyyxxxxxx (0b11110vvv, 0b10zzzzzz, 0b10yyyyyy, 0b10xxxxxx) (1111 1000 1000 0000 1000 0000 1000 0000)
+   *
+   * The masks for UTF_8 are intended to represent the bits that will never be present for the given UTF_8 group.
+   *
+   * The marks for UTF_8 are intended to represent the exact "left" order bit combination that are required to represent the UTF_8 group.
+   *
+   * The bits are for checking a single 8-bit character value (specifically, checking the first bits).
+   */
+  const MASK_1 = 0b11111111111111111111111110000000;
+  const MASK_2 = 0b11111111111111110000000000000000;
+  const MASK_3 = 0b11111111000000000000000000000000;
+  const MASK_4 = 0b00000000000000000000000000000000;
+
+  const MARK_1 = 0b00000000000000000000000000000000;
+  const MARK_2 = 0b00000000000000001100000010000000;
+  const MARK_3 = 0b00000000111000001000000010000000;
+  const MARK_4 = 0b11110000100000001000000010000000;
+
+  const BIT_1 = 0b10000000;
+  const BIT_2 = 0b11000000;
+  const BIT_3 = 0b11100000;
+  const BIT_4 = 0b11110000;
+
+
+  /**
+   * Checks whether the passed string contains only byte sequances that appear valid UTF-8 characters.
+   *
+   * @param string $text
+   *   The string to be checked.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *
+   * @see: mb_detect_encoding()
+   */
+  public static function s_is_UTF_8($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (mb_check_encoding($text, self::UTF_8)) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Calculates integer value of the given UTF-8 encoded character.
+   *
+   * @param string $character
+   *   The character of which to calculate ordinal of.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   Ordinal value of the given character.
+   *   FALSE without error bit set is returned on invalid UTF-8 byte sequence
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_character_to_ordinal($character) {
+    if (!is_string($character)) {
+      return c_base_return_error::s_false();
+    }
+
+    $ordinal = self::p_s_character_to_ordinal($character);
+    if ($ordinal === FALSE) {
+      unset($ordinal);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($ordinal);
+  }
+
+  /**
+   * Generates a UTF-8 encoded character from the given ordinal.
+   *
+   * @param int $ordinal
+   *   The ordinal for which to generate a character.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   Milti-Byte character returns empty string on failure to encode.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_ordinal_to_character($ordinal) {
+    if (!is_int($ordinal)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new(self::p_s_ordinal_to_character($ordinal));
+  }
+
+  /**
+   * Finds the length of the given string in terms of number of valid UTF-8 characters it contains.
+   *
+   * Invalid characters are ignored.
+   *
+   * @param string $text
+   *   The string for which to find the character length.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   Length of the Unicode String.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: mb_strlen()
+   */
+  public static function s_length_string($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    $length = self::p_s_length_string($text);
+    if ($length === FALSE) {
+      unset($length);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($length);
+  }
+
+  /**
+   * Removes all non-UTF-8 characters from a given string.
+   *
+   * @param string $text
+   *   The string to be sanitized.
+   * @param bool $remove_bom
+   *   (optional) When TRUE, the UTF_8 BOM character is removed.
+   *   When FALSE, no action related to the BOM character is performed.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   Clean UTF-8 encoded string.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_clean($text, $remove_bom = FALSE) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($remove_bom)) {
+      return c_base_return_error::s_false();
+    }
+
+    $sanitized = self::p_s_clean($text);
+    if ($sanitized === FALSE) {
+      unset($sanitized);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($sanitized);
+  }
+
+  /**
+   * Convert a string to an array of Unicode characters.
+   *
+   * @param string $text
+   *   The string to split into array.
+   * @param int $split_length
+   *   Max character length of each array element.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array containing chunks of the string.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_split($text, $split_length = 1) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($split_length)) {
+      return c_base_return_error::s_false();
+    }
+
+    $split = self::p_s_split($text, $split_length);
+    if ($split === FALSE) {
+      unset($split);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($split);
+  }
+
+  /**
+   * Generates an array of each character of a Unicode string separated into individual bytes.
+   *
+   * @param string $text
+   *   The original Unicode string.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array of byte lengths of each character.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_character_size_list($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    $size_list = self::p_s_character_size_list($text);
+    if ($size_list === FALSE) {
+      unset($size_list);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new($size_list);
+  }
+
+  /**
+   * Calculates and returns the maximum number of bytes taken by any UTF-8 encoded character in the given string.
+   *
+   * @param string $text
+   *   The original Unicode string.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   An integer representing the max character width found within the passed string.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: p_s_character_size_list()
+   */
+  public static function s_character_max_width($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    $size_list = self::p_s_character_size_list($text);
+    if ($size_list === FALSE) {
+      unset($size_list);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new(max($size_list));
+  }
+
+  /**
+   * Converts a UTF-8 character to HTML Numbered Entity like &#123;
+   *
+   * @param string $character
+   *   The Unicode character to be encoded as numbered entity
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   HTML numbered entity.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: p_s_encode_html_character()
+   */
+  public static function s_encode_html_character($character) {
+    if (!is_string($character)) {
+      return c_base_return_error::s_false();
+    }
+
+    $encoded = self::p_s_encode_html_character($character);
+    if ($encoded === FALSE) {
+      unset($encoded);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new($encoded);
+  }
+
+  /**
+   * Converts a UTF-8 string to a series of HTML Numbered Entities, such as: &#123;&#39;&#1740;...
+   *
+   * @param string $text
+   *   The Unicode string to be encoded as numbered entities.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   HTML numbered entities.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_encode_html($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    $split = self::p_s_split($text);
+    if ($split === FALSE) {
+      unset($split);
+      return c_base_return_error::s_false();
+    }
+
+    $new_array = array();
+    foreach ($split as $character) {
+      $encoded = self::p_s_character_to_ordinal($character);
+      if ($encoded === FALSE) {
+        // should some error reporting/handling be performed here?
+        continue;
+      }
+
+      $new_array[] = $ordinal;
+    }
+    unset($encoded);
+    unset($character);
+
+    return c_base_return_string::s_new(implode($new_array));
+  }
+
+  /**
+   * UTF-8 aware substr().
+   *
+   * substr() works with bytes, while s_substring works with characters and are identical in all other aspects.
+   *
+   * @param string $text
+   *   The string to obtain a substring from.
+   * @param int $start
+   *   (optional) The start position.
+   * @param int|null $length
+   *   (optional) The length of the substring.
+   *   If NULL, then the length is PHP_INT_MAX.
+   *
+   * @see: mb_substr()
+   */
+  public static function s_substring($text, $start = 0, $length = NULL) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($start)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($length) && !is_int($length)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new(self::p_s_substring($text, $start, $length));
+  }
+
+  /**
+   * Checks if the given string is a Byte Order Mark.
+   *
+   * @param string $text
+   *   The input string.
+   *
+   * @return c_base_return_status
+   *   TRUE if the $text is Byte Order Mark, FALSE otherwise.
+   *
+   * @see: p_s_is_bom()
+   */
+  public static function s_is_bom($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (self::p_s_is_bom($text)) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Checks if a file starts with BOM character.
+   *
+   * @param string $file_path
+   *   Path to a valid file.
+   *
+   * @return c_base_return_status
+   *   TRUE if the file has BOM at the start, FALSE otherwise.
+   */
+  public static function s_file_has_bom($file_path) {
+    if (!is_string($file_path)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!file_exists($file_path)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (self::p_s_is_bom(file_get_contents($file_path, 0, NULL, -1, 3))) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Checks if string starts with BOM character.
+   *
+   * @param string $text
+   *   The input string.
+   *
+   * @return c_base_return_status
+   *   TRUE if the string has BOM at the start, FALSE otherwise.
+   */
+  public static function s_string_has_bom($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (self::p_s_is_bom(substr($text, 0, 3))) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Prepends BOM character to the string and returns the whole string.
+   *
+   * If BOM already existed there, the Input string is returned.
+   *
+   * @param string $text
+   *   The input string.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The output string that contains BOM.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_prepend_bom($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!s_is_bom(substr($text, 0, 3))) {
+      return c_base_return_string::s_new(self::BOM . $text);
+    }
+
+    return c_base_return_string::s_new($text);
+  }
+
+  /**
+   * Reverses characters order in the string.
+   *
+   * @param string $text
+   *   The input string.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The string with characters in the reverse sequence.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_reverse($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    $split = self::p_s_split($text);
+    if ($split === FALSE) {
+      unset($split);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new(implode(array_reverse($split)));
+  }
+
+  /**
+   * Finds the number of Characters to the left of first occurance of the needle.
+   *
+   * strpos works with bytes, while s_position_string works with characters and are identical in all other aspects.
+   *
+   * @param string $haystack
+   *   The string to search within.
+   * @param string|int $needle
+   *   The characters to search for or a single ordinal integer to search for.
+   * $param int $offset
+   *   (optional) position in the string to start searching.
+   *   The offset cannot be negative.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   A number representing the character position is returned when the needle is found in the haystack.
+   *   FALSE with error bit set is returned on error.
+   *   FALSE without the error bit set is returned when the needle is not found in the haystack.
+   *
+   * @see: mb_strpos()
+   */
+  public static function s_position_string($haystack, $needle, $offset = 0) {
+    if (!is_string($haystack)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($needle) && !is_string($needle)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($offset) || $offset < 0) {
+      return c_base_return_error::s_false();
+    }
+
+    $search_for = $needle;
+    if (is_int($needle)) {
+      $search_for = self::p_s_ordinal_to_character($needle);
+    }
+
+    $search_for = self::p_s_clean($search_for);
+    $target = self::p_s_clean($haystack);
+
+    return c_base_return_int::s_new(mb_strpos($target, $search_for, $offset, self::UTF_8));
+  }
+
+  /**
+   * Accepts a string and returns an array of ordinals.
+   *
+   * @param string|array $text
+   *   A UTF-8 encoded string or an array of such strings.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   The array of ordinals.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_string_to_ordinals($text) {
+    if (!is_string($text) && !is_array($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_array::s_new(self::p_s_string_to_ordinals($text));
+  }
+
+  /**
+   * Converts an array of ordinals into a string.
+   *
+   * @param array $ordinals
+   *   An array of Unicode code points.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   A string represnting the ordinals array.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_ordinals_to_string($ordinals) {
+    if (!is_array($ordinals)) {
+      return c_base_return_error::s_false();
+    }
+
+    $string = '';
+    foreach ($ordinals as $ordinal) {
+      $character = self::p_s_ordinal_to_character($ordinal);
+      if ($character === FALSE) {
+        // @todo: generate a warning or error of some sort.
+        continue;
+      }
+
+      $string .= $character;
+    }
+    unset($ordinal);
+    unset($character);
+
+    return c_base_return_string::s_new($string);
+  }
+
+  /**
+   * Converts an array of ordinals into an array of characters.
+   *
+   * @param array $ordinals
+   *   An array of ordinals.
+   *
+   * @return c_base_return_array|c_base_return_status
+   *   An array of characters represnting the ordinals array.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_ordinals_to_string_array($ordinals) {
+    if (!is_array($ordinals)) {
+      return c_base_return_error::s_false();
+    }
+
+    $array = array();
+    foreach ($ordinals as $ordinal) {
+      $character = self::p_s_ordinal_to_character($ordinal);
+      if ($character === FALSE) {
+        // @todo: generate a warning or error of some sort.
+        continue;
+      }
+
+      $array[] = $character;
+    }
+    unset($ordinal);
+    unset($character);
+
+    return c_base_return_array::s_new($array);
+  }
+
+  /**
+   * Makes a UTF-8 string from a ordinal array.
+   *
+   * @param array $ordinals
+   *   An array of rdinals.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   UTF-8 encoded string.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_ordinal_array_to_string($ordinals) {
+    if (!is_array($ordinals)) {
+      return c_base_return_error::s_false();
+    }
+
+    $array = array();
+    foreach ($ordinals as $key => $ordinal) {
+      $array[$key] = self::p_s_ordinal_to_character($ordinal);
+    }
+    unset($key);
+    unset($ordinal);
+
+    return c_base_return_string::s_new(implode($array));
+  }
+
+  /**
+   * Converts ordinal to a unicode codepoint.
+   *
+   * @param int $ordinal
+   *   The ordinal to be converted to codepoint.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   The codepoint.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_ordinal_to_codepoint($ordinal) {
+    if (!is_int($ordinal)) {
+      return c_base_return_error::s_false();
+    }
+
+    $codepoint = self::p_s_ordinal_to_codepoint($ordinal);
+    if ($codepoint === FALSE) {
+      unset($codepoint);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_int::s_new($codepoint);
+  }
+
+  /**
+   * Count the number of sub string occurances.
+   *
+   * @param string $haystack
+   *   The string to search in.
+   * @param string $needle
+   *   The string to search for.
+   * @param int $offset
+   *   The offset where to start counting.
+   * @param null|int $length
+   *   The maximum length after the specified offset to search for the substring.
+   *
+   * @return c_base_return_int|c_base_return_status
+   *   Number of occurances of $needle.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_count_substrings($haystack, $needle, $offset = 0, $length = NULL) {
+    if (!is_string($haystack)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($needle)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_int($offset)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($length) && !is_int($length)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($offset || $length) {
+      $haystack = s_substring($haystack, $offset, $length);
+    }
+
+    if (is_null($length)) {
+      return c_base_return_int::s_new(substr_count($haystack, $needle, $offset));
+    }
+
+    return c_base_return_int::s_new(substr_count($haystack, $needle, $offset, $length));
+  }
+
+  /**
+   * Checks if a string is 7 bit ASCII.
+   *
+   * @param string
+   *   $text The string to check.
+   *
+   * @return c_base_return_status
+   *   TRUE if ASCII, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_is_ascii($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (preg_match('/[\x80-\xff]/', $text)) {
+      return new c_base_return_false();
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Strip HTML and PHP tags from a string.
+   *
+   * @param string $text
+   *   UTF-8 string.
+   * @param string $allowable_tags
+   *   The tags to allow in the string.
+   *
+   * @return c_base_return_string|c_base_return_status
+   *   The stripped string.
+   *   FALSE with error bit set is returned on error.
+   */
+  public static function s_strip_tags($text, $allowable_tags = '') {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($allowable_tags)) {
+      return c_base_return_error::s_false();
+    }
+
+    // clean broken UTF_8.
+    $sanitized = self::p_s_clean($text);
+    if ($sanitized === FALSE) {
+      unset($sanitized);
+      return c_base_return_error::s_false();
+    }
+
+    return c_base_return_string::s_new(strip_tags($sanitized, $allowable_tags));
+  }
+
+  /**
+   * Private version of s_character_to_ordinal().
+   *
+   * @see: self::s_character_to_ordinal()
+   */
+  private static function p_s_character_to_ordinal($character) {
+    $split = self::p_s_split($character);
+    if ($split === FALSE) {
+      unset($split);
+      return FALSE;
+    }
+
+    $first = $split[0];
+    unset($split);
+
+    switch(strlen($first)) {
+      case 1:
+        return ord($first);
+
+      case 2:
+        return ((ord($first[0]) << 8) | ord($first[1]));
+
+      case 3:
+        return ((ord($first[0]) << 16) | (ord($first[1]) << 8) | ord($first[2]));
+
+      case 4:
+        return ((ord($first[0]) << 24) | (ord($first[1]) << 16) | (ord($first[2]) << 8) | ord($first[3]));
+    }
+
+    unset($first);
+    return FALSE;
+  }
+
+  /**
+   * Private version of s_ordinal_to_codepoint().
+   *
+   * @see: self::s_ordinal_to_codepoint()
+   */
+  private static function p_s_ordinal_to_codepoint($ordinal) {
+    if ($ordinal == 0) {
+      return 0;
+    }
+
+    if (($ordinal & self::MASK_1) == 0) {
+      return $ordinal;
+    }
+
+    if (($ordinal & self::MASK_2) == 0) {
+      $high = ($ordinal >> 8);
+      $low = $ordinal - ($high << 8);
+
+      return ((($high & 0x1f) << 6) | ($low & 0x3f));
+    }
+
+    if (($ordinal & self::MASK_3) == 0) {
+      $high = ($ordinal >> 16);
+      $medium = ($ordinal - ($high << 16) >> 8);
+      $low = ($ordinal - ($high << 16) - ($medium << 8));
+
+      return ((($high & 0x0f) << 12) | (($medium & 0x3f) << 6) | ($low & 0x3f));
+    }
+
+
+    $high = ($ordinal >> 24);
+    $medium_1 = ($ordinal - ($high << 24) >> 16);
+    $medium_2 = ($ordinal - ($high << 24) - ($medium_1 << 16) >> 8);
+    $low = ($ordinal - ($high << 24) - ($medium_1 << 16) - ($medium_2 << 8));
+
+    return ((($high & 0x07) << 18) | (($medium_1 & 0x3f) << 12) | (($medium_2 & 0x3f) << 6) | ($low & 0x3f));
+  }
+
+  /**
+   * Private version of s_ordinal_to_character().
+   *
+   * @see: self::s_ordinal_to_character()
+   */
+  private static function p_s_ordinal_to_character($ordinal) {
+    // mb_convert_encoding() accepts codepoints, so first convert the ordinal to a codepoint.
+    $codepoint = self::p_s_ordinal_to_codepoint($ordinal);
+
+    return mb_convert_encoding('&#' . $codepoint . ';', self::UTF_8, 'HTML-ENTITIES');
+  }
+
+  /**
+   * Private version of: s_string_to_ordinals()
+   *
+   * @see: self::s_string_to_ordinals()
+   */
+  private static function p_s_string_to_ordinals($text) {
+    if (is_string($text)) {
+      $text = self::p_s_split($text);
+    }
+
+    $ordinals = array();
+    foreach ($text as $character) {
+      $value = self::p_s_character_to_ordinal($character);
+      if ($value === FALSE) {
+        continue;
+      }
+
+      $ordinals[] = $value;
+    }
+    unset($value);
+
+    return $ordinals;
+  }
+
+  /**
+   * Private version of s_length_string().
+   *
+   * @see: self::s_length_string()
+   */
+  private static function p_s_length_string($text) {
+    return mb_strlen($text, self::UTF_8);
+  }
+
+  /**
+   * Private version of s_clean().
+   *
+   * @see: self::s_clean()
+   */
+  private static function p_s_clean($text, $remove_bom = FALSE) {
+    $sanitized = preg_replace('/([\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})|./s', '$1', $text);
+
+    if (is_null($sanitized)) {
+      unset($sanitized);
+      return FALSE;
+    }
+
+    if ($remove_bom) {
+      $sanitized = str_replace(self::BOM, '', $sanitized);
+    }
+
+    return $sanitized;
+  }
+
+  /**
+   * Private version of s_split().
+   *
+   * @see: self::s_split()
+   */
+  private static function p_s_split($text, $split_length = 1) {
+    $split = array();
+    $text = self::p_s_clean($text);
+
+    preg_match_all('/\X/u', $text, $split);
+    $split = $split[0];
+
+    if ($split_length > 1) {
+      $split = array_chunk($split, $split_length);
+      if (is_null($split)) {
+        return FALSE;
+      }
+
+      $array = array();
+      foreach ($split as $key => $value) {
+        $array[$key] = implode($value);
+      }
+      unset($key);
+      unset($value);
+
+      $split = $array;
+      unset($array);
+    }
+
+    if ($split[0] === '') {
+      return array();
+    }
+
+    return $split;
+  }
+
+  /**
+   * Private version of s_character_size_list().
+   *
+   * @see: self::s_character_size_list()
+   */
+  private static function p_s_character_size_list($text) {
+    $split = self::p_s_split($text);
+    if ($split === FALSE) {
+      unset($split);
+      return FALSE;
+    }
+
+    $array = array();
+    foreach ($split as $key => $value) {
+      $array[$key] = strlen($value);
+    }
+    unset($key);
+    unset($value);
+    unset($split);
+
+    return $array;
+  }
+
+  /**
+   * Private version of s_substring().
+   *
+   * @see: self::s_substring()
+   */
+  private static function p_s_substring($text, $start = 0, $length = NULL) {
+    if (is_null($length)) {
+      $length = PHP_INT_MAX;
+    }
+
+    $sanitized = self::p_s_clean($text);
+
+    return mb_substr($sanitized, $start, $length, self::UTF_8);
+  }
+
+  /**
+   * Private version of s_encode_html_character().
+   *
+   * @see: self::s_encode_html_character()
+   */
+  private static function p_s_encode_html_character($character) {
+    $ordinal = p_s_character_to_ordinal($character);
+    if ($ordinal === FALSE) {
+      unset($ordinal);
+      return FALSE;
+    }
+
+    $codepoint = p_s_ordinal_to_codepoint($ordinal);
+    unset($ordinal);
+    if ($codepoint === FALSE) {
+      unset($codepoint);
+      return FALSE;
+    }
+
+    return '&#' . $codepoint . ';';
+  }
+
+  /**
+   * Private version of s_is_bom().
+   *
+   * @see: self::s_is_bom()
+   */
+  private static function p_s_is_bom($text) {
+    if ($text === self::BOM) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+}
diff --git a/common/theme/classes/theme_ajax.php b/common/theme/classes/theme_ajax.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/common/theme/classes/theme_dom.php b/common/theme/classes/theme_dom.php
new file mode 100644 (file)
index 0000000..14b2a4f
--- /dev/null
@@ -0,0 +1,790 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing HTML DOM.
+ *
+ * This is currently a draft/brainstorm and is subject to be completely rewritten/redesigned.
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+
+/**
+ * A generic class for managing HTML DOM.
+ *
+ * This class uses DOMDocument and assigns a type of doctype HTML.
+ * This is not technically HTML5, but HTML5 must be used to make sure the dom functionality operates as expected.
+ *
+ * The purpose of this class is to provide a context-based language for later renderring of content, be it HTML5 or otherwise.
+ *
+ * It is primarily meant to help simplify management of the DOMDocument and DOMNode classes without re-implementing anything.
+ */
+class c_theme_dom extends DOMDocument {
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    super.__construct('1.0', 'UTF-8');
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+  }
+
+  /**
+   * Initialize the object to explicit HTML5 settings.
+   */
+  public function initialize_as_html() {
+    $this->preserveWhiteSpace = TRUE;
+    $this->formatOutput = FALSE;
+    @$this->loadHTML('<!DOCTYPE html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body></body>');
+  }
+
+  /**
+   * Changes the element from one type to another.
+   *
+   * @param DOMNode $element
+   *   The element whose type will be changed.
+   * @param string $type
+   *   The new element type to use.
+   *
+   * @return DOMNode|bool
+   *   The changed element on success.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::pr_change_element()
+   */
+  public function change_element($element, $type) {
+    if (!($element instanceof DOMNode)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($type) || empty($type)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_them_return_dom_node::s_new($this->pr_change_element($element, $type));
+  }
+
+
+  /**
+   * Change all elements of a given element type to another type.
+   *
+   * @param string $type_old
+   *   The old element type to be replaced.
+   * @param string $type_new
+   *   The new element type to use.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   Otherwise FALSE with error bit set is returned.
+   *
+   * @see: self::pr_change_elements()
+   */
+  public function change_elements($type_old, $type_new) {
+    if (!is_string($type_old) || strlen($type_old) == 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_string($type_new) || strlen($type_empty) == 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!($this->content instanceof DOMNode)) {
+      return c_base_return_error::s_false();
+    }
+
+    return $this->pr_change_elements($type_old, $type_new, $this->content);
+  }
+
+  /**
+   * Removes the given element from its parent.
+   *
+   * This preserves child elements.
+   * To remove entirely, use removeChild() directly.
+   *
+   * @param DOMNode $element
+   *   The object to convert to markup text.
+   * @param bool $preserve_children
+   *   (optional) If TRUE, children are re-attached to the parent node to preserve their location in the markup.
+   *   If FALSE, the children remain attached to the removed element.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE without error bit set is returned when unable to remove element.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::pr_remove_element()
+   * @see: DOMDocument::removeChild()
+   */
+  public function remove_element($element, $preserve_children = TRUE) {
+    if (!($element instanceof DOMNode)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($preserve_children)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($this->pr_remove_element($element, $preserve_children)) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Remove all elements of a given element type.
+   *
+   * This preserves child elements.
+   * To remove elements entirely, use removeChild() directly.
+   *
+   * @param string $type
+   *   The new element type to use.
+   * @param bool $preserve_children
+   *   (optional) If TRUE, children are re-attached to the parent node to preserve their location in the markup.
+   *   If FALSE, the children remain attached to the removed element.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE without error bit set is returned when unable to remove element.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::pr_remove_elements()
+   * @see: DOMDocument::removeChild()
+   */
+  public function remove_elements($type, $preserve_children = TRUE) {
+    if (!is_string($type) || strlen($type) == 0) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_bool($preserve_children)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!($this->content instanceof DOMNode)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($this->pr_remove_elements($type, $this->content, $preserve_children)) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Converts the element into markup
+   *
+   * @param bool $include_tag
+   *   When TRUE, the head tag itself will be included in the output.
+   *   When FALSE, only the contents of the tag will be included in the output.
+   * @param DOMNode $parent
+   *   The object to operate on.
+   *
+   * @return string|bool
+   *   The markup text that the object was converted from.
+   *   FALSE with error bit set is returned on error.
+   */
+  protected function pr_get_markup($include_tag, $parent) {
+    if ($include_tag) {
+      return $this->saveHTML($parent);
+    }
+
+    $markup = '';
+    if ($parent->hasChildNodes() > 0) {
+      foreach ($parent->childNodes as $child) {
+        $markup .= $this->saveHTML($child);
+      }
+    }
+    unset($child);
+
+    return $markup;
+  }
+
+  /**
+   * Changes the element from one type to another.
+   *
+   * @param DOMNode $element
+   *   The element whose type will be changed.
+   * @param string $type
+   *   The new element type to use.
+   *
+   * @return bool
+   *   The changed element on success.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::change_element()
+   */
+  protected function pr_change_element($element, $type) {
+    $parent = $element->parentNode;
+    $new = $this->createElement($type);
+
+    if (!($new instanceof DOMNode)) {
+      return FALSE;
+    }
+
+    if ($element->hasAttributes()) {
+      foreach ($element->attributes as $attribute) {
+        $new->setAttribute($attribute->name, $attribute->value);
+      }
+    }
+
+    if ($element->hasChildNodes()) {
+      foreach ($element->childNodes as $child) {
+        $new->appendChild($child->cloneNode(TRUE));
+      }
+      unset($child);
+    }
+
+    if ($parent instanceOf DOMNode) {
+      $child = $parent->replaceChild($new, $element);
+    }
+    else {
+      $this->appendChild($element);
+      $child = $this->replaceChild($new, $element);
+
+      if ($child instanceof DOMNode) {
+        $parent = $child->parentNode;
+
+        if ($parent instanceOf DOMNode) {
+          $this->removeChild($child);
+        }
+      }
+      else {
+        $this->removeChild($element);
+      }
+    }
+
+    unset($new);
+    unset($parent);
+
+    if ($child instanceOf DOMNode) {
+      return $child;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Change all elements of a given element type to another type.
+   *
+   * @param string $type
+   *   The new element type to operate on.
+   * @param DOMNode $parent
+   *   The object to operate on.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::change_elements()
+   */
+  protected function pr_change_elements($type, $parent) {
+    $result = TRUE;
+
+    $elements = $parent->getElementsByTagName($type);
+    foreach ($elements as $element) {
+      if ($element instanceof DOMNode) {
+        $result = $this->pr_change_element($element, $type);
+      }
+      else {
+        $result = FALSE;
+      }
+
+      if (!$result) break;
+    }
+    unset($element);
+    unset($elements);
+
+    return $result;
+  }
+
+  /**
+   * Removes the given element from its parent.
+   *
+   * This preserves child elements.
+   * To remove entirely, use removeChild() directly.
+   *
+   * @param DOMNode $element
+   *   The object to convert to markup text.
+   * @param bool $preserve_children
+   *   (optional) If TRUE, children are re-attached to the parent node to preserve their location in the markup.
+   *   If FALSE, the children remain attached to the removed element.
+   *
+   * @return bool
+   *   The removed element on success.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::remove_element()
+   * @see: DOMDocument::removeChild()
+   */
+  protected function pr_remove_element($element, $preserve_children = TRUE) {
+    $parent = $element->parentNode;
+
+    if (!($parent instanceof DOMNode)) {
+      unset($parent);
+      return FALSE;
+    }
+
+    if ($preserve_children && $element->hasChildNodes()) {
+      $children = array();
+
+      foreach ($element->childNodes as $child) {
+        $children[] = $child;
+      }
+      unset($child);
+
+      foreach ($children as $child) {
+        $removed_child = $element->removeChild($child);
+
+        if (is_object($removed_child)) {
+          $parent->insertBefore($removed_child, $element);
+        }
+      }
+      unset($child);
+      unset($removed_child);
+      unset($children);
+    }
+
+    $child = $parent->removeChild($element);
+    unset($parent);
+
+    if ($child instanceof DOMNode) {
+      return $child;
+    }
+    unset($child);
+
+    return FALSE;
+  }
+
+  /**
+   * Remove all elements of a given element type.
+   *
+   * This preserves child elements.
+   * To remove elements entirely, use removeChild() directly.
+   *
+   * @param string $type
+   *   The new element type to operate on.
+   * @param DOMNode $parent
+   *   The object to operate on.
+   * @param bool $preserve_children
+   *   (optional) If TRUE, children are re-attached to the parent node to preserve their location in the markup.
+   *   If FALSE, the children remain attached to the removed element.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::remove_elements()
+   * @see: DOMDocument::removeChild()
+   */
+  protected function pr_remove_elements($type, $parent, $preserve_children = TRUE) {
+    $result = TRUE;
+
+    $elements = $parent->getElementsByTagName($type);
+    foreach ($elements as $element) {
+      $result = $this->pr_remove_element($element, $preserve_children);
+
+      if (!$result) break;
+    }
+    unset($elements);
+
+    return $result;
+  }
+}
+
+/**
+ * A return class whose value is represented as a c_theme_dom.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ */
+class c_theme_return_c_theme_dom extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, '');
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param DOMNode $value
+   *   Any value so long as it is a c_theme_dom.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_value($value) {
+    if (!$value instanceof c_theme_dom) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return string|null $value
+   *   The value array stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !($this->value instanceof c_theme_dom)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return c_theme_dom $value
+   *   The value c_theme_dom stored within this class.
+   */
+  public function get_value_exact() {
+    if (!($this->value instanceof c_theme_dom)) {
+      $this->value = new c_theme_dom();
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a DOMNode.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ */
+class c_theme_return_dom_node extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, '');
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param DOMNode $value
+   *   Any value so long as it is a DOMNode.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_value($value) {
+    if (!$value instanceof DOMNode) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return string|null $value
+   *   The value array stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !($this->value instanceof DOMNode)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return DOMNode $value
+   *   The value DOMNode stored within this class.
+   */
+  public function get_value_exact() {
+    if (!($this->value instanceof DOMNode)) {
+      $this->value = new DOMNode();
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a DOMComment.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ */
+class c_theme_return_dom_comment extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, '');
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param DOMComment $value
+   *   Any value so long as it is a DOMComment.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_value($value) {
+    if (!$value instanceof DOMComment) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return string|null $value
+   *   The value array stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !($this->value instanceof DOMComment)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return DOMComment $value
+   *   The value DOMComment stored within this class.
+   */
+  public function get_value_exact() {
+    if (!($this->value instanceof DOMComment)) {
+      $this->value = new DOMComment();
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a DOMElement.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ */
+class c_theme_return_dom_element extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, '');
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param DOMElement $value
+   *   Any value so long as it is a DOMElement.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_value($value) {
+    if (!$value instanceof DOMElement) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return string|null $value
+   *   The value array stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !($this->value instanceof DOMElement)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return DOMElement $value
+   *   The value DOMElement stored within this class.
+   */
+  public function get_value_exact() {
+    if (!($this->value instanceof DOMElement)) {
+      $this->value = new DOMElement();
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A return class whose value is represented as a DOMText.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ */
+class c_theme_return_dom_text extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, '');
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param DOMText $value
+   *   Any value so long as it is a DOMText.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_value($value) {
+    if (!$value instanceof DOMText) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return string|null $value
+   *   The value array stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !($this->value instanceof DOMText)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return DOMText $value
+   *   The value DOMText stored within this class.
+   */
+  public function get_value_exact() {
+    if (!($this->value instanceof DOMText)) {
+      $this->value = new DOMText();
+    }
+
+    return $this->value;
+  }
+}
diff --git a/common/theme/classes/theme_form.php b/common/theme/classes/theme_form.php
new file mode 100644 (file)
index 0000000..8b5da3e
--- /dev/null
@@ -0,0 +1,478 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing HTML5 Markup.
+ *
+ * This is currently a draft/brainstorm and is subject to be completely rewritten/redesigned.
+ *
+ * @see: https://www.w3.org/TR/html5/
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+require_once('common/base/classes/base_form.php');
+
+// global:
+//accesskey
+//class
+//contenteditable
+//dir
+//hidden
+//id
+//lang
+//spellcheck
+//style
+//tabindex
+//title
+//translate
+//onabort
+//onblur*
+//oncancel
+//oncanplay
+//oncanplaythrough
+//onchange
+//onclick
+//oncuechange
+//ondblclick
+//ondurationchange
+//onemptied
+//onended
+//onerror*
+//onfocus*
+//oninput
+//oninvalid
+//onkeydown
+//onkeypress
+//onkeyup
+//onload*
+//onloadeddata
+//onloadedmetadata
+//onloadstart
+//onmousedown
+//onmouseenter
+//onmouseleave
+//onmousemove
+//onmouseout
+//onmouseover
+//onmouseup
+//onmousewheel
+//onpause
+//onplay
+//onplaying
+//onprogress
+//onratechange
+//onreset
+//onresize*
+//onscroll*
+//onseeked
+//onseeking
+//onselect
+//onshow
+//onstalled
+//onsubmit
+//onsuspend
+//ontimeupdate
+//ontoggle
+//onvolumechange
+//onwaiting
+
+//accept-charset - Character encodings to use for form submission
+//action - URL to use for form submission
+//autocomplete - Default setting for autofill feature for controls in the form
+//enctype - Form data set encoding type to use for form submission
+//method - HTTP method to use for form submission
+//name - Name of form to use in the document.forms API
+//novalidate - Bypass form control validation for form submission
+//target - Browsing context for form submission
+
+
+/**
+ * A generic class for form tags.
+ *
+ * The unique tag id is an integer to be used for internal purposes but may be exposed on output.
+ * If the id attribute is defined, then on output the id attribute is used for the HTML tag.
+ *
+ * @see: https://www.w3.org/TR/html5/forms.html#forms
+ */
+class c_theme_form_tag extends c_base_form_tag {
+  private $attributes;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    parent::__construct();
+
+    $this->attributes = array();
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    parent::__destruct();
+
+    unset($this->attributes);
+  }
+
+  /**
+   * Assign the specified tag.
+   *
+   * @param int $attribute
+   *   The attribute to assign.
+   * @param $value
+   *   The value of the attribute.
+   *   The actual value type is specific to each attribute type.
+   *
+   * @return c_base_return_status
+   *   TRUE on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_attribute($attribute, $value) {
+    if (!is_int($attribute)) {
+      return c_base_return_error::s_false();
+    }
+
+    switch ($attribute) {
+      case self::ATTRIBUTE_NONE:
+        unset($this->attribute[$attribute]);
+        return new c_base_return_true();
+
+      case self::ATTRIBUTE_ACTION:
+      case self::ATTRIBUTE_DIRECTION_NAME:
+      case self::ATTRIBUTE_FOR:
+      case self::ATTRIBUTE_FORM:
+      case self::ATTRIBUTE_FORM_ACTION:
+      case self::ATTRIBUTE_FORM_TARGET:
+      case self::ATTRIBUTE_KEY_TYPE:
+      case self::ATTRIBUTE_LABEL:
+      case self::ATTRIBUTE_LIST:
+      case self::ATTRIBUTE_NAME:
+      case self::ATTRIBUTE_ON_ABORT:
+      case self::ATTRIBUTE_ON_AFTER_PRINT:
+      case self::ATTRIBUTE_ON_ANIMATION_END:
+      case self::ATTRIBUTE_ON_ANIMATION_ITERATION:
+      case self::ATTRIBUTE_ON_ANIMATION_start:
+      case self::ATTRIBUTE_ON_BEFORE_UNLOAD:
+      case self::ATTRIBUTE_ON_BEFORE_PRINT:
+      case self::ATTRIBUTE_ON_BLUR:
+      case self::ATTRIBUTE_ON_CLICK:
+      case self::ATTRIBUTE_ON_CONTEXT_MENU:
+      case self::ATTRIBUTE_ON_COPY:
+      case self::ATTRIBUTE_ON_CUT:
+      case self::ATTRIBUTE_ON_CAN_PLAY:
+      case self::ATTRIBUTE_ON_CAN_PLAY_THROUGH:
+      case self::ATTRIBUTE_ON_CHANGE:
+      case self::ATTRIBUTE_ON_DOUBLE_CLICK:
+      case self::ATTRIBUTE_ON_DRAG:
+      case self::ATTRIBUTE_ON_DRAG_END:
+      case self::ATTRIBUTE_ON_DRAG_ENTER:
+      case self::ATTRIBUTE_ON_DRAG_LEAVE:
+      case self::ATTRIBUTE_ON_DRAG_OVER:
+      case self::ATTRIBUTE_ON_DRAG_START:
+      case self::ATTRIBUTE_ON_DROP:
+      case self::ATTRIBUTE_ON_DURATION_CHANGE:
+      case self::ATTRIBUTE_ON_ERROR:
+      case self::ATTRIBUTE_ON_EMPTIED:
+      case self::ATTRIBUTE_ON_ENDED:
+      case self::ATTRIBUTE_ON_ERROR:
+      case self::ATTRIBUTE_ON_FOCUS:
+      case self::ATTRIBUTE_ON_FOCUS_IN:
+      case self::ATTRIBUTE_ON_FOCUS_OUT:
+      case self::ATTRIBUTE_ON_HASH_CHANGE:
+      case self::ATTRIBUTE_ON_INPUT:
+      case self::ATTRIBUTE_ON_INVALID:
+      case self::ATTRIBUTE_ON_KEY_DOWN:
+      case self::ATTRIBUTE_ON_KEY_PRESS:
+      case self::ATTRIBUTE_ON_KEY_UP:
+      case self::ATTRIBUTE_ON_LOAD:
+      case self::ATTRIBUTE_ON_LOADED_DATA:
+      case self::ATTRIBUTE_ON_LOADED_META_DATA:
+      case self::ATTRIBUTE_ON_LOAD_START:
+      case self::ATTRIBUTE_ON_MOUSE_DOWN:
+      case self::ATTRIBUTE_ON_MOUSE_ENTER:
+      case self::ATTRIBUTE_ON_MOUSE_LEAVE:
+      case self::ATTRIBUTE_ON_MOUSE_MOVE:
+      case self::ATTRIBUTE_ON_MOUSE_OVER:
+      case self::ATTRIBUTE_ON_MOUSE_OUT:
+      case self::ATTRIBUTE_ON_MOUSE_UP:
+      case self::ATTRIBUTE_ON_MESSAGE:
+      case self::ATTRIBUTE_ON_MOUSE_WHEEL:
+      case self::ATTRIBUTE_ON_OPEN:
+      case self::ATTRIBUTE_ON_ONLINE:
+      case self::ATTRIBUTE_ON_OFFLINE:
+      case self::ATTRIBUTE_ON_PAGE_SHOW:
+      case self::ATTRIBUTE_ON_PAGE_HIDE:
+      case self::ATTRIBUTE_ON_PASTE:
+      case self::ATTRIBUTE_ON_PAUSE:
+      case self::ATTRIBUTE_ON_PLAY:
+      case self::ATTRIBUTE_ON_PLAYING:
+      case self::ATTRIBUTE_ON_PROGRESS:
+      case self::ATTRIBUTE_ON_POP_STATE:
+      case self::ATTRIBUTE_ON_RESIZE:
+      case self::ATTRIBUTE_ON_RESET:
+      case self::ATTRIBUTE_ON_RATE_CHANGE:
+      case self::ATTRIBUTE_ON_SCROLL:
+      case self::ATTRIBUTE_ON_SEARCH:
+      case self::ATTRIBUTE_ON_SELECT:
+      case self::ATTRIBUTE_ON_SUBMIT:
+      case self::ATTRIBUTE_ON_SEEKED:
+      case self::ATTRIBUTE_ON_SEEKING:
+      case self::ATTRIBUTE_ON_STALLED:
+      case self::ATTRIBUTE_ON_SUSPEND:
+      case self::ATTRIBUTE_ON_SHOW:
+      case self::ATTRIBUTE_ON_STORAGE:
+      case self::ATTRIBUTE_ON_TIME_UPDATE:
+      case self::ATTRIBUTE_ON_TRANSITION_END:
+      case self::ATTRIBUTE_ON_TOGGLE:
+      case self::ATTRIBUTE_ON_TOUCH_CANCEL:
+      case self::ATTRIBUTE_ON_TOUCH_END:
+      case self::ATTRIBUTE_ON_TOUCH_MOVE:
+      case self::ATTRIBUTE_ON_TOUCH_START:
+      case self::ATTRIBUTE_ON_UNLOAD:
+      case self::ATTRIBUTE_ON_VOLUME_CHANGE:
+      case self::ATTRIBUTE_ON_WAITING:
+      case self::ATTRIBUTE_ON_WHEEL:
+      case self::ATTRIBUTE_PATTERN:
+      case self::ATTRIBUTE_PLACE_HOLDER:
+      case self::ATTRIBUTE_READONLY:
+      case self::ATTRIBUTE_REQUIRED:
+      case self::ATTRIBUTE_ROWS:
+      case self::ATTRIBUTE_SELECTED:
+      case self::ATTRIBUTE_SIZE:
+      case self::ATTRIBUTE_SOURCE:
+      case self::ATTRIBUTE_STEP:
+      case self::ATTRIBUTE_TYPE:
+      case self::ATTRIBUTE_WRAP:
+      case self::ATTRIBUTE_VALUE:
+        if (!is_string($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case self::ATTRIBUTE_FORM_NO_VALIDATED:
+      case self::ATTRIBUTE_AUTO_COMPLETE:
+      case self::ATTRIBUTE_AUTO_FOCUS:
+      case self::ATTRIBUTE_CHALLENGE:
+      case self::ATTRIBUTE_CHECKED:
+      case self::ATTRIBUTE_DISABLED:
+      case self::ATTRIBUTE_MULTIPLE:
+        if (!is_bool($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case self::ATTRIBUTE_ACCEPT:
+      case self::ATTRIBUTE_FORM_ENCODE_TYPE:
+        if (!this->pr_validate_value_mime_type($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case self::ATTRIBUTE_COLUMNS:
+      case self::ATTRIBUTE_MAXIMUM:
+      case self::ATTRIBUTE_MAXIMUM_LENGTH:
+      case self::ATTRIBUTE_MINIMUM:
+        if (!is_int($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      case self::ATTRIBUTE_FORM_METHOD:
+        if (!this->pr_validate_value_http_method($value)) {
+          return c_base_return_false();
+        }
+        break;
+
+      default:
+        return new c_base_return_false();
+    }
+
+    $this->attribute[$attribute] = $value;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Get the attributes assigned to this object.
+   *
+   * @return c_base_return_array
+   *   The attributes assigned to this class.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_attributes() {
+    if (!isset($this->attributes) && !is_array($this->attributes)) {
+      $this->attributes = array();
+    }
+
+    return new c_base_return_array($this->attributes);
+  }
+
+  /**
+   * Get the value of a single attribute assigned to this object.
+   *
+   * @param int $attribute
+   *   The attribute to assign.
+   *
+   * @return c_base_return_int|c_base_return_string|c_base_return_bool|c_base_return_false
+   *   The value assigned to the attribte (the data type is different per attribute).
+   *   FALSE is returned if the element does not exist.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_attribute($attribute) {
+    if (!is_int($attribute)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!isset($this->attributes) && !is_array($this->attributes)) {
+      $this->attributes = array();
+    }
+
+    if (array_key_exists($attribute, $this->attributes)) {
+      switch ($attribute) {
+        case self::ATTRIBUTE_NONE:
+          // should not be possible, so consider this an error (attributes set to NONE are actually unset from the array).
+          return c_base_return_error::s_false();
+
+        case self::ATTRIBUTE_ACTION:
+        case self::ATTRIBUTE_DIRECTION_NAME:
+        case self::ATTRIBUTE_FOR:
+        case self::ATTRIBUTE_FORM:
+        case self::ATTRIBUTE_FORM_ACTION:
+        case self::ATTRIBUTE_FORM_TARGET:
+        case self::ATTRIBUTE_KEY_TYPE:
+        case self::ATTRIBUTE_LABEL:
+        case self::ATTRIBUTE_LIST:
+        case self::ATTRIBUTE_NAME:
+        case self::ATTRIBUTE_ON_ABORT:
+        case self::ATTRIBUTE_ON_AFTER_PRINT:
+        case self::ATTRIBUTE_ON_ANIMATION_END:
+        case self::ATTRIBUTE_ON_ANIMATION_ITERATION:
+        case self::ATTRIBUTE_ON_ANIMATION_start:
+        case self::ATTRIBUTE_ON_BEFORE_UNLOAD:
+        case self::ATTRIBUTE_ON_BEFORE_PRINT:
+        case self::ATTRIBUTE_ON_BLUR:
+        case self::ATTRIBUTE_ON_CLICK:
+        case self::ATTRIBUTE_ON_CONTEXT_MENU:
+        case self::ATTRIBUTE_ON_COPY:
+        case self::ATTRIBUTE_ON_CUT:
+        case self::ATTRIBUTE_ON_CAN_PLAY:
+        case self::ATTRIBUTE_ON_CAN_PLAY_THROUGH:
+        case self::ATTRIBUTE_ON_CHANGE:
+        case self::ATTRIBUTE_ON_DOUBLE_CLICK:
+        case self::ATTRIBUTE_ON_DRAG:
+        case self::ATTRIBUTE_ON_DRAG_END:
+        case self::ATTRIBUTE_ON_DRAG_ENTER:
+        case self::ATTRIBUTE_ON_DRAG_LEAVE:
+        case self::ATTRIBUTE_ON_DRAG_OVER:
+        case self::ATTRIBUTE_ON_DRAG_START:
+        case self::ATTRIBUTE_ON_DROP:
+        case self::ATTRIBUTE_ON_DURATION_CHANGE:
+        case self::ATTRIBUTE_ON_ERROR:
+        case self::ATTRIBUTE_ON_EMPTIED:
+        case self::ATTRIBUTE_ON_ENDED:
+        case self::ATTRIBUTE_ON_ERROR:
+        case self::ATTRIBUTE_ON_FOCUS:
+        case self::ATTRIBUTE_ON_FOCUS_IN:
+        case self::ATTRIBUTE_ON_FOCUS_OUT:
+        case self::ATTRIBUTE_ON_HASH_CHANGE:
+        case self::ATTRIBUTE_ON_INPUT:
+        case self::ATTRIBUTE_ON_INVALID:
+        case self::ATTRIBUTE_ON_KEY_DOWN:
+        case self::ATTRIBUTE_ON_KEY_PRESS:
+        case self::ATTRIBUTE_ON_KEY_UP:
+        case self::ATTRIBUTE_ON_LOAD:
+        case self::ATTRIBUTE_ON_LOADED_DATA:
+        case self::ATTRIBUTE_ON_LOADED_META_DATA:
+        case self::ATTRIBUTE_ON_LOAD_START:
+        case self::ATTRIBUTE_ON_MOUSE_DOWN:
+        case self::ATTRIBUTE_ON_MOUSE_ENTER:
+        case self::ATTRIBUTE_ON_MOUSE_LEAVE:
+        case self::ATTRIBUTE_ON_MOUSE_MOVE:
+        case self::ATTRIBUTE_ON_MOUSE_OVER:
+        case self::ATTRIBUTE_ON_MOUSE_OUT:
+        case self::ATTRIBUTE_ON_MOUSE_UP:
+        case self::ATTRIBUTE_ON_MESSAGE:
+        case self::ATTRIBUTE_ON_MOUSE_WHEEL:
+        case self::ATTRIBUTE_ON_OPEN:
+        case self::ATTRIBUTE_ON_ONLINE:
+        case self::ATTRIBUTE_ON_OFFLINE:
+        case self::ATTRIBUTE_ON_PAGE_SHOW:
+        case self::ATTRIBUTE_ON_PAGE_HIDE:
+        case self::ATTRIBUTE_ON_PASTE:
+        case self::ATTRIBUTE_ON_PAUSE:
+        case self::ATTRIBUTE_ON_PLAY:
+        case self::ATTRIBUTE_ON_PLAYING:
+        case self::ATTRIBUTE_ON_PROGRESS:
+        case self::ATTRIBUTE_ON_POP_STATE:
+        case self::ATTRIBUTE_ON_RESIZE:
+        case self::ATTRIBUTE_ON_RESET:
+        case self::ATTRIBUTE_ON_RATE_CHANGE:
+        case self::ATTRIBUTE_ON_SCROLL:
+        case self::ATTRIBUTE_ON_SEARCH:
+        case self::ATTRIBUTE_ON_SELECT:
+        case self::ATTRIBUTE_ON_SUBMIT:
+        case self::ATTRIBUTE_ON_SEEKED:
+        case self::ATTRIBUTE_ON_SEEKING:
+        case self::ATTRIBUTE_ON_STALLED:
+        case self::ATTRIBUTE_ON_SUSPEND:
+        case self::ATTRIBUTE_ON_SHOW:
+        case self::ATTRIBUTE_ON_STORAGE:
+        case self::ATTRIBUTE_ON_TIME_UPDATE:
+        case self::ATTRIBUTE_ON_TRANSITION_END:
+        case self::ATTRIBUTE_ON_TOGGLE:
+        case self::ATTRIBUTE_ON_TOUCH_CANCEL:
+        case self::ATTRIBUTE_ON_TOUCH_END:
+        case self::ATTRIBUTE_ON_TOUCH_MOVE:
+        case self::ATTRIBUTE_ON_TOUCH_START:
+        case self::ATTRIBUTE_ON_UNLOAD:
+        case self::ATTRIBUTE_ON_VOLUME_CHANGE:
+        case self::ATTRIBUTE_ON_WAITING:
+        case self::ATTRIBUTE_ON_WHEEL:
+        case self::ATTRIBUTE_PATTERN:
+        case self::ATTRIBUTE_READONLY:
+        case self::ATTRIBUTE_REQUIRED:
+        case self::ATTRIBUTE_ROWS:
+        case self::ATTRIBUTE_SELECTED:
+        case self::ATTRIBUTE_SIZE:
+        case self::ATTRIBUTE_SOURCE:
+        case self::ATTRIBUTE_STEP:
+        case self::ATTRIBUTE_TYPE:
+        case self::ATTRIBUTE_WRAP:
+        case self::ATTRIBUTE_PLACE_HOLDER:
+        case self::ATTRIBUTE_VALUE:
+          return c_base_return_string::s_new($value);
+
+        case self::ATTRIBUTE_FORM_NO_VALIDATED:
+        case self::ATTRIBUTE_AUTO_COMPLETE:
+        case self::ATTRIBUTE_AUTO_FOCUS:
+        case self::ATTRIBUTE_CHALLENGE:
+        case self::ATTRIBUTE_CHECKED:
+        case self::ATTRIBUTE_DISABLED:
+        case self::ATTRIBUTE_MULTIPLE:
+          return c_base_return_bool::s_new($value);
+
+        case self::ATTRIBUTE_ACCEPT:
+        case self::ATTRIBUTE_FORM_ENCODE_TYPE:
+          return c_base_return_int::s_new($value);
+
+        case self::ATTRIBUTE_COLUMNS:
+        case self::ATTRIBUTE_MAXIMUM:
+        case self::ATTRIBUTE_MAXIMUM_LENGTH:
+        case self::ATTRIBUTE_MINIMUM:
+          return c_base_return_int::s_new($value);
+
+        case self::ATTRIBUTE_FORM_METHOD:
+          return c_base_return_int::s_new($value);
+
+        default:
+          return new c_base_return_false();
+      }
+    }
+
+    $this->attribute[$attribute] = $value;
+
+    return new c_base_return_false();
+  }
+}
diff --git a/common/theme/classes/theme_html.php b/common/theme/classes/theme_html.php
new file mode 100644 (file)
index 0000000..41bd434
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing HTML5 Markup.
+ *
+ * This is currently a draft/brainstorm and is subject to be completely rewritten/redesigned.
+ *
+ * @see: https://www.w3.org/TR/html5/
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+
+/**
+ * A generic class for HTML tag attributes.
+ *
+ * @see: https://www.w3.org/TR/html5/forms.html#forms
+ */
+class c_theme_html_attribute_value_boolean {
+}
diff --git a/common/theme/classes/theme_ical.php b/common/theme/classes/theme_ical.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/common/theme/classes/theme_markup.php b/common/theme/classes/theme_markup.php
new file mode 100644 (file)
index 0000000..fe0302b
--- /dev/null
@@ -0,0 +1,1977 @@
+<?php
+/**
+ * @file
+ * Provides a class for managing HTML DOM for the custom markup language used by this project.
+ *
+ * This is currently a draft/brainstorm and is subject to be completely rewritten/redesigned.
+ *
+ * This project provides a context-specific markup language for context-independent design with context-specific presentation.
+ *
+ * The following are the rules for this specific markup language:
+ * 1) There is no "block" or "inline", all things are "blocks" and may be presented inline (or conversely).
+ *   - That is to say, the markup does not define the block/not-block status, the theme does.
+ *
+ * 2) The only available tags are:
+ *   - <tag>:
+ *     - May only be defined in: <heading>, <title>, <presentation>, <context>, and <content> tags.
+ *     - When defined inside of <heading>:
+ *       - Provides default settings, such as: language or encoding.
+ *         - Example: <tag type="default" name="language">en-us</tag>.
+ *         - Example: <tag type="default" name="encoding">utf-8</tag>.
+ *     - When defined inside of <title>:
+ *       - Each tag may have an "id" attribute, such as: <tag id="unique_name-1"></tag>..
+ *       - Must not contain any nested markup.
+ *     - When defined inside of <presentation>:
+ *       - Each tag may have an "id" attribute, such as: <tag id="unique_name-2"></tag>.
+ *       - Each tag may have a "class" attribute, such as: <tag class="a b c"></tag>.
+ *       - Each tag may have a "context" attribute, such as: <tag context="some_name"></tag>.
+ *         - This represents the context of how the tag is presented (and HTML5 example would be: "banner").
+ *       - Each tag may have a "content" attribute, such as: <tag content="message-1"></tag>.
+ *         - Associates some block of information or text.
+ *       - Each tag may have a "tooltip" attribute, such as: <tag tooltip="message-2"></tag>.
+ *       - Each tag may have a "file" attribute, such as: <tag file="image-person.png"></tag>.
+ *         - Alternate text must not be defined for these files here because this is "presentation" and not "content".
+ *     - When defined inside of <context>:
+ *       - @todo: details need to be fleshed out.
+ *     - When defined inside of <content>:
+ *       - Each tag must have an "id" attribute, such as: <tag id="unique_name-3"></tag>.
+ *       - Each tag may have a "context" attribute, such as: <tag context="some_name"></tag>.
+ *         - This represents the context of the content and not about how it is presented (and HTML5 example would be: "aside").
+ *       - Each tag may have a "file" attribute, such as: <tag file="image-person.png">This text between the open and close tag is the "alternate text" or "description" describing the file.</tag>.
+ *     - When defined inside of <files>:
+ *       - Each tag may have an "id" attribute, such as: <tag id="css-unique_name">css/somewhere.css</tag>.
+ *       - Each tag may have a "type" attribute, such as: <tag type="text/css">http://example.com/css/somewhere.css</tag>.
+ *       - Must not contain any nested markup.
+ *   - <title>:
+ *     - A name representing the titles.
+ *     - May have multiple titles (order-sensitive), such as:
+ *       <title>
+ *         <tag id="unique_title-1">First Title</tag>
+ *         <tag id="unique_title-2">Second Title</tag>
+ *       </title>
+ *     - Must not contain any nested markup.
+ *   - <heading>:
+ *     - Provides heading information, such as external javascript or css files.
+ *     - Similar to the HTML <head> tag.
+ *     - May only contain the following tags: (@todo: determine what these will be).
+ *     - @todo: details need to be fleshed out.
+ *   - <context>:
+ *     - Provides how to interpret data (such as text or images).
+ *     - May only contain the following tags: <tag>.
+ *     - @todo: details need to be fleshed out.
+ *       - Context may or may not be provided here, instead a global standard based on HTML5, WCAG, ARIA, etc.. may be used to determine a static list of contexts.
+ *   - <files>:
+ *     - All javascript, css, documents, images, et al.. must be defined here.
+ *       - In this way, all files associated with this content are defined here and must have a unique id to be referenced.
+ *       - Example: <tag id="image-favicon.ico" type="image/ico" alternate="Minature logo for website.">/images/favicon.ico</tag>
+ *   - <content>:
+ *     - All content to be read is provided here.
+ *     - May only contain the following tags: <tag>.
+ *     - Example:
+ *       <content>
+ *         <tag id="message-1">This is some text that I wanted to present to you.</tag>.
+ *         <tag id="message-2">This could be used anywhere, including 'tooltip' and 'title' sections.</tag>.
+ *         <tag id="alternate-1">This could be used anywhere, but is likely intended for 'alt' attribute like uses.</tag>.
+ *       </content>
+ *   - <presentation>:
+ *     - The flow and presentation of the content is provided here.
+ *     - May only contain the following tags: <tag>.
+ *     - example:
+ */
+
+// include required files.
+require_once('common/base/classes/base_error.php');
+require_once('common/base/classes/base_return.php');
+require_once('common/theme/classes/theme_dom.php');
+
+/**
+ * Generic tag class for building and renderring markup.
+ */
+class c_theme_tag {
+  const TYPE_NONE         = 0;
+  const TYPE_TITLE        = 1;
+  const TYPE_HEADING      = 2;
+  const TYPE_FILES        = 3;
+  const TYPE_CONTEXT      = 4;
+  const TYPE_PRESENTATION = 5;
+  const TYPE_CONTENT      = 6;
+
+  // custom text attatched to the inside of the
+  private $text = NULL;
+
+  // the attributes, includes all attributes available.
+  private $attributes = NULL;
+  private $attributes_length = 0;
+
+  // an array of child tags (each tag must be a class/subclass of c_theme_tag).
+  private $tags = NULL;
+  private $tags_length = NULL;
+
+  private $type = NULL;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->text = NULL;
+    $this->attributes = array();
+    $this->attributes_length = 0;
+
+    $this->tags = array();
+    $this->tags_length = NULL;
+
+    $this->type = self::TYPE_NONE;
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->text);
+    unset($this->attributes);
+    unset($this->attributes_length);
+    unset($this->tags);
+    unset($this->tags_length);
+    unset($this->type);
+  }
+
+  /**
+   * Assign a text to the object.
+   *
+   * This will not set text if child tags are defined.
+   *
+   * @param string $text
+   *   A string to use as the text.
+   *
+   * @return c_base_return_status
+   *   TRUE on success.
+   *   FALSE is returned if the text cannot be set.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::delete_tags()
+   */
+  public function set_text($text) {
+    if (!is_string($text)) {
+      return c_base_return_error::s_false();
+    }
+
+    // Text may only be defined when there are no child tags and vice-versa.
+    if ($this->tags_length > 0) {
+      return new c_base_return_false();
+    }
+    elseif (!is_null($this->tags_length)) {
+      $this->tags_length = NULL;
+    }
+
+    // prevent the assigned text from including markup by translating everything to html entities.
+    $this->text = htmlspecialchars($text, ENT_HTML5 | ENT_NOQUOTES | ENT_IGNORE, 'UTF-8');
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Set the type this tag belongs to.
+   *
+   * Unsupported attributes that are not allowed will be removed.
+   *
+   * @param int $type
+   *   The numeric id representing the type of tag.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned on success.
+   *   FALSE is returned if the type cannot be set.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_type($type) {
+    if (!is_int($type) || $type < self::TYPE_NONE) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($this->type === $type) {
+      return new c_base_return_true();
+    }
+    elseif ($type === self::TYPE_NONE) {
+      $new_attributes = array();
+    }
+    elseif ($type === self::TYPE_TITLE) {
+      $new_attributes = array();
+
+      if (array_key_exists('id', $this->attributes)) {
+        $new_attributes['id'] = $this->attributes['id'];
+      }
+    }
+    elseif ($type === self::TYPE_HEADING) {
+      $new_attributes = array();
+
+      if (array_key_exists('id', $this->attributes)) {
+        $new_attributes['id'] = $this->attributes['id'];
+      }
+
+      if (array_key_exists('type', $this->attributes)) {
+        $new_attributes['type'] = $this->attributes['type'];
+      }
+
+      if (array_key_exists('name', $this->attributes)) {
+        $new_attributes['name'] = $this->attributes['name'];
+      }
+    }
+    elseif ($type === self::TYPE_FILES) {
+      $new_attributes = array();
+
+      if (array_key_exists('id', $this->attributes)) {
+        $new_attributes['id'] = $this->attributes['id'];
+      }
+
+      if (array_key_exists('type', $this->attributes)) {
+        $new_attributes['type'] = $this->attributes['type'];
+      }
+    }
+    elseif ($type === self::TYPE_CONTEXT) {
+      $new_attributes = array();
+
+      if (array_key_exists('id', $this->attributes)) {
+        $new_attributes['id'] = $this->attributes['id'];
+      }
+    }
+    elseif ($type === self::TYPE_PRESENTATION) {
+      $new_attributes = array();
+
+      if (array_key_exists('id', $this->attributes)) {
+        $new_attributes['id'] = $this->attributes['id'];
+      }
+
+      if (array_key_exists('class', $this->attributes)) {
+        $new_attributes['class'] = $this->attributes['class'];
+      }
+
+      if (array_key_exists('context', $this->attributes)) {
+        $new_attributes['context'] = $this->attributes['context'];
+      }
+
+      if (array_key_exists('content', $this->attributes)) {
+        $new_attributes['content'] = $this->attributes['content'];
+      }
+
+      if (array_key_exists('tooltip', $this->attributes)) {
+        $new_attributes['tooltip'] = $this->attributes['tooltip'];
+      }
+
+      if (array_key_exists('file', $this->attributes)) {
+        $new_attributes['file'] = $this->attributes['file'];
+      }
+    }
+    elseif ($type === self::TYPE_CONTENT) {
+      $new_attributes = array();
+
+      if (array_key_exists('id', $this->attributes)) {
+        $new_attributes['id'] = $this->attributes['id'];
+      }
+
+      if (array_key_exists('context', $this->attributes)) {
+        $new_attributes['context'] = $this->attributes['context'];
+      }
+
+      if (array_key_exists('file', $this->attributes)) {
+        $new_attributes['file'] = $this->attributes['file'];
+      }
+    }
+    else {
+      return new c_base_return_false();
+    }
+
+    unset($this->attributes);
+    $this->attributes = $new_attributes;
+    $this->attributes_length = count($new_attributes);
+    unset($new_attributes);
+
+    $this->type = $type;
+    return new c_base_return_true();
+  }
+
+  /**
+   * This will set a value for the given attribute name.
+   *
+   * This is less efficient than calling a specific attribute function such as set_attribute_name().
+   * This is because the name must be processed and only allowed names will be supported.
+   *
+   * Child classes that add additional attributes should override this function.
+   *
+   * @param string $name
+   *   The attribute name to assign.
+   * @param string $value
+   *   The value of the attribute to assign.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned on success.
+   *   FALSE is returned if unable to set the attribute (the tag type might not support the attribute).
+   *   FALSE with error bit is returned on error.
+   */
+  public function set_attribute($name, $value) {
+    return $this->pr_set_attribute($name, $value);
+  }
+
+  /**
+   * Assign tags to this object.
+   *
+   * Objects cannot be added if text is assigned.
+   *
+   * @param __class__ $tag
+   *   When a __class__ object, the tag is appended to the end of the tags array.
+   * @param int|null $index
+   *   When an integer, represents the position within the tag array to assign the tag.
+   *   - May not be less than 0 or greater than the length of the array.
+   *   When $tag is NULL, this does nothing.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the position the tag was added on success.
+   *   When $tag is NULL, TRUE is returned on success.
+   *   FALSE is returned if unable to set tags.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::set_tags()
+   * @see: self::delete_text()
+   */
+  public function set_tag($tag, $index = NULL) {
+    if (!is_object($tag) || !($tag instanceof c_theme_tag)) {
+      return c_base_return_error::s_false();
+    }
+
+    // do not allow adding tags if there is assigned text.
+    if (!is_null($this->text)) {
+      return new c_base_return_false();
+    }
+
+    if (is_null($index)) {
+      $this->tags[$this->tags_length] = $tag;
+      $this->tags_length++;
+      return c_base_return_int::s_new($this->tags_length - 1);
+    }
+    else {
+      if (!is_int($index) || $index < 0 || $index > $this->tags_length) {
+        return c_base_return_error::s_false();
+      }
+
+      $this->tags[$index] = $tag;
+      return c_base_return_int::s_new($index);
+    }
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Assign multiple tags to this object.
+   *
+   * Objects will not be added if text is assigned.
+   *
+   * @param array $tags
+   *   An array of __class__ objects.
+   *   Each individual item is checked and then appended to the end of the array.
+   *   - The array keys will not be preserved.
+   *
+   * @param int|null $index
+   *   When an integer, represents the position within the tag array to insert the tags.
+   *   - May not be less than 0 or greater than the length of the array.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the position the tag was added on success.
+   *   FALSE is returned if unable to set tags.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::set_tag()
+   * @see: self::delete_text()
+   */
+  public function set_tags($tags, $index = NULL) {
+    if (!is_array($tags)) {
+      return c_base_return_error::s_false();
+    }
+
+    // FALSE without error bit set is returned when no tags are to be added (empty array).
+    if (empty($tags)) {
+      return new c_base_return_false();
+    }
+
+    // do not allow adding tags if there is assigned text.
+    if (!is_null($this->text)) {
+      return new c_base_return_false();
+    }
+
+    if (is_null($index)) {
+      $failure = FALSE;
+      $tags_length = $this->tags_length;
+      foreach ($tags as $tag) {
+        // every single item must be validated before any part of the array will be allowed to be added to this object.
+        if (!is_object($tag) || !($tag instanceof c_theme_tag)) {
+          $failure = TRUE;
+          break;
+        }
+
+        $this->tags[$this->tags_length] = $tag;
+        $this->tags_length++;
+      }
+      unset($tag);
+
+      if ($failure) {
+        unset($failure);
+
+        // none of the array shall be added if any of the array is invalid.
+        array_splice($this->tags, $tags_length);
+
+        $this->tags_length = $tags_length;
+        unset($tags_length);
+
+        return new c_base_return_false();
+      }
+      unset($failure);
+      unset($tags_length);
+
+      return c_base_return_int::s_new($this->tags_length);
+    }
+
+    foreach ($tag as $tag) {
+      // every single item must be validated before any part of the array will be allowed to be added to this object.
+      if (!is_object($tag) || !($tag instanceof c_theme_tag)) {
+        unset($tag);
+        return c_base_return_error::s_false();
+      }
+    }
+    unset($tag);
+
+    if ($index == 0) {
+      $original_tags = $this->tags;
+      unset($this->tags);
+
+      $this->tags = $tags;
+
+      array_merge($this->tags, $original_tags);
+      unset($original_tags);
+
+      $this->tags_length += count($tags);
+      return c_base_return_int::s_new(0);
+    }
+
+    $remaining_tags = array_splice($this->tags, $index, ($this->tags_length - $index), $tags);
+    array_merge($this->tags, $remaining_tags);
+    unset($remaining_tags);
+
+    $this->tags_length += count($tags);
+    return c_base_return_int::s_new($index);
+  }
+
+  /**
+   * Get the text assigned to this object.
+   *
+   * @return c_base_return_status|c_base_return_string|c_base_return_null
+   *   The assigned text.
+   *   NULL is returned if no text is assigned.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::get_text_decoded()
+   */
+  public function get_text() {
+    if (is_null($this->text)) {
+      return new c_base_return_null();
+    }
+
+    return c_base_return_string::s_new($this->text);
+  }
+
+  /**
+   * Get the text assigned to this object.
+   *
+   * The returned text is decoded from the internally stored html entity format.
+   *
+   * @return c_base_return_status|c_base_return_string|c_base_return_null
+   *   The assigned text (decoded).
+   *   NULL is returned if no text is assigned.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::get_text()
+   */
+  public function get_text_decoded() {
+    if (is_null($this->text)) {
+      return new c_base_return_null();
+    }
+
+    return c_base_return_string::s_new(htmlspecialchars_decode($this->text, ENT_HTML5 | ENT_NOQUOTES | ENT_IGNORE, 'UTF-8'));
+  }
+
+  /**
+   * Get the type this tag belongs to.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the type is returned on success.
+   *   FALSE is returned if the type is not set.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_type() {
+    if (is_null($this->type)) {
+      return new c_base_return_false();
+    }
+
+    return c_base_return_int::s_new($this->type);
+  }
+
+  /**
+   * This will get a value for the given attribute name.
+   *
+   * This is less efficient than calling a specific attribute function such as get_attribute_name().
+   * This is because the name must be processed and only allowed names will be supported.
+   *
+   * Child classes that add additional attributes should override this function.
+   *
+   * @param string $name
+   *   The attribute name to get.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   The attribute value string is returned on success.
+   *   NULL is returned if the attribute is not set.
+   *   FALSE is returned if unable to get the attribute (the tag type might not support the attribute).
+   *   FALSE with error bit is returned on error.
+   */
+  public function get_attribute($name) {
+    return $this->pr_get_attribute($name);
+  }
+
+  /**
+   * Get the all of the attributes assigned to this object.
+   *
+   * @return c_base_return_status|c_base_return_array
+   *   An array containing all attributes.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_attributes() {
+    if (is_array($this->attributes)) {
+      return c_base_return_array($this->attributes);
+    }
+
+    return c_base_return_error::s_false();
+  }
+
+  /**
+   * Get the length of the attributes array.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the length of the attributes array.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function get_attributes_length() {
+    return c_base_return_int::s_new($this->attributes_length);
+  }
+
+  /**
+   * Get a single tag assigned to this object.
+   *
+   * @param int|null $index
+   *   (optional) An integer representing the position within the tag array to return.
+   *   When NULL, the tag at the end of the tags array is returned.
+   *
+   * @return c_base_return_status|c_theme_return_tag|c_base_return_null
+   *   A c_theme_return_tag object on success.
+   *   NULL might be returned if there is no object assigned to the index.
+   *   Otherwise FALSE is returned.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::get_tags()
+   */
+  public function get_tag($index = NULL) {
+    if (!is_null($index)) {
+      if (!is_int($index) || $index < 0 || $index >= $this->tags_length) {
+        return c_base_return_error::s_false();
+      }
+    }
+
+    if (is_null($index)) {
+      $last = end($this->tags);
+      if (!is_array($last)) {
+        unset($last);
+        return new c_base_return_false();
+      }
+
+      return c_theme_return_tag::s_new($last);
+    }
+
+    if (is_null($this->tags[$index])) {
+      return new c_base_return_null();
+    }
+
+    return c_theme_return_tag::s_new($this->tags[$index]);
+  }
+
+  /**
+   * Get tags assigned to this object.
+   *
+   * @param int $index
+   *   When a positive integer, an integer representing the position within the tag array to return.
+   * @param int|null $length
+   *   (optional) When NULL, returns all tags following the index.
+   *   When an integer, the total number of tags following the index to return.
+   * @param bool $preserve_keys
+   *   (optional) When TRUE array keys will be preserved.
+   *   When FALSE, the array keys are reset.
+   *
+   * @return c_base_return_status|c_theme_return_array
+   *   An array of tags on success, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::get_tag()
+   * @see: self::array_slice()
+   */
+  public function get_tags($index, $length = NULL, $preserve_keys = TRUE) {
+    if (!is_int($index) || $index < 0 || $index >= $this->tags_length) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($length)) {
+      if (!is_int($length) || $length < 1 || $length > $this->tags_length) {
+        return c_base_return_error::s_false();
+      }
+    }
+
+    if (!is_bool($preserve_keys)) {
+      return c_base_return_error::s_false();
+    }
+
+    return c_theme_return_array::s_new(array_slice($this->tags, $index, $length, $preserve_keys));
+  }
+
+  /**
+   * Removes text assigned to this object.
+   *
+   * @return c_base_return_false
+   *   TRUE is returned on success.
+   *   FALSE without error bit set is returned if text is already deleted.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function delete_text() {
+    if (is_null($this->text)) {
+      return new c_base_return_false();
+    }
+
+    unset($this->text);
+    $this->text = NULL;
+    return new c_base_return_true();
+  }
+
+  /**
+   * This will delete the value for the given attribute name.
+   *
+   * This is less efficient than calling a specific attribute function such as delete_attribute_name().
+   * This is because the name must be processed and only allowed names will be supported.
+   *
+   * Child classes that add additional attributes should override this function.
+   *
+   * @param string $name
+   *   The attribute name to delete.
+   *
+   * @return c_base_return_status|c_base_return_string
+   *   The attribute value string is returned on success.
+   *   FALSE is returned for unknown attributes names.
+   *   FALSE with error bit is returned on error.
+   */
+  public function delete_attribute($name) {
+    return $this->pr_delete_attribute($name);
+  }
+
+  /**
+   * This will delete all attributes assigned to this object.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned on success.
+   *   FALSE with error bit is returned on error.
+   */
+  public function delete_attributes() {
+    unset($this->attributes);
+    $this->attributes = array();
+    $this->attributes_length = 0;
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Delete tag from this object.
+   *
+   * Tags not stored at the end of the array are set to NULL instead of being deleted.
+   * The length is therefore not shortened unless the deleted tag is at the end of the array.
+   * Call $this->sanitize_tags() to ensure that the array structure contains none of these holes.
+   *
+   * @param int|null $index
+   *   (optional) When an integer, represents the position of the tag to delete.
+   *   - May not be less than 0 or greater than the length of the array.
+   *   When $tag is NULL, deletes the tag at the end of the tag array.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the position the tag was deleted on success.
+   *   Otherwise, FALSE is returned.
+   *   FALSE without error bit set is returned if the tag at the specified index is already deleted.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::delete_tags()
+   * @see: self::sanitize_tags()
+   */
+  public function delete_tag($index = NULL) {
+    if (is_null($index)) {
+      $position = $this->tags_length;
+      $this->tags_length--;
+
+      unset($this->tags[$this->tags_length]);
+
+      return c_base_return_int::s_new($position);
+    }
+
+    if (!is_int($index) || $index < 0 || $index >= $this->tags_length) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_null($this->tags[$index])) {
+      return new c_base_return_false();
+    }
+
+    $this->tags[$index] = NULL;
+    return c_base_return_int::s_new($index);
+  }
+
+  /**
+   * Delete multiple tags from this object.
+   *
+   * Tags not stored at the end of the array are set to NULL instead of being deleted.
+   * The length is therefore not shortened unless the deleted tag is at the end of the array.
+   * Call $this->sanitize_tags() to ensure that the array structure contains none of these holes.
+   *
+   * @param int $index
+   *   (optional) When an integer, represents the position of the tag to delete.
+   *   - May not be less than 0 or greater than the length of the array.
+   * @param int|null $length
+   *   (optional) When NULL, returns all tags following the index.
+   *   When an integer, the total number of tags following the index to return.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the total number of tags deleted on success.
+   *   Otherwise, FALSE is returned.
+   *   FALSE without error bit set is returned if no tags were deleted.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::delete_tag()
+   * @see: self::sanitize_tags()
+   */
+  public function delete_tags($index, $length = NULL) {
+    if (!is_int($index) || $index < 0 || $index >= $this->tags_length) {
+      return c_base_return_error::s_false();
+    }
+
+    if (is_null($length)) {
+      $total = $this->tags_length;
+    }
+    else {
+      if (!is_int($length) || $length < 1 || $length > $this->tags_length) {
+        return c_base_return_error::s_false();
+      }
+
+      $total = $length;
+    }
+
+    $count = $index;
+    $deleted = 0;
+    for (; $count < $total; $count++) {
+      if (is_null($this->tags[$count])) {
+        continue;
+      }
+
+      unset($this->tags[$count]);
+      $this->tags[$count] = NULL;
+      $deleted++;
+    }
+    unset($count);
+
+    if ($deleted == $total) {
+      $this->tags = array();
+      $this->tags_length = 0;
+    }
+
+    if ($deleted == 0) {
+      unset($deleted);
+      return new c_base_return_false();
+    }
+
+    return c_base_return_int::s_new($deleted);
+  }
+
+  /**
+   * Determine if text has been added to this object.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned if tags have been added, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function has_text() {
+    if (is_null($this->text)) {
+      return new c_base_return_false();
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Determine if an attribute has been added to this object.
+   *
+   * @param string $name
+   *   The attribute name to delete.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned if added, FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function has_attribute($name) {
+    return $this->pr_has_attribute($name);
+  }
+
+  /**
+   * Convert this object and all of its children or text to markup.
+   *
+   * @return c_base_return_string
+   *   A string representing this object in the form of HTMl compatible markup.
+   */
+  public function to_markup() {
+    $markup = '<tag';
+
+    if (!is_null($this->tags_length)) {
+      $markup .= 's';
+    }
+
+    if ($this->type === self::TYPE_TITLE) {
+      if (isset($this->attributes['id'])) {
+        $markup .= ' id="' . $this->attributes['id'] . '"';
+      }
+    }
+    elseif ($this->type === self::TYPE_HEADING) {
+      if (isset($this->attributes['id'])) {
+        $markup .= ' id="' . $this->attributes['id'] . '"';
+      }
+
+      if (isset($this->attributes['type'])) {
+        $markup .= ' type="' . $this->attributes['type'] . '"';
+      }
+
+      if (isset($this->attributes['name'])) {
+        $markup .= ' name="' . $this->attributes['name'] . '"';
+      }
+    }
+    elseif ($this->type === self::TYPE_FILES) {
+      if (isset($this->attributes['id'])) {
+        $markup .= ' id="' . $this->attributes['id'] . '"';
+      }
+
+      if (isset($this->attributes['type'])) {
+        $markup .= ' type="' . $this->attributes['type'] . '"';
+      }
+    }
+    elseif ($this->type === self::TYPE_CONTEXT) {
+      if (isset($this->attributes['id'])) {
+        $markup .= ' id="' . $this->attributes['id'] . '"';
+      }
+    }
+    elseif ($this->type === self::TYPE_PRESENTATION) {
+      if (isset($this->attributes['id'])) {
+        $markup .= ' id="' . $this->attributes['id'] . '"';
+      }
+
+      if (isset($this->attributes['class'])) {
+        $markup .= ' class="' . $this->attributes['class'] . '"';
+      }
+
+      if (isset($this->attributes['context'])) {
+        $markup .= ' context="' . $this->attributes['context'] . '"';
+      }
+
+      if (isset($this->attributes['content'])) {
+        $markup .= ' content="' . $this->attributes['content'] . '"';
+      }
+
+      if (isset($this->attributes['tooltip'])) {
+        $markup .= ' tooltip="' . $this->attributes['tooltip'] . '"';
+      }
+
+      if (isset($this->attributes['file'])) {
+        $markup .= ' file="' . $this->attributes['file'] . '"';
+      }
+    }
+    elseif ($this->type === self::TYPE_CONTENT) {
+      if (isset($this->attributes['id'])) {
+        $markup .= ' id="' . $this->attributes['id'] . '"';
+      }
+
+      if (isset($this->attributes['context'])) {
+        $markup .= ' context="' . $this->attributes['context'] . '"';
+      }
+
+      if (isset($this->attributes['file'])) {
+        $markup .= ' file="' . $this->attributes['file'] . '"';
+      }
+    }
+
+    $markup .= '>';
+
+    if (is_null($this->tags_length)) {
+      $markup .= $this->text;
+      $markup .= '</tag>';
+    }
+    else {
+      foreach ($this->tags as $tag) {
+        $markup .= $tag->to_markup()->get_value_exact();
+      }
+
+      $markup .= '</tags>';
+    }
+
+    return c_base_return_string::s_new($markup);
+  }
+
+  /**
+   * Converts this object into a DOMElement object.
+   *
+   * @param c_theme_dom|null $dom_document
+   *   (optional) The DOMDocument object to operate with.
+   *   If NULL, then the DOMDocument object is auto-generated.
+   *
+   * @return c_base_return_status|c_theme_return_dom_element
+   *   A DOMElement is returned on success.
+   *   Otherwise FALSE is returned.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::p_to_dom_element()
+   */
+  public function to_dom_element($dom_document = NULL) {
+    if (is_null($dom_document)) {
+      $dom = new DOMDocument();
+    }
+    else {
+      if (!is_object($dom_document) || !($dom_document instanceof DOMDocument)) {
+        return c_base_return_error::s_false();
+      }
+
+      $dom = $dom_document;
+    }
+
+    $element = $this->p_to_dom_element($dom);
+    unset($dom);
+
+    if ($element === FALSE) {
+      unset($element);
+      return new c_base_return_false();
+    }
+
+    return c_theme_return_dom_element::s_new($element);
+  }
+
+  /**
+   * Converts DOMNode to the structure provided by this class.
+   *
+   * @fixme: update this based on c_theme_markup design.
+   *
+   * This will delete the entire contents of this object and replace it with the contents of the provided DOMElement object.
+   *
+   * This assumes that the class follows the rules of this object.
+   *
+   * @param DOMNode $dom_node
+   *   The DOMNode object to load into this class.
+   *   This expects the supplied markup to provided tags used by this class (tag, tags, etc..).
+   *   All other tags are ignored.
+   *
+   * @return c_base_return_status
+   *   TRUE on success.
+   *   FALSE otherwise.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function from_dom_node($dom_node) {
+    if (!is_object($dom_node) || !($dom_node instanceof DOMNode)) {
+      return c_base_return_error::s_false();
+    }
+
+    // objects are passed via reference in function names, prevent accidents by acting on a clone.
+    $node = clone($dom_node);
+
+    // clear the contents of this class.
+    $this->__construct();
+
+    if ($this->type === self::TYPE_TAG) {
+      // @todo: does this need to be sanitized, or would that result in double-sanitization?
+      $this->text = $node->textContent;
+    }
+    elseif ($this->type === self::TYPE_TAGS && $node->hasChildNodes()) {
+      foreach ($node->childNodes as $child_node) {
+        if (!($child_node instanceof DOMNode)) {
+          continue;
+        }
+
+        if ($child_node->nodeName == 'tag') {
+          $child_tag = new c_theme_tag();
+          if ($child_node->hasAttributes()) {
+            foreach ($child_node->attributes as $child_attribute) {
+              $child_tag->set_attribute($child_attribute->localName, $child_attribute->nodeValue);
+            }
+            unset($child_attribute);
+          }
+
+          // <tag> only supports text.
+          $child_tag->set_text($child_node->textContent);
+
+          $this->set_tag($child_tag);
+          unset($child_tag);
+        }
+        elseif ($child_node->nodeName == 'tags') {
+          $child_tag = new c_theme_tag();
+          if ($child_node->hasAttributes()) {
+            foreach ($child_node->attributes as $child_attribute) {
+              $child_tag->set_attribute($child_attribute->localName, $child_attribute->nodeValue);
+            }
+            unset($child_attribute);
+          }
+
+          // <tags> only supports <tags> or <tag>.
+          if ($child_node->hasChildNodes()) {
+            foreach ($child_node->childNodes as $child_node_child) {
+              $child_node_child_tag = new c_theme_tag();
+              $result = $child_node_child_tag->from_dom_node($child_node_child);
+
+              if ($result instanceof c_base_return_true) {
+                $child_tag->set_tag($child_node_child_tag);
+              }
+
+              unset($result);
+              unset($child_node_child_tag);
+            }
+            unset($child_node_child);
+          }
+
+          $this->set_tag($child_tag);
+          unset($child_tag);
+        }
+      }
+    }
+
+    if ($dom_node->hasAttributes()) {
+      foreach ($dom_node->attributes as $node_attribute) {
+        $this->set_attribute($node_attribute->localName, $node_attribute->nodeValue);
+      }
+      unset($node_attribute);
+    }
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Perform attribute "set" operation.
+   *
+   * This is provided to reduce duplication of code.
+   * It is intended to be called by a public function and will return the expected results of that public function.
+   *
+   * @param string $attribute_name
+   *   The name of the attribute to operate on.
+   * @param string|null $attribute_value
+   *   A string to use as the attribute_value.
+   *
+   * @return c_base_return_status
+   *   TRUE on success.
+   *   FALSE is returned if the attribute cannot be set (the tag type might not support the attribute).
+   *   FALSE with error bit set is returned on error.
+   */
+  protected function pr_set_attribute($attribute_name, $attribute_value) {
+    if (!is_string($attribute_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    // require the non-null string to not be empty.
+    // multi-byte is unecessary here because this is a test not for characters but instead for a non-empty string.
+    if (!is_string($attribute_value) || strlen($attribute_value) < 1) {
+      return c_base_return_error::s_false();
+    }
+
+    $allowed = $this->p_allowed_attribute($attribute_name);
+    if (is_null($allowed)) {
+      unset($allowed);
+      return c_base_return_error::s_false();
+    }
+    elseif ($allowed) {
+      unset($allowed);
+    }
+    else {
+      unset($allowed);
+      return new c_base_return_false();
+    }
+
+    // attribute name needs sanitization.
+    $fixed_name = preg_replace('/[^\w]/', '', $fixed_name);
+    if (!is_string($fixed_name)) {
+      unset($fixed_name);
+      return c_base_return_error::s_false();
+    }
+
+    // double quotes are not allowed because the class is designed to wrap attribute values in double quotes.
+    $this->attributes[$fixed_name] = str_replace('"', '&quot;', $attribute_value);
+    unset($fixed_name);
+
+    return new c_base_return_true();
+  }
+
+  /**
+   * Perform attribute "get" operation.
+   *
+   * This is provided to reduce duplication of code.
+   * It is intended to be called by a public function and will return the expected results of that public function.
+   *
+   * @param string $attribute_name
+   *   The name of the attribute to operate on.
+   *
+   * @return c_base_return_status|c_base_return_string|c_base_return_null
+   *   The assigned id string.
+   *   NULL is returned if the attribute is not set.
+   *   FALSE is returned if unable to get the attribute (the tag type might not support the attribute).
+   *   FALSE with error bit set is returned on error.
+   */
+  protected function pr_get_attribute($attribute_name) {
+    if (!is_string($attribute_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    $allowed = $this->p_allowed_attribute($attribute_name);
+    if (is_null($allowed)) {
+      unset($allowed);
+      return c_base_return_error::s_false();
+    }
+    elseif ($allowed) {
+      unset($allowed);
+    }
+    else {
+      unset($allowed);
+      return new c_base_return_false();
+    }
+
+    if (!array_key_exists($attribute_name, $this->attributes)) {
+      return new c_base_return_null();
+    }
+
+    return c_base_return_string::s_new($this->attributes[$attribute_name]);
+  }
+
+  /**
+   * Perform attribute "delete" operation.
+   *
+   * This assigned the attribute value to NULL.
+   *
+   * This is provided to reduce duplication of code.
+   * It is intended to be called by a public function and will return the expected results of that public function.
+   *
+   * @param string $attribute_name
+   *   The name of the attribute to operate on.
+   *
+   * @return c_base_return_status
+   *   TRUE is returned if the attribute is successfully deleted.
+   *   FALSE is returned if the attribute cannot be deleted (the tag type might not support the attribute).
+   *   FALSE with error bit set is returned on error.
+   */
+  protected function pr_delete_attribute($attribute_name) {
+    if (!is_string($attribute_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    $allowed = $this->p_allowed_attribute($attribute_name);
+    if (is_null($allowed)) {
+      unset($allowed);
+      return c_base_return_error::s_false();
+    }
+    elseif ($allowed) {
+      unset($allowed);
+    }
+    else {
+      unset($allowed);
+      return new c_base_return_false();
+    }
+
+    unset($this->attributes[$attribute_name]);
+    return new c_base_return_true();
+  }
+
+  /**
+   * Perform attribute "has" operation.
+   *
+   * This is provided to reduce duplication of code.
+   * It is intended to be called by a public function and will return the expected results of that public function.
+   *
+   * @param string $attribute_name
+   *   The name of the attribute to operate on.
+   *
+   * @return c_base_return_status
+   *   TRUE if attribute is assigned.
+   *   FALSE if the attribute is not assigned.
+   *   FALSE with error bit set is returned on error.
+   */
+  protected function pr_has_attribute($attribute_name) {
+    if (!is_string($attribute_name)) {
+      return c_base_return_error::s_false();
+    }
+
+    $allowed = $this->p_allowed_attribute($attribute_name);
+    if (is_null($allowed)) {
+      unset($allowed);
+      return c_base_return_error::s_false();
+    }
+    elseif ($allowed) {
+      unset($allowed);
+    }
+    else {
+      unset($allowed);
+      return new c_base_return_false();
+    }
+
+    if (array_key_exists($attribute_name, $this->attributes)) {
+      return new c_base_return_true();
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Re-calculates the attributes array length.
+   *
+   * @see: self::sanitize_attributes()
+   */
+  private function p_sanitize_attributes() {
+    $this->attributes_length = count($this->attributes);
+
+    return TRUE;
+  }
+
+  /**
+   * Check to see if the given attribute name is allowed according to the tag type.
+   *
+   * @param string $attribute_name
+   *   The attribute name to test.
+   *
+   * @return bool|null
+   *   TRUE is returned if allowed.
+   *   FALSE is returned if not allowed.
+   *   NULL is returned for unknown tag type.
+   */
+  private function p_allowed_attribute($attribute_name) {
+    if ($this->type === self::TYPE_TITLE) {
+      switch ($attribute_name) {
+        case 'id':
+          return TRUE;
+        default:
+          return FALSE;
+      }
+    }
+    elseif ($this->type === self::TYPE_HEADING) {
+      switch ($attribute_name) {
+        case 'id':
+        case 'type':
+        case 'name':
+          return TRUE;
+        default:
+          return FALSE;
+      }
+    }
+    elseif ($this->type === self::TYPE_FILES) {
+      switch ($attribute_name) {
+        case 'id':
+        case 'type':
+          return TRUE;
+        default:
+          return FALSE;
+      }
+    }
+    elseif ($this->type === self::TYPE_CONTEXT) {
+      switch ($attribute_name) {
+        case 'id':
+          return TRUE;
+        default:
+          return FALSE;
+      }
+    }
+    elseif ($this->type === self::TYPE_PRESENTATION) {
+      switch ($attribute_name) {
+        case 'id':
+        case 'class':
+        case 'context':
+        case 'content':
+        case 'tooltip':
+        case 'file':
+          return TRUE;
+        default:
+          return FALSE;
+      }
+    }
+    elseif ($this->type === self::TYPE_CONTENT) {
+      switch ($attribute_name) {
+        case 'id':
+        case 'context':
+        case 'file':
+          return TRUE;
+        default:
+          return FALSE;
+      }
+    }
+
+    return NULL;
+  }
+
+  /**
+   * Removes all NULL entries from the tags array and re-calculates the length.
+   *
+   * @param bool
+   *   TRUE on success.
+   *   FALSE otherwise.
+   *
+   * @see: self::sanitize_tags();
+   */
+  private function p_sanitize_tags() {
+    if (empty($this->tags)) {
+      $this->tags_length = 0;
+    }
+    else {
+      $tags = array();
+      $total = 0;
+      foreach ($tags as $tag) {
+        if (is_null($tag)) {
+          continue;
+        }
+
+        $tags[$total] = $tag;
+        $total++;
+      }
+
+      unset($this->tags);
+      $this->tags = $tags;
+      $this->tags_length = $total;
+
+      unset($tags);
+      unset($total);
+
+      if ($this->type === self::TYPE_TAG) {
+        $this->type = self::TYPE_TAGS;
+      }
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Ensures that text is consistent with the rules of this class.
+   *
+   * @see: self::sanitize_text()
+   */
+  private function p_sanitize_text() {
+    if ($this->tags_length > 0) {
+      $this->text = NULL;
+
+      if ($this->type === self::TYPE_TAG) {
+        $this->type = self::TYPE_TAGS;
+      }
+    }
+    elseif (!is_null($this->text)) {
+      if ($this->type !== self::TYPE_TAG) {
+        $this->type = self::TYPE_TAG;
+      }
+    }
+
+    return TRUE;
+  }
+
+  /**
+   * Converts this object into a DOMElement object.
+   *
+   * @param c_theme_dom $dom
+   *   The DOMDocument object to operate with.
+   *
+   * @return DOMElement|FALSE
+   *   A DOMElement is returned on success.
+   *   FALSE is returned on error.
+   *
+   * @see: self::to_dom_element()
+   */
+  public function p_to_dom_element($dom) {
+    if (is_null($this->tags_length)) {
+      $element = $dom->createElement('tag');
+
+      if (!is_null($this->text)) {
+        $element->appendChild(new DOMText($this->text));
+      }
+    }
+    else {
+      $element = $dom->createElement('tags');
+
+      if ($this->tags_length > 0) {
+        foreach ($this->tags as $tag) {
+          if (is_null($tag)) {
+            continue;
+          }
+
+          $child = $tag->to_dom_element($dom);
+          if ($child instanceOf c_theme_return_dom_element) {
+            $element->appendChild($child->get_value_exact());
+          }
+          else {
+            unset($child);
+            unset($tag);
+            unset($element);
+            return FALSE;
+          }
+          unset($child);
+        }
+        unset($tag);
+      }
+    }
+
+    if ($this->type === self::TYPE_TITLE) {
+      if (isset($this->attributes['id'])) {
+        $element->setAttribute('id', $this->attributes['id']);
+      }
+    }
+    elseif ($this->type === self::TYPE_HEADING) {
+      if (isset($this->attributes['id'])) {
+        $element->setAttribute('id', $this->attributes['id']);
+      }
+
+      if (isset($this->attributes['type'])) {
+        $element->setAttribute('type', $this->attributes['type']);
+      }
+
+      if (isset($this->attributes['name'])) {
+        $element->setAttribute('name', $this->attributes['name']);
+      }
+    }
+    elseif ($this->type === self::TYPE_FILES) {
+      if (isset($this->attributes['id'])) {
+        $element->setAttribute('id', $this->attributes['id']);
+      }
+
+      if (isset($this->attributes['type'])) {
+        $element->setAttribute('type', $this->attributes['type']);
+      }
+    }
+    elseif ($this->type === self::TYPE_CONTEXT) {
+      if (isset($this->attributes['id'])) {
+        $element->setAttribute('id', $this->attributes['id']);
+      }
+    }
+    elseif ($this->type === self::TYPE_PRESENTATION) {
+      if (isset($this->attributes['id'])) {
+        $element->setAttribute('id', $this->attributes['id']);
+      }
+
+      if (isset($this->attributes['class'])) {
+        $element->setAttribute('class', $this->attributes['class']);
+      }
+
+      if (isset($this->attributes['context'])) {
+        $element->setAttribute('context', $this->attributes['context']);
+      }
+
+      if (isset($this->attributes['content'])) {
+        $element->setAttribute('content', $this->attributes['content']);
+      }
+
+      if (isset($this->attributes['tooltip'])) {
+        $element->setAttribute('tooltip', $this->attributes['tooltip']);
+      }
+
+      if (isset($this->attributes['file'])) {
+        $element->setAttribute('file', $this->attributes['file']);
+      }
+    }
+    elseif ($this->type === self::TYPE_CONTENT) {
+      if (isset($this->attributes['id'])) {
+        $element->setAttribute('id', $this->attributes['id']);
+      }
+
+      if (isset($this->attributes['context'])) {
+        $element->setAttribute('context', $this->attributes['context']);
+      }
+
+      if (isset($this->attributes['file'])) {
+        $element->setAttribute('file', $this->attributes['file']);
+      }
+    }
+
+    return $element;
+  }
+}
+
+/**
+ * A return class whose value is represented as a __class__.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ */
+class c_theme_return_tag extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, '');
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param __class__ $value
+   *   Any value so long as it is a __class__.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!$value instanceof c_theme_tag) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return string|null $value
+   *   The value array stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !($this->value instanceof c_theme_tag)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return __class__ $value
+   *   The value c_theme_dom stored within this class.
+   */
+  public function get_value_exact() {
+    if (!($this->value instanceof c_theme_tag)) {
+      $this->value = new c_theme_tag();
+    }
+
+    return $this->value;
+  }
+}
+
+/**
+ * A complete context markup language class.
+ */
+class c_theme_markup {
+  private $title        = NULL;
+  private $heading      = NULL;
+  private $files        = NULL;
+  private $context      = NULL;
+  private $presentation = NULL;
+  private $content      = NULL;
+
+  /**
+   * Class constructor.
+   */
+  public function __construct() {
+    $this->title = new c_theme_tag();
+    $this->title->set_type(c_theme_tag::TYPE_TITLE);
+
+    $this->heading = new c_theme_tag();
+    $this->heading->set_type(c_theme_tag::TYPE_HEADING);
+
+    $this->files = new c_theme_tag();
+    $this->files->set_type(c_theme_tag::TYPE_FILES);
+
+    $this->context = new c_theme_tag();
+    $this->context->set_type(c_theme_tag::TYPE_CONTEXT);
+
+    $this->presentation = new c_theme_tag();
+    $this->presentation->set_type(c_theme_tag::TYPE_PRESENTATION);
+
+    $this->content = new c_theme_tag();
+    $this->content->set_type(c_theme_tag::TYPE_CONTENT);
+  }
+
+  /**
+   * Class destructor.
+   */
+  public function __destruct() {
+    unset($this->title);
+    unset($this->heading);
+    unset($this->files);
+    unset($this->context);
+    unset($this->presentation);
+    unset($this->content);
+  }
+
+  /**
+   * Assign tags to this object.
+   *
+   * Tag types must be set prior to this function call.
+   *
+   * @param c_theme_tag $tag
+   *   When a c_theme_tag object, the tag is appended to the end of the tags array.
+   * @param int|null $index
+   *   When an integer, represents the position within the tag array to assign the tag.
+   *   - May not be less than 0 or greater than the length of the array.
+   *   When $tag is NULL, this does nothing.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the position the tag was added on success.
+   *   When $tag is NULL, TRUE is returned on success.
+   *   FALSE is returned if unable to set tag.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_tag($tag, $index = NULL) {
+    if (!is_object($tag) || !($tag instanceof c_theme_tag)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($index) && !is_int($index)) {
+      return c_base_return_error::s_false();
+    }
+
+    $type = $tag->get_type();
+    if (!($type instanceof c_base_return_int)) {
+      return c_base_return_error::s_false();
+    }
+
+    $type = $type->get_value_exact();
+    if ($type === c_theme_tag::TYPE_TITLE) {
+      unset($type);
+      return $this->title->set_tag($tag, $index);
+    }
+    elseif ($type === c_theme_tag::TYPE_HEADING) {
+      unset($type);
+      return $this->heading->set_tag($tag, $index);
+    }
+    elseif ($type === c_theme_tag::TYPE_FILES) {
+      unset($type);
+      return $this->files->set_tag($tag, $index);
+    }
+    elseif ($type === c_theme_tag::TYPE_CONTEXT) {
+      unset($type);
+      return $this->context->set_tag($tag, $index);
+    }
+    elseif ($type === c_theme_tag::TYPE_PRESENTATION) {
+      unset($type);
+      return $this->presentation->set_tag($tag, $index);
+    }
+    elseif ($type === c_theme_tag::TYPE_CONTENT) {
+      unset($type);
+      return $this->content->set_tag($tag, $index);
+    }
+    unset($type);
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Assign multiple tags to this object.
+   *
+   * Tag types must be set prior to this function call.
+   *
+   * @param array $tags
+   *   An array of __class__ objects.
+   *   Each individual item is checked and then appended to the end of the array.
+   *   - The array keys will not be preserved.
+   *
+   * @param int|null $index
+   *   When an integer, represents the position within the tag array to insert the tags.
+   *   - May not be less than 0 or greater than the length of the array.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the position the tag was added on success.
+   *   FALSE is returned if unable to set tags.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function set_tags($tags, $index = NULL) {
+    if (!is_array($tags)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (!is_null($index) && !is_int($index)) {
+      return c_base_return_error::s_false();
+    }
+
+    if (empty($tags)) {
+      return c_base_return_int::s_new(0);
+    }
+
+    $total_added = 0;
+    foreach ($tags as $tag) {
+      if ($tag instanceof c_theme_tag) {
+        $type = $tag->get_type();
+        if ($type instanceof c_base_return_int) {
+          $type = $type->get_value_exact();
+          if ($type === c_theme_tag::TYPE_TITLE) {
+            $result = $this->title->set_tag($tag, $index);
+          }
+          elseif ($type === c_theme_tag::TYPE_HEADING) {
+            $result = $this->heading->set_tag($tag, $index);
+          }
+          elseif ($type === c_theme_tag::TYPE_FILES) {
+            $result = $this->files->set_tag($tag, $index);
+          }
+          elseif ($type === c_theme_tag::TYPE_CONTEXT) {
+            $result = $this->context->set_tag($tag, $index);
+          }
+          elseif ($type === c_theme_tag::TYPE_PRESENTATION) {
+            $result = $this->presentation->set_tag($tag, $index);
+          }
+          elseif ($type === c_theme_tag::TYPE_CONTENT) {
+            $result = $this->content->set_tag($tag, $index);
+          }
+          else {
+            $result = new c_base_return_false();
+          }
+        }
+        else {
+          $result = new c_base_return_false();
+        }
+      }
+      else {
+        $result = new c_base_return_false();
+      }
+
+      // Don't continue if anything goes wrong.
+      if ($result instanceof c_base_return_false) {
+        unset($result);
+        unset($tag);
+        unset($type);
+        unset($total_added);
+        return c_base_return_error::s_false();
+      }
+
+      $total_added++;
+    }
+    unset($result);
+    unset($tag);
+    unset($type);
+
+    return c_base_return_int::s_new($total_added);
+  }
+
+  /**
+   * Get a single tag assigned to this object.
+   *
+   * @param int $type
+   *   The tag type to load the tag from.
+   * @param int|null $index
+   *   (optional) An integer representing the position within the tag array to return.
+   *   When NULL, the tag at the end of the tags array is returned.
+   *
+   * @return c_base_return_status|c_theme_return_tag|c_base_return_null
+   *   A c_theme_return_tag object on success.
+   *   NULL might be returned if there is no object assigned to the index.
+   *   Otherwise FALSE is returned.
+   *   FALSE with error bit set is returned on error.
+   *
+   * @see: self::get_tags()
+   */
+  public function get_tag($type, $index = NULL) {
+    if (!is_int($type)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($type === c_theme_tag::TYPE_TITLE) {
+      return $this->title->get_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_HEADING) {
+      return $this->heading->get_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_FILES) {
+      return $this->files->get_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_CONTEXT) {
+      return $this->context->get_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_PRESENTATION) {
+      return $this->presentation->get_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_CONTENT) {
+      return $this->content->get_tag($index);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Delete tag from this object.
+   *
+   * Tags not stored at the end of the array are set to NULL instead of being deleted.
+   * The length is therefore not shortened unless the deleted tag is at the end of the array.
+   * Call $this->sanitize_tags() to ensure that the array structure contains none of these holes.
+   *
+   * @param int $type
+   *   The tag type to load the tag from.
+   * @param int|null $index
+   *   (optional) When an integer, represents the position of the tag to delete.
+   *   - May not be less than 0 or greater than the length of the array.
+   *   When $tag is NULL, deletes the tag at the end of the tag array.
+   *
+   * @return c_base_return_status|c_base_return_int
+   *   An integer representing the position the tag was deleted on success.
+   *   Otherwise, FALSE is returned.
+   *   FALSE without error bit set is returned if the tag at the specified index is already deleted.
+   *   FALSE with error bit set is returned on error.
+   */
+  public function delete_tag($type, $index = NULL) {
+    if (!is_int($type)) {
+      return c_base_return_error::s_false();
+    }
+
+    if ($type === c_theme_tag::TYPE_TITLE) {
+      return $this->title->delete_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_HEADING) {
+      return $this->heading->delete_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_FILES) {
+      return $this->files->delete_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_CONTEXT) {
+      return $this->context->delete_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_PRESENTATION) {
+      return $this->presentation->delete_tag($index);
+    }
+    elseif ($type === c_theme_tag::TYPE_CONTENT) {
+      return $this->content->delete_tag($index);
+    }
+
+    return new c_base_return_false();
+  }
+
+  /**
+   * Convert this object and all of its children or text to markup.
+   *
+   * @return c_base_return_string
+   *   A string representing this object in the form of HTMl compatible markup.
+   */
+  public function to_markup() {
+    $markup = '<!DOCTYPE cml>';
+    $markup .= '<cml>';
+
+    $markup .= '<title>';
+    $markup .= $this->title->to_markup()->get_value_exact();
+    $markup .= '</title>';
+
+    $markup .= '<heading>';
+    $markup .= $this->heading->to_markup()->get_value_exact();
+    $markup .= '</heading>';
+
+    $markup .= '<files>';
+    $markup .= $this->files->to_markup()->get_value_exact();
+    $markup .= '</files>';
+
+    $markup .= '<context>';
+    $markup .= $this->context->to_markup()->get_value_exact();
+    $markup .= '</context>';
+
+    $markup .= '<presentation>';
+    $markup .= $this->presentation->to_markup()->get_value_exact();
+    $markup .= '</presentation>';
+
+    $markup .= '<content>';
+    $markup .= $this->content->to_markup()->get_value_exact();
+    $markup .= '</content>';
+
+    unset($tag_markup);
+    $markup .= '</cml>';
+
+    return c_base_return_string::s_new($markup);
+  }
+}
+
+/**
+ * A return class whose value is represented as a __class__.
+ *
+ * This should be the most commonly used class as it adds some type security over the c_base_return_value class.
+ */
+class c_theme_return_markup extends c_base_return_value {
+  use t_base_return_value_exact;
+
+  /**
+   * @see: t_base_return_value::p_s_new()
+   */
+  public static function s_new($value) {
+    return self::p_s_new($value, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value::p_s_value()
+   */
+  public static function s_value($return) {
+    return self::p_s_value($return, __CLASS__);
+  }
+
+  /**
+   * @see: t_base_return_value_exact::p_s_value_exact()
+   */
+  public static function s_value_exact($return) {
+    return self::p_s_value_exact($return, __CLASS__, '');
+  }
+
+  /**
+   * Assign the value.
+   *
+   * @param __class__ $value
+   *   Any value so long as it is a __class__.
+   *   NULL is not allowed.
+   *
+   * @return bool
+   *   TRUE on success, FALSE otherwise.
+   */
+  public function set_value($value) {
+    if (!$value instanceof c_theme_markup) {
+      return FALSE;
+    }
+
+    $this->value = $value;
+    return TRUE;
+  }
+
+  /**
+   * Return the value.
+   *
+   * @return string|null $value
+   *   The value array stored within this class.
+   */
+  public function get_value() {
+    if (!is_null($this->value) && !($this->value instanceof c_theme_markup)) {
+      $this->value = NULL;
+    }
+
+    return $this->value;
+  }
+
+  /**
+   * Return the value of the expected type.
+   *
+   * @return __class__ $value
+   *   The value c_theme_dom stored within this class.
+   */
+  public function get_value_exact() {
+    if (!($this->value instanceof c_theme_markup)) {
+      $this->value = new c_theme_tag();
+    }
+
+    return $this->value;
+  }
+}
diff --git a/common/theme/classes/theme_rss.php b/common/theme/classes/theme_rss.php
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/documentation/naming.txt b/documentation/naming.txt
new file mode 100644 (file)
index 0000000..bc8828a
--- /dev/null
@@ -0,0 +1,36 @@
+Functions, Classes, Objects, Traits, and numerous other special types of functionality used by PHP and other languages are used in this project in such a way to provide an obvious and simple way to identify them.
+
+The project loosely follows the drupal project coding scheme, but one specific exception is "camel casing", which is exclusively denied by this project.
+All names used should be lower cased.
+Local variables do not have name prefixes such as the ones described below.
+
+The following is the naming scheme used to communicate what a particular name is:
+  - f_: This represent a function.
+  - c_: This represents a class name.
+  - t_: This represents a class trait.
+
+Within a class object, special rules apply:
+  1) f_ is not prefixed on function names.
+  2) p_ is prefixed for private classes and protected classes that are intended to be private (via the use of final in the final class).
+  3) s_ is prefixed for static class function names.
+  4) All non-private and protected classes intended to be public must not be prefixed with p_.
+  5) Common operations will have the following prefixes:
+    - get_: To load, return, or otherwise obtain some data, such as the value of a variable defined within a class.
+    - set_: To save, edit, other otherwise alter some data, such as a vlaue of a variable defined within a class.
+    - push_: Is used to send or write to something that might be remote and is not a variable in this class.
+    - pull_: Is used to retrieve or load something that might be remote and is not a variable in this class.
+  6) Initialization and de-initialization should be used. (This note needs to be moved elsewhere, but was written down here while my mind was on the topic.)
+    - All classes must unset() all variables during de-initialization.
+      - This is done to help encourage the freeing of memory for when a garbage collection is performed.
+
+All uppercase letters represents some form of global variable, be it within a class object or used as a 'define'.
+
+All functions, classes, etc.. that are part of the API should be grouped in some manner based on their purpose or the best fit purpose.
+Each of these overarching purposes should be represented by a single word that can act as a prefix.
+This prefix must be applied after the type prefix.
+  - For example, all basic functionality is grouped by 'base_'.
+  - A function defined within the base group would therefore be prefixed with: 'f_base_'.
+
+Sub-group prefixes are also allowed.
+To keep things simple, try to keep names and purposes limited to a single sub-group.
+If more complexity is needed, uses classes to prevent using more than 1 sub-group prefix.
diff --git a/documentation/requirements.txt b/documentation/requirements.txt
new file mode 100644 (file)
index 0000000..6eadf78
--- /dev/null
@@ -0,0 +1,25 @@
+This project is designed for specific software.
+
+Postgresql: 9.6.0 or later.
+- see: https://www.postgresql.org/
+- In general, postgresql is very compatible with itself and rarely has any update issues.
+- Any version greater than 9.6.0, such as 9.6.6, 9.7.0, 9.8.52, 10.0.0, 12.2.3, etc..
+- This project uses functionality added as of 9.6.0.
+
+PHP: 7.1 and any patches.
+- see: https://www.php.net/
+- In general, PHP is very incompatible with itself and should use the specified versions.
+- This is initially being programmed in 5.6, but is expected to be moved to 7.1.
+
+PHP Modules:
+- mbstring
+- intl
+
+php-lzo:
+- see: https://github.com/adsr/php-lzo
+- see: http://www.oberhumer.com/opensource/lzo/
+- This must be added to the PHP source code during compile time.
+
+php-xz:
+- see: https://github.com/chobie/php-xz
+- This must be added to the PHP source code during compile time.