diff --git a/question/bank/columnsortorder/tests/column_manager_test.php b/question/bank/columnsortorder/tests/column_manager_test.php index 4c71365ce6a..16fafd1dbc3 100644 --- a/question/bank/columnsortorder/tests/column_manager_test.php +++ b/question/bank/columnsortorder/tests/column_manager_test.php @@ -40,6 +40,19 @@ require_once($CFG->dirroot . '/question/classes/external.php'); */ class column_manager_test extends advanced_testcase { + + /** @var \stdClass course record. */ + protected $course; + + /** @var \core_question\local\bank\view */ + protected $questionbank; + + /** @var array */ + protected $columns; + + /** @var \qbank_columnsortorder\column_manager */ + protected $columnmanager; + /** * Setup testcase. */ diff --git a/question/bank/customfields/backup/moodle2/restore_qbank_customfields_plugin.class.php b/question/bank/customfields/backup/moodle2/restore_qbank_customfields_plugin.class.php index 1e0de7eb207..a085ef18954 100644 --- a/question/bank/customfields/backup/moodle2/restore_qbank_customfields_plugin.class.php +++ b/question/bank/customfields/backup/moodle2/restore_qbank_customfields_plugin.class.php @@ -24,6 +24,9 @@ */ class restore_qbank_customfields_plugin extends restore_qbank_plugin { + /** @var stdClass|null a fieldset object. */ + protected $cachedcategory; + /** * Returns the paths to be handled by the plugin at question level. * diff --git a/question/bank/editquestion/tests/external/update_question_version_status_test.php b/question/bank/editquestion/tests/external/update_question_version_status_test.php index 873cff6ac1a..13baf228de0 100644 --- a/question/bank/editquestion/tests/external/update_question_version_status_test.php +++ b/question/bank/editquestion/tests/external/update_question_version_status_test.php @@ -32,6 +32,12 @@ use qbank_editquestion\external\update_question_version_status; */ class update_question_version_status_test extends \advanced_testcase { + /** @var \stdClass course record. */ + protected $course; + + /** @var mixed. */ + protected $user; + /** * Called before every test. */ diff --git a/question/bank/managecategories/category.php b/question/bank/managecategories/category.php index ade905f02ec..1ab08d264f2 100644 --- a/question/bank/managecategories/category.php +++ b/question/bank/managecategories/category.php @@ -112,11 +112,11 @@ if ($param->delete) { // Second pass, if we still have questions to move, setup the form. if ($questionstomove) { $categorycontext = context::instance_by_id($category->contextid); - $qcobject->moveform = new question_move_form($thispageurl, + $moveform = new question_move_form($thispageurl, ['contexts' => [$categorycontext], 'currentcat' => $param->delete]); - if ($qcobject->moveform->is_cancelled()) { + if ($moveform->is_cancelled()) { redirect($thispageurl); - } else if ($formdata = $qcobject->moveform->get_data()) { + } else if ($formdata = $moveform->get_data()) { list($tocategoryid, $tocontextid) = explode(',', $formdata->category); $qcobject->move_questions_and_delete_category($formdata->delete, $tocategoryid); $thispageurl->remove_params('cat', 'category'); @@ -171,7 +171,11 @@ if ($param->edit !== null || $qcobject->catform->is_submitted()) { // In this case, category id is in the 'id' hidden filed. $qcobject->edit_single_category($param->edit ?? required_param('id', PARAM_INT)); } else if ($questionstomove) { - $qcobject->display_move_form($questionstomove, $category); + $vars = new stdClass(); + $vars->name = $category->name; + $vars->count = $questionstomove; + echo $OUTPUT->box(get_string('categorymove', 'question', $vars), 'generalbox boxaligncenter'); + $moveform->display(); } else { // Display the user interface. $qcobject->display_user_interface(); diff --git a/question/bank/managecategories/classes/question_category_object.php b/question/bank/managecategories/classes/question_category_object.php index 98f8c769bac..2837486023e 100644 --- a/question/bank/managecategories/classes/question_category_object.php +++ b/question/bank/managecategories/classes/question_category_object.php @@ -306,8 +306,14 @@ class question_category_object { * @param int $questionsincategory * @param object $category * @throws \coding_exception + * + * @deprecated No longer used by internal code and not recommended since Moodle 4.2 MDL-77299. */ public function display_move_form($questionsincategory, $category): void { + debugging( + 'display_move_form() is deprecated and no longer used by internal code.', + DEBUG_DEVELOPER + ); global $OUTPUT; $vars = new stdClass(); $vars->name = $category->name; diff --git a/question/bank/tagquestion/tests/external/submit_tags_test.php b/question/bank/tagquestion/tests/external/submit_tags_test.php index f4d5602abe0..0d31bc2e0f7 100644 --- a/question/bank/tagquestion/tests/external/submit_tags_test.php +++ b/question/bank/tagquestion/tests/external/submit_tags_test.php @@ -31,6 +31,15 @@ require_once($CFG->dirroot . '/webservice/tests/helpers.php'); */ class submit_tags_test extends \externallib_advanced_testcase { + /** @var \stdClass course record. */ + protected $course; + + /** @var \stdClass user record. */ + protected $student; + + /** @var \stdClass user role record. */ + protected $studentrole; + /** * Set up for every test */ diff --git a/question/classes/bank/search/category_condition.php b/question/classes/bank/search/category_condition.php index 319905ce3b6..5af6f5074f1 100644 --- a/question/classes/bank/search/category_condition.php +++ b/question/classes/bank/search/category_condition.php @@ -59,6 +59,9 @@ class category_condition extends condition { /** @var int The maximum displayed length of the category info. */ protected $maxinfolength; + /** @var \moodle_url The URL the form is submitted to. */ + protected $baseurl; + /** * Constructor * @param string $cat categoryID,contextID as used with question_bank_view->display() diff --git a/question/classes/bank/search/tag_condition.php b/question/classes/bank/search/tag_condition.php index 521553ba18e..89453b894a6 100644 --- a/question/classes/bank/search/tag_condition.php +++ b/question/classes/bank/search/tag_condition.php @@ -35,7 +35,10 @@ class tag_condition extends condition { /** @var string SQL fragment to add to the where clause. */ protected $where; - /** @var string SQL fragment to add to the where clause. */ + /** @var array Named SQL params to be used with the SQL WHERE snippet. */ + protected $params; + + /** @var array List of contexts to show tags from. */ protected $contexts; /** @var array List of IDs for tags that have been selected in the form. */ diff --git a/question/engine/questionattemptstep.php b/question/engine/questionattemptstep.php index f7e3f936d9f..781aeb053dd 100644 --- a/question/engine/questionattemptstep.php +++ b/question/engine/questionattemptstep.php @@ -647,12 +647,12 @@ class question_attempt_step_subquestion_adapter extends question_attempt_step { /** * Constructor. - * @param question_attempt_step $realqas the step to wrap. (Can be null if you + * @param question_attempt_step $realstep the step to wrap. (Can be null if you * just want to call add/remove.prefix.) * @param string $extraprefix the extra prefix that is used for date fields. */ - public function __construct($realqas, $extraprefix) { - $this->realqas = $realqas; + public function __construct($realstep, $extraprefix) { + $this->realstep = $realstep; $this->extraprefix = $extraprefix; } @@ -704,7 +704,7 @@ class question_attempt_step_subquestion_adapter extends question_attempt_step { } public function get_state() { - return $this->realqas->get_state(); + return $this->realstep->get_state(); } public function set_state($state) { @@ -712,7 +712,7 @@ class question_attempt_step_subquestion_adapter extends question_attempt_step { } public function get_fraction() { - return $this->realqas->get_fraction(); + return $this->realstep->get_fraction(); } public function set_fraction($fraction) { @@ -720,51 +720,51 @@ class question_attempt_step_subquestion_adapter extends question_attempt_step { } public function get_user_id() { - return $this->realqas->get_user_id(); + return $this->realstep->get_user_id(); } public function get_timecreated() { - return $this->realqas->get_timecreated(); + return $this->realstep->get_timecreated(); } public function has_qt_var($name) { - return $this->realqas->has_qt_var($this->add_prefix($name)); + return $this->realstep->has_qt_var($this->add_prefix($name)); } public function get_qt_var($name) { - return $this->realqas->get_qt_var($this->add_prefix($name)); + return $this->realstep->get_qt_var($this->add_prefix($name)); } public function set_qt_var($name, $value) { - $this->realqas->set_qt_var($this->add_prefix($name), $value); + $this->realstep->set_qt_var($this->add_prefix($name), $value); } public function get_qt_data() { - return $this->filter_array($this->realqas->get_qt_data()); + return $this->filter_array($this->realstep->get_qt_data()); } public function has_behaviour_var($name) { - return $this->realqas->has_behaviour_var($this->add_prefix($name)); + return $this->realstep->has_behaviour_var($this->add_prefix($name)); } public function get_behaviour_var($name) { - return $this->realqas->get_behaviour_var($this->add_prefix($name)); + return $this->realstep->get_behaviour_var($this->add_prefix($name)); } public function set_behaviour_var($name, $value) { - return $this->realqas->set_behaviour_var($this->add_prefix($name), $value); + return $this->realstep->set_behaviour_var($this->add_prefix($name), $value); } public function get_behaviour_data() { - return $this->filter_array($this->realqas->get_behaviour_data()); + return $this->filter_array($this->realstep->get_behaviour_data()); } public function get_submitted_data() { - return $this->filter_array($this->realqas->get_submitted_data()); + return $this->filter_array($this->realstep->get_submitted_data()); } public function get_all_data() { - return $this->filter_array($this->realqas->get_all_data()); + return $this->filter_array($this->realstep->get_all_data()); } public function get_qt_files($name, $contextid) { diff --git a/question/engine/tests/questionattempt_test.php b/question/engine/tests/questionattempt_test.php index 13b11e45785..fe1397fec54 100644 --- a/question/engine/tests/questionattempt_test.php +++ b/question/engine/tests/questionattempt_test.php @@ -52,12 +52,6 @@ class questionattempt_test extends \advanced_testcase { $this->qa = new question_attempt($this->question, $this->usageid); } - protected function tearDown(): void { - $this->question = null; - $this->useageid = null; - $this->qa = null; - } - public function test_constructor_sets_maxmark() { $qa = new question_attempt($this->question, $this->usageid); $this->assertSame($this->question, $qa->get_question(false)); diff --git a/question/engine/upgrade/behaviourconverters.php b/question/engine/upgrade/behaviourconverters.php index c6bffa0afbe..632278ed103 100644 --- a/question/engine/upgrade/behaviourconverters.php +++ b/question/engine/upgrade/behaviourconverters.php @@ -66,6 +66,8 @@ abstract class question_behaviour_attempt_updater { protected $qsession; /** @var array the question states for the session to be upgraded. */ protected $qstates; + /** @var stdClass */ + protected $startstate; /** * @var int counts the question_steps as they are converted to @@ -336,6 +338,7 @@ abstract class question_behaviour_attempt_updater { class qbehaviour_deferredfeedback_converter extends question_behaviour_attempt_updater { + protected function behaviour_name() { return 'deferredfeedback'; } @@ -398,6 +401,7 @@ class qbehaviour_manualgraded_converter extends question_behaviour_attempt_updat class qbehaviour_informationitem_converter extends question_behaviour_attempt_updater { + protected function behaviour_name() { return 'informationitem'; } diff --git a/question/engine/upgrade/upgradelib.php b/question/engine/upgrade/upgradelib.php index 82eb82e03c3..09b3488be1d 100644 --- a/question/engine/upgrade/upgradelib.php +++ b/question/engine/upgrade/upgradelib.php @@ -45,6 +45,8 @@ class question_engine_attempt_upgrader { protected $questionloader; /** @var question_engine_assumption_logger */ protected $logger; + /** @var stdClass */ + protected $qsession; public function save_usage($preferredbehaviour, $attempt, $qas, $quizlayout) { global $OUTPUT; @@ -280,6 +282,9 @@ class question_engine_upgrade_question_loader { protected $cache = array(); protected $datasetcache = array(); + /** @var base_logger */ + protected $logger; + public function __construct($logger) { $this->logger = $logger; } diff --git a/question/format.php b/question/format.php index 620fc55f411..ad7d4132ec8 100644 --- a/question/format.php +++ b/question/format.php @@ -54,6 +54,8 @@ class qformat_default { protected $importcontext = null; /** @var bool $displayprogress Whether to display progress. */ public $displayprogress = true; + /** @var context[] */ + public $contexts; // functions to indicate import/export functionality // override to return true if implemented @@ -166,7 +168,7 @@ class qformat_default { /** * set an array of contexts. - * @param array $contexts Moodle course variable + * @param context[] $contexts */ public function setContexts($contexts) { $this->contexts = $contexts; diff --git a/question/format/blackboard_six/format.php b/question/format/blackboard_six/format.php index b7a926c8bc6..0d8afcf4fb0 100644 --- a/question/format/blackboard_six/format.php +++ b/question/format/blackboard_six/format.php @@ -57,6 +57,8 @@ class qformat_blackboard_six extends qformat_blackboard_six_base { const FILETYPE_QTI = 1; /** @var int Blackboard question pool files were previously handled by the blackboard plugin. */ const FILETYPE_POOL = 2; + /** @var string temporary directory/folder. */ + public $temp_dir; /** * Return the content of a file given by its path in the tempdir directory. diff --git a/question/tests/externallib_test.php b/question/tests/externallib_test.php index f81f2d18504..45b3decde68 100644 --- a/question/tests/externallib_test.php +++ b/question/tests/externallib_test.php @@ -38,6 +38,15 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); */ class externallib_test extends externallib_advanced_testcase { + /** @var \stdClass course record. */ + protected $course; + + /** @var \stdClass user record. */ + protected $student; + + /** @var \stdClass user role record. */ + protected $studentrole; + /** * Set up for every test */ diff --git a/question/type/calculated/question.php b/question/type/calculated/question.php index 5e6d2e10d23..3857afb6a4c 100644 --- a/question/type/calculated/question.php +++ b/question/type/calculated/question.php @@ -311,7 +311,6 @@ class qtype_calculated_variable_substituter { // Prepare an array for {@link substitute_values()}. $this->search = array(); - $this->replace = array(); foreach ($values as $name => $value) { if (!is_numeric($value)) { $a = new stdClass(); diff --git a/question/type/calculatedmulti/edit_calculatedmulti_form.php b/question/type/calculatedmulti/edit_calculatedmulti_form.php index 98f836456e8..010729a14e2 100644 --- a/question/type/calculatedmulti/edit_calculatedmulti_form.php +++ b/question/type/calculatedmulti/edit_calculatedmulti_form.php @@ -131,12 +131,11 @@ class qtype_calculatedmulti_edit_form extends question_edit_form { $addfieldsname = 'updatecategory'; $addstring = get_string('updatecategory', 'qtype_calculated'); $mform->registerNoSubmitButton($addfieldsname); - $this->editasmultichoice = 1; $mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring), 'listcategory'); $mform->registerNoSubmitButton('createoptionbutton'); - $mform->addElement('hidden', 'multichoice', $this->editasmultichoice); + $mform->addElement('hidden', 'multichoice', 1); $mform->setType('multichoice', PARAM_INT); $menu = array(get_string('answersingleno', 'qtype_multichoice'), diff --git a/question/type/calculatedsimple/edit_calculatedsimple_form.php b/question/type/calculatedsimple/edit_calculatedsimple_form.php index 7af36e2d43d..499f3de20f1 100644 --- a/question/type/calculatedsimple/edit_calculatedsimple_form.php +++ b/question/type/calculatedsimple/edit_calculatedsimple_form.php @@ -317,7 +317,6 @@ class qtype_calculatedsimple_edit_form extends qtype_calculated_edit_form { get_string('findwildcards', 'qtype_calculatedsimple')); $mform->registerNoSubmitButton('analyzequestion'); $mform->closeHeaderBefore('analyzequestion'); - $this->wizarddisplay = optional_param('analyzequestion', false, PARAM_BOOL); if ($this->maxnumber != -1) { $this->noofitems = $this->maxnumber; } else { diff --git a/question/type/ddimageortext/question.php b/question/type/ddimageortext/question.php index 94c2f861b55..f2839f7a389 100644 --- a/question/type/ddimageortext/question.php +++ b/question/type/ddimageortext/question.php @@ -119,6 +119,9 @@ class qtype_ddimageortext_drop_zone { /** @var array X and Y location of the drop zone */ public $xy; + /** @var string field name to use */ + public $fieldname; + /** * Create a drop zone object. * diff --git a/question/type/ddimageortext/rendererbase.php b/question/type/ddimageortext/rendererbase.php index 3187b279318..987b1f40623 100644 --- a/question/type/ddimageortext/rendererbase.php +++ b/question/type/ddimageortext/rendererbase.php @@ -108,7 +108,6 @@ class qtype_ddtoimage_renderer_base extends qtype_with_combined_feedback_rendere list($fieldname, $html) = $this->hidden_field_for_qt_var($qa, $varname, null, ['placeinput', 'place' . $placeno, 'group' . $place->group]); $output .= $html; - $question->places[$placeno]->fieldname = $fieldname; } $output .= html_writer::end_div(); diff --git a/question/type/essay/tests/question_test.php b/question/type/essay/tests/question_test.php index c9d582df97f..1e79abf54f5 100644 --- a/question/type/essay/tests/question_test.php +++ b/question/type/essay/tests/question_test.php @@ -280,9 +280,9 @@ class question_test extends \advanced_testcase { // Test the case in which we're in "no inline response" mode, // in which the response is not required (as it's not provided). - $essay->reponserequired = 0; + $essay->responserequired = 0; $essay->responseformat = 'noinline'; - $essay->attachmensrequired = 1; + $essay->attachmentsrequired = 1; $this->assertFalse($essay->is_complete_response( array())); @@ -294,7 +294,7 @@ class question_test extends \advanced_testcase { array('attachments' => $attachments[1]))); // Ensure that responserequired is ignored when we're in inline response mode. - $essay->reponserequired = 1; + $essay->responserequired = 1; $this->assertTrue($essay->is_complete_response( array('attachments' => $attachments[1]))); } diff --git a/question/type/match/tests/question_test.php b/question/type/match/tests/question_test.php index 65296420f8a..4068c11659f 100644 --- a/question/type/match/tests/question_test.php +++ b/question/type/match/tests/question_test.php @@ -191,7 +191,7 @@ class question_test extends \advanced_testcase { $match = \test_question_maker::make_question('match'); unset($match->stems[4]); - unset($match->stemsformat[4]); + unset($match->stemformat[4]); unset($match->right[4]); $match->apply_attempt_state($firststep); @@ -212,7 +212,7 @@ class question_test extends \advanced_testcase { $match = \test_question_maker::make_question('match'); $match->stems[5] = "Snake"; - $match->stemsformat[5] = FORMAT_HTML; + $match->stemformat[5] = FORMAT_HTML; $match->choices[5] = "Reptile"; $match->right[5] = 5; $match->apply_attempt_state($firststep); diff --git a/question/type/multianswer/edit_multianswer_form.php b/question/type/multianswer/edit_multianswer_form.php index 0ace56a9f14..a192637c8d5 100644 --- a/question/type/multianswer/edit_multianswer_form.php +++ b/question/type/multianswer/edit_multianswer_form.php @@ -58,7 +58,10 @@ class qtype_multianswer_edit_form extends question_edit_form { public $reload = false; /** @var qtype_numerical_answer_processor used when validating numerical answers. */ protected $ap = null; - + /** @var bool */ + public $regenerate; + /** @var array */ + public $editas; public function __construct($submiturl, $question, $category, $contexts, $formeditable = true) { $this->regenerate = true; diff --git a/question/type/multianswer/tests/helper.php b/question/type/multianswer/tests/helper.php index 9c6e2d2595d..27ab5d6e301 100644 --- a/question/type/multianswer/tests/helper.php +++ b/question/type/multianswer/tests/helper.php @@ -428,7 +428,6 @@ class qtype_multianswer_test_helper extends question_test_helper { $mc->shuffleanswers = 0; $mc->answernumbering = 'none'; $mc->layout = qtype_multichoice_base::LAYOUT_VERTICAL; - $mc->single = 0; $mc->answers = array( 16 => new question_answer(16, 'Apple', 0.3333333, @@ -461,7 +460,6 @@ class qtype_multianswer_test_helper extends question_test_helper { $mc2->shuffleanswers = 0; $mc2->answernumbering = 'none'; $mc2->layout = qtype_multichoice_base::LAYOUT_VERTICAL; - $mc2->single = 0; $mc2->answers = array( 22 => new question_answer(22, 'Raddish', 0.5, diff --git a/question/type/questiontypebase.php b/question/type/questiontypebase.php index c0328ec0227..6dddf15db74 100644 --- a/question/type/questiontypebase.php +++ b/question/type/questiontypebase.php @@ -1405,7 +1405,7 @@ class question_type { $question = new stdClass(); $question->courseid = $courseid; - $question->qtype = $this->qtype; + $question->qtype = $this->name(); return array($form, $question); } diff --git a/question/type/randomsamatch/db/upgradelib.php b/question/type/randomsamatch/db/upgradelib.php index f896c058efb..941e1a6da7b 100644 --- a/question/type/randomsamatch/db/upgradelib.php +++ b/question/type/randomsamatch/db/upgradelib.php @@ -52,6 +52,8 @@ class qtype_randomsamatch_qe2_attempt_updater extends question_qtype_attempt_upd protected $choiceorder; /** @var array flipped version of the choiceorder array. */ protected $flippedchoiceorder; + /** @var array of right answer for each stem. */ + protected $rightanswer; public function question_summary() { return ''; // Done later, after we know which shortanswer questions are used. diff --git a/question/upgrade.txt b/question/upgrade.txt index 9881c3d79f1..808662bcfdd 100644 --- a/question/upgrade.txt +++ b/question/upgrade.txt @@ -5,6 +5,28 @@ This files describes API changes for code that uses the question API. 1) The question/qengine.js has been deprecated. We create core_question/question_engine and core/scroll_manager to replace it. +2) For properties that were previously only declared dynamically, a few classes now include property declarations to support PHP 8.2. + The affected classes are: + * restore_qbank_customfields_plugin + * category_condition + * tag_condition + * question_behaviour_attempt_updater + * question_engine_attempt_upgrader + * qformat_blackboard_six + * qtype_multianswer_edit_form + * qtype_ddimageortext_drop_zone + * qtype_randomsamatch_qe2_attempt_updater + * qformat_default + + Some existing code and variables must be removed while working on this issue, including: + * Removed $replace in class qtype_calculated_variable_substituter + * Removed $wizarddisplay qtype_calculatedsimple_edit_form::definition_inner() + * Removed '$question->places[$placeno]->fieldname = $fieldname' in qtype_ddtoimage_renderer_base::formulation_and_controls() + * Removed $mc->single in qtype_multianswer_test_helper::make_multianswer_question_multiple() + +3) display_move_form() in qbank_managecategories\question_category_object class is deprecated and moved the logic to + the question/bank/managecategories/category.php. + === 4.1 === 1) get_bulk_action_key() in core_question\local\bank\bulk_action_base class is deprecated and renamed to get_key().