index/src/Autoloader.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]);
}
}