Skip to content

Instantly share code, notes, and snippets.

@Avaq
Last active December 18, 2015 02:48
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Avaq/5713209 to your computer and use it in GitHub Desktop.
Save Avaq/5713209 to your computer and use it in GitHub Desktop.
A FIG PSR-0 class loader heavily inspired on the official SplClassLoader. This version has a lot of improvements over the original. Be sure to star this gist if you liked it and leave your suggestions in the comment section beloowwwww!
<?php
/**
* Avaq's PSR-0 ClassLoader.
*
* @author Avaq <aldwin.vlasblom@gmail.com>
*
* A FIG PSR-0 class loader heavily inspired on
* [the official SplClassLoader](https://gist.github.com/jwage/221634) and originally
* developped for use in the [Forall framework](https://github.com/ForallFramework).
* This version has a lot of improvements over the original.
*
* - Proper documentation.
* * No mistakes in the DocBlocks.
* * Documented properties.
* * `see` Tags in DocBlocks.
* * Richly commented code.
* - Better code.
* * Methods return `$this` where appropriate.
* * `register` And `unregister` return the success boolean that is returned by the
* internal functions they call.
* * Structured `loadClass` to get rid of no-op code lines and be much more readable.
* - Callbacks. The `onLoad` method allows one to register a callback that is called with
* `$this, $className, $classPath` when a class successfully autoloads.
*
*/
class ClassLoader
{
/**
* Contains the file extension that is appended to the class name automatically.
* @var string
*/
private $fileExtension = '.php';
/**
* Contains the namespace separator for compatibility with underscores and the likes.
* @var string
*/
private $namespaceSeparator = '\\';
/**
* Contains the base name space to check for. Is set by the constructor.
* @var string
*/
private $namespace;
/**
* Contains the base include directory. Can be set by the constructor.
* @var string
*/
private $includePath;
/**
* A container for callbacks that will get called every time a class is auto-loaded.
* @var Closure[]
*/
private $callbacks = [];
/**
* @param string $namespace The namespace to use.
* @param string $includePath The include path.
*
* @see ClassLoader::$namespace for more information on the namespace.
* @see ClassLoader::$includePath for more information on the include path.
*/
public function __construct($namespace = null, $includePath = null)
{
//Set properties.
$this->namespace = $namespace;
$this->includePath = $includePath;
}
/**
* Setter for the namespace separator.
*
* @param string $separator The separator to use.
*
* @see ClassLoader::$namespaceSeparator for more information.
*
* @return self Chaining enabled.
*/
public function setNamespaceSeparator($separator)
{
//Set the property.
$this->namespaceSeparator = $separator;
//Enable chaining.
return $this;
}
/**
* Getter for the namespace separator.
*
* @see ClassLoader::$namespaceSeparator for more information.
*
* @return string The namespace separator used.
*/
public function getNamespaceSeparator()
{
return $this->namespaceSeparator;
}
/**
* Setter for the include path.
*
* @see ClassLoader::$includePath for more information.
*
* @param string $includePath
*
* @return self Chaining enabled.
*/
public function setIncludePath($includePath)
{
//Set the property.
$this->includePath = $includePath;
//Enable chaining.
return $this;
}
/**
* Setter for the include path.
*
* @see ClassLoader::$includePath for more information.
*
* @return string The include path used.
*/
public function getIncludePath()
{
return $this->includePath;
}
/**
* Setter for the file extension.
*
* @see ClassLoader::$fileExtension for more information.
*
* @param string $fileExtension
*
* @return self Chaining enabled.
*/
public function setFileExtension($fileExtension)
{
//Set the property.
$this->fileExtension = $fileExtension;
//Enable chaining.
return $this;
}
/**
* Getter for the file extension.
*
* @see ClassLoader::$fileExtension for more information.
*
* @return string The file extension used.
*/
public function getFileExtension()
{
return $this->fileExtension;
}
/**
* Adds a callback to our stack of callbacks that are called when a class is successfully auto-loaded.
*
* @param Closure $callback
* The closure will receive 3 parameters. The instance of the ClassLoader
* calling it, the full name of the class and the file that was found for it.
*
* @return self Chaining enabled.
*/
public function onLoad(Closure $callback)
{
//Store in callbacks.
$this->callbacks[] = $callback;
//Enable chaining.
return $this;
}
/**
* Registers this ClassLoader with the SPL auto-load stack.
*
* @return bool Whether the registry succeeded.
*/
public function register()
{
return spl_autoload_register([$this, 'loadClass']);
}
/**
* Unregisters this ClassLoader from the SPL auto-load stack.
*
* @return bool Whether the unregistry succeeded.
*/
public function unregister()
{
return spl_autoload_unregister([$this, 'loadClass']);
}
/**
* Loads the given class, trait or interface according to FIG standards described in PSR-0.
*
* @param string $className The full name (including namespace) of the class to load.
*
* @return self Chaining enabled.
*/
public function loadClass($className)
{
//Fail to load the class when the namespace does not match what we're looking for.
if(is_string($this->namespace) && strpos($className, $this->namespace.$this->namespaceSeparator) !== 0){
return $this;
}
//Prepare the file name for concatenation.
$fileName = '';
//If the class name has a separator in it, translate that to a file structure.
if($lastNsPos = strripos($className, $this->namespaceSeparator)){
$namespace = substr($className, 0, $lastNsPos);
$className = substr($className, $lastNsPos + 1);
$fileName = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $namespace).DIRECTORY_SEPARATOR;
}
//Figure out an include path.
$fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className).$this->fileExtension;
$filePath = (is_string($this->includePath) ? $this->includePath.DIRECTORY_SEPARATOR : '');
$include = "$filePath$fileName";
//Include the file.
require $include;
//The included file must contain the given class for the code to proceed.
if(!class_exists($className)){
return $this;
}
//Notify callbacks.
foreach($this->callbacks as $callback){
$callback($this, $className, $include);
}
//Enable chaining.
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment