You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
PlexShare/fuel/core/classes/autoloader.php

405 lines
9.4 KiB

7 years ago
<?php
/**
* Part of the Fuel framework.
*
* @package Fuel
* @version 1.8
* @author Fuel Development Team
* @license MIT License
* @copyright 2010 - 2016 Fuel Development Team
* @link http://fuelphp.com
*/
namespace Fuel\Core;
/**
* The Autoloader is responsible for all class loading. It allows you to define
* different load paths based on namespaces. It also lets you set explicit paths
* for classes to be loaded from.
*
* @package Fuel
* @subpackage Core
*/
class Autoloader
{
/**
* @var array $classes holds all the classes and paths
*/
protected static $classes = array();
/**
* @var array holds all the namespace paths
*/
protected static $namespaces = array();
/**
* Holds all the PSR-0 compliant namespaces. These namespaces should
* be loaded according to the PSR-0 standard.
*
* @var array
*/
protected static $psr_namespaces = array();
/**
* @var array list off namespaces of which classes will be aliased to global namespace
*/
protected static $core_namespaces = array(
'Fuel\\Core',
);
/**
* @var array the default path to look in if the class is not in a package
*/
protected static $default_path = null;
/**
* @var bool whether to initialize a loaded class
*/
protected static $auto_initialize = null;
/**
* Adds a namespace search path. Any class in the given namespace will be
* looked for in the given path.
*
* @param string $namespace the namespace
* @param string $path the path
* @param bool $psr whether this is a PSR-0 compliant class
* @return void
*/
public static function add_namespace($namespace, $path, $psr = false)
{
static::$namespaces[$namespace] = $path;
if ($psr)
{
static::$psr_namespaces[$namespace] = $path;
}
}
/**
* Adds an array of namespace paths. See {add_namespace}.
*
* @param array $namespaces the namespaces
* @param bool $prepend whether to prepend the namespace to the search path
* @return void
*/
public static function add_namespaces(array $namespaces, $prepend = false)
{
if ( ! $prepend)
{
static::$namespaces = array_merge(static::$namespaces, $namespaces);
}
else
{
static::$namespaces = $namespaces + static::$namespaces;
}
}
/**
* Returns the namespace's path or false when it doesn't exist.
*
* @param string $namespace the namespace to get the path for
* @return array|bool the namespace path or false
*/
public static function namespace_path($namespace)
{
if ( ! array_key_exists($namespace, static::$namespaces))
{
return false;
}
return static::$namespaces[$namespace];
}
/**
* Adds a classes load path. Any class added here will not be searched for
* but explicitly loaded from the path.
*
* @param string $class the class name
* @param string $path the path to the class file
* @return void
*/
public static function add_class($class, $path)
{
static::$classes[strtolower($class)] = $path;
}
/**
* Adds multiple class paths to the load path. See {@see Autoloader::add_class}.
*
* @param array $classes the class names and paths
* @return void
*/
public static function add_classes($classes)
{
foreach ($classes as $class => $path)
{
static::$classes[strtolower($class)] = $path;
}
}
/**
* Aliases the given class into the given Namespace. By default it will
* add it to the global namespace.
*
* <code>
* Autoloader::alias_to_namespace('Foo\\Bar');
* Autoloader::alias_to_namespace('Foo\\Bar', '\\Baz');
* </code>
*
* @param string $class the class name
* @param string $namespace the namespace to alias to
*/
public static function alias_to_namespace($class, $namespace = '')
{
empty($namespace) or $namespace = rtrim($namespace, '\\').'\\';
$parts = explode('\\', $class);
$root_class = $namespace.array_pop($parts);
class_alias($class, $root_class);
}
/**
* Register's the autoloader to the SPL autoload stack.
*
* @return void
*/
public static function register()
{
spl_autoload_register('Autoloader::load', true, true);
}
/**
* Returns the class with namespace prefix when available
*
* @param string $class
* @return bool|string
*/
protected static function find_core_class($class)
{
foreach (static::$core_namespaces as $ns)
{
if (array_key_exists(strtolower($ns_class = $ns.'\\'.$class), static::$classes))
{
return $ns_class;
}
}
return false;
}
/**
* Add a namespace for which classes may be used without the namespace prefix and
* will be auto-aliased to the global namespace.
* Prefixing the classes will overwrite core classes and previously added namespaces.
*
* @param string $namespace
* @param bool $prefix
* @return void
*/
public static function add_core_namespace($namespace, $prefix = true)
{
if ($prefix)
{
array_unshift(static::$core_namespaces, $namespace);
}
else
{
static::$core_namespaces[] = $namespace;
}
}
/**
* Loads a class.
*
* @param string $class Class to load
* @return bool If it loaded the class
*/
public static function load($class)
{
// deal with funny is_callable('static::classname') side-effect
if (strpos($class, 'static::') === 0)
{
// is called from within the class, so it's already loaded
return true;
}
$loaded = false;
$class = ltrim($class, '\\');
$pos = strripos($class, '\\');
if (empty(static::$auto_initialize))
{
static::$auto_initialize = $class;
}
if (isset(static::$classes[strtolower($class)]))
{
static::init_class($class, str_replace('/', DS, static::$classes[strtolower($class)]));
$loaded = true;
}
elseif ($full_class = static::find_core_class($class))
{
if ( ! class_exists($full_class, false) and ! interface_exists($full_class, false))
{
include static::prep_path(static::$classes[strtolower($full_class)]);
}
if ( ! class_exists($class, false))
{
class_alias($full_class, $class);
}
static::init_class($class);
$loaded = true;
}
else
{
$full_ns = substr($class, 0, $pos);
if ($full_ns)
{
foreach (static::$namespaces as $ns => $path)
{
$ns = ltrim($ns, '\\');
if (stripos($full_ns, $ns) === 0)
{
$path .= static::class_to_path(
substr($class, strlen($ns) + 1),
array_key_exists($ns, static::$psr_namespaces)
);
if (is_file($path))
{
static::init_class($class, $path);
$loaded = true;
break;
}
}
}
}
if ( ! $loaded)
{
$path = APPPATH.'classes'.DS.static::class_to_path($class);
if (is_file($path))
{
static::init_class($class, $path);
$loaded = true;
}
}
}
// Prevent failed load from keeping other classes from initializing
if (static::$auto_initialize == $class)
{
static::$auto_initialize = null;
}
return $loaded;
}
/**
* Reset the auto initialize state after an autoloader exception.
* This method is called by the exception handler, and is considered an
* internal method!
*
* @access protected
*/
public static function _reset()
{
static::$auto_initialize = null;
}
/**
* Takes a class name and turns it into a path. It follows the PSR-0
* standard, except for makes the entire path lower case, unless you
* tell it otherwise.
*
* Note: This does not check if the file exists...just gets the path
*
* @param string $class Class name
* @param bool $psr Whether this is a PSR-0 compliant class
* @return string Path for the class
*/
protected static function class_to_path($class, $psr = false)
{
$file = '';
if ($last_ns_pos = strripos($class, '\\'))
{
$namespace = substr($class, 0, $last_ns_pos);
$class = substr($class, $last_ns_pos + 1);
$file = str_replace('\\', DS, $namespace).DS;
}
$file .= str_replace('_', DS, $class).'.php';
if ( ! $psr)
{
$file = strtolower($file);
}
return $file;
}
/**
* Prepares a given path by making sure the directory separators are correct.
*
* @param string $path Path to prepare
* @return string Prepped path
*/
protected static function prep_path($path)
{
return str_replace(array('/', '\\'), DS, $path);
}
/**
* Checks to see if the given class has a static _init() method. If so then
* it calls it.
*
* @param string $class the class name
* @param string $file the file containing the class to include
* @throws \Exception
* @throws \FuelException
*/
protected static function init_class($class, $file = null)
{
// include the file if needed
if ($file)
{
include $file;
}
// if the loaded file contains a class...
if (class_exists($class, false))
{
// call the classes static init if needed
if (static::$auto_initialize === $class)
{
static::$auto_initialize = null;
if (method_exists($class, '_init') and is_callable($class.'::_init'))
{
call_user_func($class.'::_init');
}
}
}
// or an interface...
elseif (interface_exists($class, false))
{
// nothing to do here
}
// or a trait if you're not on 5.3 anymore...
elseif (function_exists('trait_exists') and trait_exists($class, false))
{
// nothing to do here
}
// else something went wrong somewhere, barf and exit now
elseif ($file)
{
throw new \Exception('File "'.\Fuel::clean_path($file).'" does not contain class "'.$class.'"');
}
else
{
throw new \FuelException('Class "'.$class.'" is not defined');
}
}
}