Skip to content

Commit

Permalink
Merge branch 'MDL-80677-main' of https://github.com/andrewnicols/moodle
Browse files Browse the repository at this point in the history
  • Loading branch information
junpataleta committed Mar 12, 2024
2 parents 34bb16a + ad9322a commit 70d4a92
Show file tree
Hide file tree
Showing 8 changed files with 446 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

namespace core;

use Attribute;
namespace core\attribute;

/**
* Attribute to describe a deprecated item.
Expand All @@ -25,7 +23,7 @@
* @copyright 2023 Andrew Lyons <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[Attribute]
#[\Attribute]
class deprecated {
/**
* A deprecated item.
Expand All @@ -34,20 +32,25 @@ class deprecated {
*
* Note: The mere presence of the attribute does not do anything. It must be checked by some part of the code.
*
* @param mixed $descriptor A brief descriptor of the thing that was deprecated.
* @param null|string $replacement Any replacement for the deprecated thing
* @param null|string $since When it was deprecated
* @param null|string $reason Why it was deprecated
* @param null|string $replacement Any replacement for the deprecated thing
* @param null|string $mdl Link to the Moodle Tracker issue for more information
* @param bool $final Whether this is a final deprecation
* @param bool $emit Whether to emit a deprecation warning
*/
public function __construct(
public readonly mixed $descriptor,
public readonly ?string $replacement,
public readonly ?string $since = null,
public readonly ?string $reason = null,
public readonly ?string $replacement = null,
public readonly ?string $mdl = null,
public readonly bool $final = false,
public readonly bool $emit = true,
) {
if ($replacement === null && $reason === null && $mdl === null) {
throw new \coding_exception(
'A deprecated item which is not deprecated must provide a reason, or an issue number.',
);
}
}
}
58 changes: 58 additions & 0 deletions lib/classes/attribute/deprecated_with_reference.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

namespace core\attribute;

/**
* Attribute to describe a deprecated item which contains a reference to the owning feature.
*
* @package core
* @copyright 2023 Andrew Lyons <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class deprecated_with_reference extends deprecated {
/**
* A deprecated item which also includes a reference to the owning feature.
*
* This attribute is not expected to be used more generally. It is an internal feature.
*
* @param string $owner The code which owns the usage
* @param null|string $replacement Any replacement for the deprecated thing
* @param null|string $since When it was deprecated
* @param null|string $reason Why it was deprecated
* @param null|string $mdl Link to the Moodle Tracker issue for more information
* @param bool $final Whether this is a final deprecation
* @param bool $emit Whether to emit a deprecation warning
*/
public function __construct(
public readonly string $owner,
?string $replacement,
?string $since,
?string $reason,
?string $mdl,
bool $final,
bool $emit,
) {
parent::__construct(
replacement: $replacement,
since: $since,
reason: $reason,
mdl: $mdl,
final: $final,
emit: $emit,
);
}
}
116 changes: 85 additions & 31 deletions lib/classes/deprecation.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

namespace core;

use core\attribute\deprecated;
use core\attribute\deprecated_with_reference;

/**
* Deprecation utility.
*
Expand All @@ -42,17 +45,14 @@ public static function from(array|string|object $reference): ?deprecated {
return self::from(explode('::', $reference));
}

if (class_exists($reference)) {
if (class_exists($reference) || interface_exists($reference) || trait_exists($reference)) {
// The reference looks to be a class name.
return self::from([$reference]);
}

if (function_exists($reference)) {
// The reference looks to be a global function.
$ref = new \ReflectionFunction($reference);
if ($attributes = $ref->getAttributes(deprecated::class)) {
return $attributes[0]->newInstance();
}
return self::get_attribute(new \ReflectionFunction($reference), $reference);
}

return null;
Expand All @@ -75,14 +75,45 @@ public static function from(array|string|object $reference): ?deprecated {
return self::from_reflected_object($rc, $reference[1] ?? null);
}

if (is_string($reference[0]) && class_exists($reference[0])) {
$rc = new \ReflectionClass($reference[0]);
return self::from_reflected_object($rc, $reference[1] ?? null);
if (is_string($reference[0])) {
if (class_exists($reference[0]) || interface_exists($reference[0]) || trait_exists($reference[0])) {
$rc = new \ReflectionClass($reference[0]);
return self::from_reflected_object($rc, $reference[1] ?? null);
}
}

// The reference is an array, but it's not an object or a class that currently exists.
return null;
}

// The reference is none of the above.
return null;
}

/**
* Get a deprecation attribute from a reflector.
*
* @param \Reflector $ref The reflector
* @param string $owner A descriptor of the owner of the thing that is deprecated
* @return null|deprecated_with_reference
*/
protected static function get_attribute(
\Reflector $ref,
string $owner,
): ?deprecated_with_reference {
if ($attributes = $ref->getAttributes(deprecated::class)) {
$attribute = $attributes[0]->newInstance();
return new deprecated_with_reference(
owner: $owner,
replacement: $attribute->replacement,
since: $attribute->since,
reason: $attribute->reason,
mdl: $attribute->mdl,
final: $attribute->final,
emit: $attribute->emit,
);
}
return null;
}

