MDL-88176 customfield: handle static singleton pattern in base class.

Replace duplicate implementation in all handler implementations. Ensure
that it is consistently reset during PHPUnit/Behat tests.

Co-authored-by: Yerai Rodríguez <[email protected]>
This commit is contained in:
Paul Holden
2026-03-16 14:20:55 +00:00
co-authored by Yerai Rodríguez
parent 6ebf41a6c2
commit 5890035d60
12 changed files with 40 additions and 287 deletions
@@ -0,0 +1,8 @@
issueNumber: MDL-88176
notes:
core_customfield:
- message: >-
The base `\core_customfield\handler` class now implements static
caching/reset itself, so all implementations of the same from extending
handler classes should be removed
type: changed
@@ -27,36 +27,6 @@ use core_customfield\field_controller;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class cohort_handler extends handler {
/**
* @var cohort_handler
*/
static protected $singleton;
/**
* Returns a singleton.
*
* @param int $itemid
* @return \core_customfield\handler
*/
public static function create(int $itemid = 0): handler {
if (static::$singleton === null) {
self::$singleton = new static(0);
}
return self::$singleton;
}
/**
* Run reset code after unit tests to reset the singleton usage.
*/
public static function reset_caches(): void {
if (!PHPUNIT_TEST) {
throw new \coding_exception('This feature is only intended for use in unit tests');
}
static::$singleton = null;
}
/**
* The current user can configure custom fields on this component.
*
@@ -27,12 +27,6 @@ use core_customfield\field_controller;
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class content_handler extends \core_customfield\handler {
/**
* @var content_handler
*/
static protected $singleton;
/**
* @var \context
*/
@@ -43,30 +37,6 @@ class content_handler extends \core_customfield\handler {
/** @var int Field is not displayed in the content bank edit page */
const NOTVISIBLE = 0;
/**
* Returns a singleton
*
* @param int $itemid
* @return \core_contentbank\customfield\content_handler
*/
public static function create(int $itemid = 0): \core_contentbank\customfield\content_handler {
if (static::$singleton === null) {
self::$singleton = new static(0);
}
return self::$singleton;
}
/**
* Run reset code after unit tests to reset the singleton usage.
*/
public static function reset_caches(): void {
if (!PHPUNIT_TEST) {
throw new \coding_exception('This feature is only intended for use in unit tests');
}
static::$singleton = null;
}
/**
* The current user can configure custom fields on this component.
*
@@ -14,6 +14,10 @@
// 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_course\customfield;
use core_customfield\field_controller;
/**
* Course handler for custom fields
*
@@ -21,27 +25,7 @@
* @copyright 2018 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_course\customfield;
defined('MOODLE_INTERNAL') || die;
use core_customfield\field_controller;
/**
* Course handler for custom fields
*
* @package core_course
* @copyright 2018 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_handler extends \core_customfield\handler {
/**
* @var course_handler
*/
static protected $singleton;
/**
* @var \context
*/
@@ -54,30 +38,6 @@ class course_handler extends \core_customfield\handler {
/** @var int Field is not displayed in the course listing */
const NOTVISIBLE = 0;
/**
* Returns a singleton
*
* @param int $itemid
* @return \core_course\customfield\course_handler
*/
public static function create(int $itemid = 0): \core_customfield\handler {
if (static::$singleton === null) {
self::$singleton = new static(0);
}
return self::$singleton;
}
/**
* Run reset code after unit tests to reset the singleton usage.
*/
public static function reset_caches(): void {
if (!PHPUNIT_TEST) {
throw new \coding_exception('This feature is only intended for use in unit tests');
}
static::$singleton = null;
}
/**
* The current user can configure custom fields on this component.
*
@@ -20,7 +20,6 @@ namespace core_customfield\customfield;
use core\context;
use core\context\system;
use core\exception\coding_exception;
use core\url;
use core_customfield\field_controller;
@@ -32,35 +31,6 @@ use core_customfield\field_controller;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class shared_handler extends \core_customfield\handler {
/**
* @var shared_handler|null
*/
protected static ?shared_handler $singleton = null;
/**
* Returns a singleton
*
* @param int $itemid
* @return self
*/
public static function create(int $itemid = 0): self {
if (static::$singleton === null) {
self::$singleton = new static($itemid);
}
return self::$singleton;
}
/**
* Run reset code after unit tests to reset the singleton usage.
*/
public static function reset_caches(): void {
if (!PHPUNIT_TEST) {
throw new coding_exception('This feature is only intended for use in unit tests');
}
static::$singleton = null;
}
/**
* The current user can configure custom fields on this component.
*
+25 -14
View File
@@ -14,24 +14,15 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The abstract custom fields handler
*
* @package core_customfield
* @copyright 2018 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_customfield;
use backup_nested_element;
use core\exception\coding_exception;
use core_customfield\output\field_data;
use stdClass;
defined('MOODLE_INTERNAL') || die;
/**
* Base class for custom fields handlers
* The abstract custom fields handler
*
* This handler provides callbacks for field configuration form and also allows to add the fields to the instance editing form
*
@@ -46,11 +37,13 @@ defined('MOODLE_INTERNAL') || die;
* - \core_customfield\api::get_field($fieldid)
* - \core_customfield\api::get_category($categoryid)
*
* @package core_customfield
* @package core_customfield
* @copyright 2018 David Matamoros <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class handler {
/** @var handler[] $instances */
private static $instances = [];
/**
* The component this handler handles
@@ -95,13 +88,31 @@ abstract class handler {
/**
* Returns an instance of the handler
*
* Some areas may choose to use singleton/caching here
* We statically cache the list of instances during request lifecycle, to allow this method to be called
* repeatedly without potential performance problems
*
* @param int $itemid
* @return static
*/
public static function create(int $itemid = 0): handler {
return new static($itemid);
$instancekey = static::class . ':' . $itemid;
if (!array_key_exists($instancekey, static::$instances)) {
static::$instances[$instancekey] = new static($itemid);
}
return static::$instances[$instancekey];
}
/**
* Run reset code after tests to reset the instance cache
*
* @throws coding_exception If called outside of test infrastructure
*/
public static function reset_caches(): void {
if (PHPUNIT_TEST || defined('BEHAT_TEST')) {
static::$instances = [];
} else {
throw new coding_exception('This feature is only intended for use in tests');
}
}
/**
@@ -34,36 +34,6 @@ use restore_task;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_handler extends handler {
/**
* @var group_handler
*/
static protected $singleton;
/**
* Returns a singleton.
*
* @param int $itemid
* @return \core_customfield\handler
*/
public static function create(int $itemid = 0): handler {
if (static::$singleton === null) {
self::$singleton = new static(0);
}
return self::$singleton;
}
/**
* Run reset code after unit tests to reset the singleton usage.
*/
public static function reset_caches(): void {
if (!PHPUNIT_TEST) {
throw new \coding_exception('This feature is only intended for use in unit tests');
}
static::$singleton = null;
}
/**
* The current user can configure custom fields on this component.
*
@@ -34,36 +34,6 @@ use restore_task;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grouping_handler extends handler {
/**
* @var grouping_handler
*/
static protected $singleton;
/**
* Returns a singleton.
*
* @param int $itemid
* @return \core_customfield\handler
*/
public static function create(int $itemid = 0): handler {
if (static::$singleton === null) {
self::$singleton = new static(0);
}
return self::$singleton;
}
/**
* Run reset code after unit tests to reset the singleton usage.
*/
public static function reset_caches(): void {
if (!PHPUNIT_TEST) {
throw new \coding_exception('This feature is only intended for use in unit tests');
}
static::$singleton = null;
}
/**
* The current user can configure custom fields on this component.
*
+1
View File
@@ -422,6 +422,7 @@ class behat_util extends \core\test\testing_util {
filter_manager::reset_caches();
\core_customfield\handler::reset_caches();
\core_reportbuilder\manager::reset_caches();
// Reset course and module caches.
@@ -251,27 +251,12 @@ class phpunit_util extends \core\test\testing_util {
if (class_exists(\core\update\checker::class)) {
\core\update\checker::reset_caches(true);
}
if (class_exists(\core_course\customfield\course_handler::class)) {
\core_course\customfield\course_handler::reset_caches();
if (class_exists(\core_customfield\handler::class)) {
\core_customfield\handler::reset_caches();
}
if (class_exists(\core_reportbuilder\manager::class)) {
\core_reportbuilder\manager::reset_caches();
}
if (class_exists(\core_cohort\customfield\cohort_handler::class)) {
\core_cohort\customfield\cohort_handler::reset_caches();
}
if (class_exists(\core_group\customfield\group_handler::class)) {
\core_group\customfield\group_handler::reset_caches();
}
if (class_exists(\core_group\customfield\grouping_handler::class)) {
\core_group\customfield\grouping_handler::reset_caches();
}
if (class_exists(\core_reportbuilder\customfield\report_handler::class)) {
\core_reportbuilder\customfield\report_handler::reset_caches();
}
if (class_exists(\core_customfield\customfield\shared_handler::class)) {
\core_customfield\customfield\shared_handler::reset_caches();
}
// Clear static cache within restore.
if (class_exists(\restore_section_structure_step::class)) {
@@ -29,12 +29,6 @@ use core_customfield\output\field_data;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class question_handler extends \core_customfield\handler {
/**
* @var question_handler
*/
static protected $singleton;
/**
* @var \context
*/
@@ -47,32 +41,6 @@ class question_handler extends \core_customfield\handler {
/** @var int Field is not displayed in the question display and question preview */
const NOTVISIBLE = 0;
/**
* Creates the custom field handler and returns a singleton.
* Itemid is always zero as the custom fields are the same
* for every question across the system.
*
* @param int $itemid Always zero.
* @return \qbank_customfields\customfield\question_handler
*/
public static function create(int $itemid = 0): \core_customfield\handler {
if (static::$singleton === null) {
self::$singleton = new static(0);
}
return self::$singleton;
}
/**
* Run reset code after unit tests to reset the singleton usage.
*/
public static function reset_caches(): void {
if (!PHPUNIT_TEST) {
throw new \coding_exception('This feature is only intended for use in unit tests');
}
static::$singleton = null;
}
/**
* The current user can configure custom fields on this component.
*
@@ -20,7 +20,6 @@ namespace core_reportbuilder\customfield;
use core\context;
use core\context\system;
use core\exception\coding_exception;
use core\url;
use core_customfield\field_controller;
use core_reportbuilder\local\models\report;
@@ -35,35 +34,6 @@ use core_reportbuilder\permission;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class report_handler extends \core_customfield\handler {
/**
* @var report_handler|null
*/
protected static ?report_handler $singleton = null;
/**
* Returns a singleton
*
* @param int $itemid
* @return self
*/
public static function create(int $itemid = 0): self {
if (static::$singleton === null) {
self::$singleton = new static($itemid);
}
return self::$singleton;
}
/**
* Run reset code after unit tests to reset the singleton usage.
*/
public static function reset_caches(): void {
if (!PHPUNIT_TEST) {
throw new coding_exception('This feature is only intended for use in unit tests');
}
static::$singleton = null;
}
/**
* The current user can configure custom fields on this component.
*