Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #1591 from florianv/jloader-namespaces

Namespaces support for JLoader
  • Loading branch information...
commit 187bef38412cad749c80dd211c2a164608d53a53 2 parents 9104707 + c48c0b4
@LouisLandry LouisLandry authored
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']);
+ }
+
+ /**
+ * 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
+{
+
+}
Please sign in to comment.
Something went wrong with that request. Please try again.