This commit is contained in:
Sara Arjona
2024-04-08 16:11:26 +02:00
29 changed files with 205 additions and 185 deletions
+1 -1
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.
+1 -1
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'));
+1 -1
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();
+1 -1
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
@@ -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 = [
+8 -3
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);
+3 -2
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]);
+14 -7
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);
+10 -5
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;
+8 -31
View File
@@ -45,7 +45,6 @@ use Psr\EventDispatcher\StoppableEventInterface;
final class manager implements
EventDispatcherInterface,
ListenerProviderInterface {
/** @var ?manager the one instance of listener provider and dispatcher */
private static $instance = null;
@@ -81,41 +80,25 @@ final class manager implements
/**
* Factory method for testing of hook manager in PHPUnit tests.
*
* Please note that the result of this method should typically be passed to \core\di::set().
*
* @param array $componentfiles list of hook callback files for each component.
* @param bool $persist If true, the test instance will be stored in self::$instance. Be sure to call $this->resetAfterTest()
* in your test if you use this.
* @return self
*/
public static function phpunit_get_instance(array $componentfiles, bool $persist = false): manager {
public static function phpunit_get_instance(array $componentfiles): manager {
if (!PHPUNIT_TEST) {
throw new \coding_exception('Invalid call of manager::phpunit_get_instance() outside of tests');
}
$instance = new self();
$instance->load_callbacks($componentfiles);
if ($persist) {
self::$instance = $instance;
}
return $instance;
}
/**
* Reset self::$instance so that future calls to ::get_instance() will return a regular instance.
*
* @return void
*/
public static function phpunit_reset_instance(): void {
if (!PHPUNIT_TEST) {
throw new \coding_exception('Invalid call of manager::phpunit_reset_instance() outside of tests');
}
self::$instance = null;
}
/**
* Override hook callbacks for testing purposes.
*
* @param string $hookname
* @param callable $callback
* @return void
*/
public function phpunit_redirect_hook(string $hookname, callable $callback): void {
if (!PHPUNIT_TEST) {
@@ -126,8 +109,6 @@ final class manager implements
/**
* Cancel all redirections of hook callbacks.
*
* @return void
*/
public function phpunit_stop_redirections(): void {
if (!PHPUNIT_TEST) {
@@ -167,7 +148,7 @@ final class manager implements
* callable MUST be type-compatible with $event.
* Please note that in Moodle the callable must be a string.
*/
public function getListenersForEvent(object $event): iterable {
public function getListenersForEvent(object $event): iterable { // phpcs:ignore
// Callbacks are sorted by priority, highest first at load-time.
$hookclassname = get_class($event);
$callbacks = $this->get_callbacks_for_hook($hookclassname);
@@ -254,7 +235,7 @@ final class manager implements
debugging(
"Cannot execute callback '$callablename' from '$component'" .
"Callback method not callable.",
DEBUG_DEVELOPER
DEBUG_DEVELOPER,
);
return false;
}
@@ -324,8 +305,6 @@ final class manager implements
/**
* Initialise list of all callbacks for each hook.
*
* @return void
*/
private function init_standard_callbacks(): void {
global $CFG;
@@ -380,7 +359,6 @@ final class manager implements
* Load callbacks from component db/hooks.php files.
*
* @param array $componentfiles list of all components with their callback files
* @return void
*/
private function load_callbacks(array $componentfiles): void {
$this->allcallbacks = [];
@@ -504,14 +482,13 @@ final class manager implements
*
* @param string $component component where hook callbacks are defined
* @param string $hookfile file with list of all callbacks for component
* @return void
*/
private function add_component_callbacks(string $component, string $hookfile): void {
if (!file_exists($hookfile)) {
return;
}
$parsecallbacks = function($hookfile) {
$parsecallbacks = function ($hookfile) {
$callbacks = [];
include($hookfile);
return $callbacks;
@@ -577,7 +554,7 @@ final class manager implements
if (!str_contains($classmethod, '::')) {
debugging(
"Hook callback definition contains invalid 'callback' static class method string in '$component'",
DEBUG_DEVELOPER
DEBUG_DEVELOPER,
);
return null;
}
@@ -599,7 +576,7 @@ final class manager implements
public function is_deprecated_plugin_callback(string $plugincallback): bool {
debugging(
'is_deprecated_plugin_callback method is deprecated, use get_hooks_deprecating_plugin_callback instead.',
DEBUG_DEVELOPER
DEBUG_DEVELOPER,
);
return (bool)$this->get_hooks_deprecating_plugin_callback($plugincallback);
}
+1 -1
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();
+15 -9
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;
+1
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?
+69 -2
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();
}
/**
@@ -763,4 +768,66 @@ abstract class advanced_testcase extends base_testcase {
return $clock;
}
/**
* Add a mocked plugintype to Moodle.
*
* A new plugintype name must be provided with a path to the plugintype's root.
*
* Please note that tests calling this method must be run in separate isolation mode.
* Please avoid using this if at all possible.
*
* @param string $plugintype The name of the plugintype
* @param string $path The path to the plugintype's root
*/
protected function add_mocked_plugintype(
string $plugintype,
string $path,
): void {
require_phpunit_isolation();
$mockedcomponent = new \ReflectionClass(\core_component::class);
$plugintypes = $mockedcomponent->getStaticPropertyValue('plugintypes');
if (array_key_exists($plugintype, $plugintypes)) {
throw new \coding_exception("The plugintype '{$plugintype}' already exists.");
}
$plugintypes[$plugintype] = $path;
$mockedcomponent->setStaticPropertyValue('plugintypes', $plugintypes);
$this->resetDebugging();
}
/**
* Add a mocked plugin to Moodle.
*
* A new plugin name must be provided with a path to the plugin's root.
* The plugin type must already exist (or have been mocked separately).
*
* Please note that tests calling this method must be run in separate isolation mode.
* Please avoid using this if at all possible.
*
* @param string $plugintype The name of the plugintype
* @param string $pluginname The name of the plugin
* @param string $path The path to the plugin's root
*/
protected function add_mocked_plugin(
string $plugintype,
string $pluginname,
string $path,
): void {
require_phpunit_isolation();
$mockedcomponent = new \ReflectionClass(\core_component::class);
$plugins = $mockedcomponent->getStaticPropertyValue('plugins');
if (!array_key_exists($plugintype, $plugins)) {
$plugins[$plugintype] = [];
}
$plugins[$plugintype][$pluginname] = $path;
$mockedcomponent->setStaticPropertyValue('plugins', $plugins);
$this->resetDebugging();
}
}
+6 -4
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,10 +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();
// Reset the hook manager instance.
\core\hook\manager::phpunit_reset_instance();
di::get(hook\manager::class)->phpunit_stop_redirections();
// Stop any message redirection.
self::stop_message_redirection();
+46 -107
View File
@@ -16,6 +16,8 @@
namespace core\hook;
use core\di;
/**
* Hooks tests.
*
@@ -49,27 +51,8 @@ final class manager_test extends \advanced_testcase {
$componentfiles = [
'test_plugin1' => __DIR__ . '/../fixtures/hook/hooks1_valid.php',
];
$testmanager = manager::phpunit_get_instance($componentfiles, true);
$testmanager = manager::phpunit_get_instance($componentfiles);
$this->assertSame(['test_plugin\\hook\\hook'], $testmanager->get_hooks_with_callbacks());
// With $persist = true, get_instance() returns the test instance until reset.
$manager = manager::get_instance();
$this->assertSame($testmanager, $manager);
}
/**
* Test resetting the manager test instance.
*
* @covers ::phpunit_reset_instance
* @return void
*/
public function test_phpunit_reset_instance(): void {
$testmanager = manager::phpunit_get_instance([], true);
$manager = manager::get_instance();
$this->assertSame($testmanager, $manager);
manager::phpunit_reset_instance();
$manager = manager::get_instance();
$this->assertNotSame($testmanager, $manager);
}
/**
@@ -320,42 +303,13 @@ final class manager_test extends \advanced_testcase {
/**
* Register a fake plugin called hooktest in the component manager.
*
* @return void
* Tests consuming this helpers must run in a separate process.
*/
protected function setup_hooktest_plugin(): void {
global $CFG;
$mockedcomponent = new \ReflectionClass(\core_component::class);
$mockedplugintypes = $mockedcomponent->getProperty('plugintypes');
$mockedplugintypes->setAccessible(true);
$plugintypes = $mockedplugintypes->getValue();
$plugintypes['fake'] = "{$CFG->dirroot}/lib/tests/fixtures/fakeplugins";
$mockedplugintypes->setValue(null, $plugintypes);
$mockedplugins = $mockedcomponent->getProperty('plugins');
$mockedplugins->setAccessible(true);
$plugins = $mockedplugins->getValue();
$plugins['fake'] = ['hooktest' => "{$CFG->dirroot}/lib/tests/fixtures/fakeplugins/hooktest"];
$mockedplugins->setValue(null, $plugins);
$this->resetDebugging();
}
/**
* Remove the fake plugin to avoid interference with other tests.
*
* @return void
*/
protected function remove_hooktest_plugin(): void {
$mockedcomponent = new \ReflectionClass(\core_component::class);
$mockedplugintypes = $mockedcomponent->getProperty('plugintypes');
$mockedplugintypes->setAccessible(true);
$plugintypes = $mockedplugintypes->getValue();
unset($plugintypes['fake']);
$mockedplugintypes->setValue(null, $plugintypes);
$mockedplugins = $mockedcomponent->getProperty('plugins');
$mockedplugins->setAccessible(true);
$plugins = $mockedplugins->getValue();
unset($plugins['fake']);
$mockedplugins->setValue(null, $plugins);
$this->add_mocked_plugintype('fake', "{$CFG->dirroot}/lib/tests/fixtures/hook/fakeplugins");
$this->add_mocked_plugin('fake', 'hooktest', "{$CFG->dirroot}/lib/tests/fixtures/hook/fakeplugins/hooktest");
}
/**
@@ -363,25 +317,24 @@ final class manager_test extends \advanced_testcase {
*
* The original callback should be called, but a debugging message should be output.
*
* @covers ::get_hooks_deprecating_plugin_callback()
* @covers ::is_deprecating_hook_present()
* @return void
* @throws \coding_exception
* @runInSeparateProcess
*/
public function test_migrated_callback(): void {
$this->resetAfterTest(true);
// Include plugin hook discovery agent, and the hook that replaces the callback.
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hooks.php');
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
// Register the fake plugin with the component manager.
$this->setup_hooktest_plugin();
// Register the fake plugin with the hook manager, but don't define any hook callbacks.
manager::phpunit_get_instance(
[
'fake_hooktest' => __DIR__ . '/../fixtures/fakeplugins/hooktest/db/hooks_nocallbacks.php',
],
true
di::set(
manager::class,
manager::phpunit_get_instance(
[
'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks_nocallbacks.php',
],
),
);
// Confirm a non-deprecated callback is called as expected.
@@ -393,10 +346,9 @@ final class manager_test extends \advanced_testcase {
component_callback('fake_hooktest', 'old_callback', [], null, true)
);
$this->assertDebuggingCalled(
'Callback old_callback in fake_hooktest component should be migrated to new hook '.
'Callback old_callback in fake_hooktest component should be migrated to new hook ' .
'callback for fake_hooktest\hook\hook_replacing_callback'
);
$this->remove_hooktest_plugin();
}
/**
@@ -404,26 +356,23 @@ final class manager_test extends \advanced_testcase {
*
* The original callback should not be called, and no debugging should be output.
*
* @covers ::get_hooks_deprecating_plugin_callback()
* @covers ::is_deprecating_hook_present()
* @return void
* @throws \coding_exception
* @runInSeparateProcess
*/
public function test_migrated_callback_with_replacement(): void {
$this->resetAfterTest(true);
// Include plugin hook discovery agent, and the hook that replaces the callback, and a hook callback for the hook.
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hooks.php');
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook_callbacks.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_callback.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook_callbacks.php');
// Register the fake plugin with the component manager.
$this->setup_hooktest_plugin();
// Register the fake plugin with the hook manager, including the hook callback.
manager::phpunit_get_instance(
[
'fake_hooktest' => __DIR__ . '/../fixtures/fakeplugins/hooktest/db/hooks.php',
],
true
di::set(
manager::class,
manager::phpunit_get_instance([
'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks.php',
]),
);
// Confirm a non-deprecated callback is called as expected.
@@ -432,7 +381,6 @@ final class manager_test extends \advanced_testcase {
// Confirm the deprecated callback is not called, as expected.
$this->assertNull(component_callback('fake_hooktest', 'old_callback', [], null, true));
$this->assertDebuggingNotCalled();
$this->remove_hooktest_plugin();
}
/**
@@ -440,26 +388,23 @@ final class manager_test extends \advanced_testcase {
*
* The original class callback should be called, but a debugging message should be output.
*
* @covers ::get_hooks_deprecating_plugin_callback()
* @covers ::is_deprecating_hook_present()
* @return void
* @throws \coding_exception
* @runInSeparateProcess
*/
public function test_migrated_class_callback(): void {
$this->resetAfterTest(true);
// Include plugin hook discovery agent, the class containing callbacks, and the hook that replaces the class callback.
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/callbacks.php');
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hooks.php');
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/callbacks.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
// Register the fake plugin with the component manager.
$this->setup_hooktest_plugin();
// Register the fake plugin with the hook manager, but don't define any hook callbacks.
manager::phpunit_get_instance(
[
'fake_hooktest' => __DIR__ . '/../fixtures/fakeplugins/hooktest/db/hooks_nocallbacks.php',
],
true
di::set(
manager::class,
manager::phpunit_get_instance([
'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks_nocallbacks.php',
]),
);
// Confirm a non-deprecated class callback is called as expected.
@@ -474,10 +419,9 @@ final class manager_test extends \advanced_testcase {
component_class_callback('fake_hooktest\callbacks', 'old_class_callback', [], null, true)
);
$this->assertDebuggingCalled(
'Callback callbacks::old_class_callback in fake_hooktest component should be migrated to new hook '.
'Callback callbacks::old_class_callback in fake_hooktest component should be migrated to new hook ' .
'callback for fake_hooktest\hook\hook_replacing_class_callback'
);
$this->remove_hooktest_plugin();
}
/**
@@ -485,28 +429,25 @@ final class manager_test extends \advanced_testcase {
*
* The original callback should not be called, and no debugging should be output.
*
* @covers ::get_hooks_deprecating_plugin_callback()
* @covers ::is_deprecating_hook_present()
* @return void
* @throws \coding_exception
* @runInSeparateProcess
*/
public function test_migrated_class_callback_with_replacement(): void {
$this->resetAfterTest(true);
// Include plugin hook discovery agent, the class containing callbacks, the hook that replaces the class callback,
// and a hook callback for the new hook.
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/callbacks.php');
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hooks.php');
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
require_once(__DIR__ . '/../fixtures/fakeplugins/hooktest/classes/hook_callbacks.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/callbacks.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hooks.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook/hook_replacing_class_callback.php');
require_once(__DIR__ . '/../fixtures/hook/fakeplugins/hooktest/classes/hook_callbacks.php');
// Register the fake plugin with the component manager.
$this->setup_hooktest_plugin();
// Register the fake plugin with the hook manager, including the hook callback.
manager::phpunit_get_instance(
[
'fake_hooktest' => __DIR__ . '/../fixtures/fakeplugins/hooktest/db/hooks.php',
],
true
di::set(
manager::class,
manager::phpunit_get_instance([
'fake_hooktest' => __DIR__ . '/../fixtures/hook/fakeplugins/hooktest/db/hooks.php',
]),
);
// Confirm a non-deprecated class callback is called as expected.
@@ -518,14 +459,12 @@ final class manager_test extends \advanced_testcase {
// Confirm the deprecated class callback is not called, as expected.
$this->assertNull(component_class_callback('fake_hooktest\callbacks', 'old_class_callback', [], null, true));
$this->assertDebuggingNotCalled();
$this->remove_hooktest_plugin();
}
/**
* Normalise the sort order of callbacks to help with asserts.
*
* @param array $callbacks
* @return void
*/
private function normalise_callbacks(array &$callbacks): void {
foreach ($callbacks as &$callback) {
-2
View File
@@ -103,8 +103,6 @@ information provided here is intended especially for developers.
* Deprecated core\hook\manager::is_deprecated_plugin_callback() in favour of ::get_hooks_deprecating_plugin_callback(),
which will return the classnames of hooks deprecating a callback, or null if it's not deprecated. The return value can be cast
to bool if the original functionality is desired.
* core\hook\manager::phpunit_get_instance() now sets self::$instance to the mocked instance if the optional $persist argument is
true, so future calls to ::get_instance() will return it.
* The triggerSelector method in the `core/comboboxsearch/search_combobox` JS module is deprecated. It was not used.
* PHPUnit has been upgraded to 9.6 (see MDL-81266 for details).
The main goal of the update is to allow developers to know in advance,
+5 -1
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()));
}
/**
+8 -4
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);
+6 -2
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.