Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Namespaces support for JLoader #1591

Merged
merged 3 commits into from

5 participants

@florianv

This pull request adds support for class loading and autoloading based on namespaces.

If your class is located in : BASE_PATH/Chess/Piece/Pawn.php :

<?php
namespace Chess\Piece;

class Pawn {} 

You can register it to the auto loader, by registering the ROOT namespace Chess:
JLoader::registerNamespace('Chess', BASE_PATH . '/Chess');

All classes respecting the naming convention in a given namespace :
namespace Folder\SubFolder; for classes in BASE_PATH/Folder/SubFolder/ will be autoloaded.

If you have lower case directory names and class names BASE_PATH/folder/subfolder/, you can either declarate the namespace with lower or camel cases, and it will work too.

But note that the first param of JLoader::registerNamespace is case sensitive and must match the namespace declaration case.

Examples: namespace Chess; JLoader::registerNamespace('Chess', PATH_TO_CHESS);

namespace chess; JLoader::registerNamespace('chess', PATH_TO_CHESS);

You can also register multiple lookup paths for a given namespace (like the prefix).

@LouisLandry

@florianv would you please migrate the documentation in the description from #1400 over to this one so we have it all here? The change log is generated based on merged pull requests so it's nice to have it all in one place.

Also, I wonder if you'd entertain a thought I've had on JLoader. I think something like:

// Setup the Joomla Platform autoloader.
JLoader::setup($enableJimport, $enableNamespaces);

The idea being that those two boolean flags would decide whether we even add the different autoloader methods. For now we would need to default the first argument to true and the second to false, but it would let any application using the platform decide which of those are loaded. Hopefully we can get to a point in the near future where the first one is defaulted to false and we are reconsidering the second one.

