Skip to main content
Version: 4.4

Moodle 4.4 developer update

This page highlights the important changes that are coming in Moodle 4.4 for developers.

Core changes

Dependency Injection

Since 4.4

Support for PSR-11 compatible Containers has been introduced and can be accessed via the \core\di class. Read the full documentation for information on how to use Moodle's DI infrastructure.


Since 4.4

PHP 8.0 introduced support for the Attribute language feature and Moodle is beginning to make use of this in small but helpful ways.

To simplify this adoption, a new \core\attribute_helper class has been created with methods to quickly and easily fetch the \ReflectionAttribute for any relevant attributes.

The new methods can search for an attribute using a reference, which can be:

  • a string, whose name represents a global function, a class, or a class combined with a method, property, constant, or enum
  • an instantiated object
  • an array whose first value is either a string or object, and whose optional second value is a child of the first value
Fetching attributes

use core\attribute_helper;
use core\attribute\{label, tags};

// Get a label on the \some_function_name() method.
$label = attribute_helper::one_from('some_function_name', label::class)?->newInstance();

// Get an instance of a label on the \example class.
$label = attribute_helper::one_from(example::class, label::class)?->newInstance();

// Get an instance of a label on an instance of the \example class.
$example = new example();
$label = attribute_helper::one_from($example, label::class)?->newInstance();

// Get an instance of a label on a constant, property, or method of the \example class.
$label = attribute_helper::one_from([example::class, 'some_child'], label::class)?->newInstance();

// Get an instance of a label on a constant, property, or method of an instance of the \example class.
$example = new example();
$label = attribute_helper::one_from([$example, 'some_child'], label::class)?->newInstance();

Other variations of the above are also possible.


Hook interfaces

Since 4.4

Attribute-based alternatives to the \core\hook\described_hook, and \core\hook\deprecated_callback_replacement interfaces are now supported.

  • the \core\attribute\label attribute can be used to add a string-based description of the hook.
  • the \core\attribute\tags attribute can be used to add one or more tags to describe the hook.
  • the \core\attribute\hook\replaces_callbacks attribute can be used to add one or more replaced callbacks.

namespace core\hook;

use core\attribute;

