Prepend, multiple paths, and underscore combined with namespaces. #5

Merged
merged 4 commits into from Jul 10, 2012
View
@@ -8,14 +8,13 @@
*
* PHP version 5
*
- * @category PEAR2
- * @package PEAR2_Autoload
- * @author Gregory Beaver <cellog@php.net>
- * @author Brett Bieber <saltybeagle@php.net>
- * @copyright 2012 PEAR2
- * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
- * @version GIT: $Id$
- * @link http://pear2.php.net/PEAR2_Autoload
+ * @category PEAR2
+ * @package PEAR2_Autoload
+ * @author Gregory Beaver <cellog@php.net>
+ * @author Brett Bieber <saltybeagle@php.net>
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @version GIT: $Id$
+ * @link http://pear2.php.net/PEAR2_Autoload
*/
namespace PEAR2;
if (!class_exists('\PEAR2\Autoload', false)) {
@@ -27,19 +26,48 @@
*
* PHP version 5
*
- * @category PEAR2
- * @package PEAR2_Autoload
- * @author Gregory Beaver <cellog@php.net>
- * @author Brett Bieber <saltybeagle@php.net>
- * @copyright 2012 PEAR2
- * @license http://www.opensource.org/licenses/bsd-license.php
+ * @category PEAR2
+ * @package PEAR2_Autoload
+ * @author Gregory Beaver <cellog@php.net>
+ * @author Brett Bieber <saltybeagle@php.net>
+ * @license http://www.opensource.org/licenses/bsd-license.php
* New BSDLicense
- * @version GIT: $Id$
- * @link http://pear2.php.net/PEAR2_Autoload
+ * @link http://pear2.php.net/PEAR2_Autoload
*/
class Autoload
{
/**
+ * Used at {@link initialize()} to specify that the load function, path
+ * and map should be appended to the respective lists.
+ */
+ const APPEND = 0;
+
+ /**
+ * Used at {@link initialize()} to specify that the load function should
+ * be prepended on the autoload stack, instead of being appended.
+ */
+ const PREPEND_LOAD = 1;
+
+ /**
+ * Used at {@link initialize()} to specify that the path should be
+ * prepended on the list of paths, instead of being appended.
+ */
+ const PREPEND_PATH = 2;
+
+ /**
+ * Used at {@link initialize()} to specify that the map should be
+ * prepended on the list of maps, instead of being appended.
+ */
+ const PREPEND_MAP = 4;
+
+ /**
+ * Used at {@link initialize()} to specify that the load function, path
+ * and map should be prepended on their respective lists, instead of
+ * being appended.
+ */
+ const PREPEND = 7;
+
+ /**
* Whether the autoload class has been spl_autoload_register-ed
*
* @var bool
@@ -80,77 +108,112 @@ class Autoload
* @var array
*/
protected static $unmapped = array();
+
+ /**
+ * Array of functions to be checked in exception traces.
+ *
+ * @var array
+ */
+ protected static $checkFunctions = array(
+ 'class_exists', 'interface_exists'
+ );
/**
* Initialize the PEAR2 autoloader
*
- * @param string $path Directory path to register
+ * @param string $path Directory path(s) to register.
* @param string $mapfile Path to a mapping file to register.
+ * @param int $flags A bitmaks with options for the autoloader.
+ * See the PREPEND(_*) constants for details.
*
* @return void
*/
- static function initialize($path, $mapfile = null)
- {
- self::register();
- self::addPath($path);
- self::addMap($mapfile);
+ static function initialize(
+ $path, $mapfile = null, $flags = self::APPEND
+ ) {
+ self::register(0 !== $flags & self::PREPEND_LOAD);
+ self::addPath($path, 0 !== ($flags & self::PREPEND_PATH));
+ self::addMap($mapfile, 0 !== ($flags & self::PREPEND_MAP));
}
/**
* Register the PEAR2 autoload class with spl_autoload_register
*
+ * @param bool $prepend Whether to prepend the load function to the
+ * autoload stack instead of appending it.
+ *
* @return void
*/
- protected static function register()
+ protected static function register($prepend = false)
{
if (!self::$registered) {
// set up __autoload
$autoload = spl_autoload_functions();
- spl_autoload_register('PEAR2\Autoload::load');
+ spl_autoload_register('PEAR2\Autoload::load', true, $prepend);
if (function_exists('__autoload') && ($autoload === false)) {
- // __autoload() was being used, but now would be ignored, add
- // it to the autoload stack
+ // __autoload() was being used, but now would be ignored,
+ // add it to the autoload stack
spl_autoload_register('__autoload');
}
+ if (function_exists('trait_exists')) {
+ self::$checkFunctions[] = 'trait_exists';
+ }
+ self::$registered = true;
}
- self::$registered = true;
}
/**
* Add a path
*
- * @param string $path The directory to add to the set of PEAR2 paths
+ * @param string $paths The folder(s) to add to the set of paths.
+ * @param bool $prepend Whether to prepend the path to the list of
+ * paths instead of appending it.
*
* @return void
*/
- protected static function addPath($path)
+ protected static function addPath($paths, $prepend = false)
{
- if (!in_array($path, self::$paths)) {
- self::$paths[] = $path;
+ foreach (explode(PATH_SEPARATOR, $paths) as $path) {
+ if (!in_array($path, self::$paths)) {
+ if ($prepend) {
+ self::$paths = array_merge(array($path), self::$paths);
+ } else {
+ self::$paths[] = $path;
+ }
+ }
}
}
/**
* Add a classname-to-file map
*
- * @param string $mapfile The filename of the classmap
+ * @param string $mapfile The filename of the classmap.
+ * @param bool $prepend Whether to prepend the map to the list of maps
+ * instead of appending it.
*
* @return void
*/
- protected static function addMap($mapfile)
+ protected static function addMap($mapfile, $prepend = false)
{
- if (! in_array($mapfile, self::$maps)) {
+ if (!in_array($mapfile, self::$maps)) {
// keep track of specific map file loaded in this
// instance so we can update it if necessary
self::$mapfile = $mapfile;
- if (file_exists($mapfile)) {
+ if (is_file($mapfile)) {
$map = include $mapfile;
if (is_array($map)) {
// mapfile contains a valid map, so we'll keep it
- self::$maps[] = $mapfile;
- self::$map = array_merge(self::$map, $map);
+ if ($prepend) {
+ self::$maps = array_merge(
+ array($mapfile), self::$maps
+ );
+ self::$map = array_merge($map, self::$map);
+ } else {
+ self::$maps[] = $mapfile;
+ self::$map = array_merge(self::$map, $map);
+ }
}
}
@@ -188,8 +251,8 @@ static function load($class)
// need to check if there's a current map file specified ALSO.
// this could be the first time writing it.
$mapped = self::isMapped($class);
- if ($mapped) {
- require self::$map[$class];
+ if ($mapped && is_file(self::$map[$class])) {
+ include self::$map[$class];
if (!self::loadSuccessful($class)) {
// record this failure & keep going, we may still find it
self::$unmapped[] = $class;
@@ -198,11 +261,19 @@ static function load($class)
}
}
- $file = str_replace(array('_', '\\'), DIRECTORY_SEPARATOR, $class) .
- '.php';
+ $file = '';
+ $className = $class;
+ if (false !== $lastNsPos = strrpos($class, '\\')) {
+ $namespace = substr($class, 0, $lastNsPos);
+ $className = substr($class, $lastNsPos + 1);
+ $file = str_replace(
+ '\\', DIRECTORY_SEPARATOR, $namespace
+ ) . DIRECTORY_SEPARATOR;
+ }
+ $file .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
foreach (self::$paths as $path) {
- if (file_exists($path . DIRECTORY_SEPARATOR . $file)) {
- require $path . DIRECTORY_SEPARATOR . $file;
+ if (is_file($path . DIRECTORY_SEPARATOR . $file)) {
+ include $path . DIRECTORY_SEPARATOR . $file;
if (!self::loadSuccessful($class)) {
if (count(spl_autoload_functions()) > 1) {
return false;
@@ -215,7 +286,9 @@ static function load($class)
}
if (in_array($class, self::$unmapped)) {
- self::updateMap($class, $path . DIRECTORY_SEPARATOR . $file);
+ self::updateMap(
+ $class, $path . DIRECTORY_SEPARATOR . $file
+ );
}
return true;
}
@@ -230,14 +303,13 @@ static function load($class)
'") [PEAR2_Autoload-@PACKAGE_VERSION@]'
);
$trace = $e->getTrace();
- $checkFunctions = array('class_exists', 'interface_exists');
if (isset($trace[2]) && isset($trace[2]['function'])
- && in_array($trace[2]['function'], $checkFunctions)
+ && in_array($trace[2]['function'], self::$checkFunctions)
) {
return false;
}
if (isset($trace[1]) && isset($trace[1]['function'])
- && in_array($trace[1]['function'], $checkFunctions)
+ && in_array($trace[1]['function'], self::$checkFunctions)
) {
return false;
}
@@ -253,7 +325,10 @@ static function load($class)
*/
protected static function loadSuccessful($class)
{
- return class_exists($class, false) || interface_exists($class, false);
+ return class_exists($class, false)
+ || interface_exists($class, false)
+ || (in_array('trait_exists', self::$checkFunctions, true)
+ && trait_exists($class, false));
}
/**