MDL-81456 core: Use DI for all hook access

Using DI for all hook access means that it becomes significantly easier
to mock hooks and callbacks for unit testing without fundamentally
altering the structure of the code purely for the purposes of unit
testing.
This commit is contained in:
Andrew Nicols 2024-04-07 22:33:30 +08:00
parent 26649f5750
commit 2b49ad42f5
No known key found for this signature in database
GPG Key ID: 6D1E3157C8CFBF14
17 changed files with 89 additions and 42 deletions

View File

@ -91,7 +91,7 @@ class hook_list_table extends flexible_table {
*/
public function out(): void {
// All hook consumers referenced from the db/hooks.php files.
$hookmanager = \core\hook\manager::get_instance();
$hookmanager = \core\di::get(\core\hook\manager::class);
$allhooks = (array)$hookmanager->get_all_callbacks();
// Add any unused hooks.

View File

@ -30,7 +30,7 @@ require_once($CFG->libdir . '/tablelib.php');
admin_externalpage_setup('hooksoverview');
require_capability('moodle/site:config', \core\context\system::instance());
$hookmanager = \core\hook\manager::get_instance();
$hookmanager = \core\di::get(\core\hook\manager::class);
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('hooksoverview', 'core_admin'));

View File

@ -334,7 +334,7 @@ class manager {
// Allow plugins to callback as soon possible after user has passed MFA.
$hook = new \tool_mfa\hook\after_user_passed_mfa();
\core\hook\manager::get_instance()->dispatch($hook);
\core\di::get(\core\hook\manager::class)->dispatch($hook);
// Add/update record in DB for users last mfa auth.
self::update_pass_time();

View File

@ -130,7 +130,7 @@ class user_bulk_action_form extends moodleform {
}
// Any plugin can append user bulk actions to this list by implementing a hook callback.
\core\hook\manager::get_instance()->dispatch($hook);
\core\di::get(\core\hook\manager::class)->dispatch($hook);
// This method may be called from 'Bulk actions' and 'Browse user list' pages. Some actions
// may be irrelevant in one of the contexts and they can be excluded by specifying the

View File

@ -797,7 +797,7 @@ abstract class backup_cron_automated_helper {
// Exclude events defined by hook.
$hook = new \core_backup\hook\before_course_modified_check();
\core\hook\manager::get_instance()->dispatch($hook);
\core\di::get(\core\hook\manager::class)->dispatch($hook);
foreach ($readers as $readerpluginname => $reader) {
$params = [

View File

@ -1,5 +1,10 @@
<?php
use core\{
di,
hook,
};
defined('MOODLE_INTERNAL') || die;
require_once($CFG->libdir.'/formslib.php');
@ -419,7 +424,7 @@ class course_edit_form extends moodleform {
$handler->instance_form_definition($mform, empty($course->id) ? 0 : $course->id);
$hook = new \core_course\hook\after_form_definition($this, $mform);
\core\hook\manager::get_instance()->dispatch($hook);
di::get(hook\manager::class)->dispatch($hook);
// When two elements we need a group.
$buttonarray = array();
@ -502,7 +507,7 @@ class course_edit_form extends moodleform {
$handler->instance_form_definition_after_data($mform, empty($courseid) ? 0 : $courseid);
$hook = new \core_course\hook\after_form_definition_after_data($this, $mform);
\core\hook\manager::get_instance()->dispatch($hook);
di::get(hook\manager::class)->dispatch($hook);
}
/**
@ -550,7 +555,7 @@ class course_edit_form extends moodleform {
$errors = array_merge($errors, $handler->instance_form_validation($data, $files));
$hook = new \core_course\hook\after_form_validation($this, $data, $files);
\core\hook\manager::get_instance()->dispatch($hook);
di::get(hook\manager::class)->dispatch($hook);
$pluginerrors = $hook->get_errors();
if (!empty($pluginerrors)) {
$errors = array_merge($errors, $pluginerrors);

View File

@ -72,8 +72,9 @@ class cmactions extends baseactions {
// Modules may add some logic to renaming.
$modinfo = get_fast_modinfo($cm->course);
$hook = new \core_courseformat\hook\after_cm_name_edited($modinfo->get_cm($cm->id), $name);
\core\hook\manager::get_instance()->dispatch($hook);
\core\di::get(\core\hook\manager::class)->dispatch(
new \core_courseformat\hook\after_cm_name_edited($modinfo->get_cm($cm->id), $name),
);
// Attempt to update the grade item if relevant.
$grademodule = $DB->get_record($cm->modname, ['id' => $cm->instance]);

View File

@ -24,6 +24,10 @@
defined('MOODLE_INTERNAL') || die;
use core\{
di,
hook,
};
use core_course\external\course_summary_exporter;
use core_courseformat\base as course_format;
use core_courseformat\formatactions;
@ -2032,10 +2036,11 @@ function create_course($data, $editoroptions = NULL) {
$data->id = $newcourseid;
// Dispatch the hook for post course create actions.
$hook = new \core_course\hook\after_course_created(
course: $data,
di::get(hook\manager::class)->dispatch(
new \core_course\hook\after_course_created(
course: $data,
),
);
\core\di::get(\core\hook\manager::class)->dispatch($hook);
// Setup the blocks
blocks_add_default_course_blocks($course);
@ -2068,8 +2073,9 @@ function create_course($data, $editoroptions = NULL) {
$data->id = $course->id;
$handler->instance_form_save($data, true);
$hook = new \core_course\hook\after_form_submission($data, true);
\core\hook\manager::get_instance()->dispatch($hook);
di::get(hook\manager::class)->dispatch(
new \core_course\hook\after_form_submission($data, true),
);
return $course;
}
@ -2186,8 +2192,9 @@ function update_course($data, $editoroptions = NULL) {
$handler = core_course\customfield\course_handler::create();
$handler->instance_form_save($data);
$hook = new \core_course\hook\after_form_submission($data);
\core\hook\manager::get_instance()->dispatch($hook);
di::get(hook\manager::class)->dispatch(
new \core_course\hook\after_form_submission($data),
);
// Update with the new data
$DB->update_record('course', $data);

View File

@ -27,8 +27,12 @@
defined('MOODLE_INTERNAL') || die;
use \core_grades\component_gradeitems;
use core\{
di,
hook,
};
use core_courseformat\formatactions;
use core_grades\component_gradeitems;
require_once($CFG->dirroot.'/course/lib.php');
@ -725,11 +729,12 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
}
if ($cm->name != $moduleinfo->name) {
$hook = new \core_courseformat\hook\after_cm_name_edited(
get_fast_modinfo($course)->get_cm($cm->id),
$moduleinfo->name
di::get(hook\manager::class)->dispatch(
new \core_courseformat\hook\after_cm_name_edited(
get_fast_modinfo($course)->get_cm($cm->id),
$moduleinfo->name
),
);
\core\hook\manager::get_instance()->dispatch($hook);
}
$cm->name = $moduleinfo->name;

View File

@ -82,7 +82,7 @@ class primary extends view {
// Allow plugins to add nodes to the primary navigation.
$hook = new \core\hook\navigation\primary_extend($this);
\core\hook\manager::get_instance()->dispatch($hook);
\core\di::get(\core\hook\manager::class)->dispatch($hook);
// Search and set the active node.
$this->set_active_node();

View File

@ -28,6 +28,11 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core\{
di,
hook,
};
defined('MOODLE_INTERNAL') || die();
// CONSTANTS (Encased in phpdoc proper comments).
@ -3581,7 +3586,7 @@ function delete_user(stdClass $user) {
$hook = new \core_user\hook\before_user_deleted(
user: $user,
);
\core\di::get(\core\hook\manager::class)->dispatch($hook);
di::get(hook\manager::class)->dispatch($hook);
// Keep user record before updating it, as we have to pass this to user_deleted event.
$olduser = clone $user;
@ -4085,8 +4090,7 @@ function complete_user_login($user, array $extrauserinfo = []) {
$event->trigger();
// Allow plugins to callback as soon possible after user has completed login.
$hook = new \core\hook\user\after_complete_login();
\core\hook\manager::get_instance()->dispatch($hook);
di::get(\core\hook\manager::class)->dispatch(new \core\hook\user\after_complete_login());
// Check if the user is using a new browser or session (a new MoodleSession cookie is set in that case).
// If the user is accessing from the same IP, ignore everything (most of the time will be a new session in the same browser).
@ -7436,8 +7440,8 @@ function get_plugins_with_function($function, $file = 'lib.php', $include = true
foreach ($pluginfunctions as $plugintype => $plugins) {
foreach ($plugins as $plugin => $unusedfunction) {
$component = $plugintype . '_' . $plugin;
if ($hooks = \core\hook\manager::get_instance()->get_hooks_deprecating_plugin_callback($plugincallback)) {
if (\core\hook\manager::get_instance()->is_deprecating_hook_present($component, $plugincallback)) {
if ($hooks = di::get(hook\manager::class)->get_hooks_deprecating_plugin_callback($plugincallback)) {
if (di::get(hook\manager::class)->is_deprecating_hook_present($component, $plugincallback)) {
// Ignore the old callback, it is there only for older Moodle versions.
unset($pluginfunctions[$plugintype][$plugin]);
} else {
@ -7675,8 +7679,9 @@ function component_callback($component, $function, array $params = array(), $def
if ($functionname) {
if ($migratedtohook) {
if ($hooks = \core\hook\manager::get_instance()->get_hooks_deprecating_plugin_callback($function)) {
if (\core\hook\manager::get_instance()->is_deprecating_hook_present($component, $function)) {
$hookmanager = di::get(hook\manager::class);
if ($hooks = $hookmanager->get_hooks_deprecating_plugin_callback($function)) {
if ($hookmanager->is_deprecating_hook_present($component, $function)) {
// Do not call the old lib.php callback,
// it is there for compatibility with older Moodle versions only.
return null;
@ -7776,8 +7781,9 @@ function component_class_callback($classname, $methodname, array $params, $defau
$functionparts = explode('\\', trim($fullfunction, '\\'));
$component = $functionparts[0];
$callback = end($functionparts);
if ($hooks = \core\hook\manager::get_instance()->get_hooks_deprecating_plugin_callback($callback)) {
if (\core\hook\manager::get_instance()->is_deprecating_hook_present($component, $callback)) {
$hookmanager = di::get(hook\manager::class);
if ($hooks = $hookmanager->get_hooks_deprecating_plugin_callback($callback)) {
if ($hookmanager->is_deprecating_hook_present($component, $callback)) {
// Do not call the old class callback,
// it is there for compatibility with older Moodle versions only.
return null;

View File

@ -262,6 +262,7 @@ if (PHPUNIT_UTIL) {
// Make sure the hook manager gets initialised before anybody tries to override callbacks,
// this is not using caches intentionally to help with development.
// Note: We cannot use DI at this point in the bootstrap either.
\core\hook\manager::get_instance();
// Is database and dataroot ready for testing?

View File

@ -14,6 +14,11 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
use core\{
di,
hook,
};
/**
* Advanced PHPUnit test case customised for Moodle.
*
@ -486,7 +491,7 @@ abstract class advanced_testcase extends base_testcase {
* @return void
*/
public function redirectHook(string $hookname, callable $callback): void {
\core\hook\manager::get_instance()->phpunit_redirect_hook($hookname, $callback);
di::get(hook\manager::class)->phpunit_redirect_hook($hookname, $callback);
}
/**
@ -495,7 +500,7 @@ abstract class advanced_testcase extends base_testcase {
* @return void
*/
public function stopHookRedirections(): void {
\core\hook\manager::get_instance()->phpunit_stop_redirections();
di::get(hook\manager::class)->phpunit_stop_redirections();
}
/**

View File

@ -23,6 +23,11 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core\{
di,
hook,
};
require_once(__DIR__.'/../../testing/classes/util.php');
require_once(__DIR__ . "/coverage_info.php");
@ -105,7 +110,7 @@ class phpunit_util extends testing_util {
global $DB, $CFG, $USER, $SITE, $COURSE, $PAGE, $OUTPUT, $SESSION, $FULLME, $FILTERLIB_PRIVATE;
// Stop all hook redirections.
\core\hook\manager::get_instance()->phpunit_stop_redirections();
di::get(hook\manager::class)->phpunit_stop_redirections();
// Reset the hook manager instance.
\core\hook\manager::phpunit_reset_instance();

View File

@ -17,6 +17,10 @@
namespace mod_quiz;
use coding_exception;
use core\{
di,
hook,
};
use mod_quiz\event\quiz_grade_updated;
use mod_quiz\hook\structure_modified;
use question_engine_data_mapper;
@ -100,7 +104,7 @@ class grade_calculator {
component_class_callback($callbackclass, 'callback', [$quiz->id], null, true);
}
\core\hook\manager::get_instance()->dispatch(new structure_modified($this->quizobj->get_structure()));
di::get(hook\manager::class)->dispatch(new structure_modified($this->quizobj->get_structure()));
}
/**

View File

@ -21,6 +21,10 @@ use block_contents;
use cm_info;
use coding_exception;
use context_module;
use core\{
di,
hook,
};
use Exception;
use html_writer;
use mod_quiz\hook\attempt_state_changed;
@ -1787,7 +1791,7 @@ class quiz_attempt {
// Trigger event.
$this->fire_state_transition_event('\mod_quiz\event\attempt_submitted', $timestamp, $studentisonline);
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
di::get(hook\manager::class)->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
// Tell any access rules that care that the attempt is over.
$this->get_access_manager($timestamp)->current_attempt_finished();
}
@ -1835,7 +1839,7 @@ class quiz_attempt {
$this->fire_state_transition_event('\mod_quiz\event\attempt_becameoverdue', $timestamp, $studentisonline);
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
di::get(hook\manager::class)->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
$transaction->allow_commit();
quiz_send_overdue_message($this);
@ -1859,7 +1863,7 @@ class quiz_attempt {
$this->fire_state_transition_event('\mod_quiz\event\attempt_abandoned', $timestamp, $studentisonline);
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
di::get(hook\manager::class)->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
$transaction->allow_commit();
}
@ -1890,7 +1894,7 @@ class quiz_attempt {
$this->fire_state_transition_event('\mod_quiz\event\attempt_reopened', $timestamp, false);
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
di::get(hook\manager::class)->dispatch(new attempt_state_changed($originalattempt, $this->attempt));
$timeclose = $this->get_access_manager($timestamp)->get_end_time($this->attempt);
if ($timeclose && $timestamp > $timeclose) {
$this->process_finish($timestamp, false, $timeclose);

View File

@ -35,6 +35,10 @@ require_once($CFG->libdir . '/completionlib.php');
require_once($CFG->libdir . '/filelib.php');
require_once($CFG->libdir . '/questionlib.php');
use core\{
di,
hook,
};
use core_question\local\bank\condition;
use mod_quiz\access_manager;
use mod_quiz\event\attempt_submitted;
@ -147,7 +151,7 @@ function quiz_create_attempt(quiz_settings $quizobj, $attemptnumber, $lastattemp
$attempt->timecheckstate = $timeclose;
}
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed(null, $attempt));
di::get(hook\manager::class)->dispatch(new attempt_state_changed(null, $attempt));
return $attempt;
}
@ -459,7 +463,7 @@ function quiz_delete_attempt($attempt, $quiz) {
component_class_callback($callbackclass, 'callback', [$quiz->id], null, true);
}
\core\hook\manager::get_instance()->dispatch(new attempt_state_changed($attempt, null));
di::get(hook\manager::class)->dispatch(new attempt_state_changed($attempt, null));
}
// Search quiz_attempts for other instances by this user.