diff --git a/docs/apis/core/di/index.md b/docs/apis/core/di/index.md new file mode 100644 index 0000000000..5c72508efd --- /dev/null +++ b/docs/apis/core/di/index.md @@ -0,0 +1,202 @@ +--- +title: Dependency Injection +tags: + - DI + - Container + - PSR-11 + - PSR +description: The use of PSR-11 compatible Dependency Injection in Moodle +--- + +import { + Since, + ValidExample, + InvalidExample, + Tabs, + TabItem, +} from '@site/src/components'; + + + +Moodle supports the use of [PSR-11](https://www.php-fig.org/psr/psr-11/) compatible Dependency Injection, accessed using the `\core\di` class, which internally makes use of [PHP-DI](https://php-di.org). All usage should be through the `\core\di` class. + +Most class instances can be fetched using their class name without any manual configuration. Support for configuration of constructor arguments is also possible, but is generally discouraged. + +Dependencies are stored using a string id attribute, which is typically the class or interface name of the dependency. Use of other arbitrary id values is strongly discouraged. + +## Fetching dependencies + +Moodle provides a wrapper around the PSR-11 Container implementation which should be used to fetch dependencies: + +```php title="Fetching an instance of the \core\http_client class" +$client = \core\di::get(\core\http_client::class); +``` + +## Configuring dependencies + +In some rare cases you may need to supply additional configuration for a dependency to work properly. This is usually in the case of legacy code, and can be achieved with the `\core\hook\di_configuration` hook. + + + + + +The callback must be linked to the hook by specifying a callback in the plugin's `hooks.php` file: + +```php title="mod/example/db/hooks.php" + \core\hook\di_configuration::class, + 'callback' => \mod_example\hook_listener::class . '::inject_dependenices', + ], +]; +``` + + + + + +The hook listener consists of a static method on a class. + +```php title="mod/example/classes/hook_listener.php" +add_definition( + id: complex_client::class, + definition: function ( + \moodle_database $db, + ): complex_client { + global $CFG; + + return new complex_client( + db: $db, + name: $CFG->some_value, + ); + } + ) + } +} +``` + + + + + +## Mocking dependencies in Unit Tests + +One of the most convenient features of Dependency Injection is the ability to provide a mocked version of the dependency during unit testing. + +Moodle resets the Dependency Injection Container between each unit test, which means that little-to-no cleanup is required. + +```php title="Injecting a Mocked dependency" + 'Colin'])), + ])); + + // Inject the mock. + \core\di::set( + \core\http_client::class, + new http_client(['handler' => $handlerstack]), + ); + + // Call a method on the example class. + // This method uses \core\di to fetch the client and use it to fetch data. + $example \core\di::get(example::class); + $result = $example->do_the_thing(); + + // The result will be based on the mock response. + $this->assertEquals('Colin', $result->get_name()); + } +} +``` + +## Injecting the container into classes + +In more advanced cases you may wish to make use of dependency injection within an object. One way that you can do so is to inject the dependency into the class via its constructor, for example: + +```php title="Injecting the ContainerInterface" +get(\core\formatting::class)->format_string($value); + } +} +``` + +The `example` class can also be fetched using DI: + +```php title="Consuming the example class" +use mod_example\example; + +// ... +$example = \core\di::get(example::class); +$value = $example->do_something("with this string"); +``` + +## Advanced usage + +All usage of the Container _should_ be via `\core\di`, which is a wrapper around the currently-active Container implementation. In normal circumstances it is not necessary to access the underlying Container implementation directly and such usage is generally discouraged. + +:::danger + +Moodle currently makes use of PHP-DI as its Container implementation. + +Please note that the underlying Dependency Injection system _may_ change at any time. + +Care should be taken to only make use of methods which are defined in the [PSR-11 Container Interface](https://www.php-fig.org/psr/psr-11/). + +::: + +### Resetting the Container + +The Container is normally instantiated during the bootstrap phase of a script. In normal use it is not reset and there should be no need to reset it, however it is _possible_ to reset it if required. This usage is intended to be used for situations such as Unit Testing. + +:::danger + +Resetting an actively-used container can lead to unintended consequences. + +::: + +```php title="Resetting the Container" +\core\di::reset_container(): +``` + +### Accessing the Container Interface + +In some rare circumstances you may need to perform certain advanced tasks such as accessing the `ContainerInterface` implementation directly. + +The `\core\di` wrapper provides a method to fetch the `\Psr\Container\ContainerInterface` implementation. + +```php title="Fetch the ContainerInterface" +$container = \core\di::get_container(); +``` diff --git a/docs/apis/subsystems/output/index.md b/docs/apis/subsystems/output/index.md index a1c478dc55..daab1c3699 100644 --- a/docs/apis/subsystems/output/index.md +++ b/docs/apis/subsystems/output/index.md @@ -221,15 +221,11 @@ The key parameter for this function is: #### format_text() -```php -function format_text($text, $format = FORMAT_MOODLE, $options = null, $courseid_do_not_use = null) -``` - This function should be used to: - print **any html/plain/markdown/moodle text**, needing any of the features below. It is mainly used for long strings like posts, answers, glossary items, etc. - filter content through Moodle or 3rd party language filters for multi-language support. Not to be confused with [get_string](https://docs.moodle.org/dev/String_API#get_string.28.29) which is used to access localized strings in Moodle and its language packs. Together, these functions enable Moodle multi-language support . -Note that this function is really **heavy** because it supports **cleaning** of dangerous contents, delegates processing to enabled **content filter**s, supports different **formats** of text (HTML, PLAIN, MARKDOWN, MOODLE) and performs a lot of **automatic conversions** like adding smilies, build links. Also, it includes a strong **cache mechanism** (DB based) that will alleviate the server from a lot of work processing the same texts time and again. +Note that this function is really **heavy** because it supports **cleaning** of dangerous contents, delegates processing to enabled **content filters**, supports different **formats** of text (HTML, PLAIN, MARKDOWN, MOODLE) and performs a lot of **automatic conversions** like adding smilies, build links. Also, it includes a strong **cache mechanism** (DB based) that will alleviate the server from a lot of work processing the same texts time and again. Some interesting parameters for this function are: @@ -241,18 +237,12 @@ Some interesting parameters for this function are: - `options->context`: If text is filtered (and this happens by default), it is very important to specify context (id or object) for applying filters. If context is not specified it will be taken from `$PAGE->context` and may potentially result in displaying the same text differently on different pages. For example all module-related information should have module context even when it appears in course-level reports, all course-related information such as name and description should have course context even when they are displayed on front page or system pages. - `options->param`: To decide if you want every paragraph automatically enclosed between html paragraph tags (`

...

`) (defaults to true). This option only applies to `FORMAT_MOODLE`. - `options->newlines`: To decide if line feeds in text should be converted to html newlines (`
`) (defaults to true). This option only applies to `FORMAT_MOODLE`. - - `options->nocache`: If true the string will not be cached and will be formatted every call. Default false. - `options->overflowdiv`*: If set to true the formatted text will be encased in a div with the class no-overflow before being returned. Default false. - `options->allowid` : If true then id attributes will not be removed, even when using HTML Purifier. Default false. - `options->blanktarget` : If true all `` tags will have `target="_blank"` added unless target is explicitly specified. Default false. -- **courseid_do_not_use**: This parameter was earlier used to help applying filters but now is replaced with more precise `$options->context`, see above #### format_string() -```php -function format_string ($string, $striplinks = true, $options = null) -``` - This function should be used to: - print **short non-html strings that need filter processing** (activity titles, post subjects, glossary concepts...). If the string contains HTML, it will be filtered out. If you want the HTML, use `format_text()` instead. @@ -269,14 +259,12 @@ Some interesting parameters for this function are: - `options->escape`: To decide if you want to escape HTML entities. True by default. - `options->filter`: To decide if you want to allow filters to process the text (defaults to true). This is ignored by `FORMAT_PLAIN` for which filters are never applied. -:::note -In earlier versions of Moodle, the third argument was integer `$courseid`. It is still supported for legacy - if the third argument is an integer instead of an array or object, it is considered to be course id and is this course's context is passed to the filters being applied. -::: - ### Simple elements rendering :::important + Those methods are designed to replace the old ```html_writer::tag(...)``` methods. Even if many of them are just wrappers around the old methods, they are more semantic and could be overridden by component renderers. + ::: While to render complex elements, you should use [templates](../../../guides/templates/index.md), some simple elements can be rendered using the following functions: diff --git a/docs/devupdate.md b/docs/devupdate.md index fb566178df..987ecab494 100644 --- a/docs/devupdate.md +++ b/docs/devupdate.md @@ -7,10 +7,153 @@ tags: -import { Since, ValidExample, InvalidExample } from '@site/src/components'; +import { + Since, + ValidExample, + InvalidExample, + Tabs, + TabItem, +} from '@site/src/components'; This page highlights the important changes that are coming in Moodle 4.4 for developers. +## Core changes + +### Dependency Injection + + + +Support for PSR-11 compatible Containers has been introduced and can be accessed via the `\core\di` class. Read the [full documentation](./apis/core/di/index.md) for information on how to use Moodle's DI infrastructure. + +### 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 + + + +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. + +:::note + +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. + +::: + +If choosing to use these new methods it is highly advised that you use the `\core\di` container to access them. + +```php title="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; +} +``` + +:::info + + + + + +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. + + + +```php +$formatter = \core\di::get(\core\formatting::class); + +$formatter->format_string( + "The content to be formatted", + context: \core\context\course::instance($courseid), + filter: false, +); + +$formatter->format_text( + "The content to be formatted", + context: \core\context\course::instance($courseid), + filter: false, + blanktarget: true, +); +``` + + + + + + + +The use of positional arguments to the format methods is strongly discouraged. + + + +```php +$formatter = \core\di::get(\core\formatting::class); + +$formatter->format_string( + "The content to be formatted", + true, + \core\context\course::instance($courseid), + false, +); + +$formatter->format_text( + "The content to be formatted", + FORMAT_MOODLE, + \core\context\course::instance($courseid), + false, + null, + false, + true, + true, + false, + true, +); +``` + + + + + + + +::: + ## Enrolment ### Support for multiple instances in csv course upload