Przeglądaj źródła

Backported ZF2 autoloaders to ZF1

- New Classes:
  - Zend_File_ClassFileLocator
  - Zend_Loader_SplAutoloader
  - Zend_Loader_AutoloaderFactory
  - Zend_Loader_StandardAutoloader
  - Zend_Loader_ClassMapAutoloader
  - Zend_Loader_Exception_InvalidArgumentException
- All tests and documentation are up-to-date
- TODO: use the AutoloaderFactory from Zend_Application?

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@24678 44c647ce-9c0f-0410-b52a-842ac1e357ba
matthew 14 lat temu
rodzic
commit
660b84f8d5
37 zmienionych plików z 3793 dodań i 0 usunięć
  1. 155 0
      bin/classmap_generator.php
  2. 25 0
      documentation/manual/en/manual.xml.in
  3. 170 0
      documentation/manual/en/module_specs/Zend_Loader-AutoloaderFactory.xml
  4. 353 0
      documentation/manual/en/module_specs/Zend_Loader-ClassMapAutoloader.xml
  5. 91 0
      documentation/manual/en/module_specs/Zend_Loader-Classmap_Generator.xml
  6. 213 0
      documentation/manual/en/module_specs/Zend_Loader-SplAutoloader.xml
  7. 520 0
      documentation/manual/en/module_specs/Zend_Loader-StandardAutoloader.xml
  8. 174 0
      library/Zend/File/ClassFileLocator.php
  9. 211 0
      library/Zend/Loader/AutoloaderFactory.php
  10. 225 0
      library/Zend/Loader/ClassMapAutoloader.php
  11. 34 0
      library/Zend/Loader/Exception/InvalidArgumentException.php
  12. 75 0
      library/Zend/Loader/SplAutoloader.php
  13. 342 0
      library/Zend/Loader/StandardAutoloader.php
  14. 2 0
      tests/Zend/File/AllTests.php
  15. 98 0
      tests/Zend/File/ClassFileLocatorTest.php
  16. 30 0
      tests/Zend/File/TestAsset/LocatorShouldFindThis.php
  17. 2 0
      tests/Zend/File/_files/locator-should-skip-this.php
  18. 0 0
      tests/Zend/File/_files/locator-should-skip-this.xml
  19. 6 0
      tests/Zend/Loader/AllTests.php
  20. 231 0
      tests/Zend/Loader/AutoloaderFactoryTest.php
  21. 217 0
      tests/Zend/Loader/ClassMapAutoloaderTest.php
  22. 206 0
      tests/Zend/Loader/StandardAutoloaderTest.php
  23. 39 0
      tests/Zend/Loader/TestAsset/ClassMappedClass.php
  24. 33 0
      tests/Zend/Loader/TestAsset/Name_Space/Namespaced/Class.php
  25. 35 0
      tests/Zend/Loader/TestAsset/NamespacedClass.php
  26. 52 0
      tests/Zend/Loader/TestAsset/StandardAutoloader.php
  27. 53 0
      tests/Zend/Loader/TestAsset/TestPluginMap.php
  28. 31 0
      tests/Zend/Loader/TestAsset/TestPlugins/Bar.php
  29. 33 0
      tests/Zend/Loader/TestAsset/TestPlugins/Baz.php
  30. 31 0
      tests/Zend/Loader/TestAsset/TestPlugins/Foo.php
  31. 31 0
      tests/Zend/Loader/TestAsset/TestPrefix/FallbackCase.php
  32. 31 0
      tests/Zend/Loader/TestAsset/TestPrefix/NoDuplicateAutoloadersCase.php
  33. 31 0
      tests/Zend/Loader/TestAsset/UnusualPrefix/PrefixedClass.php
  34. 4 0
      tests/Zend/Loader/_files/InvalidInterfaceAutoloader.php
  35. 2 0
      tests/Zend/Loader/_files/badmap.php
  36. BIN
      tests/Zend/Loader/_files/classmap.phar
  37. 7 0
      tests/Zend/Loader/_files/goodmap.php

+ 155 - 0
bin/classmap_generator.php

@@ -0,0 +1,155 @@
+<?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 Exception
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * Generate class maps for use with autoloading.
+ *
+ * Usage:
+ * --help|-h                    Get usage message
+ * --library|-l [ <string> ]    Library to parse; if none provided, assumes 
+ *                              current directory
+ * --output|-o [ <string> ]     Where to write autoload file; if not provided, 
+ *                              assumes ".classmap.php" in library directory
+ * --overwrite|-w               Whether or not to overwrite existing autoload 
+ *                              file
+ */
+
+$libPath = dirname(__FILE__) . '/../library';
+if (!is_dir($libPath)) {
+    // Try to load StandardAutoloader from include_path
+    if (false === include('Zend/Loader/StandardAutoloader.php')) {
+        echo "Unable to locate autoloader via include_path; aborting" . PHP_EOL;
+        exit(2);
+    }
+} else {
+    // Try to load StandardAutoloader from library
+    if (false === include(dirname(__FILE__) . '/../library/Zend/Loader/StandardAutoloader.php')) {
+        echo "Unable to locate autoloader via library; aborting" . PHP_EOL;
+        exit(2);
+    }
+}
+
+// Setup autoloading
+$loader = new Zend_Loader_StandardAutoloader();
+$loader->setFallbackAutoloader(true);
+$loader->register();
+
+$rules = array(
+    'help|h'        => 'Get usage message',
+    'library|l-s'   => 'Library to parse; if none provided, assumes current directory',
+    'output|o-s'    => 'Where to write autoload file; if not provided, assumes ".classmap.php" in library directory',
+    'overwrite|w'   => 'Whether or not to overwrite existing autoload file',
+);
+
+try {
+    $opts = new Zend_Console_Getopt($rules);
+    $opts->parse();
+} catch (Zend_Console_Getopt_Exception $e) {
+    echo $e->getUsageMessage();
+    exit(2);
+}
+
+if ($opts->getOption('h')) {
+    echo $opts->getUsageMessage();
+    exit();
+}
+
+$path = $libPath;
+if (array_key_exists('PWD', $_SERVER)) {
+    $path = $_SERVER['PWD'];
+}
+if (isset($opts->l)) {
+    $path = $opts->l;
+    if (!is_dir($path)) {
+        echo "Invalid library directory provided" . PHP_EOL . PHP_EOL;
+        echo $opts->getUsageMessage();
+        exit(2);
+    }
+    $path = realpath($path);
+}
+
+$usingStdout = false;
+$output = $path . DIRECTORY_SEPARATOR . '.classmap.php';
+if (isset($opts->o)) {
+    $output = $opts->o;
+    if ('-' == $output) {
+        $output = STDOUT;
+        $usingStdout = true;
+    } elseif (!is_writeable(dirname($output))) {
+        echo "Cannot write to '$output'; aborting." . PHP_EOL
+            . PHP_EOL
+            . $opts->getUsageMessage();
+        exit(2);
+    } elseif (file_exists($output)) {
+        if (!$opts->getOption('w')) {
+            echo "Autoload file already exists at '$output'," . PHP_EOL
+                . "but 'overwrite' flag was not specified; aborting." . PHP_EOL 
+                . PHP_EOL
+                . $opts->getUsageMessage();
+            exit(2);
+        }
+    }
+}
+
+$strip     = $path;
+
+if (!$usingStdout) {
+    echo "Creating class file map for library in '$path'..." . PHP_EOL;
+}
+
+// Get the ClassFileLocator, and pass it the library path
+$l = new Zend_File_ClassFileLocator($path);
+
+// Iterate over each element in the path, and create a map of 
+// classname => filename, where the filename is relative to the library path
+$map    = new stdClass;
+$strip .= DIRECTORY_SEPARATOR;
+function createMap(Iterator $i, $map, $strip) {
+    $file      = $i->current();
+    $namespace = empty($file->namespace) ? '' : $file->namespace . '\\';
+    $filename  = str_replace($strip, '', $file->getRealpath());
+
+    // Windows portability
+    $filename  = str_replace(array('/', '\\'), "' . DIRECTORY_SEPARATOR . '", $filename);
+
+    $map->{$namespace . $file->classname} = $filename;
+
+    return true;
+}
+iterator_apply($l, 'createMap', array($l, $map, $strip));
+
+// Create a file with the class/file map.
+// Stupid syntax highlighters make separating < from PHP declaration necessary
+$dirStore = 'dirname_' . uniqid();
+$content = '<' . "?php\n"
+         . '$' . $dirStore . " = dirname(__FILE__);\n"
+         . 'return ' . var_export((array) $map, true) . ';';
+
+// Prefix with dirname(__FILE__); modify the generated content
+$content = preg_replace('#(=> )#', '$1$' . $dirStore . ' . DIRECTORY_SEPARATOR . ', $content);
+$content = str_replace("\\'", "'", $content);
+
+// Write the contents to disk
+file_put_contents($output, $content);
+
+if (!$usingStdout) {
+    echo "Wrote classmap file to '" . realpath($output) . "'" . PHP_EOL;
+}

+ 25 - 0
documentation/manual/en/manual.xml.in

@@ -1280,6 +1280,31 @@
                     <xi:include href="../en/module_specs/Zend_Loader-PluginLoader.xml" />
                 </xi:fallback>
             </xi:include>
+            <xi:include href="module_specs/Zend_Loader-SplAutoloader.xml">
+                <xi:fallback>
+                    <xi:include href="../en/module_specs/Zend_Loader-SplAutoloader.xml" />
+                </xi:fallback>
+            </xi:include>
+            <xi:include href="module_specs/Zend_Loader-AutoloaderFactory.xml">
+                <xi:fallback>
+                    <xi:include href="../en/module_specs/Zend_Loader-AutoloaderFactory.xml" />
+                </xi:fallback>
+            </xi:include>
+            <xi:include href="module_specs/Zend_Loader-StandardAutoloader.xml">
+                <xi:fallback>
+                    <xi:include href="../en/module_specs/Zend_Loader-StandardAutoloader.xml" />
+                </xi:fallback>
+            </xi:include>
+            <xi:include href="module_specs/Zend_Loader-ClassMapAutoloader.xml">
+                <xi:fallback>
+                    <xi:include href="../en/module_specs/Zend_Loader-ClassMapAutoloader.xml" />
+                </xi:fallback>
+            </xi:include>
+            <xi:include href="module_specs/Zend_Loader-Classmap_Generator.xml">
+                <xi:fallback>
+                    <xi:include href="../en/module_specs/Zend_Loader-Classmap_Generator.xml" />
+                </xi:fallback>
+            </xi:include>
         </chapter>
 
         <chapter id="zend.locale">

+ 170 - 0
documentation/manual/en/module_specs/Zend_Loader-AutoloaderFactory.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Reviewed: no -->
+<sect1 id="zend.loader.autoloader-factory">
+    <title>The AutoloaderFactory</title>
+
+    <sect2 id="zend.loader.autoloader-factory.intro">
+        <title>Overview</title>
+
+        <para>
+            Starting with version 1.12.0, Zend Framework now offers multiple autoloader strategies.
+            Often, it will be useful to employ multiple autoloading strategies; as an example, you
+            may have a class map for your most used classes, but want to use a PSR-0 style
+            autoloader for 3rd party libraries.
+        </para>
+
+        <para>
+            While you could potentially manually configure these, it may be more useful to define
+            the autoloader configuration somewhere and cache it. For these cases, the
+            <classname>AutoloaderFactory</classname> will be useful.
+        </para>
+    </sect2>
+
+    <sect2 id="zend.loader.autoloader-factory.quick-start">
+        <title>Quick Start</title>
+
+        <para>
+            Configuration may be stored as a PHP array, or in some form of configuration file. As an
+            example, consider the following PHP array:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$config = array(
+    'Zend_Loader_ClassMapAutoloader' => array(
+        'application' => APPLICATION_PATH . '/.classmap.php',
+        'zf'          => APPLICATION_PATH . '/../library/Zend/.classmap.php',
+    ),
+    'Zend_Loader_StandardAutoloader' => array(
+        'namespaces' => array(
+            'Phly\Mustache' => APPLICATION_PATH . '/../library/Phly/Mustache',
+            'Doctrine'      => APPLICATION_PATH . '/../library/Doctrine',
+        ),
+    ),
+);
+]]></programlisting>
+
+        <para>
+            An equivalent INI-style configuration might look like the following:
+        </para>
+
+        <programlisting xml:lang="ini"><![CDATA[
+Zend_Loader_ClassMapAutoloader.application = APPLICATION_PATH "/.classmap.php"
+Zend_Loader_ClassMapAutoloader.zf          = APPLICATION_PATH "/../library/Zend/.classmap.php"
+Zend_Loader_StandardAutoloader.namespaces.Phly\Mustache = APPLICATION_PATH "/../library/Phly/Mustache"
+Zend_Loader_StandardAutoloader.namespaces.Doctrine       = APPLICATION_PATH "/../library/Doctrine"
+]]></programlisting>
+
+        <para>
+            Once you have your configuration in a PHP array, you simply pass it to the
+            <classname>AutoloaderFactory</classname>.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// This example assumes ZF is on your include_path.
+// You could also load the factory class from a path relative to the
+// current script, or via an absolute path.
+require_once 'Zend_Loader_AutoloaderFactory.php';
+Zend_Loader_AutoloaderFactory::factory($config);
+]]></programlisting>
+
+        <para>
+            The <classname>AutoloaderFactory</classname> will instantiate each autoloader with the
+            given options, and also call its <methodname>register()</methodname> method to register
+            it with the SPL autoloader.
+        </para>
+    </sect2>
+
+    <sect2 id="zend.loader.autoloader-factory.options">
+        <title>Configuration Options</title>
+
+        <variablelist>
+            <title>AutoloaderFactory Options</title>
+
+            <varlistentry>
+                <term>$options</term>
+
+                <listitem>
+                    <para>
+                        The <classname>AutoloaderFactory</classname> expects an associative array or
+                        <interfacename>Traversable</interfacename> object. Keys should be valid
+                        autoloader class names, and the values should be the options that should be
+                        passed to the class constructor.
+                    </para>
+
+                    <para>
+                        Internally, the <classname>AutoloaderFactory</classname> checks to see if
+                        the autoloader class referenced exists. If not, it will use the <link linkend="zend.loader.standard-autoloader">StandardAutoloader</link> to
+                        attempt to load the class via the <varname>include_path</varname> (or, in
+                        the case of "Zend"-namespaced classes, using the Zend Framework library
+                        path). If the class is not found, or does not implement the
+                        <link linkend="zend.loader.spl-autoloader">SplAutoloader</link> interface,
+                        an exception will be raised.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </sect2>
+
+    <sect2 id="zend.loader.autoloader-factory.methods">
+        <title>Available Methods</title>
+
+        <refentry id="zend.loader.autoloader-factory.methods.factory">
+            <refnamediv>
+                <refname>factory</refname>
+                <refpurpose>Instantiate and register autoloaders</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>factory</methodname>
+                    <methodparam>
+                        <funcparams>$options</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>factory()</title>
+
+                <para>
+                    This method is <emphasis>static</emphasis>, and is used to instantiate
+                    autoloaders and register them with the SPL autoloader. It expects either an
+                    array or <interfacename>Traversable</interfacename> object as denoted in the
+                    <link linkend="zend.loader.autoloader-factory.options">Options section</link>.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.autoloader-factory.methods.get-registered-autoloaders">
+            <refnamediv>
+                <refname>getRegisteredAutoloaders</refname>
+                <refpurpose>Retrieve a list of all autoloaders registered using the factory</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>getRegisteredAutoloaders</methodname>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>getRegisteredAutoloaders()</title>
+
+                <para>
+                    This method is <emphasis>static</emphasis>, and may be used to retrieve a list
+                    of all autoloaders registered via the <methodname>factory()</methodname> method.
+                    It returns simply an array of autoloader instances.
+                </para>
+            </refsection>
+        </refentry>
+    </sect2>
+
+    <sect2 id="zend.loader.autoloader-factory.examples">
+        <title>Examples</title>
+
+        <para>
+            Please see the <link linkend="zend.loader.autoloader-factory.quick-start">Quick
+                Start</link> for a detailed example.
+        </para>
+    </sect2>
+</sect1>

