109 lines
3.4 KiB
PHP
109 lines
3.4 KiB
PHP
|
<?php
|
||
|
// Autoloader.php
|
||
|
// Created: 2021-05-04
|
||
|
// Updated: 2021-05-12
|
||
|
|
||
|
namespace Index;
|
||
|
|
||
|
use InvalidArgumentException;
|
||
|
|
||
|
/**
|
||
|
* Provides a simple PSR-4 style autoloader.
|
||
|
*
|
||
|
* Only basic types should be used in this class because this is the first file included
|
||
|
* and obviously can't autoload things before it has been set up.
|
||
|
*/
|
||
|
final class Autoloader {
|
||
|
private const EXTENSION = '.php';
|
||
|
|
||
|
private static array $namespaces = [];
|
||
|
|
||
|
/**
|
||
|
* Registers this autoloader with PHP.
|
||
|
*/
|
||
|
public static function register(): void {
|
||
|
spl_autoload_register([self::class, 'autoloader']);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Unregistered this autoloader with PHP.
|
||
|
*/
|
||
|
public static function unregister(): void {
|
||
|
spl_autoload_unregister([self::class, 'autoloader']);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Cleans a PHP class path for file system look up.
|
||
|
*
|
||
|
* @param string $name A PHP class path.
|
||
|
* @return string A cleaned PHP class path.
|
||
|
*/
|
||
|
public static function cleanName(string $name): string {
|
||
|
return trim($name, '\\');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tries to find a class in the registered namespaces directories.
|
||
|
*
|
||
|
* Only supports the .php extension, others are silly and only add overhead.
|
||
|
*
|
||
|
* @param string $className Target class path.
|
||
|
*/
|
||
|
public static function autoloader(string $className): void {
|
||
|
$classPath = explode('\\', self::cleanName($className));
|
||
|
|
||
|
for($i = 0; $i < count($classPath); ++$i) {
|
||
|
$rootSpace = implode('\\', array_slice($classPath, 0, $i + 1));
|
||
|
|
||
|
if(isset(self::$namespaces[$rootSpace])) {
|
||
|
$path = self::$namespaces[$rootSpace]
|
||
|
. DIRECTORY_SEPARATOR
|
||
|
. implode(DIRECTORY_SEPARATOR, array_slice($classPath, $i + 1))
|
||
|
. self::EXTENSION;
|
||
|
|
||
|
if(is_file($path)) {
|
||
|
require_once $path;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Registers a directory with a namespace. Projects making use of Index may use this.
|
||
|
*
|
||
|
* @param string $namespace Target namespace.
|
||
|
* @param string $directory Directory containing the classes of this namespace.
|
||
|
* @throws InvalidArgumentException if $namespace is an empty string.
|
||
|
* @throws InvalidArgumentException if $directory is a non-existent directory.
|
||
|
* @throws InvalidArgumentException if $namespace is already registered.
|
||
|
*/
|
||
|
public static function addNamespace(string $namespace, string $directory): void {
|
||
|
if(empty($namespace))
|
||
|
throw new InvalidArgumentException('$namespace may not be an empty string.');
|
||
|
if(!is_dir($directory))
|
||
|
throw new InvalidArgumentException('$directory must point to an existing directory.');
|
||
|
|
||
|
$namespace = self::cleanName($namespace);
|
||
|
$directory = rtrim(realpath($directory), DIRECTORY_SEPARATOR);
|
||
|
|
||
|
if(isset(self::$namespaces[$namespace]))
|
||
|
throw new InvalidArgumentException("{$namespace} is already a registered namespace.");
|
||
|
|
||
|
self::$namespaces[$namespace] = $directory;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes a registered namespace.
|
||
|
*
|
||
|
* Attempts to unregister Index are ignored.
|
||
|
*
|
||
|
* @param string $namespace Namespace to be removed.
|
||
|
*/
|
||
|
public static function removeNamespace(string $namespace): void {
|
||
|
$namespace = self::cleanName($namespace);
|
||
|
if($namespace !== 'Index')
|
||
|
unset(self::$namespaces[$namespace]);
|
||
|
}
|
||
|
}
|