Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 80 additions & 87 deletions flight/core/Loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@

namespace flight\core;

use Closure;
use Exception;
use Throwable;

/**
* The Loader class is responsible for loading objects. It maintains
* a list of reusable class instances and can generate a new class
* instances with custom initialization parameters. It also performs
* class autoloading.
* Responsible for instantiating classes. It maintains a list of reusable
* instances and can generate new instances with custom constructor arguments.
* It also performs automatic class loading.
*
* @license MIT, http://flightphp.com/license
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
Expand All @@ -21,102 +19,89 @@ class Loader
/**
* Registered classes.
*
* @var array<string, array{class-string|Closure(): object, array<int, mixed>, ?callable}> $classes
* @var array<string, array{
* class-string<object>|callable(mixed ...$constructorArguments): object,
* mixed[],
* ?callable(object $instance): void,
* }>
*/
protected array $classes = [];

/**
* If this is disabled, classes can load with underscores
*/
/** If this is disabled, classes can load with underscores */
protected static bool $v2ClassLoading = true;

/**
* Class instances.
*
* @var array<string, object>
*/
/** @var array<string, object> Class instances */
protected array $instances = [];

/**
* Autoload directories.
*
* @var array<int, string>
*/
/** @var string[] Autoload directories */
protected static array $dirs = [];

/**
* Registers a class.
*
* @param string $name Registry name
* @param class-string<T>|(Closure(): T) $class Class name or function to instantiate class
* @param array<int, mixed> $params Class initialization parameters
* @param null|(Closure(T $instance): void) $callback Function to call after object instantiation
*
* @template T of object
* @param string $name Class alias.
* @param class-string<T>|callable(mixed ...$constructorArguments): T $class Class factory.
* @param mixed[] $params Class constructor arguments.
* @param ?callable(T $instance): void $callback After instantiation callable.
*/
public function register(string $name, $class, array $params = [], ?callable $callback = null): void
{
unset($this->instances[$name]);

$this->classes[$name] = [$class, $params, $callback];
}

/**
* Unregisters a class.
*
* @param string $name Registry name
* @param string $name Class alias.
*/
public function unregister(string $name): void
{
unset($this->classes[$name]);
}

/**
* Loads a registered class.
*
* @param string $name Method name
* @param bool $shared Shared instance
* Gets an instance of a registered class.
*
* @throws Exception
*
* @return ?object Class instance
* @param string $name Class alias.
* @param bool $shared Whether to return a shared instance or create a new one.
* @return ?object
* @throws Throwable
*/
public function load(string $name, bool $shared = true): ?object
{
$obj = null;
$instance = null;

if (isset($this->classes[$name])) {
[0 => $class, 1 => $params, 2 => $callback] = $this->classes[$name];
if (!isset($this->classes[$name])) {
return null;
}

$exists = isset($this->instances[$name]);
[$factory, $constructorArguments, $onAfterInstantiating] = $this->classes[$name];
$exists = isset($this->instances[$name]);

if ($shared) {
$obj = ($exists) ?
$this->getInstance($name) :
$this->newInstance($class, $params);
if ($shared) {
$instance = $exists
? $this->getInstance($name)
: $this->newInstance($factory, $constructorArguments);

if (!$exists) {
$this->instances[$name] = $obj;
}
} else {
$obj = $this->newInstance($class, $params);
}
$this->instances[$name] ??= $instance;
} else {
$instance = $this->newInstance($factory, $constructorArguments);
}

if ($callback && (!$shared || !$exists)) {
$ref = [&$obj];
\call_user_func_array($callback, $ref);
}
if ($onAfterInstantiating && (!$shared || !$exists)) {
$onAfterInstantiating($instance);
}

return $obj;
return $instance;
}

/**
* Gets a single instance of a class.
* Gets a single instance.
*
* @param string $name Instance name
*
* @return ?object Class instance
* @param string $name Class alias.
* @return ?object
*/
public function getInstance(string $name): ?object
{
Expand All @@ -126,39 +111,37 @@ public function getInstance(string $name): ?object
/**
* Gets a new instance of a class.
*
* @param class-string<T>|Closure(): class-string<T> $class Class name or callback function to instantiate class
* @param array<int, string> $params Class initialization parameters
*
* @template T of object
*
* @throws Exception
*
* @return T Class instance
* @template T of object = object
* @param class-string<T>|callable(mixed ...$constructorArguments): T $class Class factory.
* @param mixed[] $params Class constructor arguments.
* @return T
* @throws Throwable
*/
public function newInstance($class, array $params = [])
public function newInstance($class, array $params = []): object
{
if (\is_callable($class)) {
return \call_user_func_array($class, $params);
if (is_callable($class)) {
return $class(...$params);
}

return new $class(...$params);
}

/**
* Gets a registered callable
*
* @param string $name Registry name
*
* @return mixed Class information or null if not registered
* Gets a registered class factory, constructor arguments and after instantiation callable.
*
* @param string $name Class alias.
* @return ?array{
* class-string<object>|callable(mixed ...$constructorArguments): object,
* mixed[],
* ?callable(object $instance): void,
* }
*/
public function get(string $name)
{
return $this->classes[$name] ?? null;
}

/**
* Resets the object to the initial state.
*/
/** Resets the Loader by clearing registered classes and instances */
public function reset(): void
{
$this->classes = [];
Expand All @@ -170,8 +153,8 @@ public function reset(): void
/**
* Starts/stops autoloader.
*
* @param bool $enabled Enable/disable autoloading
* @param string|iterable<int, string> $dirs Autoload directories
* @param bool $enabled Enable/disable autoloading.
* @param string|string[] $dirs Autoload directories.
Comment thread
fadrian06 marked this conversation as resolved.
*/
public static function autoload(bool $enabled = true, $dirs = []): void
{
Expand Down Expand Up @@ -211,30 +194,40 @@ public static function loadClass(string $class): void
/**
* Adds a directory for autoloading classes.
*
* @param string|iterable<int, string> $dir Directory path
* @param string|string[] $dir Directory path.
*/
public static function addDirectory($dir): void
{
if (is_iterable($dir)) {
foreach ($dir as $value) {
self::addDirectory($value);
}
} elseif (is_string($dir)) {
$dir = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $dir);

if (!in_array($dir, self::$dirs, true)) {
self::$dirs[] = $dir;
}
return;
}

if (!is_string($dir)) {
return;
}

$dir = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $dir);

if (in_array($dir, self::$dirs)) {
Comment thread
fadrian06 marked this conversation as resolved.
return;
}

self::$dirs[] = $dir;
}


/**
* Sets the value for V2 class loading.
* Sets v2 class loading mode.
*
* @param bool $value The value to set for V2 class loading.
* When true (default), underscores in class names are converted to directory
* separators (e.g. "Foo_Bar" loads "Foo/Bar.php"). Set to false to disable
* this conversion and treat underscores as literal characters in the filename.
*
* @return void
* @param bool $value True to convert underscores to directory separators (v2 behaviour); false to disable.
*/
Comment thread
fadrian06 marked this conversation as resolved.
public static function setV2ClassLoading(bool $value): void
{
Expand Down