+ 353 - 0
documentation/manual/en/module_specs/Zend_Loader-ClassMapAutoloader.xml

@@ -0,0 +1,353 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Reviewed: no -->
+<sect1 id="zend.loader.class-map-autoloader">
+    <title>The ClassMapAutoloader</title>
+    
+    <sect2 id="zend.loader.class-map-autoloader.intro">
+        <title>Overview</title>
+
+        <para>
+            The <classname>ClassMapAutoloader</classname> is designed with performance in mind. The
+            idea behind it is simple: when asked to load a class, see if it's in the map, and, if
+            so, load the file associated with the class in the map. This avoids unnecessary
+            filesystem operations, and can also ensure the autoloader "plays nice" with opcode
+            caches and PHP's realpath cache.
+        </para>
+
+        <para>
+            In order to use the <classname>ClassMapAutoloader</classname>, you first need class
+            maps. Zend Framework ships with a class map per component or, if you grabbed the entire
+            ZF distribution, a class map for the entire Zend Framework. These maps are typically in
+            a file named <filename>.classmap.php</filename> within either the "Zend" directory, or
+            an individual component's source directory.
+        </para>
+
+        <para>
+            Zend Framework also provides a tool for generating these class maps; you can find it in
+            <filename>bin/classmap_generator.php</filename> of the distribution. Full documentation
+            of this too is provided in <xref linkend="zend.loader.classmap-generator"/>.
+        </para>
+    </sect2>
+
+    <sect2 id="zend.loader.class-map-autoloader.quick-start">
+        <title>Quick Start</title>
+
+        <para>
+            The first step is to generate a class map file. You may run this over any directory
+            containing source code anywhere underneath it.
+        </para>
+
+        <programlisting language="sh"><![CDATA[
+php classmap_generator.php Some/Directory/
+]]></programlisting>
+
+        <para>
+            This will create a file named <filename>Some/Directory/.classmap.php</filename>, which
+            is a PHP file returning an associative array that represents the class map.
+        </para>
+
+        <para>
+            Within your code, you will now instantiate the
+            <classname>ClassMapAutoloader</classname>, and provide it the location of the map.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+// This example assumes ZF is on your include_path.
+// You could also load the autoloader class from a path relative to the
+// current script, or via an absolute path.
+require_once 'Zend/Loader/ClassMapAutoloader.php';
+$loader = new Zend_Loader_ClassMapAutoloader();
+
+// Register the class map:
+$loader->registerAutoloadMap('Some/Directory/.classmap.php');
+
+// Register with spl_autoload:
+$loader->register();
+]]></programlisting>
+
+        <para>
+            At this point, you may now use any classes referenced in your class map.
+        </para>
+    </sect2>
+
+    <sect2 id="zend.loader.class-map-autoloader.options">
+        <title>Configuration Options</title>
+
+        <para>
+            The <classname>ClassMapAutoloader</classname> defines the following options.
+        </para>
+
+        <variablelist>
+            <title>ClassMapAutoloader Options</title>
+
+            <varlistentry>
+                <term>$options</term>
+
+                <listitem>
+                    <para>
+                        The <classname>ClassMapAutoloader</classname> expects an array of options,
+                        where each option is either a filename referencing a class map, or an
+                        associative array of class name/filename pairs.
+                    </para>
+
+                    <para>
+                        As an example:
+                    </para>
+
+                    <programlisting language="php"><![CDATA[
+// Configuration defining both a file-based class map, and an array map
+$config = array(
+    __DIR__ . '/library/.classmap.php', // file-based class map
+    array(                              // array class map
+        'Application_Bootstrap' => __DIR__ . '/application/Bootstrap.php',
+        'Test_Bootstrap'        => __DIR__ . '/tests/Bootstrap.php',
+    ),
+);
+]]></programlisting>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </sect2>
+
+    <sect2 id="zend.loader.class-map-autoloader.methods">
+        <title>Available Methods</title>
+
+        <refentry id="zend.loader.class-map-autoloader.methods.constructor">
+            <refnamediv>
+                <refname>__construct</refname>
+                <refpurpose>Initialize and configure the object</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>__construct</methodname>
+                    <methodparam>
+                        <funcparams>$options = null</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>Constructor</title>
+
+                <para>
+                    Used during instantiation of the object. Optionally, pass options, which may be
+                    either an array or <interfacename>Traversable</interfacename> object; this
+                    argument will be passed to <link linkend="zend.loader.class-map-autoloader.methods.set-options">setOptions()</link>.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.class-map-autoloader.methods.set-options">
+            <refnamediv>
+                <refname>setOptions</refname>
+                <refpurpose>Configure the autoloader</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>setOptions</methodname>
+                    <methodparam>
+                        <funcparams>$options</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>setOptions()</title>
+
+                <para>
+                    Configures the state of the autoloader, including registering class maps.
+                    Expects an array or <interfacename>Traversable</interfacename> object; the
+                    argument will be passed to <link linkend="zend.loader.class-map-autoloader.methods.register-autoloadmaps">registerAutoloadMaps()</link>.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.class-map-autoloader.methods.register-autoload-map">
+            <refnamediv>
+                <refname>registerAutoloadMap</refname>
+                <refpurpose>Register a class map</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>registerAutoloadMap</methodname>
+                    <methodparam>
+                        <funcparams>$map</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>registerAutoloadMap()</title>
+
+                <para>
+                    Registers a class map with the autoloader. <varname>$map</varname> may be either
+                    a string referencing a PHP script that returns a class map, or an array defining
+                    a class map. 
+                </para>
+
+                <para>
+                    More than one class map may be registered; each will be merged with the
+                    previous, meaning it's possible for a later class map to overwrite entries from
+                    a previously registered map.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.class-map-autoloader.methods.register-autoload-maps">
+            <refnamediv>
+                <refname>registerAutoloadMaps</refname>
+                <refpurpose>Register multiple class maps at once</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>registerAutoloadMaps</methodname>
+                    <methodparam>
+                        <funcparams>$maps</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>registerAutoloadMaps()</title>
+
+                <para>
+                    Register multiple class maps with the autoloader. Expects either an array or
+                    <interfacename>Traversable</interfacename> object; it then iterates over the
+                    argument and passes each value to <link linkend="zend.loader.class-map-autoloader.methods.register-autoload-map">registerAutoloadMap()</link>.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.get-autoload-map">
+            <refnamediv>
+                <refname>getAutoloadMap</refname>
+                <refpurpose>Retrieve the current class map</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>getAutoloadMap</methodname>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>getAutoloadMap()</title>
+
+                <para>
+                    Retrieves the state of the current class map; the return value is simply an
+                    array.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.autoload">
+            <refnamediv>
+                <refname>autoload</refname>
+                <refpurpose>Attempt to load a class.</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>autoload</methodname>
+                    <methodparam>
+                        <funcparams>$class</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>autoload()</title>
+
+                <para>
+                    Attempts to load the class specified. Returns a boolean
+                    <constant>false</constant> on failure, or a string indicating the class loaded
+                    on success.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.register">
+            <refnamediv>
+                <refname>register</refname>
+                <refpurpose>Register with spl_autoload.</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>register</methodname>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>register()</title>
+
+                <para>
+                    Registers the <methodname>autoload()</methodname> method of the current instance
+                    with <function>spl_autoload_register()</function>.
+                </para>
+            </refsection>
+        </refentry>
+    </sect2>
+
+    <sect2 id="zend.loader.class-map-autoloader.examples">
+        <title>Examples</title>
+
+        <example id="zend.loader.class-map-autoloader.examples.configuration">
+            <title>Using configuration to seed ClassMapAutoloader</title>
+
+            <para>
+                Often, you will want to configure your <classname>ClassMapAutoloader</classname>.
+                These values may come from a configuration file, a cache (such as ShMem or
+                memcached), or a simple PHP array. The following is an example of a PHP array that
+                could be used to configure the autoloader:
+            </para>
+
+            <programlisting language="php"><![CDATA[
+// Configuration defining both a file-based class map, and an array map
+$config = array(
+APPLICATION_PATH . '/../library/.classmap.php', // file-based class map
+    array(                              // array class map
+        'Application_Bootstrap' => APPLICATION_PATH . '/Bootstrap.php',
+        'Test_Bootstrap'        => APPLICATION_PATH . '/../tests/Bootstrap.php',
+    ),
+);
+]]></programlisting>
+            
+            <para>
+                An eqivalent INI style configuration might look like this:
+            </para>
+
+            <programlisting language="ini"><![CDATA[
+classmap.library = APPLICATION_PATH "/../library/.classmap.php"
+classmap.resources.Application_Bootstrap = APPLICATION_PATH "/Bootstrap.php"
+classmap.resources.Test_Bootstrap = APPLICATION_PATH "/../tests/Bootstrap.php"
+]]></programlisting>
+
+            <para>
+                Once you have your configuration, you can pass it either to the constructor of the
+                <classname>ClassMapAutoloader</classname>, to its
+                <methodname>setOptions()</methodname> method, or to
+                <methodname>registerAutoloadMaps()</methodname>.
+            </para>
+
+            <programlisting language="php"><![CDATA[
+/* The following are all equivalent */
+
+// To the constructor:
+$loader = new Zend_Loader_ClassMapAutoloader($config);
+
+// To setOptions():
+$loader = new Zend_Loader_ClassMapAutoloader();
+$loader->setOptions($config);
+
+// To registerAutoloadMaps():
+$loader = new Zend_Loader_ClassMapAutoloader();
+$loader->registerAutoloadMaps($config);
+]]></programlisting>
+        </example>
+    </sect2>
+</sect1>

+ 91 - 0
documentation/manual/en/module_specs/Zend_Loader-Classmap_Generator.xml

@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Reviewed: no -->
+<sect1 id="zend.loader.classmap-generator">
+    <title>The Class Map Generator utility: bin/classmap_generator.php</title>
+
+    <sect2 id="zend.loader.classmap-generator.intro">
+        <title>Overview</title>
+
+        <para>
+            The script <filename>bin/classmap_generator.php</filename> can be used to generate class
+            map files for use with <link linkend="zend.loader.class-map-autoloader">the
+                ClassMapAutoloader</link>.
+        </para>
+
+        <para>
+            Internally, it consumes both <link linkend="zend.console.getopt">Zend_Console_Getopt</link> (for parsing command-line
+            options) and <link linkend="zend.file.class-file-locater">Zend_File_ClassFileLocater</link> for
+            recursively finding all PHP class files in a given tree.
+        </para>
+    </sect2>
+
+    <sect2 id="zend.loader.classmap-generator.quick-start">
+        <title>Quick Start</title>
+
+        <para>
+            You may run the script over any directory containing source code. By default, it will
+            look in the current directory, and will write the script to
+            <filename>.classmap.php</filename> in the directory you specify.
+        </para>
+
+        <programlisting language="sh"><![CDATA[
+php classmap_generator.php Some/Directory/
+]]></programlisting>
+
+    </sect2>
+
+    <sect2 id="zend.loader.classmap-generator.options">
+        <title>Configuration Options</title>
+
+        <variablelist>
+            <title>Class Map Generator Options</title>
+
+            <varlistentry>
+                <term>--help or -h</term>
+
+                <listitem>
+                    <para>
+                        Returns the usage message. If any other options are provided, they will be
+                        ignored.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>--library or -l</term>
+
+                <listitem>
+                    <para>
+                        Expects a single argument, a string specifying the library directory to
+                        parse. If this option is not specified, it will assume the current working
+                        directory.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>--output or -o</term>
+
+                <listitem>
+                    <para>
+                        Where to write the autoload class map file. If not provided, assumes
+                        ".classmap.php" in the library directory.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>--overwrite or -w</term>
+
+                <listitem>
+                    <para>
+                        If an autoload class map file already exists with the name as specified via
+                        the <varname>--output</varname> option, you can overwrite it by specifying
+                        this flag. Otherwise, the script will not write the class map and return a
+                        warning.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </sect2>
+</sect1>

