moodle/lib/classes/di.php
Andrew Nicols 298c13ac3b
MDL-80838 core: Add PSR-20/Clock support
This commit adds the PSR-20 ClockInterface to core, with a
moodle-specific extension to the Interface at `\core\clock`, and a
standard clock at `\core\system_clock`.

Further clocks are provided as `\incrementing_clock` and `\frozen_clock`
which are available to unit tests using:

- `$this->mock_clock_with_incrementing(?int $starttime = null);`
- `$this->mock_clock_with_frozen(?int $time = null);`

For the incrementing clock, every call to fetch the time will bump the
current time by one second.

For the frozen clock the time will not change, but can be modified with:
- `$clock->set_to(int $time);`; and
- `$clock->bump(int $seconds = 1);`
2024-04-02 10:24:54 +08:00

134 lines
4.6 KiB
PHP

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
use Psr\Container\ContainerInterface;
/**
* DI Container Helper.
*
* @package core
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class di {
/** @var ContainerInterface The stored container */
protected static ?ContainerInterface $container;
/**
* Get the DI Container.
*
* @return ContainerInterface
*/
public static function get_container(): ContainerInterface {
if (!isset(self::$container)) {
self::$container = self::create_container();
}
return self::$container;
}
/**
* Reset the DI Container.
*
* This is primarily intended for Unit Testing, and for use in Scheduled tasks.
*/
public static function reset_container(): void {
self::$container = null;
}
/**
* Finds an entry of the container by its identifier and returns it.
*
* This is a shortcut helper for \core\di::get_container()->get($id).
*
* @param string $id Identifier of the entry to look for.
* @return mixed Entry.
*/
public static function get(string $id): mixed {
return self::get_container()->get($id);
}
/**
* Set an entry in the container by its identifier.
*
* @param string $id Identifier of the entry to set
* @param mixed $value The value to set
*/
public static function set(string $id, mixed $value): void {
// Please note that the `set` method is not a part of the PSR-11 standard.
// We currently make use of PHP-DI which does have this method, but its use is not guaranteed.
// If Moodle switches to alternative DI resolution, this method _must_ be updated to work with it.
/** @var \DI\Container */
$container = self::get_container();
$container->set($id, $value);
}
/**
* Create a new Container Instance.
*
* @return ContainerInterface
*/
protected static function create_container(): ContainerInterface {
global $CFG, $DB;
// PHP Does not support function autoloading. We must manually include the file.
require_once("{$CFG->libdir}/php-di/php-di/src/functions.php");
// Configure the Container builder.
$builder = new \DI\ContainerBuilder();
// At the moment we are using autowiring, but not automatic attribute injection.
// Automatic attribute injection is a php-di specific feature.
$builder->useAutowiring(true);
if (!$CFG->debugdeveloper) {
// Enable compilation of the container and write proxies to disk in production.
// See https://php-di.org/doc/performances.html for information.
$cachedir = make_localcache_directory('di');
$builder->enableCompilation($cachedir);
$builder->writeProxiesToFile(true, $cachedir);
}
// Get the hook manager.
$hookmanager = \core\hook\manager::get_instance();
// Configure some basic definitions.
$builder->addDefinitions([
// The hook manager should be in the container.
\core\hook\manager::class => $hookmanager,
// The database.
\moodle_database::class => $DB,
// The string manager.
\core_string_manager::class => fn() => get_string_manager(),
// The Moodle Clock implementation, which itself is an extension of PSR-20.
// Alias the PSR-20 clock interface to the Moodle clock. They are compatible.
\core\clock::class => fn() => new \core\system_clock(),
\Psr\Clock\ClockInterface::class => \DI\get(\core\clock::class),
]);
// Add any additional definitions using hooks.
$hookmanager->dispatch(new \core\hook\di_configuration($builder));
// Build the container and return.
return $builder->build();
}
}