Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * @package    moodlecore
  20   * @subpackage backup-structure
  21   * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   *
  24   * TODO: Finish phpdocs
  25   */
  26  
  27  /**
  28   * Instantiable class representing one optigroup element for conditional branching
  29   *
  30   * Objects of this class are internally nested elements, so they support having both
  31   * final elements and children (more nested elements) and are able to have one source
  32   * and all the stuff supported by nested elements. Their main differences are:
  33   *
  34   * - Support for conditional execution, using simple equality checks with outer values.
  35   * - Don't have representation in the hierarchy, so:
  36   *     - Their level is the level of the parent of their enclosing optigroup.
  37   *     - Act as one "path bridge" when looking for parent path values
  38   *     - They don't support attributes
  39   *
  40   * Their main use is to allow conditional branching, basically for optional submodules
  41   * like question types, assignment subtypes... where different subtrees of information
  42   * must be exported. It's correct to assume that each submodule will define its own
  43   * optigroup_element for backup purposes.
  44   */
  45  class backup_optigroup_element extends backup_nested_element {
  46  
  47      private $conditionparam;     // Unprocessed param representing on path to look for value
  48      private $procconditionparam; // Processed base_element param to look for value
  49      private $conditionvalue;     // Value to compare the the param value with
  50  
  51      /**
  52       * Constructor - instantiates one backup_optigroup_element
  53       *
  54       * @param string $name of the element
  55       * @param array $final_elements this element will handle (optional, defaults to null)
  56       * @param string $condition_param param (path) we are using as source for comparing (optional, defaults to null)
  57       * @param string $condition_value   value we are comparing to (optional, defaults to null)
  58       */
  59      public function __construct($name, $final_elements = null, $conditionparam = null, $conditionvalue = null) {
  60          parent::__construct($name, null, $final_elements);
  61          $this->set_condition($conditionparam, $conditionvalue);
  62      }
  63  
  64  // Public API starts here
  65  
  66      /**
  67       * Sets the condition for this optigroup
  68       */
  69      public function set_condition($conditionparam, $conditionvalue) {
  70          // We only resolve the condition if the parent of the element (optigroup) already has parent
  71          // else, we'll resolve it once the optigroup parent is defined
  72          if ($this->get_parent() && $this->get_parent()->get_parent() && $conditionparam !== null) {
  73              $this->procconditionparam = $this->find_element($conditionparam);
  74          }
  75          $this->conditionparam = $conditionparam;
  76          $this->conditionvalue = $conditionvalue;
  77      }
  78  
  79      public function get_condition_param() {
  80          return $this->conditionparam;
  81      }
  82  
  83      public function get_condition_value() {
  84          return $this->conditionvalue;
  85      }
  86  
  87      /**
  88       * Evaluate the condition, returning if matches (true) or no (false)
  89       */
  90      public function condition_matches() {
  91          $match = false; // By default no match
  92          $param = $this->procconditionparam;
  93          if ($param instanceof base_atom && $param->is_set()) {
  94              $match = ($param->get_value() == $this->conditionvalue); // blame $DB for not having === !
  95          } else {
  96              $match = ($param == $this->conditionvalue);
  97          }
  98          return $match;
  99      }
 100  
 101      /**
 102       * Return the level of this element, that will be, the level of the parent (doesn't consume level)
 103       * (note this os only a "cosmetic" effect (to_string) as fact as the real responsible for this
 104       * is the corresponding structure_processor for the final output.
 105       */
 106      public function get_level() {
 107          return $this->get_parent() == null ? 1 : $this->get_parent()->get_level();
 108      }
 109  
 110      /**
 111       * process one optigroup_element
 112       *
 113       * Note that this ONLY processes the final elements in order to get all them
 114       * before processing any nested element. Pending nested elements are processed
 115       * by the optigroup caller.
 116       */
 117      public function process($processor) {
 118          if (!$processor instanceof base_processor) { // No correct processor, throw exception
 119              throw new base_element_struct_exception('incorrect_processor');
 120          }
 121  
 122          $iterator = $this->get_iterator($processor); // Get the iterator over backup-able data
 123  
 124          $itcounter = 0; // To check that the iterator only has 1 ocurrence
 125          foreach ($iterator as $key => $values) { // Process each "ocurrrence" of the nested element (recordset or array)
 126  
 127              // Fill the values of the attributes and final elements with the $values from the iterator
 128              $this->fill_values($values);
 129  
 130              // Delegate the process of each final_element
 131              foreach ($this->get_final_elements() as $final_element) {
 132                  $final_element->process($processor);
 133              }
 134  
 135              // Everything processed, clean values before next iteration
 136              $this->clean_values();
 137  
 138              // Increment counters for this element
 139              $this->counter++;
 140              $itcounter++;
 141  
 142              // optigroup_element, check we only have 1 element always
 143              if ($itcounter > 1) {
 144                  throw new base_element_struct_exception('optigroup_element_only_one_ocurrence', $this->get_name());
 145              }
 146          }
 147          // Close the iterator (DB recordset / array iterator)
 148          $iterator->close();
 149      }
 150  
 151  // Forbidden API starts here
 152  
 153      /**
 154       * Adding optigroups is forbidden
 155       */
 156      public function add_add_optigroup($optigroup) {
 157          throw new base_element_struct_exception('optigroup_element_not_optigroup');
 158      }
 159  
 160      /**
 161       * Adding attributes is forbidden
 162       */
 163      public function add_attributes($attributes) {
 164          throw new base_element_struct_exception('optigroup_element_not_attributes');
 165      }
 166  
 167      /**
 168       * Instantiating attributes is forbidden
 169       */
 170      protected function get_new_attribute($name) {
 171          throw new base_element_struct_exception('optigroup_element_not_attributes');
 172      }
 173  
 174  // Protected API starts here
 175  
 176      /**
 177       * Returns one instace of the @final_element class to work with
 178       * when final_elements are added simply by name
 179       */
 180      protected function get_new_final_element($name) {
 181          return new backup_final_element($name);
 182      }
 183  
 184      /**
 185       * Set the parent of the optigroup_element and, at the same time,
 186       * process the condition param
 187       */
 188      protected function set_parent($element) {
 189          parent::set_parent($element);
 190          // Force condition param calculation
 191          $this->set_condition($this->conditionparam, $this->conditionvalue);
 192      }
 193  
 194  }