+ 213 - 0
documentation/manual/en/module_specs/Zend_Loader-SplAutoloader.xml

@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Reviewed: no -->
+<sect1 id="zend.loader.spl-autoloader">
+    <title>The SplAutoloader Interface</title>
+
+    <sect2 id="zend.loader.spl-autoloader.intro">
+        <title>Overview</title>
+
+        <para>
+            While any valid PHP callback may be registered with
+            <function>spl_autoload_register()</function>, Zend Framework autoloaders often provide
+            more flexibility by being stateful and allowing configuration. To provide a common
+            interface, Zend Framework provides the <interfacename>SplAutoloader</interfacename>
+            interface.
+        </para>
+
+        <para>
+            Objects implementing this interface provide a standard mechanism for configuration, a
+            method that may be invoked to attempt to load a class, and a method for registering with
+            the SPL autoloading mechanism.
+        </para>
+    </sect2>
+
+    <sect2 id="zend.loader.spl-autoloader.quick-start">
+        <title>Quick Start</title>
+
+        <para>
+            To create your own autoloading mechanism, simply create a class implementing the
+            <interfacename>SplAutoloader</interfacename> interface (you may review the methods
+            defined in the <link linkend="zend.loader.spl-autoloader.methods">Methods
+            section</link>). As a simple example, consider the following autoloader, which will look
+            for a class file named after the class within a list of registered directories.
+        </para>
+
+        <programlisting language="php"><![CDATA[
+require_once 'Zend/Loader/SplAutoloader.php';
+
+class Custom_ModifiedIncludePathAutoloader implements Zend_Loader_SplAutoloader
+{
+    protected $paths = array();
+
+    public function __construct($options = null)
+    {
+        if (null !== $options) {
+            $this->setOptions($options);
+        }
+    }
+
+    public function setOptions($options)
+    {
+        if (!is_array($options) && !($options instanceof Traversable)) {
+            throw new InvalidArgumentException();
+        }
+
+        foreach ($options as $path) {
+            if (!in_array($path, $this->paths)) {
+                $this->paths[] = $path;
+            }
+        }
+        return $this;
+    }
+
+    public function autoload($classname)
+    {
+        $filename = $classname . '.php';
+        foreach ($this->paths as $path) {
+            $test = $path . DIRECTORY_SEPARATOR . $filename;
+            if (file_exists($test)) {
+                return include($test);
+            }
+        }
+        return false;
+    }
+
+    public function register()
+    {
+        spl_autoload_register(array($this, 'autoload'));
+    }
+}
+]]></programlisting>
+    </sect2>
+
+    <sect2 id="zend.loader.spl-autoloader.options">
+        <title>Configuration Options</title>
+
+        <para>
+            This component defines no configuration options, as it is an interface.
+        </para>
+    </sect2>
+
+    <sect2 id="zend.loader.spl-autoloader.methods">
+        <title>Available Methods</title>
+
+        <refentry id="zend.loader.spl-autoloader.methods.constructor">
+            <refnamediv>
+                <refname>__construct</refname>
+                <refpurpose>Initialize and configure an autoloader</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>__construct</methodname>
+                    <methodparam>
+                        <funcparams>$options = null</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>Constructor</title>
+
+                <para>
+                    Autoloader constructors should optionally receive configuration options.
+                    Typically, if received, these will be passed to the
+                    <methodname>setOptions()</methodname> method to process.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.spl-autoloader.methods.set-options">
+            <refnamediv>
+                <refname>setOptions</refname>
+                <refpurpose>Configure the autoloader state</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>setOptions</methodname>
+                    <methodparam>
+                        <funcparams>$options</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>setOptions()</title>
+
+                <para>
+                    Used to configure the autoloader. Typically, it should expect either an array or
+                    a <interfacename>Traversable</interfacename> object, though validation of the
+                    options is left to implementation. Additionally, it is recommended that the
+                    method return the autoloader instance in order to implement a fluent interface.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.spl-autoloader.methods.autoload">
+            <refnamediv>
+                <refname>autoload</refname>
+                <refpurpose>Attempt to resolve a class name to the file defining it</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>autoload</methodname>
+                    <methodparam>
+                        <funcparams>$classname</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>autoload()</title>
+
+                <para>
+                    This method should be used to resolve a class name to the file defining it. When
+                    a positive match is found, return the class name; otherwise, return a boolean
+                    false.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.spl-autoloader.methods.register">
+            <refnamediv>
+                <refname>register</refname>
+                <refpurpose>Register the autoloader with the SPL autoloader</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>register</methodname>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>register()</title>
+
+                <para>
+                    Should be used to register the autoloader instance with
+                    <function>spl_autoload_register()</function>. Invariably, the method
+                    should look like the following:
+                </para>
+
+                <programlisting language="php"><![CDATA[
+public function register()
+{
+    spl_autoload_register(array($this, 'autoload'));
+}
+]]></programlisting>
+            </refsection>
+        </refentry>
+    </sect2>
+
+    <sect2 id="zend.loader.spl-autoloader.examples">
+        <title>Examples</title>
+        
+
+        <para>
+            Please see the <link linkend="zend.loader.spl-autoloader.quick-start">Quick Start</link>
+            for a complete example.
+        </para>
+    </sect2>
+</sect1>

+ 520 - 0
documentation/manual/en/module_specs/Zend_Loader-StandardAutoloader.xml

