Index: trunk/library/Zend/Loader/Exception.php
===================================================================
--- trunk/library/Zend/Loader/Exception.php	(revision 5146)
+++ trunk/library/Zend/Loader/Exception.php	(revision 5146)
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Exception
+ */
+require_once 'Zend/Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Loader
+ * @uses       Zend_Exception
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Loader_Exception extends Zend_Exception
+{}
Index: trunk/library/Zend/Loader/Autoloader.php
===================================================================
--- trunk/library/Zend/Loader/Autoloader.php	(revision 5146)
+++ trunk/library/Zend/Loader/Autoloader.php	(revision 5146)
@@ -0,0 +1,582 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage Autoloader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @version    $Id: Autoloader.php 23161 2010-10-19 16:08:36Z matthew $
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/** Zend_Loader */
+require_once 'Zend/Loader.php';
+
+/**
+ * Autoloader stack and namespace autoloader
+ *
+ * @uses       Zend_Loader_Autoloader
+ * @package    Zend_Loader
+ * @subpackage Autoloader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Loader_Autoloader
+{
+    /**
+     * @var Zend_Loader_Autoloader Singleton instance
+     */
+    protected static $_instance;
+
+    /**
+     * @var array Concrete autoloader callback implementations
+     */
+    protected $_autoloaders = array();
+
+    /**
+     * @var array Default autoloader callback
+     */
+    protected $_defaultAutoloader = array('Zend_Loader', 'loadClass');
+
+    /**
+     * @var bool Whether or not to act as a fallback autoloader
+     */
+    protected $_fallbackAutoloader = false;
+
+    /**
+     * @var array Callback for internal autoloader implementation
+     */
+    protected $_internalAutoloader;
+
+    /**
+     * @var array Supported namespaces 'Zend' and 'ZendX' by default.
+     */
+    protected $_namespaces = array(
+        'Zend_'  => true,
+        'ZendX_' => true,
+    );
+
+    /**
+     * @var array Namespace-specific autoloaders
+     */
+    protected $_namespaceAutoloaders = array();
+
+    /**
+     * @var bool Whether or not to suppress file not found warnings
+     */
+    protected $_suppressNotFoundWarnings = false;
+
+    /**
+     * @var null|string
+     */
+    protected $_zfPath;
+
+    /**
+     * Retrieve singleton instance
+     *
+     * @return Zend_Loader_Autoloader
+     */
+    public static function getInstance()
+    {
+        if (null === self::$_instance) {
+            self::$_instance = new self();
+        }
+        return self::$_instance;
+    }
+
+    /**
+     * Reset the singleton instance
+     *
+     * @return void
+     */
+    public static function resetInstance()
+    {
+        self::$_instance = null;
+    }
+
+    /**
+     * Autoload a class
+     *
+     * @param  string $class
+     * @return bool
+     */
+    public static function autoload($class)
+    {
+        $self = self::getInstance();
+
+        foreach ($self->getClassAutoloaders($class) as $autoloader) {
+            if ($autoloader instanceof Zend_Loader_Autoloader_Interface) {
+                if ($autoloader->autoload($class)) {
+                    return true;
+                }
+            } elseif (is_array($autoloader)) {
+                if (call_user_func($autoloader, $class)) {
+                    return true;
+                }
+            } elseif (is_string($autoloader) || is_callable($autoloader)) {
+                if ($autoloader($class)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Set the default autoloader implementation
+     *
+     * @param  string|array $callback PHP callback
+     * @return void
+     */
+    public function setDefaultAutoloader($callback)
+    {
+        if (!is_callable($callback)) {
+            throw new Zend_Loader_Exception('Invalid callback specified for default autoloader');
+        }
+
+        $this->_defaultAutoloader = $callback;
+        return $this;
+    }
+
+    /**
+     * Retrieve the default autoloader callback
+     *
+     * @return string|array PHP Callback
+     */
+    public function getDefaultAutoloader()
+    {
+        return $this->_defaultAutoloader;
+    }
+
+    /**
+     * Set several autoloader callbacks at once
+     *
+     * @param  array $autoloaders Array of PHP callbacks (or Zend_Loader_Autoloader_Interface implementations) to act as autoloaders
+     * @return Zend_Loader_Autoloader
+     */
+    public function setAutoloaders(array $autoloaders)
+    {
+        $this->_autoloaders = $autoloaders;
+        return $this;
+    }
+
+    /**
+     * Get attached autoloader implementations
+     *
+     * @return array
+     */
+    public function getAutoloaders()
+    {
+        return $this->_autoloaders;
+    }
+
+    /**
+     * Return all autoloaders for a given namespace
+     *
+     * @param  string $namespace
+     * @return array
+     */
+    public function getNamespaceAutoloaders($namespace)
+    {
+        $namespace = (string) $namespace;
+        if (!array_key_exists($namespace, $this->_namespaceAutoloaders)) {
+            return array();
+        }
+        return $this->_namespaceAutoloaders[$namespace];
+    }
+
+    /**
+     * Register a namespace to autoload
+     *
+     * @param  string|array $namespace
+     * @return Zend_Loader_Autoloader
+     */
+    public function registerNamespace($namespace)
+    {
+        if (is_string($namespace)) {
+            $namespace = (array) $namespace;
+        } elseif (!is_array($namespace)) {
+            throw new Zend_Loader_Exception('Invalid namespace provided');
+        }
+
+        foreach ($namespace as $ns) {
+            if (!isset($this->_namespaces[$ns])) {
+                $this->_namespaces[$ns] = true;
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Unload a registered autoload namespace
+     *
+     * @param  string|array $namespace
+     * @return Zend_Loader_Autoloader
+     */
+    public function unregisterNamespace($namespace)
+    {
+        if (is_string($namespace)) {
+            $namespace = (array) $namespace;
+        } elseif (!is_array($namespace)) {
+            throw new Zend_Loader_Exception('Invalid namespace provided');
+        }
+
+        foreach ($namespace as $ns) {
+            if (isset($this->_namespaces[$ns])) {
+                unset($this->_namespaces[$ns]);
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Get a list of registered autoload namespaces
+     *
+     * @return array
+     */
+    public function getRegisteredNamespaces()
+    {
+        return array_keys($this->_namespaces);
+    }
+
+    public function setZfPath($spec, $version = 'latest')
+    {
+        $path = $spec;
+        if (is_array($spec)) {
+            if (!isset($spec['path'])) {
+                throw new Zend_Loader_Exception('No path specified for ZF');
+            }
+            $path = $spec['path'];
+            if (isset($spec['version'])) {
+                $version = $spec['version'];
+            }
+        }
+
+        $this->_zfPath = $this->_getVersionPath($path, $version);
+        set_include_path(implode(PATH_SEPARATOR, array(
+            $this->_zfPath,
+            get_include_path(),
+        )));
+        return $this;
+    }
+
+    public function getZfPath()
+    {
+        return $this->_zfPath;
+    }
+
+    /**
+     * Get or set the value of the "suppress not found warnings" flag
+     *
+     * @param  null|bool $flag
+     * @return bool|Zend_Loader_Autoloader Returns boolean if no argument is passed, object instance otherwise
+     */
+    public function suppressNotFoundWarnings($flag = null)
+    {
+        if (null === $flag) {
+            return $this->_suppressNotFoundWarnings;
+        }
+        $this->_suppressNotFoundWarnings = (bool) $flag;
+        return $this;
+    }
+
+    /**
+     * Indicate whether or not this autoloader should be a fallback autoloader
+     *
+     * @param  bool $flag
+     * @return Zend_Loader_Autoloader
+     */
+    public function setFallbackAutoloader($flag)
+    {
+        $this->_fallbackAutoloader = (bool) $flag;
+        return $this;
+    }
+
+    /**
+     * Is this instance acting as a fallback autoloader?
+     *
+     * @return bool
+     */
+    public function isFallbackAutoloader()
+    {
+        return $this->_fallbackAutoloader;
+    }
+
+    /**
+     * Get autoloaders to use when matching class
+     *
+     * Determines if the class matches a registered namespace, and, if so,
+     * returns only the autoloaders for that namespace. Otherwise, it returns
+     * all non-namespaced autoloaders.
+     *
+     * @param  string $class
+     * @return array Array of autoloaders to use
+     */
+    public function getClassAutoloaders($class)
+    {
+        $namespace   = false;
+        $autoloaders = array();
+
+        // Add concrete namespaced autoloaders
+        foreach (array_keys($this->_namespaceAutoloaders) as $ns) {
+            if ('' == $ns) {
+                continue;
+            }
+            if (0 === strpos($class, $ns)) {
+                $namespace   = $ns;
+                $autoloaders = $autoloaders + $this->getNamespaceAutoloaders($ns);
+                break;
+            }
+        }
+
+        // Add internal namespaced autoloader
+        foreach ($this->getRegisteredNamespaces() as $ns) {
+            if (0 === strpos($class, $ns)) {
+                $namespace     = $ns;
+                $autoloaders[] = $this->_internalAutoloader;
+                break;
+            }
+        }
+
+        // Add non-namespaced autoloaders
+        $autoloaders = $autoloaders + $this->getNamespaceAutoloaders('');
+
+        // Add fallback autoloader
+        if (!$namespace && $this->isFallbackAutoloader()) {
+            $autoloaders[] = $this->_internalAutoloader;
+        }
+
+        return $autoloaders;
+    }
+
+    /**
+     * Add an autoloader to the beginning of the stack
+     *
+     * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
+     * @param  string|array $namespace Specific namespace(s) under which to register callback
+     * @return Zend_Loader_Autoloader
+     */
+    public function unshiftAutoloader($callback, $namespace = '')
+    {
+        $autoloaders = $this->getAutoloaders();
+        array_unshift($autoloaders, $callback);
+        $this->setAutoloaders($autoloaders);
+
+        $namespace = (array) $namespace;
+        foreach ($namespace as $ns) {
+            $autoloaders = $this->getNamespaceAutoloaders($ns);
+            array_unshift($autoloaders, $callback);
+            $this->_setNamespaceAutoloaders($autoloaders, $ns);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Append an autoloader to the autoloader stack
+     *
+     * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
+     * @param  string|array $namespace Specific namespace(s) under which to register callback
+     * @return Zend_Loader_Autoloader
+     */
+    public function pushAutoloader($callback, $namespace = '')
+    {
+        $autoloaders = $this->getAutoloaders();
+        array_push($autoloaders, $callback);
+        $this->setAutoloaders($autoloaders);
+
+        $namespace = (array) $namespace;
+        foreach ($namespace as $ns) {
+            $autoloaders = $this->getNamespaceAutoloaders($ns);
+            array_push($autoloaders, $callback);
+            $this->_setNamespaceAutoloaders($autoloaders, $ns);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Remove an autoloader from the autoloader stack
+     *
+     * @param  object|array|string $callback PHP callback or Zend_Loader_Autoloader_Interface implementation
+     * @param  null|string|array $namespace Specific namespace(s) from which to remove autoloader
+     * @return Zend_Loader_Autoloader
+     */
+    public function removeAutoloader($callback, $namespace = null)
+    {
+        if (null === $namespace) {
+            $autoloaders = $this->getAutoloaders();
+            if (false !== ($index = array_search($callback, $autoloaders, true))) {
+                unset($autoloaders[$index]);
+                $this->setAutoloaders($autoloaders);
+            }
+
+            foreach ($this->_namespaceAutoloaders as $ns => $autoloaders) {
+                if (false !== ($index = array_search($callback, $autoloaders, true))) {
+                    unset($autoloaders[$index]);
+                    $this->_setNamespaceAutoloaders($autoloaders, $ns);
+                }
+            }
+        } else {
+            $namespace = (array) $namespace;
+            foreach ($namespace as $ns) {
+                $autoloaders = $this->getNamespaceAutoloaders($ns);
+                if (false !== ($index = array_search($callback, $autoloaders, true))) {
+                    unset($autoloaders[$index]);
+                    $this->_setNamespaceAutoloaders($autoloaders, $ns);
+                }
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Constructor
+     *
+     * Registers instance with spl_autoload stack
+     *
+     * @return void
+     */
+    protected function __construct()
+    {
+        spl_autoload_register(array(__CLASS__, 'autoload'));
+        $this->_internalAutoloader = array($this, '_autoload');
+    }
+
+    /**
+     * Internal autoloader implementation
+     *
+     * @param  string $class
+     * @return bool
+     */
+    protected function _autoload($class)
+    {
+        $callback = $this->getDefaultAutoloader();
+        try {
+            if ($this->suppressNotFoundWarnings()) {
+                @call_user_func($callback, $class);
+            } else {
+                call_user_func($callback, $class);
+            }
+            return $class;
+        } catch (Zend_Exception $e) {
+            return false;
+        }
+    }
+
+    /**
+     * Set autoloaders for a specific namespace
+     *
+     * @param  array $autoloaders
+     * @param  string $namespace
+     * @return Zend_Loader_Autoloader
+     */
+    protected function _setNamespaceAutoloaders(array $autoloaders, $namespace = '')
+    {
+        $namespace = (string) $namespace;
+        $this->_namespaceAutoloaders[$namespace] = $autoloaders;
+        return $this;
+    }
+
+    /**
+     * Retrieve the filesystem path for the requested ZF version
+     *
+     * @param  string $path
+     * @param  string $version
+     * @return void
+     */
+    protected function _getVersionPath($path, $version)
+    {
+        $type = $this->_getVersionType($version);
+
+        if ($type == 'latest') {
+            $version = 'latest';
+        }
+
+        $availableVersions = $this->_getAvailableVersions($path, $version);
+        if (empty($availableVersions)) {
+            throw new Zend_Loader_Exception('No valid ZF installations discovered');
+        }
+
+        $matchedVersion = array_pop($availableVersions);
+        return $matchedVersion;
+    }
+
+    /**
+     * Retrieve the ZF version type
+     *
+     * @param  string $version
+     * @return string "latest", "major", "minor", or "specific"
+     * @throws Zend_Loader_Exception if version string contains too many dots
+     */
+    protected function _getVersionType($version)
+    {
+        if (strtolower($version) == 'latest') {
+            return 'latest';
+        }
+
+        $parts = explode('.', $version);
+        $count = count($parts);
+        if (1 == $count) {
+            return 'major';
+        }
+        if (2 == $count) {
+            return 'minor';
+        }
+        if (3 < $count) {
+            throw new Zend_Loader_Exception('Invalid version string provided');
+        }
+        return 'specific';
+    }
+
+    /**
+     * Get available versions for the version type requested
+     *
+     * @param  string $path
+     * @param  string $version
+     * @return array
+     */
+    protected function _getAvailableVersions($path, $version)
+    {
+        if (!is_dir($path)) {
+            throw new Zend_Loader_Exception('Invalid ZF path provided');
+        }
+
+        $path       = rtrim($path, '/');
+        $path       = rtrim($path, '\\');
+        $versionLen = strlen($version);
+        $versions   = array();
+        $dirs       = glob("$path/*", GLOB_ONLYDIR);
+        foreach ((array) $dirs as $dir) {
+            $dirName = substr($dir, strlen($path) + 1);
+            if (!preg_match('/^(?:ZendFramework-)?(\d+\.\d+\.\d+((a|b|pl|pr|p|rc)\d+)?)(?:-minimal)?$/i', $dirName, $matches)) {
+                continue;
+            }
+
+            $matchedVersion = $matches[1];
+
+            if (('latest' == $version)
+                || ((strlen($matchedVersion) >= $versionLen)
+                    && (0 === strpos($matchedVersion, $version)))
+            ) {
+                $versions[$matchedVersion] = $dir . '/library';
+            }
+        }
+
+        uksort($versions, 'version_compare');
+        return $versions;
+    }
+}
Index: trunk/library/Zend/Loader/PluginLoader/Exception.php
===================================================================
--- trunk/library/Zend/Loader/PluginLoader/Exception.php	(revision 5146)
+++ trunk/library/Zend/Loader/PluginLoader/Exception.php	(revision 5146)
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Exception.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * @see Zend_Loader_Exception
+ */
+require_once 'Zend/Loader/Exception.php';
+
+/**
+ * Plugin class loader exceptions
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Loader_PluginLoader_Exception extends Zend_Loader_Exception
+{
+}
Index: trunk/library/Zend/Loader/PluginLoader/Interface.php
===================================================================
--- trunk/library/Zend/Loader/PluginLoader/Interface.php	(revision 5146)
+++ trunk/library/Zend/Loader/PluginLoader/Interface.php	(revision 5146)
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: Interface.php 20096 2010-01-06 02:05:09Z bkarwin $
+ */
+
+/**
+ * Plugin class loader interface
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Loader_PluginLoader_Interface
+{
+    /**
+     * Add prefixed paths to the registry of paths
+     *
+     * @param string $prefix
+     * @param string $path
+     * @return Zend_Loader_PluginLoader
+     */
+    public function addPrefixPath($prefix, $path);
+
+    /**
+     * Remove a prefix (or prefixed-path) from the registry
+     *
+     * @param string $prefix
+     * @param string $path OPTIONAL
+     * @return Zend_Loader_PluginLoader
+     */
+    public function removePrefixPath($prefix, $path = null);
+
+    /**
+     * Whether or not a Helper by a specific name
+     *
+     * @param string $name
+     * @return Zend_Loader_PluginLoader
+     */
+    public function isLoaded($name);
+
+    /**
+     * Return full class name for a named helper
+     *
+     * @param string $name
+     * @return string
+     */
+    public function getClassName($name);
+
+    /**
+     * Load a helper via the name provided
+     *
+     * @param string $name
+     * @return string
+     */
+    public function load($name);
+}
Index: trunk/library/Zend/Loader/PluginLoader.php
===================================================================
--- trunk/library/Zend/Loader/PluginLoader.php	(revision 5146)
+++ trunk/library/Zend/Loader/PluginLoader.php	(revision 5146)
@@ -0,0 +1,484 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id: PluginLoader.php 22603 2010-07-17 00:02:10Z ramon $
+ */
+
+/** Zend_Loader_PluginLoader_Interface */
+require_once 'Zend/Loader/PluginLoader/Interface.php';
+
+/** Zend_Loader */
+require_once 'Zend/Loader.php';
+
+/**
+ * Generic plugin class loader
+ *
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage PluginLoader
+ * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Loader_PluginLoader implements Zend_Loader_PluginLoader_Interface
+{
+    /**
+     * Class map cache file
+     * @var string
+     */
+    protected static $_includeFileCache;
+
+    /**
+     * Instance loaded plugin paths
+     *
+     * @var array
+     */
+    protected $_loadedPluginPaths = array();
+
+    /**
+     * Instance loaded plugins
+     *
+     * @var array
+     */
+    protected $_loadedPlugins = array();
+
+    /**
+     * Instance registry property
+     *
+     * @var array
+     */
+    protected $_prefixToPaths = array();
+
+    /**
+     * Statically loaded plugin path mappings
+     *
+     * @var array
+     */
+    protected static $_staticLoadedPluginPaths = array();
+
+    /**
+     * Statically loaded plugins
+     *
+     * @var array
+     */
+    protected static $_staticLoadedPlugins = array();
+
+    /**
+     * Static registry property
+     *
+     * @var array
+     */
+    protected static $_staticPrefixToPaths = array();
+
+    /**
+     * Whether to use a statically named registry for loading plugins
+     *
+     * @var string|null
+     */
+    protected $_useStaticRegistry = null;
+
+    /**
+     * Constructor
+     *
+     * @param array $prefixToPaths
+     * @param string $staticRegistryName OPTIONAL
+     */
+    public function __construct(Array $prefixToPaths = array(), $staticRegistryName = null)
+    {
+        if (is_string($staticRegistryName) && !empty($staticRegistryName)) {
+            $this->_useStaticRegistry = $staticRegistryName;
+            if(!isset(self::$_staticPrefixToPaths[$staticRegistryName])) {
+                self::$_staticPrefixToPaths[$staticRegistryName] = array();
+            }
+            if(!isset(self::$_staticLoadedPlugins[$staticRegistryName])) {
+                self::$_staticLoadedPlugins[$staticRegistryName] = array();
+            }
+        }
+
+        foreach ($prefixToPaths as $prefix => $path) {
+            $this->addPrefixPath($prefix, $path);
+        }
+    }
+
+    /**
+     * Format prefix for internal use
+     *
+     * @param  string $prefix
+     * @return string
+     */
+    protected function _formatPrefix($prefix)
+    {
+        if($prefix == "") {
+            return $prefix;
+        }
+
+        $last = strlen($prefix) - 1;
+        if ($prefix{$last} == '\\') {
+            return $prefix;
+        }
+
+        return rtrim($prefix, '_') . '_';
+    }
+
+    /**
+     * Add prefixed paths to the registry of paths
+     *
+     * @param string $prefix
+     * @param string $path
+     * @return Zend_Loader_PluginLoader
+     */
+    public function addPrefixPath($prefix, $path)
+    {
+        if (!is_string($prefix) || !is_string($path)) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Zend_Loader_PluginLoader::addPrefixPath() method only takes strings for prefix and path.');
+        }
+
+        $prefix = $this->_formatPrefix($prefix);
+        $path   = rtrim($path, '/\\') . '/';
+
+        if ($this->_useStaticRegistry) {
+            self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix][] = $path;
+        } else {
+            if (!isset($this->_prefixToPaths[$prefix])) {
+                $this->_prefixToPaths[$prefix] = array();
+            }
+            if (!in_array($path, $this->_prefixToPaths[$prefix])) {
+                $this->_prefixToPaths[$prefix][] = $path;
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Get path stack
+     *
+     * @param  string $prefix
+     * @return false|array False if prefix does not exist, array otherwise
+     */
+    public function getPaths($prefix = null)
+    {
+        if ((null !== $prefix) && is_string($prefix)) {
+            $prefix = $this->_formatPrefix($prefix);
+            if ($this->_useStaticRegistry) {
+                if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
+                    return self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix];
+                }
+
+                return false;
+            }
+
+            if (isset($this->_prefixToPaths[$prefix])) {
+                return $this->_prefixToPaths[$prefix];
+            }
+
+            return false;
+        }
+
+        if ($this->_useStaticRegistry) {
+            return self::$_staticPrefixToPaths[$this->_useStaticRegistry];
+        }
+
+        return $this->_prefixToPaths;
+    }
+
+    /**
+     * Clear path stack
+     *
+     * @param  string $prefix
+     * @return bool False only if $prefix does not exist
+     */
+    public function clearPaths($prefix = null)
+    {
+        if ((null !== $prefix) && is_string($prefix)) {
+            $prefix = $this->_formatPrefix($prefix);
+            if ($this->_useStaticRegistry) {
+                if (isset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix])) {
+                    unset(self::$_staticPrefixToPaths[$this->_useStaticRegistry][$prefix]);
+                    return true;
+                }
+
+                return false;
+            }
+
+            if (isset($this->_prefixToPaths[$prefix])) {
+                unset($this->_prefixToPaths[$prefix]);
+                return true;
+            }
+
+            return false;
+        }
+
+        if ($this->_useStaticRegistry) {
+            self::$_staticPrefixToPaths[$this->_useStaticRegistry] = array();
+        } else {
+            $this->_prefixToPaths = array();
+        }
+
+        return true;
+    }
+
+    /**
+     * Remove a prefix (or prefixed-path) from the registry
+     *
+     * @param string $prefix
+     * @param string $path OPTIONAL
+     * @return Zend_Loader_PluginLoader
+     */
+    public function removePrefixPath($prefix, $path = null)
+    {
+        $prefix = $this->_formatPrefix($prefix);
+        if ($this->_useStaticRegistry) {
+            $registry =& self::$_staticPrefixToPaths[$this->_useStaticRegistry];
+        } else {
+            $registry =& $this->_prefixToPaths;
+        }
+
+        if (!isset($registry[$prefix])) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' was not found in the PluginLoader.');
+        }
+
+        if ($path != null) {
+            $pos = array_search($path, $registry[$prefix]);
+            if (false === $pos) {
+                require_once 'Zend/Loader/PluginLoader/Exception.php';
+                throw new Zend_Loader_PluginLoader_Exception('Prefix ' . $prefix . ' / Path ' . $path . ' was not found in the PluginLoader.');
+            }
+            unset($registry[$prefix][$pos]);
+        } else {
+            unset($registry[$prefix]);
+        }
+
+        return $this;
+    }
+
+    /**
+     * Normalize plugin name
+     *
+     * @param  string $name
+     * @return string
+     */
+    protected function _formatName($name)
+    {
+        return ucfirst((string) $name);
+    }
+
+    /**
+     * Whether or not a Plugin by a specific name is loaded
+     *
+     * @param string $name
+     * @return Zend_Loader_PluginLoader
+     */
+    public function isLoaded($name)
+    {
+        $name = $this->_formatName($name);
+        if ($this->_useStaticRegistry) {
+            return isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name]);
+        }
+
+        return isset($this->_loadedPlugins[$name]);
+    }
+
+    /**
+     * Return full class name for a named plugin
+     *
+     * @param string $name
+     * @return string|false False if class not found, class name otherwise
+     */
+    public function getClassName($name)
+    {
+        $name = $this->_formatName($name);
+        if ($this->_useStaticRegistry
+            && isset(self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name])
+        ) {
+            return self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name];
+        } elseif (isset($this->_loadedPlugins[$name])) {
+            return $this->_loadedPlugins[$name];
+        }
+
+        return false;
+    }
+
+    /**
+     * Get path to plugin class
+     *
+     * @param  mixed $name
+     * @return string|false False if not found
+     */
+    public function getClassPath($name)
+    {
+        $name = $this->_formatName($name);
+        if ($this->_useStaticRegistry
+            && !empty(self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name])
+        ) {
+            return self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name];
+        } elseif (!empty($this->_loadedPluginPaths[$name])) {
+            return $this->_loadedPluginPaths[$name];
+        }
+
+        if ($this->isLoaded($name)) {
+            $class = $this->getClassName($name);
+            $r     = new ReflectionClass($class);
+            $path  = $r->getFileName();
+            if ($this->_useStaticRegistry) {
+                self::$_staticLoadedPluginPaths[$this->_useStaticRegistry][$name] = $path;
+            } else {
+                $this->_loadedPluginPaths[$name] = $path;
+            }
+            return $path;
+        }
+
+        return false;
+    }
+
+    /**
+     * Load a plugin via the name provided
+     *
+     * @param  string $name
+     * @param  bool $throwExceptions Whether or not to throw exceptions if the
+     * class is not resolved
+     * @return string|false Class name of loaded class; false if $throwExceptions
+     * if false and no class found
+     * @throws Zend_Loader_Exception if class not found
+     */
+    public function load($name, $throwExceptions = true)
+    {
+        $name = $this->_formatName($name);
+        if ($this->isLoaded($name)) {
+            return $this->getClassName($name);
+        }
+
+        if ($this->_useStaticRegistry) {
+            $registry = self::$_staticPrefixToPaths[$this->_useStaticRegistry];
+        } else {
+            $registry = $this->_prefixToPaths;
+        }
+
+        $registry  = array_reverse($registry, true);
+        $found     = false;
+        $classFile = str_replace('_', DIRECTORY_SEPARATOR, $name) . '.php';
+        $incFile   = self::getIncludeFileCache();
+        foreach ($registry as $prefix => $paths) {
+            $className = $prefix . $name;
+
+            if (class_exists($className, false)) {
+                $found = true;
+                break;
+            }
+
+            $paths     = array_reverse($paths, true);
+
+            foreach ($paths as $path) {
+                $loadFile = $path . $classFile;
+                if (Zend_Loader::isReadable($loadFile)) {
+                    include_once $loadFile;
+                    if (class_exists($className, false)) {
+                        if (null !== $incFile) {
+                            self::_appendIncFile($loadFile);
+                        }
+                        $found = true;
+                        break 2;
+                    }
+                }
+            }
+        }
+
+        if (!$found) {
+            if (!$throwExceptions) {
+                return false;
+            }
+
+            $message = "Plugin by name '$name' was not found in the registry; used paths:";
+            foreach ($registry as $prefix => $paths) {
+                $message .= "\n$prefix: " . implode(PATH_SEPARATOR, $paths);
+            }
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception($message);
+       }
+
+        if ($this->_useStaticRegistry) {
+            self::$_staticLoadedPlugins[$this->_useStaticRegistry][$name]     = $className;
+        } else {
+            $this->_loadedPlugins[$name]     = $className;
+        }
+        return $className;
+    }
+
+    /**
+     * Set path to class file cache
+     *
+     * Specify a path to a file that will add include_once statements for each
+     * plugin class loaded. This is an opt-in feature for performance purposes.
+     *
+     * @param  string $file
+     * @return void
+     * @throws Zend_Loader_PluginLoader_Exception if file is not writeable or path does not exist
+     */
+    public static function setIncludeFileCache($file)
+    {
+        if (null === $file) {
+            self::$_includeFileCache = null;
+            return;
+        }
+
+        if (!file_exists($file) && !file_exists(dirname($file))) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Specified file does not exist and/or directory does not exist (' . $file . ')');
+        }
+        if (file_exists($file) && !is_writable($file)) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
+        }
+        if (!file_exists($file) && file_exists(dirname($file)) && !is_writable(dirname($file))) {
+            require_once 'Zend/Loader/PluginLoader/Exception.php';
+            throw new Zend_Loader_PluginLoader_Exception('Specified file is not writeable (' . $file . ')');
+        }
+
+        self::$_includeFileCache = $file;
+    }
+
+    /**
+     * Retrieve class file cache path
+     *
+     * @return string|null
+     */
+    public static function getIncludeFileCache()
+    {
+        return self::$_includeFileCache;
+    }
+
+    /**
+     * Append an include_once statement to the class file cache
+     *
+     * @param  string $incFile
+     * @return void
+     */
+    protected static function _appendIncFile($incFile)
+    {
+        if (!file_exists(self::$_includeFileCache)) {
+            $file = '<?php';
+        } else {
+            $file = file_get_contents(self::$_includeFileCache);
+        }
+        if (!strstr($file, $incFile)) {
+            $file .= "\ninclude_once '$incFile';";
+            file_put_contents(self::$_includeFileCache, $file);
+        }
+    }
+}
