diff --git a/docs/apis/core/deprecation/index.md b/docs/apis/core/deprecation/index.md new file mode 100644 index 0000000000..8bfa64596f --- /dev/null +++ b/docs/apis/core/deprecation/index.md @@ -0,0 +1,113 @@ +--- +title: Deprecation API +tags: +- deprecation +--- + + + +import { Since } from '@site/src/components'; + + + +When deprecating a code feature, it is often desirable to include a reasonable amount of information, and to provide a consistent deprecation message. + +In some cases it is also desirable to check if a called class, or method, has been marked as deprecated. + +One way to simplify this is through use of the `\core\attribute\deprecated` PHP attribute, which can be used in conjunction with the `\core\deprecation` class. + +:::note + +Please note that the attribute does _not_ replace the `@deprecated` phpdoc annotation. They serve slightly different purposes. + +::: + +The attribute can be used to specify information including: + +- the replacement for that feature +- the version that the feature was deprecated in +- the relevant MDL +- the reason for deprecation +- whether the deprecation is final + +## The `deprecated` attribute + +The attribute is a Moodle PHP Attribute and can be applied to: + +- classes, traits, interfaces, and enums +- enum cases +- global functions +- class constants, properties, and methods + +```php title="Example attribute usage" +// On a global function: +#[\core\attribute\deprecated('random_bytes', since: '4.3')] +function random_bytes_emulate($length) { + // Replaced by random_bytes since Moodle 4.3. +} + +// On a class: +#[\core\attribute\deprecated(replacement: null, since: '4.4', reason: 'This functionality has been removed.')] +class example { + #[\core\attribute\deprecated( + replacement: '\core\example::do_something', + since: '4.3', + reason: 'No longer required', + mdl: 'MDL-12345', + )] + public function do_something(): void {} +} + +// On an enum case: +enum example { + #[\core\attribute\deprecated('example::OPTION', since: '4.4', final: true)] + case OPTION; +} +``` + +## Inspecting the attribute + +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 +- fetching an instance of the attribute to query further + +```php title="Examples of usage" +// A method which has been initially deprecated, and replaced by 'random_bytes'. It should show debugging. +/** @deprecated since 4.3 */ +#[\core\attribute\deprecated('random_bytes', since: '4.3')] +function random_bytes_emulate($length) { + \core\deprecation::emit_deprecation_if_present(__FUNCTION__); + 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() { + \core\deprecation::emit_deprecation_if_present(__FUNCTION__); +} + +// 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. + +// On an collection of items. +foreach ($values as $value) { + \core\deprecation::emit_deprecation_if_present($value); + $value->do_things(); +} + +// 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. diff --git a/docs/devupdate.md b/docs/devupdate.md index a8be5a8baf..2a7fbf5665 100644 --- a/docs/devupdate.md +++ b/docs/devupdate.md @@ -260,6 +260,110 @@ $formatter->format_text( ::: +## Parameters + +### API Change + + + +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: + +```php title="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', []); +``` + +:::note + +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. + +::: + +### Deprecations + + + +A number of deprecated parameter types have been deprecated, these include: + +- `PARAM_CLEAN` +- `PARAM_INTEGER` +- `PARAM_NUMBER` +- `PARAM_ACTION` +- `PARAM_FORMAT` +- `PARAM_MULTILANG` +- `PARAM_CLEANFILE` + +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 + +```php title="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) { + \core\deprecation::emit_deprecation_if_present(__FUNCTION__); + 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() { + \core\deprecation::emit_deprecation_if_present(__FUNCTION__); +} + +// 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. + ## Enrolment ### Support for multiple instances in csv course upload diff --git a/general/development/policies/deprecation/index.md b/general/development/policies/deprecation/index.md index a40b12c225..9a9ac184d3 100644 --- a/general/development/policies/deprecation/index.md +++ b/general/development/policies/deprecation/index.md @@ -49,9 +49,28 @@ Deprecation affects only the current master version, in other words, the depreca - Besides, if the entire class is being moved (for example, moving multiple class definitions from a monolithic file in to individual files), follow the process for [renaming classes](/docs/apis/commonfiles#dbrenamedclassesphp). - A debugging message should be added to the function so that, when [developer debugging mode](https://docs.moodle.org/en/Debugging) is on, attention is drawn to the deprecation. The message should state that the function being called has been deprecated. The message should help a developer whose code currently calls the function that has gone. Tell them what they should do instead. + + + + +```php +debugging('foobar() is deprecated. Please use foobar::blah() instead.', DEBUG_DEVELOPER); ``` - debugging('foobar() is deprecated. Please use foobar::blah() instead.', DEBUG_DEVELOPER); - ``` + + + + + +```php +#[\core\deprecated('foobar::blahv()', since: '4.4', mdl: 'MDL-XXXXX')] +public function foobar(): void { + \core\deprecation::emit_deprecation_if_present([self, __FUNCTION__]); +} +``` + + + + - Unit tests that call the function should have `assertDebuggingCalled()` added to allow them to continue running. - If the deprecated function has been replaced with a new function, ideally the new function should be called from the deprecated function, so that the new functionality is used. This will make maintenance easier moving forward. @@ -84,9 +103,30 @@ Longer deprecation periods can be considered for functions that are widely used. - If a function has been marked as deprecated for `3.[x]` (eg. 3.1) and set for removal at `3.[x + 4]` (eg. 3.5), soon after the release of `3.[x + 3].1` (eg. 3.4.1), the `3.[x + 4]` deprecation META will be processed. This means that the deprecated function will undergo final deprecation before `3.[x + 4]`, but only in the master version. This allows any potential regressions caused by the final deprecation of the function to be exposed as soon as possible. - When a function undergoes final deprecation, all content of the function should be removed. In the skeleton that remains, an error statement should be included that indicates that the function cannot be used anymore. You can also direct developers to the new function(s) in this message. + + + + +```php +throw new coding_exception( + 'foobar() can not be used any more, please use foobar::blah' +); ``` - throw new coding_exception('check_potential_filename() can not be used any more, please use new file API'); - ``` + + + + + +```php +#[\core\deprecated('foobar::blah()', since: '4.4', mdl: 'MDL-XXXXX', final: true)] +public function foobar(): void { + \core\deprecation::emit_deprecation_if_present([self, __FUNCTION__]); +} +``` + + + + - All function parameters should be removed. - Deprecated classes must be completely removed. @@ -231,6 +271,7 @@ Named parameter arguments are available from PHP 8.0 onwards. ## See also - [String deprecation](../../../projects/api/string-deprecation.md) +- [Deprecation attributes](/docs/apis/core/deprecation/) - [External functions deprecation](/docs/apis/subsystems/external/writing-a-service#deprecation) - [Capabilities deprecation](/docs/apis/subsystems/access#deprecating-a-capability) - [SCSS deprecation](./scss-deprecation.md)