@@ -0,0 +1,520 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Reviewed: no -->
+<sect1 id="zend.loader.standard-autoloader">
+    <title>The StandardAutoloader</title>
+
+    <sect2 id="zend.loader.standard-autoloader.intro">
+        <title>Overview</title>
+
+        <para>
+            <classname>Zend_Loader_StandardAutoloader</classname> is designed as a <ulink
+                url="http://groups.google.com/group/php-standards/web/psr-0-final-proposal">PSR-0</ulink>-compliant
+            autoloader. It assumes a 1:1 mapping of the namespace+classname to the filesystem,
+            wherein namespace separators and underscores are translated to directory separators. A
+            simple statement that illustrates how resolution works is as follows:
+        </para>
+
+        <programlisting language="php"><![CDATA[
+$filename = str_replace(array('_', '\\'), DIRECTORY_SEPARATOR, $classname) 
+          . '.php';
+]]></programlisting>
+
+        <para>
+            Previous incarnations of PSR-0-compliant autoloaders in Zend Framework have relied upon
+            the <varname>include_path</varname> for file lookups. This has led to a number of
+            issues:
+        </para>
+
+        <itemizedlist>
+            <listitem>
+                <para>
+                    Due to the use of <function>include</function>, if the file is not
+                    found, a warning is raised -- even if another autoloader is capable of resolving
+                    the class later.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    Documenting how to setup the <varname>include_path</varname> has proven to be
+                    a difficult concept to convey.
+                </para>
+            </listitem>
+
+            <listitem>
+                <para>
+                    If multiple Zend Framework installations exist on the
+                    <varname>include_path</varname>, the first one on the path wins -- even if that
+                    was not the one the developer intended.
+                </para>
+            </listitem>
+        </itemizedlist>
+
+        <para>
+            To solve these problems, the <classname>StandardAutoloader</classname> by default
+            requires that you explicitly register namespace/path pairs (or vendor prefix/path
+            pairs), and will only load a file if it exists within the given path. Multiple pairs may
+            be provided.
+        </para>
+
+        <para>
+            As a measure of last resort, you may also use the
+            <classname>StandardAutoloader</classname> as a "fallback" autoloader -- one that will
+            look for classes of any namespace or vendor prefix on the
+            <classname>include_path</classname>. This practice is not recommended, however, due to
+            performance implications.
+        </para>
+
+        <para>
+            Finally, as with all autoloaders in Zend Framework, the
+            <classname>StandardAutoloader</classname> is capable of registering itself with PHP's
+            SPL autoloader registry.
+        </para>
+
+        <note>
+            <title>Vocabulary: Namespaces vs. Vendor Prefixes</title>
+            
+
+            <para>
+                In terms of autloading, a "namespace" corresponds to PHP's own definition of
+                namespaces in PHP versions 5.3 and above.
+            </para>
+
+            <para>
+                A "vendor prefix" refers to the practice, popularized in PHP versions prior to 5.3,
+                of providing a pseudo-namespace in the form of underscore-separated words in class
+                names. As an example, the class <classname>Phly_Couch_Document</classname> uses a
+                vendor prefix of "Phly", and a component prefix of "Phly_Couch" -- but it is a class
+                sitting in the global namespace within PHP 5.3.
+            </para>
+
+            <para>
+                The <classname>StandardAutoloader</classname> is capable of loading either
+                namespaced or vendor prefixed class names, but treats them separately when
+                attempting to match them to an appropriate path.
+            </para>
+        </note>
+    </sect2>
+
+    <sect2 id="zend.loader.standard-autoloader.quick-start">
+        <title>Quick Start</title>
+
+        <para>
+            Basic use of the <classname>StandardAutoloader</classname> requires simply registering
+            namespace/path pairs. This can either be done at instantiation, or via explicit method
+            calls after the object has been initialized. Calling <methodname>register()</methodname>
+            will register the autoloader with the SPL autoloader registry.
+        </para>
+
+        <para>
+            By default, the class will register the "Zend" namespace to the directory above where
+            its own classfile is located on the filesystem.
+        </para>
+
+        <example id="zend.loader.standard-autoloader.quick-start.example-manual-configuration">
+            <title>Manual Configuration</title>
+            
+            <programlisting language="php"><![CDATA[
+// This example assumes ZF is on your include_path.
+// You could also load the autoloader class from a path relative to the
+// current script, or via an absolute path.
+require_once 'Zend/Loader/StandardAutoloader.php';
+$loader = new Zend_Loader_StandardAutoloader();
+
+// Register the "Phly" namespace:
+$loader->registerNamespace('Phly', APPLICATION_PATH . '/../library/Phly');
+
+// Register the "Scapi" vendor prefix:
+$loader->registerPrefix('Scapi', APPLICATION_PATH . '/../library/Scapi');
+
+// Optionally, specify the autoloader as a "fallback" autoloader;
+// this is not recommended.
+$loader->setFallbackAutoloader(true);
+
+// Register with spl_autoload:
+$loader->register();
+]]></programlisting>
+        </example>
+
+        <example id="zend.loader.standard-autoloader.quick-start.example-constructor-configuration">
+            <title>Configuration at Instantiation</title>
+
+            <para>
+                The <classname>StandardAutoloader</classname> may also be configured at
+                instantiation. Please note:
+            </para>
+
+            <itemizedlist>
+                <listitem>
+                    <para>
+                        The argument passed may be either an array or a
+                        <interface>Traversable</interface> object (such as a
+                        <classname>Zend_Config</classname> object.
+                    </para>
+                </listitem>
+
+                <listitem>
+                    <para>
+                        The argument passed is also a valid argument for passing to the
+                        <methodname>setOptions()</methodname> method.
+                    </para>
+                </listitem>
+            </itemizedlist>
+
+            <para>
+                The following is equivalent to the previous example.
+            </para>
+
+            <programlisting language="php"><![CDATA[
+require_once 'Zend/Loader/StandardAutoloader.php';
+$loader = new Zend_Loader_StandardAutoloader(array(
+    'namespaces' => array(
+        'Phly' => APPLICATION_PATH . '/../library/Phly',
+    ),
+    'prefixes' => array(
+        'Scapi' => APPLICATION_PATH . '/../library/Scapi',
+    ),
+    'fallback_autoloader' => true,
+));
+
+// Register with spl_autoload:
+$loader->register();
+]]></programlisting>
+        </example>
+    </sect2>
+
+    <sect2 id="zend.loader.standard-autoloader.options">
+        <title>Configuration Options</title>
+        
+        <para>
+            The <classname>StandardAutoloader</classname> defines the following options.
+        </para>
+
+        <variablelist>
+            <title>StandardAutoloader Options</title>
+            
+
+            <varlistentry>
+                <term>namespaces</term>
+
+                <listitem>
+                    <para>
+                        An associative array of namespace/path pairs. The path should be an absolute
+                        path or path relative to the calling script, and contain only classes that
+                        live in that namespace (or its subnamespaces). By default, the "Zend"
+                        namespace is registered, pointing to the arent directory of the file
+                        defining the <classname>StandardAutoloader</classname>.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>prefixes</term>
+
+                <listitem>
+                    <para>
+                        An associative array of vendor prefix/path pairs. The path should be an absolute
+                        path or path relative to the calling script, and contain only classes that
+                        begin with the provided vendor prefix.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
+                <term>fallback_autoloader</term>
+
+                <listitem>
+                    <para>
+                        A boolean value indicating whether or not this instance should act as a
+                        "fallback" autoloader (i.e., look for classes of any namespace or vendor
+                        prefix on the <varname>include_path</varname>). By default,
+                        <constant>false</constant>.
+                    </para>
+                </listitem>
+            </varlistentry>
+        </variablelist>
+    </sect2>
+
+    <sect2 id="zend.loader.standard-autoloader.methods">
+        <title>Available Methods</title>
+
+        <refentry id="zend.loader.standard-autoloader.methods.constructor">
+            <refnamediv>
+                <refname>__construct</refname>
+                <refpurpose>Initialize a new instance of the object</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>__construct</methodname>
+                    <methodparam>
+                        <funcparams>$options = null</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>Constructor</title>
+
+                <para>
+                    Takes an optional <varname>$options</varname> argument. This argument may be an
+                    associative array or <interfacename>Traversable</interfacename> object. If not
+                    null, the argument is passed to <link linkend="zend.loader.standard-autoloader.methods.set-options"><methodname>setOptions()</methodname></link>.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.set-options">
+            <refnamediv>
+                <refname>setOptions</refname>
+                <refpurpose>Set object state based on provided options.</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>setOptions</methodname>
+                    <methodparam>
+                        <funcparams>$options</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>setOptions()</title>
+
+                <para>
+                    Takes an argument of either an associative array or
+                    <interfacename>Traversable</interfacename> object. Recognized keys are detailed
+                    under <xref linkend="zend.loader.standard-autoloader.options"/>, with the
+                    following behaviors:
+                </para>
+
+                <itemizedlist>
+                    <listitem>
+                        <para>
+                            The <varname>namespaces</varname> value will be passed to <link linkend="zend.loader.standard-autoloader.methods.register-namespaces">registerNamespaces()</link>.
+                        </para>
+                    </listitem>
+
+                    <listitem>
+                        <para>
+                            The <varname>prefixes</varname> value will be passed to <link linkend="zend.loader.standard-autoloader.methods.register-prefixes">registerPrefixes()</link>.
+                        </para>
+                    </listitem>
+
+                    <listitem>
+                        <para>
+                            The <varname>fallback_autoloader</varname> value will be passed to <link linkend="zend.loader.standard-autoloader.methods.set-fallback-autoloader">setFallbackAutoloader()</link>.
+                        </para>
+                    </listitem>
+                </itemizedlist>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.set-fallback-autoloader">
+            <refnamediv>
+                <refname>setFallbackAutoloader</refname>
+                <refpurpose>Enable/disable fallback autoloader status</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>setFallbackAutoloader</methodname>
+                    <methodparam>
+                        <funcparams>$flag</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>setFallbackAutoloader()</title>
+                
+                <para>
+                    Takes a boolean flag indicating whether or not to act as a fallback autoloader
+                    when registered with the SPL autoloader.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.is-fallback-autoloader">
+            <refnamediv>
+                <refname>isFallbackAutoloader</refname>
+                <refpurpose>Query fallback autoloader status</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>isFallbackAutoloader</methodname>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>isFallbackAutoloader()</title>
+
+                <para>
+                    Indicates whether or not this instance is flagged as a fallback autoloader.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.register-namespace">
+            <refnamediv>
+                <refname>registerNamespace</refname>
+                <refpurpose>Register a namespace with the autoloader</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>registerNamespace</methodname>
+                    <methodparam>
+                        <funcparams>$namespace, $directory</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>registerNamespace()</title>
+
+                <para>
+                    Register a namespace with the autoloader, pointing it to a specific directory on
+                    the filesystem for class resolution. For classes matching that initial
+                    namespace, the autoloader will then perform lookups within that directory.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.register-namespaces">
+            <refnamediv>
+                <refname>registerNamespaces</refname>
+                <refpurpose>Register multiple namespaces with the autoloader</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>registerNamespaces</methodname>
+                    <methodparam>
+                        <funcparams>$namespaces</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>registerNamespaces()</title>
+
+                <para>
+                    Accepts either an array or <interfacename>Traversable</interfacename> object. It
+                    will then iterate through the argument, and pass each item to <link linkend="zend.loader.standard-autoloader.methods.register-namespace">registerNamespace()</link>.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.register-prefix">
+            <refnamediv>
+                <refname>registerPrefix</refname>
+                <refpurpose>Register a vendor prefix with the autoloader.</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>registerPrefix</methodname>
+                    <methodparam>
+                        <funcparams>$prefix, $directory</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>registerPrefix()</title>
+
+                <para>
+                    Register a vendor prefix with the autoloader, pointing it to a specific
+                    directory on the filesystem for class resolution. For classes matching that
+                    initial vendor prefix, the autoloader will then perform lookups within that
+                    directory.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.register-prefixes">
+            <refnamediv>
+                <refname>registerPrefixes</refname>
+                <refpurpose>Register many vendor prefixes with the autoloader</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>registerPrefixes</methodname>
+                    <methodparam>
+                        <funcparams>$prefixes</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>registerPrefixes()</title>
+
+                <para>
+                    Accepts either an array or <interfacename>Traversable</interfacename> object. It
+                    will then iterate through the argument, and pass each item to <link linkend="zend.loader.standard-autoloader.methods.register-prefix">registerPrefix()</link>.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.autoload">
+            <refnamediv>
+                <refname>autoload</refname>
+                <refpurpose>Attempt to load a class.</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>autoload</methodname>
+                    <methodparam>
+                        <funcparams>$class</funcparams>
+                    </methodparam>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>autoload()</title>
+
+                <para>
+                    Attempts to load the class specified. Returns a boolean
+                    <constant>false</constant> on failure, or a string indicating the class loaded
+                    on success.
+                </para>
+            </refsection>
+        </refentry>
+
+        <refentry id="zend.loader.standard-autoloader.methods.register">
+            <refnamediv>
+                <refname>register</refname>
+                <refpurpose>Register with spl_autoload.</refpurpose>
+            </refnamediv>
+
+            <refsynopsisdiv>
+                <methodsynopsis>
+                    <methodname>register</methodname>
+                </methodsynopsis>
+            </refsynopsisdiv>
+
+            <refsection>
+                <title>register()</title>
+
+                <para>
+                    Registers the <methodname>autoload()</methodname> method of the current instance
+                    with <function>spl_autoload_register()</function>.
+                </para>
+            </refsection>
+        </refentry>
+    </sect2>
+
+    <sect2 id="zend.loader.standard-autoloader.examples">
+        <title>Examples</title>
+
+        <para>
+            Please review the <link linkend="zend.loader.standard-autoloader.quick-start">examples
+                in the quick start</link> for usage.
+        </para>
+    </sect2>
+</sect1>

+ 174 - 0
library/Zend/File/ClassFileLocator.php

@@ -0,0 +1,174 @@
+<?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_File
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * Locate files containing PHP classes, interfaces, or abstracts
+ * 
+ * @package    Zend_File
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    New BSD {@link http://framework.zend.com/license/new-bsd}
+ */
+class Zend_File_ClassFileLocator extends FilterIterator
+{
+    /**
+     * Create an instance of the locator iterator
+     * 
+     * Expects either a directory, or a DirectoryIterator (or its recursive variant) 
+     * instance.
+     * 
+     * @param  string|DirectoryIterator $dirOrIterator 
+     * @return void
+     */
+    public function __construct($dirOrIterator = '.')
+    {
+        if (is_string($dirOrIterator)) {
+            if (!is_dir($dirOrIterator)) {
+                throw new InvalidArgumentException('Expected a valid directory name');
+            }
+
+            $dirOrIterator = new RecursiveDirectoryIterator($dirOrIterator);
+        }
+        if (!$dirOrIterator instanceof DirectoryIterator) {
+            throw new InvalidArgumentException('Expected a DirectoryIterator');
+        }
+
+        if ($dirOrIterator instanceof RecursiveIterator) {
+            $iterator = new RecursiveIteratorIterator($dirOrIterator);
+        } else {
+            $iterator = $dirOrIterator;
+        }
+
+        parent::__construct($iterator);
+        $this->rewind();
+
+        // Forward-compat with PHP 5.3
+        if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+            if (!defined('T_NAMESPACE')) {
+                define('T_NAMESPACE', 'namespace');
+            }
+            if (!defined('T_NS_SEPARATOR:')) {
+                define('T_NS_SEPARATOR', '\\');
+            }
+        }
+    }
+
+    /**
+     * Filter for files containing PHP classes, interfaces, or abstracts
+     * 
+     * @return bool
+     */
+    public function accept()
+    {
+        $file = $this->getInnerIterator()->current();
+
+        // If we somehow have something other than an SplFileInfo object, just 
+        // return false
+        if (!$file instanceof SplFileInfo) {
+            return false;
+        }
+
+        // If we have a directory, it's not a file, so return false
+        if (!$file->isFile()) {
+            return false;
+        }
+
+        // If not a PHP file, skip
+        if ($file->getBasename('.php') == $file->getBasename()) {
+            return false;
+        }
+
+        $contents = file_get_contents($file->getRealPath());
+        $tokens   = token_get_all($contents);
+        $count    = count($tokens);
+        $i        = 0;
+        while ($i < $count) {
+            $token = $tokens[$i];
+
+            if (!is_array($token)) {
+                // single character token found; skip
+                $i++;
+                continue;
+            }
+
+            list($id, $content, $line) = $token;
+
+            switch ($id) {
+                case T_NAMESPACE:
+                    // Namespace found; grab it for later
+                    $namespace = '';
+                    $done      = false;
+                    do {
+                        ++$i;
+                        $token = $tokens[$i];
+                        if (is_string($token)) {
+                            if (';' === $token) {
+                                $done = true;
+                            }
+                            continue;
+                        }
+                        list($type, $content, $line) = $token;
+                        switch ($type) {
+                            case T_STRING:
+                            case T_NS_SEPARATOR:
+                                $namespace .= $content;
+                                break;
+                        }
+                    } while (!$done && $i < $count);
+
+                    // Set the namespace of this file in the object
+                    $file->namespace = $namespace;
+                    break;
+                case T_CLASS:
+                case T_INTERFACE:
+                    // Abstract class, class, or interface found
+
+                    // Get the classname
+                    $class = '';
+                    do {
+                        ++$i;
+                        $token = $tokens[$i];
+                        if (is_string($token)) {
+                            continue;
+                        }
+                        list($type, $content, $line) = $token;
+                        switch ($type) {
+                            case T_STRING:
+                                $class = $content;
+                                break;
+                        }
+                    } while (empty($class) && $i < $count);
+
+                    // If a classname was found, set it in the object, and 
+                    // return boolean true (found)
+                    if (!empty($class)) {
+                        $file->classname = $class;
+                        return true;
+                    }
+                    break;
+                default:
+                    break;
+            }
+            ++$i;
+        }
+
+        // No class-type tokens found; return false
+        return false;
+    }
+}

+ 211 - 0
library/Zend/Loader/AutoloaderFactory.php

@@ -0,0 +1,211 @@
+<?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-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+require_once dirname(__FILE__) . '/SplAutoloader.php';
+
+if (class_exists('Zend_Loader_AutoloaderFactory')) return;
+
+/**
+ * @package    Zend_Loader
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+abstract class Zend_Loader_AutoloaderFactory
+{
+    const STANDARD_AUTOLOADER = 'Zend_Loader_StandardAutoloader';
+
+    /**
+     * @var array All autoloaders registered using the factory
+     */
+    protected static $loaders = array();
+
+    /**
+     * @var Zend_Loader_StandardAutoloader StandardAutoloader instance for resolving 
+     * autoloader classes via the include_path
+     */
+    protected static $standardAutoloader;
+
+    /**
+     * Factory for autoloaders
+     *
+     * Options should be an array or Traversable object of the following structure:
+     * <code>
+     * array(
+     *     '<autoloader class name>' => $autoloaderOptions,
+     * )
+     * </code>
+     *
+     * The factory will then loop through and instantiate each autoloader with
+     * the specified options, and register each with the spl_autoloader.
+     *
+     * You may retrieve the concrete autoloader instances later using
+     * {@link getRegisteredAutoloaders()}.
+     *
+     * Note that the class names must be resolvable on the include_path or via
+     * the Zend library, using PSR-0 rules (unless the class has already been
+     * loaded).
+     *
+     * @param  array|Traversable $options (optional) options to use. Defaults to Zend_Loader_StandardAutoloader
+     * @return void
+     * @throws Zend_Loader_Exception_InvalidArgumentException for invalid options
+     * @throws Zend_Loader_Exception_InvalidArgumentException for unloadable autoloader classes
+     */
+    public static function factory($options = null)
+    {
+        if (null === $options) {
+            if (!isset(self::$loaders[self::STANDARD_AUTOLOADER])) {
+                $autoloader = self::getStandardAutoloader();
+                $autoloader->register();
+                self::$loaders[self::STANDARD_AUTOLOADER] = $autoloader;
+            }
+
+            // Return so we don't hit the next check's exception (we're done here anyway)
+            return;
+        }
+
+        if (!is_array($options) && !($options instanceof Traversable)) {
+            require_once 'Exception/InvalidArgumentException.php';
+            throw new Zend_Loader_Exception_InvalidArgumentException(
+                'Options provided must be an array or Traversable'
+            );
+        }
+
+        foreach ($options as $class => $options) {
+            if (!isset(self::$loaders[$class])) {
+                $autoloader = self::getStandardAutoloader();
+                if (!class_exists($class) && !$autoloader->autoload($class)) {
+                    require_once 'Exception/InvalidArgumentException.php';
+                    throw new Zend_Loader_Exception_InvalidArgumentException(sprintf(
+                        'Autoloader class "%s" not loaded', 
+                        $class
+                    ));
+                }
+
+                // unfortunately is_subclass_of is broken on some 5.3 versions
+                // additionally instanceof is also broken for this use case
+                if (version_compare(PHP_VERSION, '5.3.7', '>=')) {
+                        if (!is_subclass_of($class, 'Zend_Loader_SplAutoloader')) {
+                        require_once 'Exception/InvalidArgumentException.php';
+                        throw new Zend_Loader_Exception_InvalidArgumentException(sprintf(
+                            'Autoloader class %s must implement Zend\\Loader\\SplAutoloader', 
+                            $class
+                        ));
+                    }
+                }
+
+                if ($class === self::STANDARD_AUTOLOADER) {
+                    $autoloader->setOptions($options);
+                } else {
+                    $autoloader = new $class($options);
+                }
+                $autoloader->register();
+                self::$loaders[$class] = $autoloader;
+            } else {
+                self::$loaders[$class]->setOptions($options);
+            }
+        }
+    }
+
+    /**
+     * Get an list of all autoloaders registered with the factory
+     *
+     * Returns an array of autoloader instances.
+     *
+     * @return array
+     */
+    public static function getRegisteredAutoloaders()
+    {
+        return static::$loaders;
+    }
+
+    /**
+     * Retrieves an autoloader by class name
+     *
+     * @param string $class
+     * @return Zend_Loader_SplAutoloader
+     * @throws Zend_Loader_Exception_InvalidArgumentException for non-registered class
+     */
+    public static function getRegisteredAutoloader($class)
+    {
+        if (!isset(self::$loaders[$class])) {
+            require_once 'Exception/InvalidArgumentException.php';
+            throw new Zend_Loader_Exception_InvalidArgumentException(sprintf('Autoloader class "%s" not loaded', $class));
+        }
+        return self::$loaders[$class];
+    }
+
+    /**
+     * Unregisters all autoloaders that have been registered via the factory.
+     * This will NOT unregister autoloaders registered outside of the fctory.
+     *
+     * @return void
+     */
+    public static function unregisterAutoloaders()
+    {
+        foreach (self::getRegisteredAutoloaders() as $class => $autoloader) {
+            spl_autoload_unregister(array($autoloader, 'autoload'));
+            unset(self::$loaders[$class]);
+        }
+    }
+
+    /**
+     * Unregister a single autoloader by class name
+     *
+     * @param  string $autoloaderClass
+     * @return bool
+     */
+    public static function unregisterAutoloader($autoloaderClass)
+    {
+        if (!isset(self::$loaders[$autoloaderClass])) {
+            return false;
+        }
+
+        $autoloader = self::$loaders[$autoloaderClass];
+        spl_autoload_unregister(array($autoloader, 'autoload'));
+        unset(self::$loaders[$autoloaderClass]);
+        return true;
+    }
+
+    /**
+     * Get an instance of the standard autoloader
+     *
+     * Used to attempt to resolve autoloader classes, using the 
+     * StandardAutoloader. The instance is marked as a fallback autoloader, to 
+     * allow resolving autoloaders not under the "Zend" or "Zend" namespaces.
+     * 
+     * @return Zend_Loader_SplAutoloader
+     */
+    protected static function getStandardAutoloader()
+    {
+        if (null !== self::$standardAutoloader) {
+            return self::$standardAutoloader;
+        }
+
+        // Extract the filename from the classname
+        $stdAutoloader = substr(strrchr(self::STANDARD_AUTOLOADER, '_'), 1);
+
+        if (!class_exists(self::STANDARD_AUTOLOADER)) {
+            require_once dirname(__FILE__) . "/$stdAutoloader.php";
+        }
+        $loader = new Zend_Loader_StandardAutoloader();
+        self::$standardAutoloader = $loader;
+        return self::$standardAutoloader;
+    }
+}

