diff --git a/includes/cache.inc b/includes/cache.inc
index 2323ad4..590d6b6 100644
--- a/includes/cache.inc
+++ b/includes/cache.inc
@@ -93,63 +93,25 @@ function _views_fetch_plugin_data($type = NULL, $plugin = NULL) {
 /**
  * Scan all modules for default views and rebuild the default views cache.
  *
- * @return An associative array of all known default views.
+ * @return
+ *   For PHP > 5.0, returns an ArrayObject. For PHP < 5.0
+ *   returns an array of all default views.
  */
-function _views_discover_default_views() {
-  static $cache = NULL;
-
-  if (!isset($cache)) {
-    $index = views_cache_get('views_default_views_index', TRUE);
-
-    // Retrieve each cached default view
-    if (isset($index->data) && is_array($index->data)) {
-      $cache = array();
-      foreach ($index->data as $view_name) {
-        $data = views_cache_get('views_default:' . $view_name, TRUE);
-        if (isset($data->data) && is_object($data->data)) {
-          $cache[$view_name] = $data->data;
-        }
-      }
-    }
-    // If missing index, rebuild the cache
-    else {
-      views_include_default_views();
-      $cache = array();
-
-      foreach(module_implements('views_default_views') as $module) {
-        $results = call_user_func($module . "_views_default_views");
-        if (!empty($results) && is_array($results)) {
-          foreach($results as $name => $view) {
-            // Only views with a sufficiently high api version are eligible.
-            if (!empty($view->api_version) && $view->api_version >= 2) {
-              // Do not cache dead handlers.
-              $view->destroy();
-              if (!isset($cache[$name])) {
-                $cache[$name] = $view;
-              }
-              else {
-                watchdog('view', "View name '@name' is already taken", array('@name' => $name), WATCHDOG_ERROR);
-              }
-            }
-          }
-        }
-      }
-
-      // Allow modules to modify default views before they are cached.
-      drupal_alter('views_default_views', $cache);
+function _views_discover_default_views($reset = FALSE) {
+  static $php5;
 
-      // Cache the index
-      $index = array_keys($cache);
-      views_cache_set('views_default_views_index', $index, TRUE);
-
-      // Cache each view
-      foreach ($cache as $name => $view) {
-        views_cache_set('views_default:' . $name, $view, TRUE);
-      }
-    }
+  // ArrayObject is not available in PHP < 5.0. Therefore this function
+  // supports separate code paths depending on version.
+  if (!isset($php5)) {
+    $php5 = version_compare(PHP_VERSION, '5.0', '>=');
   }
-
-  return $cache;
+  if ($php5) {
+    module_load_include('inc', 'views', 'includes/cache.php5');
+  }
+  else {
+    module_load_include('inc', 'views', 'includes/cache.php4');
+  }
+  return _views_default_views_cache($reset = FALSE);
 }
 
 /**
diff --git a/includes/cache.php4.inc b/includes/cache.php4.inc
new file mode 100644
index 0000000..734650f
--- /dev/null
+++ b/includes/cache.php4.inc
@@ -0,0 +1,60 @@
+<?php
+/**
+ * @file PHP 4 compatibility.
+ */
+function _views_default_views_cache($reset = FALSE) {
+  static $cache = NULL;
+
+  if (!isset($cache) || $reset) {
+    $index = views_cache_get('views_default_views_index', TRUE);
+
+    // Retrieve each cached default view
+    if (!$reset && isset($index->data) && is_array($index->data)) {
+      $cache = array();
+      foreach ($index->data as $view_name) {
+        $data = views_cache_get('views_default:' . $view_name, TRUE);
+        if (isset($data->data) && is_object($data->data)) {
+          $cache[$view_name] = $data->data;
+        }
+      }
+    }
+    // If missing index, rebuild the cache
+    else {
+      views_include_default_views();
+      $cache = array();
+
+      foreach (module_implements('views_default_views') as $module) {
+        $results = call_user_func($module . "_views_default_views");
+        if (!empty($results) && is_array($results)) {
+          foreach($results as $name => $view) {
+            // Only views with a sufficiently high api version are eligible.
+            if (!empty($view->api_version) && $view->api_version >= 2) {
+              // Do not cache dead handlers.
+              $view->destroy();
+              if (!isset($cache[$name])) {
+                $cache[$name] = $view;
+              }
+              else {
+                watchdog('view', "View name '@name' is already taken", array('@name' => $name), WATCHDOG_ERROR);
+              }
+            }
+          }
+        }
+      }
+
+      // Allow modules to modify default views before they are cached.
+      drupal_alter('views_default_views', $cache);
+
+      // Cache the index
+      $index = array_keys($cache);
+      views_cache_set('views_default_views_index', $index, TRUE);
+
+      // Cache each view
+      foreach ($cache as $name => $view) {
+        views_cache_set('views_default:' . $name, $view, TRUE);
+      }
+    }
+  }
+
+  return $cache;
+}
diff --git a/includes/cache.php5.inc b/includes/cache.php5.inc
new file mode 100644
index 0000000..f909b31
--- /dev/null
+++ b/includes/cache.php5.inc
@@ -0,0 +1,284 @@
+<?php
+
+/**
+ * @file Code that can only be executed in PHP 5.0 or higher.
+ */
+
+/**
+ * Extends ArrayObject to enable it to be used as a caching wrapper.
+ *
+ * This class should be extended by systems that need to cache large amounts
+ * of data and have it represented as an array to calling functions. These
+ * arrays can become very large, so ArrayObject is used to allow different
+ * strategies to be used for caching internally (lazy loading, building caches
+ * over time etc.). This can dramatically reduce the amount of data that needs
+ * to be loaded from cache backends on each request, and memory usage from
+ * static caches of that same data.
+ *
+ * Note that array_* functions do not work with ArrayObject. Systems using
+ * CacheArrayObject should use this only internally. If providing API functions
+ * that return the full array, this can be cached separately or returned
+ * directly. However since CacheArrayObject holds partial content by design, it
+ * should be a normal PHP array or otherwise contain the full structure.
+ *
+ * By default, the class accounts for caches where calling functions might
+ * request keys in the array that won't exist even after a cache rebuild. This
+ * prevents situations where a cache rebuild would be triggered over and over
+ * due to a 'missing' item. These cases are stored internally as a value of
+ * NULL. This means that the offsetGet() and offsetExists() methods
+ * must be overridden if caching an array where the top level values can
+ * legitimately be NULL, and where $object->offsetExists() needs to correctly
+ * return (equivalent to array_key_exists() vs. isset()). This should not
+ * be necessary in the majority of cases.
+ *
+ * Classes extending this class must override at least the
+ * resolveCacheMiss() method to have a working implementation.
+ *
+ * offsetSet() is not overridden by this class by default. In practice this
+ * means that assigning an offset via arrayAccess will only apply while the
+ * object is in scope and will not be written back to the persistent cache.
+ * This follows a similar pattern to static vs. persistent caching in
+ * procedural code. Extending classes may wish to alter this behaviour, for
+ * example by overriding offsetSet() and adding an automatic call to persist().
+ *
+ * @see SchemaCache
+ */
+abstract class ViewsCacheArrayObject extends ArrayObject {
+
+  /**
+   * A cid to pass to cache_set() and cache_get().
+   */
+  private $cid;
+
+  /**
+   * A bin to pass to cache_set() and cache_get().
+   */
+  private $bin;
+
+  /**
+   * An array of keys to add to the cache at the end of the request.
+   */
+  protected $keysToSave = array();
+
+  /**
+   * Private variable holding a reference to the mysql connection.
+   *
+   *  The active database connection consistently goes out of scope before
+   * the destructor gets called. Destroying (or otherwise triggering the
+   * cache_set() manually in hook_exit() or register_shutdown_function is
+   * fragile. Therefore as a workaround, add a reference to the $active_db
+   * object within this class. That keeps the class active until this object
+   * has been destroed.
+   *
+   * @todo: find a better way to deal with this.
+   */
+   private $_connection;
+
+  /**
+   * Constructor.
+   *
+   * @param $cid
+   *   The cid for the array being cached.
+   * @param $bin
+   *   The bin to cache the array.
+   */
+  public function __construct($cid, $bin) {
+    $this->_connection = &$GLOBALS['active_db'];
+
+    $this->cid = $cid;
+    $this->bin = $bin;
+
+    if ($cached = cache_get($this->cid, $this->bin)) {
+      parent::__construct($cached->data);
+    }
+    else {
+      parent::__construct(array());
+    }
+  }
+
+  public function offsetExists($offset) {
+    return $this->offsetGet($offset) !== NULL;
+  }
+
+  public function offsetGet($offset) {
+    if (!parent::offsetExists($offset)) {
+      $this->resolveCacheMiss($offset);
+    }
+    return parent::offsetGet($offset);
+  }
+
+  /**
+   * Flags an offset value to be written to the persistent cache.
+   *
+   * If a value is assigned to a cache object with offsetSet(), by default it
+   * will not be written to the persistent cache unless it is flagged with this
+   * method. This allows items to be cached for the duration of a request,
+   * without necessarily writing back to the persistent cache at the end.
+   *
+   * @param $offset
+   *   The array offset that was request.
+   * @param $persist
+   *   Optional boolean to specify whether the offset should be persisted or
+   *   not, defaults to TRUE. When called with $persist = FALSE the offset will
+   *   be unflagged so that it will not written at the end of the request.
+   */
+  protected function persist($offset, $persist = TRUE) {
+    $this->keysToSave[$offset] = $persist;
+  }
+
+  public function exchangeArray($input) {
+    // Reset keysToSave to ensure there is no attempt to cache keys that are
+    // not part of the cache object.
+    $this->keysToSave = array();
+    return parent::exchangeArray($input);
+  }
+
+  /**
+   * Resolves a cache miss.
+   *
+   * When an offset is not found in the object, this is treated as a cache
+   * miss. This method allows classes implementing the interface to look up
+   * the actual value and allow it to be cached.
+   *
+   * @param $offset
+   *   The offset that was requested.
+   */
+  abstract protected function resolveCacheMiss($offset);
+
+  /**
+   * Immediately write a value to the persistent cache.
+   *
+   * @param $cid
+   *   The cache ID.
+   * @param $bin
+   *   The cache bin.
+   * @param $data
+   *   The data to write to the persistent cache.
+   * @param $lock
+   *   Whether to acquire a lock before writing to cache.
+   */
+  protected function set($cid, $data, $bin, $lock = TRUE) {
+    $lock_name = $cid . ':' . $bin;
+    // Since this method merges with the existing cache entry if it exists,
+    // ensure that only one process can update the cache item at any one time.
+    // This ensures that different requests can't overwrite each others'
+    // partial version of the cache and should help to avoid stampedes.
+    // When a lock cannot be acquired, the cache will not be written by
+    // that request. To implement locking for cache misses, override
+    // __construct().
+    if (!$lock || lock_acquire($lock_name)) {
+      if ($cached = cache_get($cid, $bin)) {
+        $data = $cached->data + $data;
+      }
+      cache_set($cid, $data, $bin);
+      if ($lock) {
+        lock_release($lock_name);
+      }
+    }
+  }
+
+  public function __destruct() {
+    $data = array();
+    foreach ($this->keysToSave as $offset => $persist) {
+      if ($persist) {
+        $data[$offset] = parent::offsetGet($offset);
+      }
+    }
+    if (!empty($data)) {
+      $this->set($this->cid, $data, $this->bin);
+    }
+  }
+}
+
+function _views_default_views_cache($reset = FALSE) {
+  static $cache;
+  if ($reset) {
+    unset($cache);
+    cache_clear_all('views_default', 'cache_views', TRUE);
+  }
+  if (!isset($cache)) {
+    $cache = new ViewsDefaultViewsCache();
+  }
+  return $cache;
+}
+
+/**
+ * Extends CacheArrayObject to support lazy-loading of default views.
+ */
+class ViewsDefaultViewsCache extends ViewsCacheArrayObject {
+  function __construct() {
+    $this->_connection = &$GLOBALS['active_db'];
+    // There is no global cache of default views, so __construct does not
+    // need to do anything.
+
+  }
+
+  function offsetGet($offset) {
+    if ($cached = views_cache_get('views_default:' . $offset, TRUE)) {
+      $this->offsetSet($offset, $cached->data);
+    }
+    else {
+      // The default view exists, but is not cached.
+      $this->resolveCacheMiss($offset);
+    }
+    return parent::offsetGet($offset);
+  }
+
+  function resolveCacheMiss($offset) {
+    views_include_default_views();
+    foreach (module_implements('views_default_views') as $module) {
+      $results = call_user_func($module . "_views_default_views");
+      if (!empty($results) && is_array($results)) {
+        if (isset($results[$offset])) {
+          $view = $results[$offset];
+          $alter = array($offset => &$view);
+          drupal_alter('views_default_views', $alter);
+          if (!empty($view->api_version) && $view->api_version >= 2) {
+            CacheArrayObject::offsetSet($offset, $view);
+            $view_clone = clone $view;
+            // Do not cache dead handlers.
+            $view->destroy();
+            views_cache_set('views_default:' . $offset, $view, TRUE);
+            // Nothing more to do here, return.
+            return;
+          }
+        }
+      }
+    }
+    // If no view was eligible, cache FALSE.
+    CacheArrayObject::offsetSet($offset, FALSE);
+    views_cache_set('views_default:' . $offset, FALSE);
+  }
+
+  function __destruct() {
+    // Nothing to do here either.
+    return;
+  }
+}
+
+/**
+ * Build an index of default views.
+ *
+ * @return
+ *   An array with both the keys and values as view names.
+ */
+function _views_build_default_index() {
+  $index = array();
+  views_include_default_views();
+  foreach (module_implements('views_default_views') as $module) {
+    $results = call_user_func($module, "_views_default_views");
+    if (!empty($results) && is_array($results)) {
+      foreach ($results as $name => $view) {
+        if (!empty($view->api_version) && $view->api_version >= 2) {
+          if (!isset($index[$name])) {
+            $index[$name] = $name;
+          }
+          else {
+            watchdog('view', "View name '@name' is already taken", array('@name' => $name), WATCHDOG_ERROR);
+          }
+        }
+      }
+    }
+  }
+  return $index;
+}
