MDL-26405 restore - dispatch able to skip branches
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 16 Feb 2011 18:13:56 +0000 (19:13 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 16 Feb 2011 18:13:56 +0000 (19:13 +0100)
after this change any restore_structure_step processor
method is able to instruct the dispatcher about to skip
any path below it. Until now, we were doing the checks on
each child processor method, but that was inneficient and
prone to errors (easy to miss the check in a child so some
orphaned piezes of restore may be causing mess here and there).
Once implemented, it's simlpy a matter of the parent deciding if
all its children must be processed or no. Easier for developers
and also small speed improvement because avoids unnecesary
dispatching/processing to happen.

Surely only will be used in parts of core, like in question_categories,
saving 50-60 sub processors (sub-paths) to be dispatched.

backup/moodle2/restore_stepslib.php
backup/util/plan/restore_structure_step.class.php

index 2a3fe48..463a58a 100644 (file)
@@ -2219,7 +2219,7 @@ class restore_create_categories_and_questions extends restore_structure_step {
 
         // Check we have one mapping for this category
         if (!$mapping = $this->get_mapping('question_category', $oldid)) {
-            return; // No mapping = this category doesn't need to be created/mapped
+            return self::SKIP_ALL_CHILDREN; // No mapping = this category doesn't need to be created/mapped
         }
 
         // Check we have to create the category (newitemid = 0)
index 2d1c6d7..970605e 100644 (file)
@@ -38,6 +38,11 @@ abstract class restore_structure_step extends restore_step {
     protected $elementsoldid; // Array to store last oldid used on each element
     protected $elementsnewid; // Array to store last newid used on each element
 
+    protected $pathlock;      // Path currently locking processing of children
+
+    const SKIP_ALL_CHILDREN = -991399; // To instruct the dispatcher about to ignore
+                                       // all children below path processor returning it
+
     /**
      * Constructor - instantiates one object of this class
      */
@@ -50,10 +55,11 @@ abstract class restore_structure_step extends restore_step {
         $this->pathelements = array();
         $this->elementsoldid = array();
         $this->elementsnewid = array();
+        $this->pathlock = null;
         parent::__construct($name, $task);
     }
 
-    public function execute() {
+    final public function execute() {
 
         if (!$this->execute_condition()) { // Check any condition to execute this
             return;
@@ -106,18 +112,37 @@ abstract class restore_structure_step extends restore_step {
      * Receive one chunk of information form the xml parser processor and
      * dispatch it, following the naming rules
      */
-    public function process($data) {
+    final public function process($data) {
         if (!array_key_exists($data['path'], $this->pathelements)) { // Incorrect path, must not happen
             throw new restore_step_exception('restore_structure_step_missing_path', $data['path']);
         }
         $element = $this->pathelements[$data['path']];
         $object = $element->get_processing_object();
         $method = $element->get_processing_method();
+        $rdata = null;
         if (empty($object)) { // No processing object defined
             throw new restore_step_exception('restore_structure_step_missing_pobject', $object);
         }
-        $rdata = $object->$method($data['tags']); // Dispatch to proper object/method
-        if ($rdata !== null) { // If the method has returned any info, set element data to it
+        // Release the lock if we aren't anymore within children of it
+        if (!is_null($this->pathlock) and strpos($data['path'], $this->pathlock) === false) {
+            $this->pathlock = null;
+        }
+        if (is_null($this->pathlock)) { // Only dispatch if there isn't any lock
+            $rdata = $object->$method($data['tags']); // Dispatch to proper object/method
+        }
+
+        // If the dispatched method returns SKIP_ALL_CHILDREN, we grab current path in order to
+        // lock dispatching to any children
+        if ($rdata === self::SKIP_ALL_CHILDREN) {
+            // Check we haven't any previous lock
+            if (!is_null($this->pathlock)) {
+                throw new restore_step_exception('restore_structure_step_already_skipping', $data['path']);
+            }
+            // Set the lock
+            $this->pathlock = $data['path'] . '/'; // Lock everything below current path
+
+        // Continue with normal processing of return values
+        } else if ($rdata !== null) { // If the method has returned any info, set element data to it
             $element->set_data($rdata);
         } else {               // Else, put the original parsed data
             $element->set_data($data);