+ 225 - 0
library/Zend/Loader/ClassMapAutoloader.php

@@ -0,0 +1,225 @@
+<?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-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+// Grab SplAutoloader interface
+require_once dirname(__FILE__) . '/SplAutoloader.php';
+
+/**
+ * Class-map autoloader
+ *
+ * Utilizes class-map files to lookup classfile locations.
+ * 
+ * @package    Zend_Loader
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    New BSD {@link http://framework.zend.com/license/new-bsd}
+ */
+class Zend_Loader_ClassMapAutoloader implements Zend_Loader_SplAutoloader
+{
+    /**
+     * Registry of map files that have already been loaded
+     * @var array
+     */
+    protected $mapsLoaded = array();
+
+    /**
+     * Class name/filename map
+     * @var array
+     */
+    protected $map = array();
+
+    /**
+     * Constructor
+     *
+     * Create a new instance, and optionally configure the autoloader.
+     * 
+     * @param  null|array|Traversable $options 
+     * @return void
+     */
+    public function __construct($options = null)
+    {
+        if (null !== $options) {
+            $this->setOptions($options);
+        }
+    }
+
+    /**
+     * Configure the autoloader
+     *
+     * Proxies to {@link registerAutoloadMaps()}.
+     * 
+     * @param  array|Traversable $options 
+     * @return Zend_Loader_ClassMapAutoloader
+     */
+    public function setOptions($options)
+    {
+        $this->registerAutoloadMaps($options);
+        return $this;
+    }
+
+    /**
+     * Register an autoload map
+     *
+     * An autoload map may be either an associative array, or a file returning
+     * an associative array.
+     *
+     * An autoload map should be an associative array containing 
+     * classname/file pairs.
+     * 
+     * @param  string|array $location 
+     * @return Zend_Loader_ClassMapAutoloader
+     */
+    public function registerAutoloadMap($map)
+    {
+        if (is_string($map)) {
+            $location = $map;
+            if ($this === ($map = $this->loadMapFromFile($location))) {
+                return $this;
+            }
+        }
+
+        if (!is_array($map)) {
+            require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
+            throw new Zend_Loader_Exception_InvalidArgumentException('Map file provided does not return a map');
+        }
+
+        $this->map = array_merge($this->map, $map);
+
+        if (isset($location)) {
+            $this->mapsLoaded[] = $location;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Register many autoload maps at once
+     * 
+     * @param  array $locations 
+     * @return Zend_Loader_ClassMapAutoloader
+     */
+    public function registerAutoloadMaps($locations)
+    {
+        if (!is_array($locations) && !($locations instanceof Traversable)) {
+            require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
+            throw new Zend_Loader_Exception_InvalidArgumentException('Map list must be an array or implement Traversable');
+        }
+        foreach ($locations as $location) {
+            $this->registerAutoloadMap($location);
+        }
+        return $this;
+    }
+
+    /**
+     * Retrieve current autoload map
+     * 
+     * @return array
+     */
+    public function getAutoloadMap()
+    {
+        return $this->map;
+    }
+
+    /**
+     * Defined by Autoloadable
+     * 
+     * @param  string $class 
+     * @return void
+     */
+    public function autoload($class)
+    {
+        if (isset($this->map[$class])) {
+            require_once $this->map[$class];
+        }
+    }
+
+    /**
+     * Register the autoloader with spl_autoload registry
+     * 
+     * @return void
+     */
+    public function register()
+    {
+        if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
+            spl_autoload_register(array($this, 'autoload'), true, true);
+        } else {
+            spl_autoload_register(array($this, 'autoload'), true);
+        }
+    }
+
+    /**
+     * Load a map from a file
+     *
+     * If the map has been previously loaded, returns the current instance;
+     * otherwise, returns whatever was returned by calling include() on the
+     * location.
+     * 
+     * @param  string $location 
+     * @return Zend_Loader_ClassMapAutoloader|mixed
+     * @throws Zend_Loader_Exception_InvalidArgumentException for nonexistent locations
+     */
+    protected function loadMapFromFile($location)
+    {
+        if (!file_exists($location)) {
+            require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
+            throw new Zend_Loader_Exception_InvalidArgumentException('Map file provided does not exist');
+        }
+
+        if (!$path = self::realPharPath($location)) {
+            $path = realpath($location);
+        }
+
+        if (in_array($path, $this->mapsLoaded)) {
+            // Already loaded this map
+            return $this;
+        }
+
+        $map = include $path;
+
+        return $map;
+    }
+
+    /**
+     * Resolve the real_path() to a file within a phar.
+     *
+     * @see    https://bugs.php.net/bug.php?id=52769 
+     * @param  string $path 
+     * @return string
+     */
+    public static function realPharPath($path)
+    {
+        if (strpos($path, 'phar:///') !== 0) {
+            return;
+        }
+        
+        $parts = explode('/', str_replace(array('/','\\'), '/', substr($path, 8)));
+        $parts = array_values(array_filter($parts, function($p) { return ($p !== '' && $p !== '.'); }));
+
+        array_walk($parts, function ($value, $key) use(&$parts) {
+            if ($value === '..') {
+                unset($parts[$key], $parts[$key-1]);
+                $parts = array_values($parts);
+            }
+        });
+
+        if (file_exists($realPath = 'phar:///' . implode('/', $parts))) {
+            return $realPath;
+        }
+    }
+}

+ 34 - 0
library/Zend/Loader/Exception/InvalidArgumentException.php

@@ -0,0 +1,34 @@
+<?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 Exception
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+require_once dirname(__FILE__) . '/../Exception.php';
+
+/**
+ * @category   Zend
+ * @package    Zend_Loader
+ * @subpackage Exception
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+class Zend_Loader_Exception_InvalidArgumentException
+    extends Zend_Loader_Exception
+{
+}

+ 75 - 0
library/Zend/Loader/SplAutoloader.php

@@ -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
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+if (interface_exists('Zend_Loader_SplAutoloader')) return;
+
+/**
+ * Defines an interface for classes that may register with the spl_autoload 
+ * registry
+ *
+ * @package    Zend_Loader
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_Loader_SplAutoloader
+{
+    /**
+     * Constructor
+     *
+     * Allow configuration of the autoloader via the constructor.
+     * 
+     * @param  null|array|Traversable $options 
+     * @return void
+     */
+    public function __construct($options = null);
+
+    /**
+     * Configure the autoloader
+     *
+     * In most cases, $options should be either an associative array or 
+     * Traversable object.
+     * 
+     * @param  array|Traversable $options 
+     * @return SplAutoloader
+     */
+    public function setOptions($options);
+
+    /**
+     * Autoload a class
+     *
+     * @param   $class
+     * @return  mixed
+     *          False [if unable to load $class]
+     *          get_class($class) [if $class is successfully loaded]
+     */
+    public function autoload($class);
+
+    /**
+     * Register the autoloader with spl_autoload registry
+     *
+     * Typically, the body of this will simply be:
+     * <code>
+     * spl_autoload_register(array($this, 'autoload'));
+     * </code>
+     * 
+     * @return void
+     */
+    public function register();
+}

+ 342 - 0
library/Zend/Loader/StandardAutoloader.php