/**
Expand All @@ -107,47 +138,59 @@ public static function emit_deprecation_if_present(array|string|object $referenc
}

/**
* Fetch a deprecation attribute from a reflected object.
* Fetch a referenced deprecation attribute from a reflected object.
*
* @param \ReflectionClass $rc The reflected object
* @param null|string $name The name of the thing to check for deprecation
* @return null|deprecated
* @return null|deprecated_with_reference
*/
protected static function from_reflected_object(
\ReflectionClass $rc,
?string $name,
): ?deprecated {
if ($name === null) {
// No name specified. This may be a deprecated class.
if ($attributes = $rc->getAttributes(deprecated::class)) {
return $attributes[0]->newInstance();
): ?deprecated_with_reference {
// Check if the class itself is deprecated first.
$classattribute = self::get_attribute($rc, $rc->name);
if ($classattribute || $name === null) {
return $classattribute;
}

// Check for any deprecated interfaces.
foreach ($rc->getInterfaces() as $interface) {
if ($attribute = self::get_attribute($interface, $interface->name)) {
return $attribute;
}
}

// And any deprecated traits.
foreach ($rc->getTraits() as $trait) {
if ($attribute = self::get_attribute($trait, $trait->name)) {
return $attribute;
}
return null;
}

if ($rc->hasConstant($name)) {
// This class has a constant with the specified name.
// Note: This also applies to enums.
$ref = $rc->getReflectionConstant($name);
if ($attributes = $ref->getAttributes(deprecated::class)) {
return $attributes[0]->newInstance();
}
return self::get_attribute(
$rc->getReflectionConstant($name),
"{$rc->name}::{$name}",
);
}

if ($rc->hasMethod($name)) {
// This class has a method with the specified name.
$ref = $rc->getMethod($name);
if ($attributes = $ref->getAttributes(deprecated::class)) {
return $attributes[0]->newInstance();
}
return self::get_attribute(
$rc->getMethod($name),
"{$rc->name}::{$name}",
);
}

if ($rc->hasProperty($name)) {
// This class has a property with the specified name.
$ref = $rc->getProperty($name);
if ($attributes = $ref->getAttributes(deprecated::class)) {
return $attributes[0]->newInstance();
}
return self::get_attribute(
$rc->getProperty($name),
"{$rc->name}::{$name}",
);
}

return null;
Expand All @@ -157,10 +200,19 @@ protected static function from_reflected_object(
* Get a string describing the deprecation.
*
* @param deprecated $attribute
* @param string $owner
* @return string
*/
public static function get_deprecation_string(deprecated $attribute): string {
$output = "Deprecation: {$attribute->descriptor} has been deprecated";
public static function get_deprecation_string(
deprecated $attribute,
): string {
$output = "Deprecation:";

if ($attribute instanceof deprecated_with_reference) {
$output .= " {$attribute->owner}";
}
$output .= " has been deprecated";

if ($attribute->since) {
$output .= " since {$attribute->since}";
}
Expand All @@ -187,7 +239,9 @@ public static function get_deprecation_string(deprecated $attribute): string {
*
* @param deprecated $attribute
*/
public static function emit_deprecation_notice(deprecated $attribute): void {
protected static function emit_deprecation_notice(
deprecated $attribute,
): void {
if (!$attribute->emit) {
return;
}
Expand Down
17 changes: 9 additions & 8 deletions lib/classes/param.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

use coding_exception;
use core_text;
use core\attribute\deprecated;
use core\ip_utils;
use invalid_parameter_exception;
use moodle_exception;
Expand Down Expand Up @@ -222,9 +223,9 @@ enum param: string {
* @deprecated since 2.0
*/
#[deprecated(
'param::CLEAN',
replacement: 'a more specific type of parameter',
since: '2.0',
reason: 'Please use a more specific type of parameter',
reason: 'The CLEAN param type is too generic to perform satisfactory validation',
emit: false,
)]
case CLEAN = 'clean';
Expand All @@ -234,7 +235,7 @@ enum param: string {
* @deprecated since 2.0
*/
#[deprecated(
'param::INTEGER',
replacement: 'param::INT',
since: '2.0',
reason: 'Alias for INT',
final: true,
Expand All @@ -246,7 +247,7 @@ enum param: string {
* @deprecated since 2.0
*/
#[deprecated(
'param::NUMBER',
replacement: 'param::FLOAT',
since: '2.0',
reason: 'Alias for FLOAT',
final: true,
Expand All @@ -259,7 +260,7 @@ enum param: string {
* @deprecated since 2.0
*/
#[deprecated(
'param::ACTION',
replacement: 'param::ALPHANUMEXT',
since: '2.0',
reason: 'Alias for PARAM_ALPHANUMEXT',
final: true,
Expand All @@ -272,7 +273,7 @@ enum param: string {
* @deprecated since 2.0
*/
#[deprecated(
'param::FORMAT',
replacement: 'param::ALPHANUMEXT',
since: '2.0',
reason: 'Alias for PARAM_ALPHANUMEXT',
final: true,
Expand All @@ -284,7 +285,7 @@ enum param: string {
* @deprecated since 2.0
*/
#[deprecated(
'param::MULTILANG',
replacement: 'param::TEXT',
since: '2.0',
reason: 'Alias for PARAM_TEXT',
final: true,
Expand All @@ -303,7 +304,7 @@ enum param: string {
* @deprecated since 2.0
*/
#[deprecated(
'param::CLEANFILE',
replacement: 'param::FILE',
since: '2.0',
reason: 'Alias for PARAM_FILE',
)]
Expand Down
1 change: 0 additions & 1 deletion lib/moodlelib.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@
// We currently include \core\param manually here to avoid broken upgrades.
// This may change after the next LTS release as LTS releases require the previous LTS release.
require_once(__DIR__ . '/classes/deprecation.php');
require_once(__DIR__ . '/classes/deprecated.php');
require_once(__DIR__ . '/classes/param.php');

/**
Expand Down
Loading

0 comments on commit 70d4a92

Please sign in to comment.