#[attribute\label('This is a description of the hook')]
#[attribute\tags('examples', 'navigation', 'authentication')]
class before_navigation_render {
public function __construct(
public readonly \navigation_node $navigation,
) {

It is still possible to use the \core\hook\described_hook and \core\hook\deprecated_callback_replacement interfaces. The attribute approach is provided as a more light-weight alternative.

Callable notation

Since 4.4

When specifying the callback for a hook, you can now do so using the callable array notation, for example:

$callbacks = [
'hook' => 'test_plugin\\hook\\hook',
// Array notation:
'callback' => [\test_plugin\callbacks::class, 'test1'],
Callbacks in earlier versions of Moodle

If writing a callback for Moodle 4.3, you must use the string notation.

String formatting

Deprecation of format_* parameters

A number of legacy features and ways of calling format_text, and format_string now emit deprecation notices. These were all deprecated a long time ago but did not emit:

  • Changes to format_string:
    • The $options array must not be a \context or \core\context object. This was never supported and behaviour was previously undetected
    • Unknown values passed to the $options parameter will now emit appropriate debugging notices
  • Changes to format_text:
    • The fourth parameter, $courseiddonotuse now emits a deprecation notice if provided. This was deprecated for Moodle 2.0 in MDL-22001
    • The smiley option now emits a deprecation notice. This was deprecated for Moodle 2.0.
    • The nocache option now emits a deprecation notice. This was deprecated for Moodle 2.3 in MDL-34347
    • The use of FORMAT_WIKI as a source format now throws an exception. Previously it added debugging information to the rendered content. This was deprecated in Moodle 1.5.
    • Unknown values passed to the $options parameter will now emit appropriate debugging notices

New \core\formatting class

Since 4.4

A new \core\formatting class has been introduced with new format_string(), and format_text() methods.

These methods no longer user the array $options style configuration, instead expanding options into method parameters.


The existing \format_string(), and \format_text() methods remain in place, and are not currently deprecated. At this time you do not need to migrate to the APIs.

New \core\formatting methods
namespace core;

class formatting {
public function format_string(
?string $string,
bool $striplinks = true,
?context $context = null,
bool $filter = true,
bool $escape = true,
): string;

public function format_text(
?string $text,
string $format = FORMAT_MOODLE,
?context $context = null,
bool $trusted = false,
?bool $clean = null,
bool $filter = true,
bool $para = true,
bool $newlines = true,
bool $overflowdiv = false,
bool $blanktarget = false,
bool $allowid = false,
): string;

If choosing to use these new methods you must use Dependency Injection either via injection into the class, or via the \core\di container. You must not instantiate the \core\formatting class directly.

Usage in an injected class
namespace core;

class example {
public function __construct(
protected readonly \core\formatting $formatter,
) {

public function do_something(string $input): string {
return $this->formatter->format_string($input, ...);
Named Parameters

It is strongly recommended that you make use of named parameters for all but the first argument when calling them, rather than using positional arguments.

Named arguments
$formatter = \core\di::get(\core\formatting::class);

"The content to be formatted",
context: \core\context\course::instance($courseid),
filter: false,

"The content to be formatted",
context: \core\context\course::instance($courseid),
filter: false,
blanktarget: true,

Tasks API

Since 4.4

Support for tasks which block all other tasks has been removed. The ability to configure a task as blocking is no longer supported and will not work.

This functionality had the ability to cause significant performance issues, and contained a number of serious bugs.


If you are using this functionality in older versions of Moodle you are advised to move away from it as soon as possible in all Moodle versions.

Since 4.4

The behaviour of the task runner has been changed to limit the number of retries allowed before a failing adhoc task is removed. Scheduled tasks are not affected.

The default value for the maximum retry limit on an Ad-hoc task is 12.

Retries can be entirely disabled by passing a value of 0, for example:

$task = new example_task();
// This task wil not be retried at all.

See the detailed documentation on how to use these new APIs.


Since 4.4

A new JS module, core/dropzone, has been introduced to provide a standardised way to create a Drop-zone in Moodle. Read the full documentation for information on how to use it.


API Change

Since 4.4

Parameter constants, and the cleaning of values using these parameters, has been moved to a new enum in \core\param.

The enum contains relevant associated methods for fetching, validating, and cleaning the content of values, for example:

Examples of enum-based parameters
// Clean an existing variable.
$value = \core\param::ALPHANUMEXT->clean($value);
$value = \core\param::ALPHANUMEXT->validate_param($value);
$value = \core\param::ALPHANUMEXT->clean_param_array($value);

// Require a param (replaced required_param).
$value = \core\param::ALPHANUMEXT->required_param('someparamname');
$value = \core\param::ALPHANUMEXT->optional_param('someparamname', 'defaultvalue');
$value = \core\param::ALPHANUMEXT->required_param_array('someparamname');
$value = \core\param::ALPHANUMEXT->optional_param_array('someparamname', []);

The existing PARAM_* constants, and related methods (required_param, optional_param(), clean_param(), and so on) remain in place, and are not currently deprecated. At this time you do not need to migrate to the APIs.


Since 4.4

A number of deprecated parameter types have been deprecated, these include:


These param types have all been deprecated since Moodle 2.0.

Introduction of deprecated attribute

A new \core\attribute\deprecated attribute, and related \core\deprecation class have been introduced to provide a standardised way to emit deprecation notices.

The attribute can be applied to:

  • classes, traits, interfaces, and enums
  • enum cases
  • global functions
  • class constants, properties, and methods

The attribute can be used to specify information including:

  • the version that a feature was deprecated
  • the relevant MDL
  • the reason for deprecation
  • any replacement
  • whether the deprecation is final

The \core\deprecation class contains helper methods to inspect for use of the deprecated attribute and allows usage including:

  • checking if a feature is deprecated
  • emitting a deprecation notice if a feature is deprecated
Examples of usage
// A method which has been initially deprecated and should show debugging.
/** @deprecated since 4.3 */
#[\core\attribute\deprecated(replacement: 'random_bytes', since: '4.3')]
function random_bytes_emulate($length) {
return random_bytes($length);

// A method which has been finally deprecated and should throw an exception.
/** @deprecated since 2.7 */
#[\core\attribute\deprecated(replacement: 'Events API', since: '2.3', final: true)]
function add_to_log() {

// Checking when an enum case is deprecated:
\core\deprecation::is_deprecated(\core\param::RAW); // Returns false.
\core\deprecation::is_deprecated(\core\param::INTEGER); // Returns true.

// Checking if a class is deprecated:
\core\deprecation::is_deprecated(\core\task\manager::class); // Returns false.

// Checking if an instantiated class is deprecated:
\core\deprecation::is_deprecated(new \moodle_url('/example/'));

// Checking if a class method is deprecated:
\core\deprecation::is_deprecated([\moodle_url::class, 'out']);
\core\deprecation::is_deprecated([new \moodle_url('/example/'), 'out']);

This functionality is intended to simplify deprecation of features such as constants, enums, and related items which are called from centralised APIs and difficult to detect as deprecated.

This functionality does not replace the phpdoc @deprecated docblock.

Clock interface

Since 4.4

Moodle now supports use of a PSR-20 compliant Clock Interface, accessed via Dependency Injection.

See the detailed documentation on how to use this new interface.


Meta enrolment support in CSV course upload

Since 4.4

It is now possible to use meta enrolment when uploading courses using CSV file. Here is an example of CSV file that can be used to upload a course with meta enrolment:

Example of CSV for meta enrolment
course1,Course 1,CAT1,meta,course2

Support for multiple instances in csv course upload

Since 4.4

It is now possible to upload a CSV file with multiple enrol instances of the same type in same course. This is useful for example when you want to enrol users in a course using two different cohorts.

To support this, a new method has been added to allow the UI to locate existing enrolment instances:

* Finds matching instances for a given course.
* @param array $enrolmentdata enrolment data.
* @param int $courseid Course ID.
* @return stdClass|null Matching instance
public function find_instance(
array $enrolmentdata,
int $courseid,
) : ?stdClass;

If your enrolment plugins supports multiple instances within the same course, you should implement this method.

Format of the CSV file

Each line of the CSV should only have data for one enrolment instance.

C1,Course 1,CAT1,cohort,student,CV1
C1,Course 1,CAT1,cohort,teacher,CV4

If a single line format is used, only the final enrolment instance will be updated. For example in the following example only the second enrolment instance will be updated:


Refactoring BS4 features dropped in BS5

Since 4.4

Some of the Bootstrap 4 classes will be deprecated or dropped in its version 5. To prepare for this, some of the current Bootstrap 4 classes usages have been replaced with version 5 compatible classes. This will help us to upgrade to Bootstrap 5 in the future.

See more information in Bootstrap 5 migration.

New course section page

Since 4.4

The new course/section.php page is designed exclusively for displaying individual section content. It only requires sectionid, eliminating the need for the legacy sectionnumber.

Enhancements to this page:

  • The General section will no longer appear above the selected section, ensuring a focused display of content for the specified section id.
  • Regardless of the Course layout ("All sections on one page" vs "One section per page"), all section names in the main course page content will now link to the new page (excluding editing mode).
  • The $CFG->linkcoursesections setting has been entirely removed. Initially used for the Classic theme to empower admins in deciding whether section names in the Navigation block should be linked, this setting is now obsolete. With the introduction of the course/section.php page, dedicated to displaying content for any single section, and a convenient link from the main course page, users can seamlessly focus on section content without the need for this setting.
  • The methods core_courseformat\base::set_section_number() and core_courseformat\base::get_section_number() have been deprecated and replaced by core_courseformat\base::set_sectionnum() and core_courseformat\base::get_sectionnum(). The new methods use the null value when all the sections must be displayed (instead of 0). That way, section 0 (General), can be displayed on a single page too.

Activity icons

Since 4.4

New designs

The activity icons have been updated to prioritize readability and transform the existing filled square shapes with white icons into outlined colored icons against a transparent background. This modification ensures that the icons are not only more accessible but also easily distinguishable for users. When upgrading the SVG files, the .png files have been removed.

Considerations for creating/updating SVG icons
  • Please make sure that the icons are filled with #212529 and that the background is transparent.
  • The icons should be 24x24px. It's recommended to define width and height into the SVG files to guarantee that the icons are displayed correctly.

New activity icons

Activity purposes

Since Moodle 4.0, in places like the course page and the activity chooser icons have a more prominent role which is defined using the FEATURE_MOD_PURPOSE feature flag.

The available activity purposes for this feature are:

Changes in purposes for Moodle 4.4
  • MOD_PURPOSE_INTERFACE has been deprecated, so it's not recommended to use it.
  • MOD_PURPOSE_INTERACTIVECONTENT has been added. In core, it's used for activities like H5P, Lesson, SCORM and IMS package.

Branded icons

A new callback, <modname>_is_branded() has been added to the modules. Branded icons are displayed with their original colours and they are not affected by the activity purpose colours. By default, none of the modules are branded.

* Whether the activity is branded.
* This information is used, for instance, to decide if a filter should be applied to the icon or not.
* @return bool True if the activity is branded, false otherwise.
function h5pactivity_is_branded(): bool {
return true;

PHPUnit 9.6 update

Since 4.4


The main goal of this minor update, from PHPUnit 9.5 to 9.6, is to start informing to developers in advance about the functionality that has been deprecated in the 9.x series and will be removed completely with the next PHPUnit 10.x update.

Deprecated stuff

Here you can find all the functionality that has been deprecated in PHPUnit 9.x and needs to be fixed, converted or removed in order to allow the tests to continue working once Moodle gets upgraded to PHPUnit 10.x:

  • A number of attribute-related assertions have been deprecated, will be removed with PHPUnit 10. Alternatives for some of them are available (Ref. MDL-81281):
    • assertClassHasAttribute()
    • assertClassNotHasAttribute()
    • assertClassHasStaticAttribute()
    • assertClassNotHasStaticAttribute()
    • assertObjectHasAttribute() => assertObjectHasProperty()
    • assertObjectNotHasAttribute() => assertObjectNotHasProperty()
  • A number of deprecation, notice, warning and error expectations have been deprecated, will be removed with PHPUnit 10. No alternative exists. A working replacement is available in the linked issue, hopefully there aren't many cases (Ref. MDL-81266):
    • expectDeprecation()
    • expectDeprecationMessage()
    • expectDeprecationMessageMatches()
    • expectError()
    • expectErrorMessage()
    • expectErrorMessageMatches()
    • expectNotice()
    • expectNoticeMessage()
    • expectNoticeMessageMatches()
    • expectWarning()
    • expectWarningMessage()
    • expectWarningMessageMatches()
  • The ->withConsecutive() functionality on PHPUnit mocks has been silently deprecated, will be removed with PHPUnit 10. Note that this won't affect PHPUnit 9.6 runs and an alternative path will be proposed in the linked issue, part of the PHPUnit 10 epic (Ref. MDL-81308).
  • PHPUnit\Framework\TestCase::getMockClass() has been deprecated, will be removed with PHPUnit 10. No clear alternative exists and won't be investigated, because there aren't cases in core.
  • Cannot use the Test suffix on abstract test case classes anymore. Proceed to rename them to end with TestCase instead.

Developer metadata

Since 4.4

The list of plugins which are shipped with Moodle core is now available in a JSON format and located at /lib/plugins.json.

The schema for this format can be found at /lib/plugins.schema.json.

See Developer metadata for more information on available metadata.

Previous versions