@@ -0,0 +1,342 @@
+<?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-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+// Grab SplAutoloader interface
+require_once dirname(__FILE__) . '/SplAutoloader.php';
+
+/**
+ * PSR-0 compliant autoloader
+ *
+ * Allows autoloading both namespaced and vendor-prefixed classes. Class
+ * lookups are performed on the filesystem. If a class file for the referenced
+ * class is not found, a PHP warning will be raised by include().
+ *
+ * @package    Zend_Loader
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    New BSD {@link http://framework.zend.com/license/new-bsd}
+ */
+class Zend_Loader_StandardAutoloader implements Zend_Loader_SplAutoloader
+{
+    const NS_SEPARATOR     = '\\';
+    const PREFIX_SEPARATOR = '_';
+    const LOAD_NS          = 'namespaces';
+    const LOAD_PREFIX      = 'prefixes';
+    const ACT_AS_FALLBACK  = 'fallback_autoloader';
+
+    /**
+     * @var array Namespace/directory pairs to search; ZF library added by default
+     */
+    protected $namespaces = array();
+
+    /**
+     * @var array Prefix/directory pairs to search
+     */
+    protected $prefixes = array();
+
+    /**
+     * @var bool Whether or not the autoloader should also act as a fallback autoloader
+     */
+    protected $fallbackAutoloaderFlag = false;
+
+    /**
+     * Constructor
+     *
+     * @param  null|array|Traversable $options
+     * @return void
+     */
+    public function __construct($options = null)
+    {
+        $this->registerPrefix('Zend', dirname(dirname(__FILE__)));
+
+        $zfDir = dirname(dirname(dirname(__FILE__))) . '/Zend';
+        if (file_exists($zfDir)) {
+             $this->registerPrefix('Zend', $zfDir);
+        }
+
+        if (null !== $options) {
+            $this->setOptions($options);
+        }
+    }
+
+    /**
+     * Configure autoloader
+     *
+     * Allows specifying both "namespace" and "prefix" pairs, using the
+     * following structure:
+     * <code>
+     * array(
+     *     'namespaces' => array(
+     *         'Zend'     => '/path/to/Zend/library',
+     *         'Doctrine' => '/path/to/Doctrine/library',
+     *     ),
+     *     'prefixes' => array(
+     *         'Phly_'     => '/path/to/Phly/library',
+     *     ),
+     *     'fallback_autoloader' => true,
+     * )
+     * </code>
+     *
+     * @param  array|Traversable $options
+     * @return Zend_Loader_StandardAutoloader
+     */
+    public function setOptions($options)
+    {
+        if (!is_array($options) && !($options instanceof Traversable)) {
+            require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
+            throw new Zend_Loader_Exception_InvalidArgumentException('Options must be either an array or Traversable');
+        }
+
+        foreach ($options as $type => $pairs) {
+            switch ($type) {
+                case self::LOAD_NS:
+                    if (is_array($pairs) || $pairs instanceof Traversable) {
+                        $this->registerNamespaces($pairs);
+                    }
+                    break;
+                case self::LOAD_PREFIX:
+                    if (is_array($pairs) || $pairs instanceof Traversable) {
+                        $this->registerPrefixes($pairs);
+                    }
+                    break;
+                case self::ACT_AS_FALLBACK:
+                    $this->setFallbackAutoloader($pairs);
+                    break;
+                default:
+                    // ignore
+            }
+        }
+        return $this;
+    }
+
+    /**
+     * Set flag indicating fallback autoloader status
+     *
+     * @param  bool $flag
+     * @return Zend_Loader_StandardAutoloader
+     */
+    public function setFallbackAutoloader($flag)
+    {
+        $this->fallbackAutoloaderFlag = (bool) $flag;
+        return $this;
+    }
+
+    /**
+     * Is this autoloader acting as a fallback autoloader?
+     *
+     * @return bool
+     */
+    public function isFallbackAutoloader()
+    {
+        return $this->fallbackAutoloaderFlag;
+    }
+
+    /**
+     * Register a namespace/directory pair
+     *
+     * @param  string $namespace
+     * @param  string $directory
+     * @return Zend_Loader_StandardAutoloader
+     */
+    public function registerNamespace($namespace, $directory)
+    {
+        $namespace = rtrim($namespace, self::NS_SEPARATOR). self::NS_SEPARATOR;
+        $this->namespaces[$namespace] = $this->normalizeDirectory($directory);
+        return $this;
+    }
+
+    /**
+     * Register many namespace/directory pairs at once
+     *
+     * @param  array $namespaces
+     * @return Zend_Loader_StandardAutoloader
+     */
+    public function registerNamespaces($namespaces)
+    {
+        if (!is_array($namespaces) && !$namespaces instanceof Traversable) {
+            require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
+            throw new Zend_Loader_Exception_InvalidArgumentException('Namespace pairs must be either an array or Traversable');
+        }
+
+        foreach ($namespaces as $namespace => $directory) {
+            $this->registerNamespace($namespace, $directory);
+        }
+        return $this;
+    }
+
+    /**
+     * Register a prefix/directory pair
+     *
+     * @param  string $prefix
+     * @param  string $directory
+     * @return Zend_Loader_StandardAutoloader
+     */
+    public function registerPrefix($prefix, $directory)
+    {
+        $prefix = rtrim($prefix, self::PREFIX_SEPARATOR). self::PREFIX_SEPARATOR;
+        $this->prefixes[$prefix] = $this->normalizeDirectory($directory);
+        return $this;
+    }
+
+    /**
+     * Register many namespace/directory pairs at once
+     *
+     * @param  array $prefixes
+     * @return Zend_Loader_StandardAutoloader
+     */
+    public function registerPrefixes($prefixes)
+    {
+        if (!is_array($prefixes) && !$prefixes instanceof Traversable) {
+            require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
+            throw new Zend_Loader_Exception_InvalidArgumentException('Prefix pairs must be either an array or Traversable');
+        }
+
+        foreach ($prefixes as $prefix => $directory) {
+            $this->registerPrefix($prefix, $directory);
+        }
+        return $this;
+    }
+
+    /**
+     * Defined by Autoloadable; autoload a class
+     *
+     * @param  string $class
+     * @return false|string
+     */
+    public function autoload($class)
+    {
+        $isFallback = $this->isFallbackAutoloader();
+        if (false !== strpos($class, self::NS_SEPARATOR)) {
+            if ($this->loadClass($class, self::LOAD_NS)) {
+                return $class;
+            } elseif ($isFallback) {
+                return $this->loadClass($class, self::ACT_AS_FALLBACK);
+            }
+            return false;
+        }
+        if (false !== strpos($class, self::PREFIX_SEPARATOR)) {
+            if ($this->loadClass($class, self::LOAD_PREFIX)) {
+                return $class;
+            } elseif ($isFallback) {
+                return $this->loadClass($class, self::ACT_AS_FALLBACK);
+            }
+            return false;
+        }
+        if ($isFallback) {
+            return $this->loadClass($class, self::ACT_AS_FALLBACK);
+        }
+        return false;
+    }
+
+    /**
+     * Register the autoloader with spl_autoload
+     *
+     * @return void
+     */
+    public function register()
+    {
+        spl_autoload_register(array($this, 'autoload'));
+    }
+
+    /**
+     * Transform the class name to a filename
+     *
+     * @param  string $class
+     * @param  string $directory
+     * @return string
+     */
+    protected function transformClassNameToFilename($class, $directory)
+    {
+        // $class may contain a namespace portion, in  which case we need
+        // to preserve any underscores in that portion.
+        $matches = array();
+        preg_match('/(?P<namespace>.+\\\)?(?P<class>[^\\\]+$)/', $class, $matches);
+
+        $class     = (isset($matches['class'])) ? $matches['class'] : '';
+        $namespace = (isset($matches['namespace'])) ? $matches['namespace'] : '';
+
+        return $directory
+             . str_replace(self::NS_SEPARATOR, '/', $namespace)
+             . str_replace(self::PREFIX_SEPARATOR, '/', $class)
+             . '.php';
+    }
+
+    /**
+     * Load a class, based on its type (namespaced or prefixed)
+     *
+     * @param  string $class
+     * @param  string $type
+     * @return void
+     */
+    protected function loadClass($class, $type)
+    {
+        if (!in_array($type, array(self::LOAD_NS, self::LOAD_PREFIX, self::ACT_AS_FALLBACK))) {
+            require_once dirname(__FILE__) . '/Exception/InvalidArgumentException.php';
+            throw new Zend_Loader_Exception_InvalidArgumentException();
+        }
+
+        // Fallback autoloading
+        if ($type === self::ACT_AS_FALLBACK) {
+            // create filename
+            $filename     = $this->transformClassNameToFilename($class, '');
+            if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
+                $resolvedName = stream_resolve_include_path($filename);
+                if ($resolvedName !== false) {
+                    return include $resolvedName;
+                }
+                return false;
+            }
+            return include $filename;
+        }
+
+        // Namespace and/or prefix autoloading
+        foreach ($this->$type as $leader => $path) {
+            if (0 === strpos($class, $leader)) {
+                // Trim off leader (namespace or prefix)
+                $trimmedClass = substr($class, strlen($leader));
+
+                // create filename
+                $filename = $this->transformClassNameToFilename($trimmedClass, $path);
+                if (file_exists($filename)) {
+                    return include $filename;
+                }
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Normalize the directory to include a trailing directory separator
+     *
+     * @param  string $directory
+     * @return string
+     */
+    protected function normalizeDirectory($directory)
+    {
+        $last = $directory[strlen($directory) - 1];
+        if (in_array($last, array('/', '\\'))) {
+            $directory[strlen($directory) - 1] = DIRECTORY_SEPARATOR;
+            return $directory;
+        }
+        $directory .= DIRECTORY_SEPARATOR;
+        return $directory;
+    }
+
+}

+ 2 - 0
tests/Zend/File/AllTests.php

@@ -25,6 +25,7 @@ if (!defined('PHPUnit_MAIN_METHOD')) {
 }
 
 require_once 'Zend/File/Transfer/AllTests.php';
+require_once 'Zend/File/ClassFileLocatorTest.php';
 
 /**
  * @category   Zend
@@ -45,6 +46,7 @@ class Zend_File_AllTests
     {
         $suite = new PHPUnit_Framework_TestSuite('Zend Framework - Zend_File');
 
+        $suite->addTestSuite('Zend_File_ClassFileLocatorTest');
         $suite->addTest(Zend_File_Transfer_AllTests::suite());
 
         return $suite;

+ 98 - 0
tests/Zend/File/ClassFileLocatorTest.php

@@ -0,0 +1,98 @@
+<?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_File
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Zend_File_ClassFileLocatorTest::main');
+}
+
+require_once 'Zend/File/ClassFileLocator.php';
+
+/**
+ * Test class for Zend_File_ClassFileLocator
+ *
+ * @category   Zend
+ * @package    Zend_File
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Zend_File
+ */
+class Zend_File_ClassFileLocatorTest extends PHPUnit_Framework_TestCase
+{
+
+    public function testConstructorThrowsInvalidArgumentExceptionForInvalidStringDirectory()
+    {
+        $this->setExpectedException('InvalidArgumentException');
+        $locator = new Zend_File_ClassFileLocator('__foo__');
+    }
+
+    public function testConstructorThrowsInvalidArgumentExceptionForNonDirectoryIteratorArgument()
+    {
+        $iterator = new ArrayIterator(array());
+        $this->setExpectedException('InvalidArgumentException');
+        $locator = new Zend_File_ClassFileLocator($iterator);
+    }
+
+    public function testIterationShouldReturnOnlyPhpFiles()
+    {
+        $locator = new Zend_File_ClassFileLocator(dirname(__FILE__));
+        foreach ($locator as $file) {
+            $this->assertRegexp('/\.php$/', $file->getFilename());
+        }
+    }
+
+    public function testIterationShouldReturnOnlyPhpFilesContainingClasses()
+    {
+        $locator = new Zend_File_ClassFileLocator(dirname(__FILE__));
+        $found = false;
+        foreach ($locator as $file) {
+            if (preg_match('/locator-should-skip-this\.php$/', $file->getFilename())) {
+                $found = true;
+            }
+        }
+        $this->assertFalse($found, "Found PHP file not containing a class?");
+    }
+
+    public function testIterationShouldReturnInterfaces()
+    {
+        $locator = new Zend_File_ClassFileLocator(dirname(__FILE__));
+        $found = false;
+        foreach ($locator as $file) {
+            if (preg_match('/LocatorShouldFindThis\.php$/', $file->getFilename())) {
+                $found = true;
+            }
+        }
+        $this->assertTrue($found, "Locator skipped an interface?");
+    }
+
+    public function testIterationShouldInjectClassInFoundItems()
+    {
+        $locator = new Zend_File_ClassFileLocator(dirname(__FILE__));
+        $found = false;
+        foreach ($locator as $file) {
+            $this->assertTrue(isset($file->classname));
+        }
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Zend_File_ClassFileLocatorTest::main') {
+    Zend_File_ClassFileLocatorTest::main();
+}

+ 30 - 0
tests/Zend/File/TestAsset/LocatorShouldFindThis.php

@@ -0,0 +1,30 @@
+<?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 Exception
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * @package    Zend_Loader
+ * @subpackage Exception
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+interface Zend_File_TestAsset_LocatorShouldFindThis
+{
+}

+ 2 - 0
tests/Zend/File/_files/locator-should-skip-this.php

@@ -0,0 +1,2 @@
+<?php
+// Locator should skip this file; no classes in it

+ 0 - 0
tests/Zend/File/_files/locator-should-skip-this.xml


+ 6 - 0
tests/Zend/Loader/AllTests.php

@@ -25,9 +25,12 @@ if (!defined('PHPUnit_MAIN_METHOD')) {
 }
 
 require_once 'Zend/Loader/AutoloaderTest.php';
+require_once 'Zend/Loader/AutoloaderFactoryTest.php';
 require_once 'Zend/Loader/AutoloaderMultiVersionTest.php';
 require_once 'Zend/Loader/Autoloader/ResourceTest.php';
+require_once 'Zend/Loader/ClassMapAutoloaderTest.php';
 require_once 'Zend/Loader/PluginLoaderTest.php';
+require_once 'Zend/Loader/StandardAutoloaderTest.php';
 
 /**
  * @category   Zend
@@ -49,9 +52,12 @@ class Zend_Loader_AllTests
         $suite = new PHPUnit_Framework_TestSuite('Zend Framework - Zend_Loader');
 
         $suite->addTestSuite('Zend_Loader_AutoloaderTest');
+        $suite->addTestSuite('Zend_Loader_AutoloaderFactoryTest');
         $suite->addTestSuite('Zend_Loader_AutoloaderMultiVersionTest');
         $suite->addTestSuite('Zend_Loader_Autoloader_ResourceTest');
+        $suite->addTestSuite('Zend_Loader_ClassMapAutoloaderTest');
         $suite->addTestSuite('Zend_Loader_PluginLoaderTest');
+        $suite->addTestSuite('Zend_Loader_StandardAutoloaderTest');
 
         return $suite;
     }

+ 231 - 0
tests/Zend/Loader/AutoloaderFactoryTest.php

@@ -0,0 +1,231 @@
+<?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 UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Zend_Loader_AutoloaderFactoryTest::main');
+}
+
+/*
+ * Preload a number of classes to ensure they're available once we've disabled
+ * other autoloaders.
+ */
+require_once 'PHPUnit/Framework/Constraint/IsEqual.php';
+require_once 'PHPUnit/Framework/Constraint/IsInstanceOf.php';
+require_once 'PHPUnit/Framework/Constraint/IsNull.php';
+require_once 'PHPUnit/Framework/Constraint/IsTrue.php';
+require_once 'Zend/Loader/AutoloaderFactory.php';
+require_once 'Zend/Loader/ClassMapAutoloader.php';
+require_once 'Zend/Loader/StandardAutoloader.php';
+
+/**
+ * @package    Zend_Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class Zend_Loader_AutoloaderFactoryTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        // Store original autoloaders
+        $this->loaders = spl_autoload_functions();
+        if (!is_array($this->loaders)) {
+            // spl_autoload_functions does not return empty array when no
+            // autoloaders registered...
+            $this->loaders = array();
+        }
+
+        // Clear out other autoloaders to ensure those being tested are at the 
+        // top of the stack
+        foreach ($this->loaders as $loader) {
+            spl_autoload_unregister($loader);
+        }
+
+        // Store original include_path
+        $this->includePath = get_include_path();
+    }
+
+    public function tearDown()
+    {
+        Zend_Loader_AutoloaderFactory::unregisterAutoloaders();
+        // Restore original autoloaders
+        $loaders = spl_autoload_functions();
+        if (is_array($loaders)) {
+            foreach ($loaders as $loader) {
+                spl_autoload_unregister($loader);
+            }
+        }
+
+        foreach ($this->loaders as $loader) {
+            spl_autoload_register($loader);
+        }
+
+        // Restore original include_path
+        set_include_path($this->includePath);
+    }
+
+    public function testRegisteringValidMapFilePopulatesAutoloader()
+    {
+        Zend_Loader_AutoloaderFactory::factory(array(
+            'Zend_Loader_ClassMapAutoloader' => array(
+                dirname(__FILE__) . '/_files/goodmap.php',
+            ),
+        ));
+        $loader = Zend_Loader_AutoloaderFactory::getRegisteredAutoloader('Zend_Loader_ClassMapAutoloader');
+        $map = $loader->getAutoloadMap();
+        $this->assertTrue(is_array($map));
+        $this->assertEquals(2, count($map));
+    }
+
+    /**
+     * This tests checks if invalid autoloaders cause exceptions
+     *
+     * @expectedException Zend_Loader_Exception_InvalidArgumentException
+     */
+    public function testFactoryCatchesInvalidClasses()
+    {
+        if (!version_compare(PHP_VERSION, '5.3.7', '>=')) {
+            $this->markTestSkipped('Cannot test invalid interface loader with versions less than 5.3.7');
+        }
+        include dirname(__FILE__) . '/_files/InvalidInterfaceAutoloader.php';
+        Zend_Loader_AutoloaderFactory::factory(array(
+            'InvalidInterfaceAutoloader' => array()            
+        ));
+    }
+
+    public function testFactoryDoesNotRegisterDuplicateAutoloaders()
+    {
+        Zend_Loader_AutoloaderFactory::factory(array(
+            'Zend_Loader_StandardAutoloader' => array(
+                'prefixes' => array(
+                    'TestPrefix' => dirname(__FILE__) . '/TestAsset/TestPrefix',
+                ),
+            ),
+        ));
+        $this->assertEquals(1, count(Zend_Loader_AutoloaderFactory::getRegisteredAutoloaders()));
+        Zend_Loader_AutoloaderFactory::factory(array(
+            'Zend_Loader_StandardAutoloader' => array(
+                'prefixes' => array(
+                    'ZendTest_Loader_TestAsset_TestPlugins' => dirname(__FILE__) . '/TestAsset/TestPlugins',
+                ),
+            ),
+        ));
+        $this->assertEquals(1, count(Zend_Loader_AutoloaderFactory::getRegisteredAutoloaders()));
+        $this->assertTrue(class_exists('TestPrefix_NoDuplicateAutoloadersCase'));
+        $this->assertTrue(class_exists('ZendTest_Loader_TestAsset_TestPlugins_Foo'));
+    }
+
+    public function testCanUnregisterAutoloaders()
+    {
+        Zend_Loader_AutoloaderFactory::factory(array(
+            'Zend_Loader_StandardAutoloader' => array(
+                'prefixes' => array(
+                    'TestPrefix' => dirname(__FILE__) . '/TestAsset/TestPrefix',
+                ),
+            ),
+        ));
+        Zend_Loader_AutoloaderFactory::unregisterAutoloaders();
+        $this->assertEquals(0, count(Zend_Loader_AutoloaderFactory::getRegisteredAutoloaders()));
+    }
+
+    public function testCanUnregisterAutoloadersByClassName()
+    {
+        Zend_Loader_AutoloaderFactory::factory(array(
+            'Zend_Loader_StandardAutoloader' => array(
+                'namespaces' => array(
+                    'TestPrefix' => dirname(__FILE__) . '/TestAsset/TestPrefix',
+                ),
+            ),
+        ));
+        Zend_Loader_AutoloaderFactory::unregisterAutoloader('Zend_Loader_StandardAutoloader');
+        $this->assertEquals(0, count(Zend_Loader_AutoloaderFactory::getRegisteredAutoloaders()));
+    }
+
+    public function testCanGetValidRegisteredAutoloader()
+    {
+        Zend_Loader_AutoloaderFactory::factory(array(
+            'Zend_Loader_StandardAutoloader' => array(
+                'namespaces' => array(
+                    'TestPrefix' => dirname(__FILE__) . '/TestAsset/TestPrefix',
+                ),
+            ),
+        ));
+        $autoloader = Zend_Loader_AutoloaderFactory::getRegisteredAutoloader('Zend_Loader_StandardAutoloader');
+        $this->assertInstanceOf('Zend_Loader_StandardAutoloader', $autoloader);
+    }
+
+    public function testDefaultAutoloader()
+    {
+        Zend_Loader_AutoloaderFactory::factory();
+        $autoloader = Zend_Loader_AutoloaderFactory::getRegisteredAutoloader('Zend_Loader_StandardAutoloader');
+        $this->assertInstanceOf('Zend_Loader_StandardAutoloader', $autoloader);
+        $this->assertEquals(1, count(Zend_Loader_AutoloaderFactory::getRegisteredAutoloaders()));
+    }
+
+    public function testGetInvalidAutoloaderThrowsException()
+    {
+        $this->setExpectedException('Zend_Loader_Exception_InvalidArgumentException');
+        $loader = Zend_Loader_AutoloaderFactory::getRegisteredAutoloader('InvalidAutoloader');
+    }
+
+    public function testFactoryWithInvalidArgumentThrowsException()
+    {
+        $this->setExpectedException('Zend_Loader_Exception_InvalidArgumentException');
+        Zend_Loader_AutoloaderFactory::factory('InvalidArgument');
+    }
+
+    public function testFactoryWithInvalidAutoloaderClassThrowsException()
+    {
+        $this->setExpectedException('Zend_Loader_Exception_InvalidArgumentException');
+        Zend_Loader_AutoloaderFactory::factory(array('InvalidAutoloader' => array()));
+    }
+
+    public function testCannotBeInstantiatedViaConstructor()
+    {
+        $reflection = new ReflectionClass('Zend_Loader_AutoloaderFactory');
+        $constructor = $reflection->getConstructor();
+        $this->assertNull($constructor);
+    }
+
+    public function testPassingNoArgumentsToFactoryInstantiatesAndRegistersStandardAutoloader()
+    {
+        Zend_Loader_AutoloaderFactory::factory();
+        $loaders = Zend_Loader_AutoloaderFactory::getRegisteredAutoloaders();
+        $this->assertEquals(1, count($loaders));
+        $loader = array_shift($loaders);
+        $this->assertInstanceOf('Zend_Loader_StandardAutoloader', $loader);
+
+        $test  = array($loader, 'autoload');
+        $found = false;
+        foreach (spl_autoload_functions() as $function) {
+            if ($function === $test) {
+                $found = true;
+                break;
+            }
+        }
+        $this->assertTrue($found, 'StandardAutoloader not registered with spl_autoload');
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Zend_Loader_AutoloaderFactoryTest::main') {
+    Zend_Loader_AutoloaderFactoryTest::main();
+}

+ 217 - 0
tests/Zend/Loader/ClassMapAutoloaderTest.php

@@ -0,0 +1,217 @@
+<?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    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Zend_Loader_ClassMapAutoloaderTest::main');
+}
+
+require_once 'Zend/Loader/ClassMapAutoloader.php';
+
+/**
+ * @category   Zend
+ * @package    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class Zend_Loader_ClassMapAutoloaderTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        // Store original autoloaders
+        $this->loaders = spl_autoload_functions();
+        if (!is_array($this->loaders)) {
+            // spl_autoload_functions does not return empty array when no
+            // autoloaders registered...
+            $this->loaders = array();
+        }
+
+        // Store original include_path
+        $this->includePath = get_include_path();
+
+        $this->loader = new Zend_Loader_ClassMapAutoloader();
+    }
+
+    public function tearDown()
+    {
+        // Restore original autoloaders
+        $loaders = spl_autoload_functions();
+        if (is_array($loaders)) {
+            foreach ($loaders as $loader) {
+                spl_autoload_unregister($loader);
+            }
+        }
+
+        foreach ($this->loaders as $loader) {
+            spl_autoload_register($loader);
+        }
+
+        // Restore original include_path
+        set_include_path($this->includePath);
+    }
+
+    public function testRegisteringNonExistentAutoloadMapRaisesInvalidArgumentException()
+    {
+        $dir = dirname(__FILE__) . '__foobar__';
+        $this->setExpectedException('Zend_Loader_Exception_InvalidArgumentException');
+        $this->loader->registerAutoloadMap($dir);
+    }
+
+    public function testValidMapFileNotReturningMapRaisesInvalidArgumentException()
+    {
+        $this->setExpectedException('Zend_Loader_Exception_InvalidArgumentException');
+        $this->loader->registerAutoloadMap(dirname(__FILE__) . '/_files/badmap.php');
+    }
+
+    public function testAllowsRegisteringArrayAutoloadMapDirectly()
+    {
+        $map = array(
+            'Zend_Loader_Exception' => dirname(__FILE__) . '/../../../library/Zend/Loader/Exception.php',
+        );
+        $this->loader->registerAutoloadMap($map);
+        $test = $this->loader->getAutoloadMap();
+        $this->assertSame($map, $test);
+    }
+
+    public function testAllowsRegisteringArrayAutoloadMapViaConstructor()
+    {
+        $map = array(
+            'Zend_Loader_Exception' => dirname(__FILE__) . '/../../../library/Zend/Loader/Exception.php',
+        );
+        $loader = new Zend_Loader_ClassMapAutoloader(array($map));
+        $test = $loader->getAutoloadMap();
+        $this->assertSame($map, $test);
+    }
+
+    public function testRegisteringValidMapFilePopulatesAutoloader()
+    {
+        $this->loader->registerAutoloadMap(dirname(__FILE__) . '/_files/goodmap.php');
+        $map = $this->loader->getAutoloadMap();
+        $this->assertTrue(is_array($map));
+        $this->assertEquals(2, count($map));
+        // Just to make sure nothing changes after loading the same map again 
+        // (loadMapFromFile should just return)
+        $this->loader->registerAutoloadMap(dirname(__FILE__) . '/_files/goodmap.php');
+        $map = $this->loader->getAutoloadMap();
+        $this->assertTrue(is_array($map));
+        $this->assertEquals(2, count($map));
+    }
+
+    public function testRegisteringMultipleMapsMergesThem()
+    {
+        $map = array(
+            'Zend_Loader_Exception' => dirname(__FILE__) . '/../../../library/Zend/Loader/Exception.php',
+            'Zend_Loader_StandardAutoloaderTest' => 'some/bogus/path.php',
+        );
+        $this->loader->registerAutoloadMap($map);
+        $this->loader->registerAutoloadMap(dirname(__FILE__) . '/_files/goodmap.php');
+
+        $test = $this->loader->getAutoloadMap();
+        $this->assertTrue(is_array($test));
+        $this->assertEquals(3, count($test));
+        $this->assertNotEquals($map['Zend_Loader_StandardAutoloaderTest'], $test['Zend_Loader_StandardAutoloaderTest']);
+    }
+
+    public function testCanRegisterMultipleMapsAtOnce()
+    {
+        $map = array(
+            'Zend_Loader_Exception' => dirname(__FILE__) . '/../../../library/Zend/Loader/Exception.php',
+            'Zend_Loader_StandardAutoloaderTest' => 'some/bogus/path.php',
+        );
+        $maps = array($map, dirname(__FILE__) . '/_files/goodmap.php');
+        $this->loader->registerAutoloadMaps($maps);
+        $test = $this->loader->getAutoloadMap();
+        $this->assertTrue(is_array($test));
+        $this->assertEquals(3, count($test));
+    }
+
+    public function testRegisterMapsThrowsExceptionForNonTraversableArguments()
+    {
+        $tests = array(true, 'string', 1, 1.0, new stdClass);
+        foreach ($tests as $test) {
+            try {
+                $this->loader->registerAutoloadMaps($test);
+                $this->fail('Should not register non-traversable arguments');
+            } catch (Zend_Loader_Exception_InvalidArgumentException $e) {
+                $this->assertContains('array or implement Traversable', $e->getMessage());
+            }
+        }
+    }
+
+    public function testAutoloadLoadsClasses()
+    {
+        $map = array('Zend_UnusualNamespace_ClassMappedClass' => dirname(__FILE__) . '/TestAsset/ClassMappedClass.php');
+        $this->loader->registerAutoloadMap($map);
+        $this->loader->autoload('Zend_UnusualNamespace_ClassMappedClass');
+        $this->assertTrue(class_exists('Zend_UnusualNamespace_ClassMappedClass', false));
+    }
+
+    public function testIgnoresClassesNotInItsMap()
+    {
+        $map = array('Zend_UnusualNamespace_ClassMappedClass' => dirname(__FILE__) . '/TestAsset/ClassMappedClass.php');
+        $this->loader->registerAutoloadMap($map);
+        $this->loader->autoload('Zend_UnusualNamespace_UnMappedClass');
+        $this->assertFalse(class_exists('Zend_UnusualNamespace_UnMappedClass', false));
+    }
+
+    public function testRegisterRegistersCallbackWithSplAutoload()
+    {
+        $this->loader->register();
+        $loaders = spl_autoload_functions();
+        $this->assertTrue(count($this->loaders) < count($loaders));
+        $test = array_shift($loaders);
+        $this->assertEquals(array($this->loader, 'autoload'), $test);
+    }
+
+    public function testCanLoadClassMapFromPhar()
+    {
+        $map = 'phar://' . __DIR__ . '/_files/classmap.phar/test/.//../autoload_classmap.php';
+        $this->loader->registerAutoloadMap($map);
+        $this->loader->autoload('some_loadedclass');
+        $this->assertTrue(class_exists('some_loadedclass', false));
+        $test = $this->loader->getAutoloadMap();
+        $this->assertEquals(2, count($test));
+
+        // will not register duplicate, even with a different relative path
+        $map = 'phar://' . __DIR__ . '/_files/classmap.phar/test/./foo/../../autoload_classmap.php';
+        $this->loader->registerAutoloadMap($map);
+        $test = $this->loader->getAutoloadMap();
+        $this->assertEquals(2, count($test));
+    }
+
+    public function testCanLoadNamespacedClassFromPhar()
+    {
+        if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+            $this->markTestSkipped('Namespace support is valid for PHP >= 5.3.0 only');
+        }
+
+        $map = 'phar://' . __DIR__ . '/_files/classmap.phar/test/.//../autoload_classmap.php';
+        $this->loader->registerAutoloadMap($map);
+        $this->loader->autoload('some\namespacedclass');
+        $this->assertTrue(class_exists('some\namespacedclass', false));
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Zend_Loader_ClassMapAutoloaderTest::main') {
+    Zend_Loader_ClassMapAutoloaderTest::main();
+}

+ 206 - 0
tests/Zend/Loader/StandardAutoloaderTest.php

@@ -0,0 +1,206 @@
+<?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    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Zend_Loader_StandardAutoloaderTest::main');
+}
+
+require_once 'Zend/Loader/StandardAutoloader.php';
+require_once 'Zend/Loader/TestAsset/StandardAutoloader.php';
+
+/**
+ * @category   Zend
+ * @package    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class Zend_Loader_StandardAutoloaderTest extends PHPUnit_Framework_TestCase
+{
+    public function setUp()
+    {
+        // Store original autoloaders
+        $this->loaders = spl_autoload_functions();
+        if (!is_array($this->loaders)) {
+            // spl_autoload_functions does not return empty array when no
+            // autoloaders registered...
+            $this->loaders = array();
+        }
+
+        // Store original include_path
+        $this->includePath = get_include_path();
+    }
+
+    public function tearDown()
+    {
+        // Restore original autoloaders
+        $loaders = spl_autoload_functions();
+        if (is_array($loaders)) {
+            foreach ($loaders as $loader) {
+                spl_autoload_unregister($loader);
+            }
+        }
+
+        foreach ($this->loaders as $loader) {
+            spl_autoload_register($loader);
+        }
+
+        // Restore original include_path
+        set_include_path($this->includePath);
+    }
+
+    public function testFallbackAutoloaderFlagDefaultsToFalse()
+    {
+        $loader = new Zend_Loader_StandardAutoloader();
+        $this->assertFalse($loader->isFallbackAutoloader());
+    }
+
+    public function testFallbackAutoloaderStateIsMutable()
+    {
+        $loader = new Zend_Loader_StandardAutoloader();
+        $loader->setFallbackAutoloader(true);
+        $this->assertTrue($loader->isFallbackAutoloader());
+        $loader->setFallbackAutoloader(false);
+        $this->assertFalse($loader->isFallbackAutoloader());
+    }
+
+    public function testPassingNonTraversableOptionsToSetOptionsRaisesException()
+    {
+        $loader = new Zend_Loader_StandardAutoloader();
+
+        $obj  = new stdClass();
+        foreach (array(true, 'foo', $obj) as $arg) {
+            try {
+                $loader->setOptions(true);
+                $this->fail('Setting options with invalid type should fail');
+            } catch (Zend_Loader_Exception_InvalidArgumentException $e) {
+                $this->assertContains('array or Traversable', $e->getMessage());
+            }
+        }
+    }
+
+    public function testPassingArrayOptionsPopulatesProperties()
+    {
+        $options = array(
+            'namespaces' => array(
+                'Zend\\'   => dirname(dirname(__FILE__)) . '/',
+            ),
+            'prefixes'   => array(
+                'Zend_'  => dirname(dirname(__FILE__)) . '/',
+            ),
+            'fallback_autoloader' => true,
+        );
+        $loader = new Zend_Loader_TestAsset_StandardAutoloader();
+        $loader->setOptions($options);
+        $this->assertEquals($options['namespaces'], $loader->getNamespaces());
+        $this->assertEquals($options['prefixes'], $loader->getPrefixes());
+        $this->assertTrue($loader->isFallbackAutoloader());
+    }
+
+    public function testPassingTraversableOptionsPopulatesProperties()
+    {
+        $namespaces = new \ArrayObject(array(
+            'Zend\\' => dirname(dirname(__FILE__)) . '/',
+        ));
+        $prefixes = new \ArrayObject(array(
+            'Zend_' => dirname(dirname(__FILE__)) . '/',
+        ));
+        $options = new \ArrayObject(array(
+            'namespaces' => $namespaces,
+            'prefixes'   => $prefixes,
+            'fallback_autoloader' => true,
+        ));
+        $loader = new Zend_Loader_TestAsset_StandardAutoloader();
+        $loader->setOptions($options);
+        $this->assertEquals((array) $options['namespaces'], $loader->getNamespaces());
+        $this->assertEquals((array) $options['prefixes'], $loader->getPrefixes());
+        $this->assertTrue($loader->isFallbackAutoloader());
+    }
+
+    public function testAutoloadsNamespacedClasses()
+    {
+        if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+            $this->markTestSkipped();
+        }
+        $loader = new Zend_Loader_StandardAutoloader();
+        $loader->registerNamespace('Zend\UnusualNamespace', dirname(__FILE__) . '/TestAsset');
+        $loader->autoload('Zend\UnusualNamespace\NamespacedClass');
+        $this->assertTrue(class_exists('Zend\UnusualNamespace\NamespacedClass', false));
+    }
+
+    public function testAutoloadsVendorPrefixedClasses()
+    {
+        $loader = new Zend_Loader_StandardAutoloader();
+        $loader->registerPrefix('ZendTest_UnusualPrefix', dirname(__FILE__) . '/TestAsset/UnusualPrefix');
+        $loader->autoload('ZendTest_UnusualPrefix_PrefixedClass');
+        $this->assertTrue(class_exists('ZendTest_UnusualPrefix_PrefixedClass', false));
+    }
+
+    public function testCanActAsFallbackAutoloader()
+    {
+        $loader = new Zend_Loader_StandardAutoloader();
+        $loader->setFallbackAutoloader(true);
+        set_include_path(dirname(__FILE__) . '/TestAsset/' . PATH_SEPARATOR . $this->includePath);
+        $loader->autoload('TestPrefix_FallbackCase');
+        $this->assertTrue(class_exists('TestPrefix_FallbackCase', false));
+    }
+
+    public function testReturnsFalseForUnresolveableClassNames()
+    {
+        $loader = new Zend_Loader_StandardAutoloader();
+        $this->assertFalse($loader->autoload('Some\Fake\Classname'));
+    }
+
+    public function testReturnsFalseForInvalidClassNames()
+    {
+        $loader = new Zend_Loader_StandardAutoloader();
+        $loader->setFallbackAutoloader(true);
+        $this->assertFalse($loader->autoload('Some_Invalid_Classname_'));
+    }
+
+    public function testRegisterRegistersCallbackWithSplAutoload()
+    {
+        $loader = new Zend_Loader_StandardAutoloader();
+        $loader->register();
+        $loaders = spl_autoload_functions();
+        $this->assertTrue(count($this->loaders) < count($loaders));
+        $test = array_pop($loaders);
+        $this->assertEquals(array($loader, 'autoload'), $test);
+    }
+
+    public function testAutoloadsNamespacedClassesWithUnderscores()
+    {
+        if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+            $this->markTestSkipped('Test only relevant for PHP >= 5.3.0');
+        }
+
+        $loader = new Zend_Loader_StandardAutoloader();
+        $loader->registerNamespace('ZendTest\UnusualNamespace', dirname(__FILE__) . '/TestAsset');
+        $loader->autoload('ZendTest\UnusualNamespace\Name_Space\Namespaced_Class');
+        $this->assertTrue(class_exists('ZendTest\UnusualNamespace\Name_Space\Namespaced_Class', false));
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Zend_Loader_StandardAutoloaderTest::main') {
+    Zend_Loader_StandardAutoloaderTest::main();
+}

+ 39 - 0
tests/Zend/Loader/TestAsset/ClassMappedClass.php

@@ -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    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @category   Zend
+ * @package    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class Zend_UnusualNamespace_ClassMappedClass
+{
+    public $options;
+
+    public function __construct($options = null)
+    {
+        $this->options = $options;
+    }
+}

+ 33 - 0
tests/Zend/Loader/TestAsset/Name_Space/Namespaced/Class.php

@@ -0,0 +1,33 @@
+<?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 UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+namespace ZendTest\UnusualNamespace\Name_Space;
+
+/**
+ * @package    Zend_Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class Namespaced_Class
+{
+}

+ 35 - 0
tests/Zend/Loader/TestAsset/NamespacedClass.php

@@ -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    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+namespace Zend\UnusualNamespace;
+
+/**
+ * @category   Zend
+ * @package    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class NamespacedClass
+{
+}

+ 52 - 0
tests/Zend/Loader/TestAsset/StandardAutoloader.php

@@ -0,0 +1,52 @@
+<?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    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @version    $Id$
+ */
+
+/**
+ * @category   Zend
+ * @package    Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class Zend_Loader_TestAsset_StandardAutoloader extends Zend_Loader_StandardAutoloader
+{
+    /**
+     * Get registered namespaces
+     * 
+     * @return array
+     */
+    public function getNamespaces()
+    {
+        return $this->namespaces;
+    }
+
+    /**
+     * Get registered prefixes
+     * 
+     * @return array
+     */
+    public function getPrefixes()
+    {
+        return $this->prefixes;
+    }
+}

+ 53 - 0
tests/Zend/Loader/TestAsset/TestPluginMap.php

@@ -0,0 +1,53 @@
+<?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 UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+namespace ZendTest\Loader\TestAsset;
+
+/**
+ * @package    Zend_Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class ZendTest_Loader_TestAsset_TestPluginMap implements IteratorAggregate
+{
+    /**
+     * Plugin map
+     * 
+     * @var array
+     */
+    public $map = array(
+        'map'    => __CLASS__,
+        'test'   => 'Zend_Loader_PluginClassLoaderTest',
+        'loader' => 'Zend_Loader_PluginClassLoader',
+    );
+
+    /**
+     * Return iterator
+     * 
+     * @return Traversable
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->map);
+    }
+}

+ 31 - 0
tests/Zend/Loader/TestAsset/TestPlugins/Bar.php

@@ -0,0 +1,31 @@
+<?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 UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * @package    Zend_Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class ZendTest_Loader_TestAsset_TestPlugins_Bar
+{
+}

+ 33 - 0
tests/Zend/Loader/TestAsset/TestPlugins/Baz.php

@@ -0,0 +1,33 @@
+<?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 UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+namespace ZendTest\Loader\TestAsset\TestPlugins;
+
+/**
+ * @package    Zend_Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class ZendTest_Loader_TestAsset_TestPlugins_Baz
+{
+}

+ 31 - 0
tests/Zend/Loader/TestAsset/TestPlugins/Foo.php

@@ -0,0 +1,31 @@
+<?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 UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * @package    Zend_Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class ZendTest_Loader_TestAsset_TestPlugins_Foo
+{
+}

+ 31 - 0
tests/Zend/Loader/TestAsset/TestPrefix/FallbackCase.php

@@ -0,0 +1,31 @@
+<?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 UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * @package    Zend_Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class TestPrefix_FallbackCase
+{
+}

+ 31 - 0
tests/Zend/Loader/TestAsset/TestPrefix/NoDuplicateAutoloadersCase.php

@@ -0,0 +1,31 @@
+<?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 UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * @package    Zend_Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class TestPrefix_NoDuplicateAutoloadersCase
+{
+}

+ 31 - 0
tests/Zend/Loader/TestAsset/UnusualPrefix/PrefixedClass.php

@@ -0,0 +1,31 @@
+<?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 UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ */
+
+/**
+ * @package    Zend_Loader
+ * @subpackage UnitTests
+ * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license    http://framework.zend.com/license/new-bsd     New BSD License
+ * @group      Loader
+ */
+class ZendTest_UnusualPrefix_PrefixedClass
+{
+}

+ 4 - 0
tests/Zend/Loader/_files/InvalidInterfaceAutoloader.php

@@ -0,0 +1,4 @@
+<?php
+class InvalidInterfaceAutoloader
+{
+}

+ 2 - 0
tests/Zend/Loader/_files/badmap.php

@@ -0,0 +1,2 @@
+<?php
+return true;

BIN
tests/Zend/Loader/_files/classmap.phar


+ 7 - 0
tests/Zend/Loader/_files/goodmap.php

@@ -0,0 +1,7 @@
+<?php
+$ds       = DIRECTORY_SEPARATOR;
+$basePath = realpath(dirname(__FILE__) . "$ds..");
+return array(
+    'Zend_Loader_StandardAutoloaderTest' => $basePath . $ds . 'StandardAutoloaderTest.php',
+    'Zend_Loader_ClassMapAutoloaderTest' => $basePath . $ds . 'ClassMapAutoloaderTest.php',
+);