tests/suites/unit/JLoaderTest.php
((5 lines not shown))
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespace
+ */
+ public function testLoadByNamespace()
+ {
+ // Try with a namespace matching the directory structure letter case.
+ $path = dirname(__FILE__) . '/stubs/Color';
+ JLoader::registerNamespace('Color', $path);
+
+ // Test with leading '\\'.
+ $this->assertTrue(JLoader::loadByNamespace('\\Color\\Rgb\\Red'));
+
+ // Try with a namespace containing upper case letters but lower case directory and file names.
@ianmacl
ianmacl added a note

These should be broken up into different test methods so you can get a granular picture of what is breaking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ianmacl ianmacl commented on the diff
tests/suites/unit/JLoaderTest.php
((12 lines not shown))
+ {
+ // Try with a valid path.
+ $path = dirname(__FILE__) . '/stubs/discover1';
+ JLoader::registerNamespace('discover', $path);
+
+ $namespaces = JLoader::getNamespaces();
+
+ $this->assertContains($path, $namespaces['discover']);
+
+ // Try to add an other path for the namespace.
+ $path = dirname(__FILE__) . '/stubs/discover2';
+ JLoader::registerNamespace('discover', $path);
+ $namespaces = JLoader::getNamespaces();
+
+ $this->assertCount(2, $namespaces['discover']);
+ $this->assertContains($path, $namespaces['discover']);
@ianmacl
ianmacl added a note

Again, break these up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@florianv

Yes it is a good idea :)

What I was thinking before is eventually to have 2 loaders : JLoader and JLoaderNamespace having the same base.

<?php
interface JLoaderBase
{
  public function load($class);

  public function setup();

  // Something = prefix / namespace
  public function register($something, $path, $reset = false);
}

So people can set up the namespace one if they want to independently of the actual one.

Both solutions are correct I think, but yours implies less changes.

But, supposing in the future Joomla is fully namespaced and wants to get rid of the actual loader, I think maybe we would like not having param refering to the old one in setup().

Edit : But my solution is only valid if the loader keeps his name : JLoaderNamespace, when JLoader leaves. Otherwise people would have to modify their calls to it.

@florianv

@ianmacl Thanks, I will split them asap.

@LouisLandry

@florianv I think your solution is probably the more "academically correct" one. That being said, the class is static and the way it is currently used I am not sure that I think there'd be much benefit in changing that behavior. As for the parameter ordering I actually think I agree with you. How about we do something like:

<?php
/**
 * @package    Joomla.Platform
 *
 * @copyright  Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
 * @license    GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

/**
 * Static class to handle loading of libraries.
 *
 * @package  Joomla.Platform
 * @since    11.1
 */
abstract class JLoader
{
    // ...

    /**
     * Method to setup the autoloaders for the Joomla Platform.  Since the SPL autoloaders are
     * called in a queue we will add our explicit, class-registration based loader first, then
     * fall back on the autoloader based on conventions.  This will allow people to register a
     * class in a specific location and override platform libraries as was previously possible.
     *
     * @param   boolean  $allowMixedCasePaths  True to allow class paths to use mixed case paths that match class names.
     * @param   boolean  $enableNamespaces     True to enable PHP namespace based class autoloading.
     * @param   boolean  $enableJimport        True to enable legacy jimport() based library loading.
     *
     * @return  void
     *
     * @since   11.3
     */
    public static function setup($allowMixedCasePaths = false, $enableNamespaces = false, $enableJimport = true)

    // ...
}

// Setup the Joomla Platform autoloader.
JLoader::setup($enableJimport, $enableNamespaces);

This has the advantage of not forcing a change in method signature down the road. At some point the $enableJimport flag will be deprecated most likely so we would want it last. Similarly at some point we likely will only support namespace based class loading so the $enableNamespaces flag will be deprecated as well.

The one we end up keeping is the flag to allow the mixed case paths. I actually like that you have that option in there, but I'd rather not incur the cost of an extra filestat on every load if it is just completely unnecessary and all paths are lower case.

Does that make sense?

@florianv

Yes it does. The $allowMixedCasePaths is important.

I don't know if you have planned about having the Joomla directories and files names camel cased, that's why I did the two options.

I think there are a few advantages using camel cased directories and files :
The directories and files names directly match the namespace/class names so the loading is faster.
Also, it leads to clearer names, when you have two parts in a directory/file name, it doesn't look so good when lower case.

@LouisLandry

I wouldn't say we have plans one way or another, but we do plan to explore our options. I like the possibility of using it regardless of the path we take for the core platform libraries going forward.

@florianv

Ok. Maybe 3 options are needed just in case :

1- Lower case

2- Mixed case

3- Camel case

In the future, if we want the best speed, we can use maps : an array of keys containing namespace names and values the full path to it, so the loading is direct.
And we could have a cli app to generate the map.

http://www.zyxist.com/en/archives/140

@florianv

So, I went ahead with the three options and updated the setup method.
You will tell me if you agree with that.
I also added the $allowClasses param for the class map loader at the end because it might be first depreceated, once Joomla is fully auto loadable.

Then, I played around and tried various methods, and it seems that my first implementation was not the fastest one.
Moreover the explode/implode speed changes too much dependending on the length of the class name which is not good.

I also removed the cost of the class_exists in the namespace load functions and used include_once instead.

In a nutshell, it is three times faster on my machine.

I also decided to split the three loaders in three separated methods.
I know some people will tell this is duplicated code, but why affording the cost of some 'if' at each load when we are aware of the case strategy to use on setup.

I also complemented the test with an other class JLoaderNamespaceTest to test that it really works when the class names are passed to the auto load function by PHP. I thought they were a leading '\' but it is not the case.

It also splitted the tests as @ianmacl said.

I fixed a few editor warnings in the other load methods. One @throws missed, and some other were declared void but returned a boolean.

@eddieajau

@florianv would you mind taking a copy of this file:
https://github.com/LouisLandry/joomla-platform/blob/manual/docs/manual/en-US/chapters/introduction.md

and making a gist or similar for with with additional documentation explaining the new namespace support and how developers can use it. We can merge it later when we redo the docs.

Thanks in advance.

@florianv

@eddieajau Do you want me to replace the actual loader.xml docbook by a markdown file, merge the additions and include it in the commit ? If I understand the docs will be translated to markdown files ? Thanks.

@eddieajau

No, don't replace it. A gist will be fine for now.

@eddieajau
@florianv

I have rearranged a few things in the doc

@LouisLandry

This is great @florianv. I'm going to give you one last nit-picky thing then I promise I'm going to merge it. Would you please add JLoader class constants for your JLoader::setup() $caseStrategy argument? It's JLoader::setup(JLoader::NATURAL_CASE) is more immediately understandable than JLoader::setup(2), even if the latter is more verbose.

@florianv

Thanks. Yes indeed it is clearer. I don't squash the commits so you don't have to read the whole diff.

@LouisLandry

Awesome @florianv, you are the very model of a fantastic contributor... thank you!

@LouisLandry LouisLandry merged commit 187bef3 into joomla:staging
@dongilbert
Collaborator

Awe yeah!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
220 docs/manual/en-US/chapters/introduction.md
@@ -113,62 +113,160 @@ JPlatform::getLongVersion() | A really verbose representation of the platf
## Class Auto-loading
-The Joomla Platform implements a class auto-loader removing the need for
-the developer to include files by hand, or by using a fall to the
-`jimport` function (this is now discourage in favor of using
-JLoader::register). Only class names that begin with upper case "J" will
-be considered for auto-loading. Following the "J" prefix, the class name
-must be in camel case and each segment of the name will represent a
-folder path below `JPLATFORM_PATH/joomla` where the last segment of the
-name is the name of the class file. If there is only one part to the
-class name, the auto-loader will look for the file in a folder of the
-same name. Folder names must be in lower case.
+`JLoader` is the mainstay of the Joomla Platform as it controls auto-loading of classes.
-JDatabase should be located in `JPATH_PLATFORM/joomla/database/database.php`
+It removes the need for the developer to include files by hand, or by using a fall to the `jimport` function.
-JDatabaseQuery should be located in `JPATH_PLATFORM/joomla/database/query.php`
+Multiple ways of auto loading classes, following different conventions are proposed by JLoader.
-JDatabaseQueryMysql should be located in `JPATH_PLATFORM/joomla/database/query/mysql.php`
+### The Namespace Loader
-There is no limit to the depth to which the auto-loader will search,
-providing it forms a valid path based on the camel case natural of the
-class name. Note that while acronyms and names such as HTML, XML and
-MySQL have a standard presention in text, such terms should observe camel
-case rules programmatically ("HTML" becomes "Html", "XML" becomes "Xml"
-and so on).
+Since the release 12.3 of the Joomla Platform there is the possibility to auto classes within namespaces.
-The `JLoader` class allows additional customisation including, but not
-limited to, providing the ability to override core classes and cater for
-classes that do not conform with the auto-loader naming and path
-convention.
+* A developer can register the full path to a top level (root) namespace where the loader can find classes (within this namespace).
-### JLoader
+* A developer can override an existing namespace path by replacing it with a new one.
-`JLoader` is the mainstay of the Joomla Platform as it controls
-auto-loading of classes. Wherever possible, class names and paths should
-conform to the auto-loader convention in the form:
+* A developer can register multiple paths to the same namespace.
-`JClassname` located in `JPATH\_PLATFORM/joomla/classname/classname.php`, or
-`JPathtoClassname` located in `JPATH\_PLATFORM/joomla/pathto/classname.php`.
-However, deviations, and even overrides can be handled by `JLoader`'s
-register and discover methods.
+#### Convention
-#### Registering Classes
+The convention is to have the namespace names matching the directories names.
-New classes, or override classes can be registered using the register
-method. This method takes the class name, the path to the class file,
-and an option boolean to force an update of the class register.
+For example :
```php
-// Register an adhoc class.
-JLoader::register('AdhocClass', '/the/path/adhoc.php');
+<?php
+namespace Chess\Piece;
-// Register a custom class to override as core class.
-// This must be done before the core class is loaded.
-JLoader::register('JDatabase', '/custom/path/database_driver.php', true);
+class Pawn
+{
+
+}
+```
+
+must be found in `BASE_PATH/chess/piece/pawn.php` or in `BASE_PATH/Chess/Piece/Pawn.php`.
+
+For the namespace declaration, it is recommanded to use camel case letters as you will have for a class name.
+
+But as you saw above there are different possibilities for the paths case :
+
+#### Lower Case :
+
+The directory structure is lower case and the namespace can be any case.
+
+It must be used when the path is lower case and the namespace camel case.
+
+Example :
+
+```php
+<?php
+namespace Chess\Piece;
+
+class Pawn
+{
+
+}
+```
+
+for a class in `BASE_PATH/chess/piece/pawn.php`.
+
+#### Natural Case :
+
+The namespace case matches the path case.
+
+It must be used when you have lower case namespaces and paths or when you have camel case namespaces and paths.
+
+Examples :
+
+```php
+<?php
+namespace Chess\Piece;
+
+class Pawn
+{
+
+}
+```
+
+for a class in `BASE_PATH/Chess/Pieces/Pawn.php`.
+
+```php
+<?php
+namespace chess\piece;
+
+class Pawn
+{
+
+}
+```
+
+for a class in `BASE_PATH/chess/pieces/pawn.php`.
+
+#### Mixed Case :
+
+It regroups the two options.
+
+It must be used when you have some lower case and camel case paths and camel case or lower case namespace declarations.
+
+For example, Joomla can stay lower case and your application can have a camel case directory structure.
+Both can be auto loaded using the same Mixed Case loader.
+
+#### Usage
+
+#### Setup the Loader
+
+In order to correctly use the namespace auto loader you need to setup it according the case strategy you choosed.
+
+```php
+<?php
+
+// Setup the loader with the Lower Case strategy.
+JLoader::setup(JLoader::LOWER_CASE, true);
+
+// Setup the loader with the Natural Case strategy.
+JLoader::setup(JLoader::NATURAL_CASE, true);
+
+// Setup the loader with the Mixed Case strategy.
+JLoader::setup(JLoader::MIXED_CASE, true);
+```
+
+#### Registering a namespace
+
+You can register a top level namespace by using `JLoader::registerNamespace`.
+
+For example :
+
+```php
+<?php
+
+// The two parameters are case sensitive.
+// The first one must match the namespace declaration case.
+// The second one must match the path case.
+JLoader::registerNamespace('Chess', BASE_PATH . '/chess');
+```
+
+All classes respecting the naming and path convention will be auto loaded.
+
+#### Appending an other path
+
+```php
+<?php
+
+// Adding an other path to the Chess namespace.
+JLoader::registerNamespace('Chess', AN_OTHER_PATH . '/chess');
+```
+
+#### Reseting a path
+
+```php
+<?php
+
+// Reseting a path by adding an other one.
+JLoader::registerNamespace('Chess', AN_OTHER_PATH . '/chess', true);
```
-#### Registering a Class Prefix
+### The Prefix Loader
Since 12.1, there is the ability to register where the auto-loader will
look based on a class prefix (previously only the "J" prefix was
@@ -179,6 +277,29 @@ several scenarios:
* A developer can register an extra path for an existing prefix (for example, this allows the Joomla CMS to have custom libraries but still using the "J" prefix).
* A developer can register a force override for a prefix. This could be used to completely override the core classes with a custom replacement.
+#### Convention
+
+The class name must be in camel case and each segment of the name will represent a folder path
+where the last segment of the name is the name of the class file.
+If there is only one part to the class name, the auto-loader will look for the file in a folder of the
+same name.
+Folder names must be in lower case.
+
+Examples :
+
+`PrefixUserModel` should be located in `PATH_TO_PREFIX/user/model.php`.
+
+`PrefixUser` should be located in `PATH_TO_PREFIX/user/user.php`.
+
+There is no limit to the depth to which the auto-loader will search,
+providing it forms a valid path based on the camel case natural of the
+class name.
+Note that while acronyms and names such as HTML, XML and MySQL have a standard presention in text,
+such terms should observe camel case rules programmatically ("HTML" becomes "Html", "XML" becomes "Xml"
+and so on).
+
+#### Usage
+
```php
// Tell the auto-loader to also look in the /libraries/cms folder for "J" prefixed classes.
JLoader::registerPrefix('J', JPATH_PLATFORM . '/cms');
@@ -190,7 +311,22 @@ JLoader::registerPrefix('Foo', '/path/to/custom/packages');
JLoader::registerPrefix('J', '/my/platform/fork', true);
```
-#### Discovering Classes
+### Registering Classes
+
+New classes, or override classes can be registered using the register
+method. This method takes the class name, the path to the class file,
+and an option boolean to force an update of the class register.
+
+```php
+// Register an adhoc class.
+JLoader::register('AdhocClass', '/the/path/adhoc.php');
+
+// Register a custom class to override as core class.
+// This must be done before the core class is loaded.
+JLoader::register('JDatabase', '/custom/path/database_driver.php', true);
+```
+
+### Discovering Classes
Classes in a folder that follow a naming convention, but not one the
auto-loader immediately recognises, can be registered collectively with
View
272 libraries/loader.php
@@ -16,6 +16,10 @@
*/
abstract class JLoader
{
+ const LOWER_CASE = 1;
+ const NATURAL_CASE = 2;
+ const MIXED_CASE = 3;
+
/**
* Container for already imported library paths.
*
@@ -41,6 +45,14 @@
protected static $prefixes = array();
/**
+ * Container for namespace => path map.
+ *
+ * @var array
+ * @since 12.3
+ */
+ protected static $namespaces = array();
+
+ /**
* Method to discover classes of a given type in a given path.
*
* @param string $classPrefix The class name prefix to use for discovery.
@@ -106,6 +118,18 @@ public static function getClassList()
}
/**
+ * Method to get the list of registered namespaces.
+ *
+ * @return array The array of namespace => path values for the autoloader.
+ *
+ * @since 12.3
+ */
+ public static function getNamespaces()
+ {
+ return self::$namespaces;
+ }
+
+ /**
* Loads a class from specified directories.
*
* @param string $key The class name to look for (dot notation).
@@ -203,6 +227,143 @@ public static function load($class)
}
/**
+ * Load a class based on namespace using the Lower Case strategy.
+ * This loader might be used when the namespace is lower case or camel case
+ * and the path lower case.
+ *
+ * @param string $class The class (including namespace) to load.
+ *
+ * @return boolean True on success, false otherwise.
+ *
+ * @since 12.3
+ */
+ public static function loadByNamespaceLowerCase($class)
+ {
+ // Get the root namespace name.
+ $namespace = strstr($class, '\\', true);
+
+ // If we find the namespace in the stack.
+ if (isset(self::$namespaces[$namespace]))
+ {
+ // Remove the namespace name from the class.
+ $class = str_replace($namespace, '', $class);
+
+ // Create a lower case relative path.
+ $relativePath = strtolower(str_replace('\\', '/', $class));
+
+ // Iterate the registered root paths.
+ foreach (self::$namespaces[$namespace] as $rootPath)
+ {
+ // Create the full path.
+ $path = $rootPath . '/' . $relativePath . '.php';
+
+ // Include the file if it exists.
+ if (file_exists($path))
+ {
+ return (bool) include_once $path;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Load a class based on namespace using the Natural Case strategy.
+ * This loader might be used when the namespace case matches the path case.
+ *
+ * @param string $class The class (including namespace) to load.
+ *
+ * @return boolean True on success, false otherwise.
+ *
+ * @since 12.3
+ */
+ public static function loadByNamespaceNaturalCase($class)
+ {
+ // Get the root namespace name.
+ $namespace = strstr($class, '\\', true);
+
+ // If we find the namespace in the stack.
+ if (isset(self::$namespaces[$namespace]))
+ {
+ // Remove the namespace name from the class.
+ $class = str_replace($namespace, '', $class);
+
+ // Create a relative path.
+ $relativePath = str_replace('\\', '/', $class);
+
+ // Iterate the registered root paths.
+ foreach (self::$namespaces[$namespace] as $rootPath)
+ {
+ // Create the full path.
+ $path = $rootPath . '/' . $relativePath . '.php';
+
+ // Include the file if it exists.
+ if (file_exists($path))
+ {
+ return (bool) include_once $path;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Load a class based on namespace using the Mixed Case strategy.
+ * This loader might be used when the namespace case matches the path case,
+ * or when the namespace is camel case and the path lower case.
+ *
+ * @param string $class The class (including namespace) to load.
+ *
+ * @return boolean True on success, false otherwise.
+ *
+ * @since 12.3
+ */
+ public static function loadByNamespaceMixedCase($class)
+ {
+ // Get the root namespace name.
+ $namespace = strstr($class, '\\', true);
+
+ // If we find the namespace in the stack.
+ if (isset(self::$namespaces[$namespace]))
+ {
+ // Remove the namespace name from the class.
+ $class = str_replace($namespace, '', $class);
+
+ // Create a relative path.
+ $relativePath = str_replace('\\', '/', $class);
+
+ // Create a relative lower case path.
+ $relativeLowPath = strtolower($relativePath);
+
+ // Iterate the registered root paths.
+ foreach (self::$namespaces[$namespace] as $rootPath)
+ {
+ // Create the full lower case path.
+ $lowerPath = $rootPath . '/' . $relativeLowPath . '.php';
+
+ // Include the file if it exists.
+ if (file_exists($lowerPath))
+ {
+ return (bool) include_once $lowerPath;
+ }
+
+ // Create the full natural case path.
+ $naturalPath = $rootPath . '/' . $relativePath . '.php';
+
+ // Include the file if it exists.
+ if (file_exists($naturalPath))
+ {
+ return (bool) include_once $naturalPath;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Directly register a class to the autoload list.
*
* @param string $class The class name to register.
@@ -242,6 +403,8 @@ public static function register($class, $path, $force = true)
*
* @return void
*
+ * @throws RuntimeException
+ *
* @since 12.1
*/
public static function registerPrefix($prefix, $path, $reset = false)
@@ -265,23 +428,102 @@ public static function registerPrefix($prefix, $path, $reset = false)
}
/**
- * Method to setup the autoloaders for the Joomla Platform. Since the SPL autoloaders are
- * called in a queue we will add our explicit, class-registration based loader first, then
- * fall back on the autoloader based on conventions. This will allow people to register a
- * class in a specific location and override platform libraries as was previously possible.
+ * Register a namespace to the autoloader.
+ *
+ * @param string $namespace A case sensitive Namespace to register.
+ * @param string $path A case sensitive absolute file path to the library root where classes of the given namespace can be found.
+ * @param boolean $reset True to reset the namespace with only the given lookup path.
*
* @return void
*
- * @since 11.3
+ * @throws RuntimeException
+ *
+ * @since 12.3
*/
- public static function setup()
+ public static function registerNamespace($namespace, $path, $reset = false)
{
- // Register the base path for Joomla platform libraries.
- self::registerPrefix('J', JPATH_PLATFORM . '/joomla');
+ // Verify the library path exists.
+ if (!file_exists($path))
+ {
+ throw new RuntimeException('Library path ' . $path . ' cannot be found.', 500);
+ }
+
+ // If the namespace is not yet registered or we have an explicit reset flag then set the path.
+ if (!isset(self::$namespaces[$namespace]) || $reset)
+ {
+ self::$namespaces[$namespace] = array($path);
+ }
- // Register the autoloader functions.
- spl_autoload_register(array('JLoader', 'load'));
- spl_autoload_register(array('JLoader', '_autoload'));
+ // Otherwise we want to simply add the path to the namespace.
+ else
+ {
+ self::$namespaces[$namespace][] = $path;
+ }
+ }
+
+ /**
+ * Method to setup the autoloaders for the Joomla Platform.
+ * Since the SPL autoloaders are called in a queue we will add our explicit
+ * class-registration based loader first, then fall back on the autoloader based on conventions.
+ * This will allow people to register a class in a specific location and override platform libraries
+ * as was previously possible.
+ *
+ * @param integer $caseStrategy An option to define the class finding strategy for the namespace loader
+ * depending on the namespace and class path case.
+ * The possible values are :
+ * JLoader::LOWER_CASE : The namespace can be either lower case or camel case and the path lower case.
+ * JLoader::NATURAL_CASE : The namespace case matches the path case.
+ * JLoader::MIXED_CASE : It regroups option 1 and option 2.
+ * @param boolean $enableNamespaces True to enable PHP namespace based class autoloading.
+ * @param boolean $enablePrefixes True to enable prefix based class loading (needed to auto load the Joomla core).
+ * @param boolean $enableClasses True to enable class map based class loading (needed to auto load the Joomla core).
+ *
+ * @return void
+ *
+ * @since 12.3
+ */
+ public static function setup($caseStrategy = self::LOWER_CASE, $enableNamespaces = false, $enablePrefixes = true, $enableClasses = true)
+ {
+ if ($enableClasses)
+ {
+ // Register the class map based autoloader.
+ spl_autoload_register(array('JLoader', 'load'));
+ }
+
+ if ($enablePrefixes)
+ {
+ // Register the J prefix and base path for Joomla platform libraries.
+ self::registerPrefix('J', JPATH_PLATFORM . '/joomla');
+
+ // Register the prefix autoloader.
+ spl_autoload_register(array('JLoader', '_autoload'));
+ }
+
+ if ($enableNamespaces)
+ {
+ switch ($caseStrategy)
+ {
+ // Register the lower case namespace loader.
+ case self::LOWER_CASE:
+ spl_autoload_register(array('JLoader', 'loadByNamespaceLowerCase'));
+ break;
+
+ // Register the natural case namespace loader.
+ case self::NATURAL_CASE:
+ spl_autoload_register(array('JLoader', 'loadByNamespaceNaturalCase'));
+ break;
+
+ // Register the mixed case namespace loader.
+ case self::MIXED_CASE:
+ spl_autoload_register(array('JLoader', 'loadByNamespaceMixedCase'));
+ break;
+
+ // Default to the lower case namespace loader.
+ default:
+ spl_autoload_register(array('JLoader', 'loadByNamespaceLowerCase'));
+ break;
+ }
+ }
}
/**
@@ -289,7 +531,7 @@ public static function setup()
*
* @param string $class The class to be loaded.
*
- * @return void
+ * @return boolean True if the class was loaded, false otherwise.
*
* @since 11.3
*/
@@ -304,6 +546,8 @@ private static function _autoload($class)
return self::_load(substr($class, strlen($prefix)), $lookup);
}
}
+
+ return false;
}
/**
@@ -312,7 +556,7 @@ private static function _autoload($class)
* @param string $class The class to be loaded (wihtout prefix).
* @param array $lookup The array of base paths to use for finding the class file.
*
- * @return void
+ * @return boolean True if the class was loaded, false otherwise.
*
* @since 12.1
*/
@@ -335,6 +579,8 @@ private static function _load($class, $lookup)
return include $path;
}
}
+
+ return false;
}
}
View
567 tests/suites/unit/JLoaderTest.php
@@ -183,6 +183,166 @@ public function testLoad()
}
/**
+ * Test the JLoader::loadByNamespaceLowerCase method
+ * with lower case namespace and path.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceLowerCase
+ */
+ public function testLoadByNamespaceLowerCase()
+ {
+ // Register the 'animal' lower case namespace and lower case path.
+ $path = dirname(__FILE__) . '/stubs/animal1';
+ JLoader::registerNamespace('animal', $path);
+
+ // Register a second lower case path for that namespace.
+ $path = dirname(__FILE__) . '/stubs/animal2';
+ JLoader::registerNamespace('animal', $path);
+
+ // Check we can load a class from the first path.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('animal\\Cat'));
+
+ // Check we can load a class from the second path.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('animal\\Dog'));
+ }
+
+ /**
+ * Test the JLoader::loadByNamespaceLowerCase method
+ * with camel case namespace and lower case path.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceLowerCase
+ */
+ public function testLoadByNamespaceLowerCaseCamelCaseNamespace()
+ {
+ // Register a camel cased namespace but lower case path.
+ $path = dirname(__FILE__) . '/stubs/chess';
+ JLoader::registerNamespace('Chess', $path);
+
+ // Check we can load it by using his camel cased name.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('Chess\\Piece\\Pawn'));
+ }
+
+ /**
+ * Tests the JLoader::loadByNamespaceNaturalCase method
+ * with lower case namespace and lower case path.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceNaturalCase
+ */
+ public function testLoadByNamespaceNaturalCaseLowCase()
+ {
+ // Test with a lower case path and lower case namespace.
+ $path = dirname(__FILE__) . '/stubs/animal1';
+ JLoader::registerNamespace('animal', $path);
+
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('animal\\Cat'));
+ }
+
+ /**
+ * Tests the JLoader::loadByNamespaceNaturalCase method
+ * with a camel case namespace and camel case path.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceNaturalCase
+ */
+ public function testLoadByNamespaceNaturalCaseCamelCase()
+ {
+ // Register the Color namespace and its path (camel case).
+ $path = dirname(__FILE__) . '/stubs/Color';
+ JLoader::registerNamespace('Color', $path);
+
+ // Register a second path for that namespace (camel case).
+ $path = dirname(__FILE__) . '/stubs/Color2';
+ JLoader::registerNamespace('Color', $path);
+
+ // Check we can load a class from the first path.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('Color\\Rgb\\Red'));
+
+ // Check we can load a class from the second path.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('Color\\Blue'));
+ }
+
+ /**
+ * Tests the JLoader::loadByNamespaceMixedCase method
+ * with a lower case namespace and path.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceMixedCase
+ */
+ public function testLoadByNamespaceMixedCaseLow()
+ {
+ // Register the 'animal' lower case namespace and lower case path.
+ $path = dirname(__FILE__) . '/stubs/animal1';
+ JLoader::registerNamespace('animal', $path);
+
+ // Register a second lower case path for that namespace.
+ $path = dirname(__FILE__) . '/stubs/animal2';
+ JLoader::registerNamespace('animal', $path);
+
+ // Check we can load a class from the first path.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('animal\\Cat'));
+
+ // Check we can load a class from the second path.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('animal\\Dog'));
+ }
+
+ /**
+ * Tests the JLoader::loadByNamespaceMixedCase method
+ * with a camel case namespace and path.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceMixedCase
+ */
+ public function testLoadByNamespaceMixedCaseCamelCase()
+ {
+ // Register the Color namespace and its path (camel case).
+ $path = dirname(__FILE__) . '/stubs/Color';
+ JLoader::registerNamespace('Color', $path);
+
+ // Register a second path for that namespace (camel case).
+ $path = dirname(__FILE__) . '/stubs/Color2';
+ JLoader::registerNamespace('Color', $path);
+
+ // Check we can load a class from the first path.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('Color\\Rgb\\Red'));
+
+ // Check we can load a class from the second path.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('Color\\Blue'));
+ }
+
+ /**
+ * Tests the JLoader::loadByNamespaceMixedCase method
+ * with a camel case namespace and low case path.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceMixedCase
+ */
+ public function testLoadByNamespaceMixedCaseCamelCaseNamespaceLowCasePath()
+ {
+ // Register a camel cased namespace but lower case path.
+ $path = dirname(__FILE__) . '/stubs/chess';
+ JLoader::registerNamespace('Chess', $path);
+
+ // Check we can load it by using his camel cased name.
+ $this->assertTrue(JLoader::loadByNamespaceLowerCase('Chess\\Piece\\Pawn'));
+ }
+
+ /**
* The success of this test depends on some files being in the file system to be imported. If the FS changes, this test may need revisited.
*
* @param string $filePath Path to object
@@ -257,6 +417,70 @@ public function testRegister()
}
/**
+ * Tests the JLoader::registerNamespace method.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::registerNamespace
+ */
+ public function testRegisterNamespace()
+ {
+ // Try with a valid path.
+ $path = dirname(__FILE__) . '/stubs/discover1';
+ JLoader::registerNamespace('discover', $path);
+
+ $namespaces = JLoader::getNamespaces();
+
+ $this->assertContains($path, $namespaces['discover']);
+
+ // Try to add an other path for the namespace.
+ $path = dirname(__FILE__) . '/stubs/discover2';
+ JLoader::registerNamespace('discover', $path);
+ $namespaces = JLoader::getNamespaces();
+
+ $this->assertCount(2, $namespaces['discover']);
+ $this->assertContains($path, $namespaces['discover']);
@ianmacl
ianmacl added a note

Again, break these up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+
+ /**
+ * Tests the JLoader::registerNamespace method when reseting the paths.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::registerNamespace
+ */
+ public function testRegisterNamespaceResetPath()
+ {
+ // Insert a first path.
+ $path = dirname(__FILE__) . '/stubs/discover1';
+ JLoader::registerNamespace('discover', $path);
+
+ // Reset the path with a new path.
+ $path = dirname(__FILE__) . '/stubs/discover2';
+ JLoader::registerNamespace('discover', $path, true);
+
+ $namespaces = JLoader::getNamespaces();
+ $this->assertCount(1, $namespaces['discover']);
+ $this->assertContains($path, $namespaces['discover']);
+ }
+
+ /**
+ * Tests the exception thrown by the JLoader::registerNamespace method.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::registerNamespace
+ * @expectedException RuntimeException
+ */
+ public function testRegisterNamespaceException()
+ {
+ JLoader::registerNamespace('Color', 'dummy');
+ }
+
+ /**
* Tests the JLoader::registerPrefix method.
*
* @return void
@@ -288,52 +512,357 @@ public function testRegisterPrefixException()
}
/**
- * Tests the JLoader::setup method.
+ * Tests the JLoader::setup method with the default parameters.
+ * We expect the class map, prefix loaders and the J prefix to be registered correctly.
*
* @return void
*
- * @since 11.4
+ * @since 12.3
* @covers JLoader::setup
*/
- public function testSetup()
+ public function testSetupDefaultParameters()
{
+ // Reset the prefixes.
+ TestReflection::setValue('JLoader', 'prefixes', array());
+
+ // We unregister all loader functions if registered.
+ $this->unregisterLoaders();
+
+ // Setup the autoloader with the default parameters.
+ JLoader::setup();
+
+ // Get the list of autoload functions.
+ $newLoaders = spl_autoload_functions();
+
+ $foundLoad = false;
+ $foundAutoload = false;
+ $foundLoadByNamespaceLowerCase = false;
+ $loadByNamespaceNaturalCase = false;
+ $loadByNamespaceMixedCase = false;
+
+ // We search the list of autoload functions to see if our methods are there.
+ foreach ($newLoaders as $loader)
+ {
+ if (is_array($loader) && $loader[0] === 'JLoader')
+ {
+ if ($loader[1] === 'load')
+ {
+ $foundLoad = true;
+ }
+
+ if ($loader[1] === '_autoload')
+ {
+ $foundAutoload = true;
+ }
+
+ if ($loader[1] === 'loadByNamespaceLowerCase')
+ {
+ $foundLoadByNamespaceLowerCase = true;
+ }
+
+ if ($loader[1] === 'loadByNamespaceNaturalCase')
+ {
+ $loadByNamespaceNaturalCase = true;
+ }
+
+ if ($loader[1] === 'loadByNamespaceMixedCase')
+ {
+ $loadByNamespaceMixedCase = true;
+ }
+ }
+ }
+
+ // Assert the class map loader is found.
+ $this->assertTrue($foundLoad);
+
+ // Assert the J prefix has been registered.
+ $prefixes = TestReflection::getValue('JLoader', 'prefixes');
+ $this->assertArrayHasKey('J', $prefixes);
+
+ // Assert the prefix loader is found.
+ $this->assertTrue($foundAutoload);
+
+ // Assert the namespace loaders are not found.
+ $this->assertFalse($foundLoadByNamespaceLowerCase);
+ $this->assertFalse($loadByNamespaceNaturalCase);
+ $this->assertFalse($loadByNamespaceMixedCase);
+ }
+
+ /**
+ * Tests the JLoader::setup method with $enableClasses = false.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::setup
+ */
+ public function testSetupWithoutClasses()
+ {
+ // We unregister all loader functions if registered.
+ $this->unregisterLoaders();
+
+ // Set up the auto loader with $enableClasses = false.
+ JLoader::setup(1, true, true, false);
+
+ // Get the list of autoload functions.
$loaders = spl_autoload_functions();
- // We unregister the two loaders in case they are missing
+ $foundLoad = false;
+
+ // We search the list of autoload functions to see if our methods are there.
foreach ($loaders as $loader)
{
- if (is_array($loader) && $loader[0] == 'JLoader' && ($loader[1] == 'load' || $loader[1] == '_autoload'))
+ if (is_array($loader) && $loader[0] === 'JLoader')
{
- spl_autoload_unregister($loader);
+ if ($loader[1] === 'load')
+ {
+ $foundLoad = true;
+ }
}
}
- // We call the method under test.
- JLoader::setup();
+ // We don't expect to find it.
+ $this->assertFalse($foundLoad);
+ }
- // We get the list of autoload functions
- $newLoaders = spl_autoload_functions();
+ /**
+ * Tests the JLoader::setup method with $enablePrefixes = false.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::setup
+ */
+ public function testSetupWithoutPrefixes()
+ {
+ // Reset the prefixes.
+ TestReflection::setValue('JLoader', 'prefixes', array());
- $foundLoad = false;
- $foundAutoload = false;
+ // We unregister all loader functions if registered.
+ $this->unregisterLoaders();
+
+ // Setup the loader with $enablePrefixes = false.
+ JLoader::setup(1, true, false, true);
+
+ // Get the autoload functions
+ $loaders = spl_autoload_functions();
+
+ $foundAutoLoad = false;
// We search the list of autoload functions to see if our methods are there.
- foreach ($newLoaders as $loader)
+ foreach ($loaders as $loader)
{
- if (is_array($loader) && $loader[0] == 'JLoader' && $loader[1] == 'load')
+ if (is_array($loader) && $loader[0] === 'JLoader')
{
- $foundLoad = true;
+ if ($loader[1] === '_autoload')
+ {
+ $foundAutoLoad = true;
+ }
}
+ }
+
+ // We don't expect to find it.
+ $this->assertFalse($foundAutoLoad);
+
+ // Assert the J prefix hasn't been registered.
+ $prefixes = TestReflection::getValue('JLoader', 'prefixes');
+ $this->assertFalse(isset($prefixes['J']));
+ }
- if (is_array($loader) && $loader[0] == 'JLoader' && $loader[1] == '_autoload')
+ /**
+ * Tests the JLoader::setup method.
+ * We test the registration of the lower case namespace loader.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::setup
+ */
+ public function testSetupNamespacesLowerCase()
+ {
+ // We unregister all loader functions if registered.
+ $this->unregisterLoaders();
+
+ // Setup the loader with $caseStrategy = 1 (lower case) and enableNamespace = true.
+ JLoader::setup(JLoader::LOWER_CASE, true, false, false);
+
+ // Get the autoload functions
+ $loaders = spl_autoload_functions();
+
+ $foundLoadByNamespaceLowerCase = false;
+
+ // We search the list of autoload functions to see if our method is here.
+ foreach ($loaders as $loader)
+ {
+ if (is_array($loader) && $loader[0] === 'JLoader')
{
- $foundAutoload = true;
+ if ($loader[1] === 'loadByNamespaceLowerCase')
+ {
+ $foundLoadByNamespaceLowerCase = true;
+ }
}
}
- $this->assertThat($foundLoad, $this->isTrue());
+ // We expect to find it.
+ $this->assertTrue($foundLoadByNamespaceLowerCase);
+ }
- $this->assertThat($foundAutoload, $this->isTrue());
+ /**
+ * Tests the JLoader::setup method.
+ * We test the registration of the Natural case namespace loader.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::setup
+ */
+ public function testSetupNamespacesNaturalCase()
+ {
+ // We unregister all loader functions if registered.
+ $this->unregisterLoaders();
+
+ // Setup the loader with $caseStrategy = 2 (natural case) and enableNamespace = true.
+ JLoader::setup(JLoader::NATURAL_CASE, true, false, false);
+
+ // Get the autoload functions
+ $loaders = spl_autoload_functions();
+
+ $loadByNamespaceNaturalCase = false;
+
+ // We search the list of autoload functions to see if our method is here.
+ foreach ($loaders as $loader)
+ {
+ if (is_array($loader) && $loader[0] === 'JLoader')
+ {
+ if ($loader[1] === 'loadByNamespaceNaturalCase')
+ {
+ $loadByNamespaceNaturalCase = true;
+ }
+ }
+ }
+
+ // We expect to find it.
+ $this->assertTrue($loadByNamespaceNaturalCase);
+ }
+
+ /**
+ * Tests the JLoader::setup method.
+ * We test the registration of the Mixed case namespace loader.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::setup
+ */
+ public function testSetupNamespacesMixedCase()
+ {
+ // We unregister all loader functions if registered.
+ $this->unregisterLoaders();
+
+ // Setup the loader with $caseStrategy = 3 (mixed case) and enableNamespace = true.
+ JLoader::setup(JLoader::MIXED_CASE, true, false, false);
+
+ // Get the autoload functions
+ $loaders = spl_autoload_functions();
+
+ $loadByNamespaceMixedCase = false;
+
+ // We search the list of autoload functions to see if our method is here.
+ foreach ($loaders as $loader)
+ {
+ if (is_array($loader) && $loader[0] === 'JLoader')
+ {
+ if ($loader[1] === 'loadByNamespaceMixedCase')
+ {
+ $loadByNamespaceMixedCase = true;
+ }
+ }
+ }
+
+ // We expect to find it.
+ $this->assertTrue($loadByNamespaceMixedCase);
+ }
+
+ /**
+ * Tests the JLoader::setup method.
+ * We test the registration of the namespace loader with an invalid case strategy.
+ * We expect the lower case namespace loader to be registered by default.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::setup
+ */
+ public function testSetupNamespacesInvalidCase()
+ {
+ // We unregister all loader functions if registered.
+ $this->unregisterLoaders();
+
+ // Setup the loader with and invalid case strategy and enableNamespace = true.
+ JLoader::setup('invalid', true, false, false);
+
+ // Get the autoload functions
+ $loaders = spl_autoload_functions();
+
+ $foundLoadByNamespaceLowerCase = false;
+ $loadByNamespaceNaturalCase = false;
+ $loadByNamespaceMixedCase = false;
+
+ // We search the list of autoload functions to see if our methods are here.
+ foreach ($loaders as $loader)
+ {
+ if (is_array($loader) && $loader[0] === 'JLoader')
+ {
+ if ($loader[1] === 'loadByNamespaceLowerCase')
+ {
+ $foundLoadByNamespaceLowerCase = true;
+ }
+
+ if ($loader[1] === 'loadByNamespaceNaturalCase')
+ {
+ $loadByNamespaceNaturalCase = true;
+ }
+
+ if ($loader[1] === 'loadByNamespaceMixedCase')
+ {
+ $loadByNamespaceMixedCase = true;
+ }
+ }
+ }
+
+ // We expect to find only the lower case loader registered.
+ $this->assertTrue($foundLoadByNamespaceLowerCase);
+ $this->assertFalse($loadByNamespaceNaturalCase);
+ $this->assertFalse($loadByNamespaceMixedCase);
+ }
+
+ /**
+ * A function to unregister the Joomla auto loaders.
+ *
+ * @return void
+ *
+ * @since 12.3
+ */
+ protected function unregisterLoaders()
+ {
+ // Get all auto load functions.
+ $loaders = spl_autoload_functions();
+
+ // Unregister all Joomla loader functions if registered.
+ foreach ($loaders as $loader)
+ {
+ if (is_array($loader) && $loader[0] === 'JLoader' &&
+ ($loader[1] === 'load'
+ || $loader[1] === '_autoload'
+ || $loader[1] === 'loadByNamespaceLowerCase'
+ || $loader[1] === 'loadByNamespaceNaturalCase'
+ || $loader[1] === 'loadByNamespaceMixedCase'
+ )
+ )
+ {
+ spl_autoload_unregister($loader);
+ }
+ }
}
/**
View
154 tests/suites/unit/JloaderNamespaceTest.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * @package Joomla.UnitTest
+ *
+ * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE
+ */
+
+namespace Joomla\Tests\Suites\Unit;
+
+use JLoader;
+use animal\Dog;
+use Color\Blue;
+
+/**
+ * This is a complementary class to JLoaderTest for the namespace loaders.
+ * To check the classes are correctly loaded from a given namespace with use or without use.
+ *
+ * @package Joomla.UnitTest
+ * @since 12.3
+ */
+class JLoaderNamespaceTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * Test the JLoader::loadByNamespaceLowerCase method.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceLowerCase
+ */
+ public function testLoadByNamespaceLowerCase()
+ {
+ // Make sure the loaders are unregistered.
+ $this->unregisterLoaders();
+
+ // Set up the loader with the lower case strategy.
+ JLoader::setup(JLoader::LOWER_CASE, true, false, false);
+
+ // Register the animal namespace where we can find the Cat class.
+ $path = dirname(__FILE__) . '/stubs/animal1';
+ JLoader::registerNamespace('animal', $path);
+
+ // Test with full namespace.
+ $cat = new \animal\Cat;
+ $this->assertEquals($cat->say(), 'hello');
+
+ // Register the second namespace where we can find the Dog class.
+ $path = dirname(__FILE__) . '/stubs/animal2';
+ JLoader::registerNamespace('animal', $path);
+
+ // Test with use.
+ $dog = new Dog;
+ $this->assertEquals($dog->say(), 'hello');
+ }
+
+ /**
+ * Test the JLoader::loadByNamespaceLowerCase method.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceLowerCase
+ */
+ public function testLoadByNamespaceNaturalCase()
+ {
+ // Make sure the loaders are unregistered.
+ $this->unregisterLoaders();
+
+ // Set up the loader with the natural case strategy.
+ JLoader::setup(JLoader::NATURAL_CASE, true, false, false);
+
+ // Register the Color namespace where we can find the Red class.
+ $path = dirname(__FILE__) . '/stubs/Color';
+ JLoader::registerNamespace('Color', $path);
+
+ // Test with full namespace.
+ $red = new \Color\Rgb\Red;
+ $this->assertEquals($red->color(), 'red');
+
+ // Register a second path for that namespace where we can find
+ // the Blue class.
+ $path = dirname(__FILE__) . '/stubs/Color2';
+ JLoader::registerNamespace('Color', $path);
+
+ // Test with use.
+ $blue = new Blue;
+ $this->assertEquals($blue->color(), 'blue');
+ }
+
+ /**
+ * Test the JLoader::loadByNamespaceMixedCase method.
+ *
+ * @return void
+ *
+ * @since 12.3
+ * @covers JLoader::loadByNamespaceMixedCase
+ */
+ public function testLoadByNamespaceMixedCase()
+ {
+ // Make sure the loaders are unregistered.
+ $this->unregisterLoaders();
+
+ // Set up the loader with the mixed case strategy.
+ JLoader::setup(JLoader::MIXED_CASE, true, false, false);
+
+ // Register the animal namespace where we can find the Cat class.
+ $path = dirname(__FILE__) . '/stubs/animal1';
+ JLoader::registerNamespace('animal', $path);
+
+ // Test with full namespace.
+ $cat = new \animal\Cat;
+ $this->assertEquals($cat->say(), 'hello');
+
+ // Register a second path for that namespace where we can find
+ // the Blue class.
+ $path = dirname(__FILE__) . '/stubs/Color2';
+ JLoader::registerNamespace('Color', $path);
+
+ // Test with use.
+ $blue = new Blue;
+ $this->assertEquals($blue->color(), 'blue');
+ }
+
+ /**
+ * This function unregisters the namespace loaders and reset the namespaces stack of JLoader.
+ *
+ * @return void
+ *
+ * @since 12.3
+ */
+ protected function unregisterLoaders()
+ {
+ // Make sure no namespaces are registered.
+ \TestReflection::setValue('JLoader', 'namespaces', array());
+
+ // Get all auto load functions.
+ $loaders = spl_autoload_functions();
+
+ // Unregister the namespace auto loaders if any.
+ foreach ($loaders as $loader)
+ {
+ if (is_array($loader) && $loader[0] === 'JLoader' &&
+ ($loader[1] === 'loadByNamespaceLowerCase'
+ || $loader[1] === 'loadByNamespaceNaturalCase'
+ || $loader[1] === 'loadByNamespaceMixedCase'
+ )
+ )
+ {
+ spl_autoload_unregister($loader);
+ }
+ }
+ }
+}
View
23 tests/suites/unit/stubs/Color/Rgb/Red.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * @package Joomla.UnitTest
+ *
+ * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE
+ */
+
+namespace Color\Rgb;
+
+/**
+ * A lambda class to test the namespace loader.
+ *
+ * @package Joomla.UnitTest
+ * @since 12.3
+ */
+class Red
+{
+ public function color()
+ {
+ return 'red';
+ }
+}
View
23 tests/suites/unit/stubs/Color2/Blue.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * @package Joomla.UnitTest
+ *
+ * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE
+ */
+
+namespace Color;
+
+/**
+ * A lambda class to test the namespace loader.
+ *
+ * @package Joomla.UnitTest
+ * @since 12.3
+ */
+class Blue
+{
+ public function color()
+ {
+ return 'blue';
+ }
+}
View
23 tests/suites/unit/stubs/animal1/cat.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * @package Joomla.UnitTest
+ *
+ * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE
+ */
+
+namespace animal;
+
+/**
+ * A lambda class to test the namespace loader.
+ *
+ * @package Joomla.UnitTest
+ * @since 12.3
+ */
+class Cat
+{
+ public function say()
+ {
+ return 'hello';
+ }
+}
View
23 tests/suites/unit/stubs/animal2/dog.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * @package Joomla.UnitTest
+ *
+ * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE
+ */
+
+namespace animal;
+
+/**
+ * A lambda class to test the namespace loader.
+ *
+ * @package Joomla.UnitTest
+ * @since 12.3
+ */
+class Dog
+{
+ public function say()
+ {
+ return 'hello';
+ }
+}
View
20 tests/suites/unit/stubs/chess/piece/pawn.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * @package Joomla.UnitTest
+ *
+ * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
+ * @license GNU General Public License version 2 or later; see LICENSE
+ */
+
+namespace Chess\Piece;
+
+/**
+ * A lambda class to test the namespace loader.
+ *
+ * @package Joomla.UnitTest
+ * @since 12.3
+ */
+class Pawn
+{
+
+}
Something went wrong with that request. Please try again.