Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/apis/core/di/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ class example_with_promotion {

:::

## Creating instances {/* #creating-instances */}

<Since version="5.3" issueNumber="MDL-89528" />

Use `\core\di::make()` to create a new instance of a class on each call, with dependencies resolved by the container. `\core\di::get()` returns a shared instance.

Pass constructor argument overrides as a named array in the optional second argument:

```php
$thing = \core\di::make(my_thing::class);
$thing = \core\di::make(my_thing::class, ['client' => $client]);
```

## Configuring dependencies {/* #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.
Expand Down Expand Up @@ -204,6 +217,21 @@ It is generally inadvisable to inject the Container itself. Please do not inject

:::

## Attribute-based injection {/* #attribute-based-injection */}

<Since version="5.3" issueNumber="MDL-89528" />

Use `#[\DI\Attribute\Inject]` to inject dependencies into typed properties. This is the recommended approach for controllers.

```php
class example_class {
#[\DI\Attribute\Inject]
private \core\formatting $formatter;
}

$example = \core\di::get(example_class::class);
```

## Advanced usage {/* #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.
Expand Down
49 changes: 49 additions & 0 deletions docs/apis/subsystems/task/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,55 @@ Adhoc tasks are great for situations such as:

## Usage {/* #usage */}

### Dependency injection {/* #dependency-injection */}

<Since version="5.3" issueNumber="MDL-89528" />

Adhoc and scheduled tasks can declare dependencies in their constructors using [dependency injection](../../core/di/index.md#fetching-dependencies). Moodle's DI container supplies the dependencies when it creates the task:

```php
namespace mod_example\task;

class do_something extends \core\task\adhoc_task {
public function __construct(
protected readonly \core\clock $clock,
) {
}

public function execute(): void {
$now = $this->clock->time();
mtrace("Task started at {$now}");
}
}
```

In tests, use `\core\di::set()` to replace the clock in the container:

```php
$clock = $this->createMock(\core\clock::class);
$clock->method('time')->willReturn(1234567890);
\core\di::set(\core\clock::class, $clock);

// The manager creates the task via the container, which supplies the mock clock.
$task = \core\task\manager::adhoc_task_from_record((object) [
'classname' => \mod_example\task\do_something::class,
]);

$this->expectOutputString("Task started at 1234567890\n");
$task->execute();
```

You can also test the task directly by passing the mock clock to its constructor:

```php
$clock = $this->createMock(\core\clock::class);
$clock->method('time')->willReturn(1234567890);

$task = new \mod_example\task\do_something($clock);
$this->expectOutputString("Task started at 1234567890\n");
$task->execute();
```

### Failures and error handling {/* #failures-and-error-handling */}

A task, either scheduled or adhoc, can sometimes fail. An example would be updating an RSS field when the network is temporarily down. This is handled by the task system automatically - all the failing task needs to do is throw an exception. The task will be retried after 1 minute. If the task keeps failing, the retry algorithm will add more time between each successive attempts up to a max of 24 hours.
Expand Down
Loading