public function testBrokenErase()
 {
     $a = self::$c->getAvailableModules(array(self::data_dir(__CLASS__) . "/OneModuleWithClass"));
     TestMagicDataTearDown::brokenTearDown();
     I2CE::resetFileSearch();
     $this->assertEquals(array(), self::$not_errors);
     $this->assertEquals(array(), self::$errors);
     $this->setUp();
     $a = self::$c->getAvailableModules(array(self::data_dir(__CLASS__) . "/OneModuleWithClass"));
     $b = array();
     $this->assertEquals($b, $a);
     $this->assertRegExp("{Trying to get property of non-object}", self::$not_errors[0]);
     $this->assertContains("Got a NULL value for storage.", self::$errors);
     self::$errors = array();
     TestMagicDataTearDown::tearDown();
     I2CE::resetFileSearch();
     $this->assertEquals(array(), self::$errors);
 }
 function tearDown()
 {
     I2CE_MagicData::tearDown();
     I2CE::resetFileSearch();
 }
 protected function tearDown()
 {
     I2CE_MagicData::tearDown();
     I2CE::resetFileSearch();
     I2CE::popErrorHandler(array(__CLASS__, "errorHandler"));
 }
 /**
  * Checks the requirements.  if all requirements are met and
  * there are no conflicts, it returns a list of those that
  * need to be installed, an empty array() if none are needed.
  * On failure returns null.
  *
  * @param array $shortnames an array of shortnames
  *
  * @param mixed $disables a shortname or an array of
  * shortnames of modules to disable.  Defaults to empty array
  *
  * @param array $remove an array of potential shortnames to
  * remove -- if we did not find them somplace else. (it may
  * have just been moved) defaults to an empty array.
  * 
  * @param array $enabled_modules An array of shortnames of
  * enabled modules -- we check for conflicts against this
  * array
  *
  * @param mixed $required_modules A shortname or an array of
  * shortnames of required modules -- if a module is set to be
  * updated and it is not required by on of the modules in this
  * array, it will be set as an optional modules.  Defaults to
  * the empty array
  *
  * @returns array.  Keys are:
  *    'failure'      -  string with the reason for failture
  *    'requirements' -  array which has as keys shortnames and
  *                      values files for the requirements.
  *    'removals'     -  array of shortnames that need to be removed.
  *    'optional'     -  array with key shortnames and value true
  *    'moved'        -  array with key  shortnames and value true
  */
 public function checkRequirements($updates, $disables = array(), $removals = array(), $enabled_modules = array(), $required_modules = array(), $reset_moved = true)
 {
     if ($reset_moved) {
         $this->moved = array();
     }
     if ($this->storage->setIfIsSet($i2ce_config_file, "config/data/I2CE/file") && $i2ce_config_file != ($new_i2ce_config_file = rtrim(dirname(dirname(__FILE__)), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'I2CE_Configuration.xml')) {
         //we have moved location of I2CE.  Other things have likely gotten moved around as well and we do not want to use the stored paths.
         //the file search has been set in I2CE_Update->_updateSite()->needs_upgrade to  I2CE::setupFileSearch( array( "CLASSES" => "./", 'MODULES'=>array(dirname($site_module_file),dirname(dirname(__FILE__)))), true);
         $this->resetCheckedPaths();
         I2CE::resetFileSearch();
         $file_search = I2CE::getFileSearch();
         if (null === $file_search) {
             I2CE::raiseError("Resetting file search");
             I2CE::setupFileSearch();
             $file_search = I2CE::getFileSearch();
         }
         // just to make sure all of our module paths are loaded.
         $mod_factory = I2CE_ModuleFactory::instance();
         $mod_factory->resetStoredLoadedPaths();
         $mod_factory->loadPaths($mod, $path, true, $file_search, $this->storage->config->data);
     } else {
         $mods = array_unique(array_merge(array('I2CE'), $enabled_modules));
         $file_search = $this->setupFileSearch($mods);
     }
     $this->findAvailableConfigs($file_search, true);
     if (!$this->storage->is_parent("config/data")) {
         I2CE::raiseError("Data not loaded.", E_ERROR);
         return array('failure' => "Data not loaded");
     }
     I2CE::raiseError("Done looking for available config files.  Available:\n\t" . implode(',', $this->storage->config->data->getKeys()));
     $conflicts = array();
     $available = $this->storage->config->data;
     if (!is_array($required_modules)) {
         $required_modules = array($required_modules);
     }
     foreach ($removals as $indx => $remove) {
         if (isset($available->{$remove})) {
             // I found the thing I thought I might need to remove
             // someplace else.
             unset($removals[$indx]);
             $this->moved[$remove] = true;
             $updates[] = $remove;
         }
     }
     foreach ($updates as $indx => $update) {
         // make sure our desired updates are really available
         if (!isset($available->{$update})) {
             I2CE::raiseError("Desired update {$update} cannot be found. Instead it is being removed");
             I2CE::raiseError(implode(',', $available->getKeys()));
             $removals[] = $update;
             unset($updates[$indx]);
         }
     }
     // this now holds everything that needs to be disabled or removed
     $removals = array_merge($disables, $removals);
     $tmp_removals = array();
     $avails = $available->getKeys();
     while (count($removals) > 0) {
         $r = array_pop($removals);
         if (!is_scalar($r)) {
             I2CE::raiseError("Internal misconfiguration on removals", E_USER_ERROR);
             continue;
         }
         if (array_key_exists($r, $tmp_removals) && $tmp_removals[$r]) {
             continue;
         }
         /*
          * start removing anything that is required by this
          * guy.  work your way up the requirement tree.  we
          * will use the avialable rather than currently
          * installed information as we want the status at the
          * _end_ of the process to be correct.  we are going to
          * make sure everything that could be possibly
          * installed is on the removal list.  we will check
          * against this list as we process the depndencies for
          * updates and installs to make sure there are no
          * conflicts
          */
         $t_removals = array($r);
         while (count($t_removals) > 0) {
             $remove = array_pop($t_removals);
             if (!is_string($remove)) {
                 return array('failure' => "bad removals: " . print_r($remove, true));
             }
             if (in_array($remove, $t_removals)) {
                 return array('failure' => "Recursion in dependency " . "list for removal on {$remove}:\n" . implode(',', $t_removals));
             }
             $tmp_removals[$remove] = true;
             foreach ($avails as $avail) {
                 // should we check versions here?
                 if (isset($available->{$avail}->requirement->{$remove})) {
                     array_push($t_removals, $avail);
                 }
             }
         }
     }
     $removals = $tmp_removals;
     //make sure our desired updates are clean of anything set to be removed
     foreach ($removals as $removal => $do_remove) {
         if (!is_string($removal) || !$do_remove) {
             continue;
         }
         if (array_key_exists($removal, $updates)) {
             unset($updates[$removal]);
         }
     }
     //now start working on the dependencies/requirements
     //updates is an array which holds the things we need to
     //install or update, but have not processed the requirements for
     $requirementsDOM = new DOMDocument('1.0', 'UTF-8');
     $new_req = array();
     //I2CE::raiseError("Making main dependecy tree for:\n\t" . implode(',',$updates));
     foreach ($updates as $i => $update) {
         $optional = !$this->moduleRequires($update, $available->{$update}->version, $required_modules);
         // if ($optional) {
         //     I2CE::raiseError("Optional $update");
         // } else {
         //     I2CE::raiseError("Not optional $update");
         // }
         $t_requirementsDOM = new DOMDocument('1.0', 'UTF-8');
         for ($i = 0; $i < $requirementsDOM->childNodes->length; $i++) {
             $child = $requirementsDOM->childNodes->item($i);
             $clone = $child->cloneNode(true);
             $clone = $t_requirementsDOM->importNode($clone, true);
             $t_requirementsDOM->appendChild($clone);
         }
         $new_req = $this->processRequirementsDOM($update, $t_requirementsDOM, $optional);
         if (is_string($new_req)) {
             if (!$optional) {
                 return array('failure' => $new_req);
             } else {
                 //we failed on this update.  Try and recover.
                 I2CE::raiseError("Unable to process optional module {$update}, skipping:  {$new_req}");
                 unset($updates[$i]);
                 continue;
             }
         }
         //if (count($new_req) > 0) {
         // if ($optional) {
         //     I2CE::raiseError("Optional update $update adds the following additional required modules:\n\t" . implode(',',$new_req));
         // } else {
         //     I2CE::raiseError("Required update $update adds the following additional required modules:\n\t" . implode(',',$new_req));
         // }
         //}
         if (!$optional) {
             foreach ($new_req as $req) {
                 // check to see if this gal is on the removal list
                 if (array_key_exists($req, $removals)) {
                     return array('failure' => "Module {$req}  is set to be removed but is also a requirement");
                 }
             }
         }
         //we are good to to
         $requirementsDOM = $t_requirementsDOM;
     }
     list($requirements, $optionals) = $this->flattenRequirements($requirementsDOM);
     //the only optional modules here are the ones that are marked to be updated, but not a part of the required modules.
     //this does not include, in general, all the optional modules that the site wants to enable.  these are added below
     //I2CE::raiseError("Required modules are=" . implode(',',$requirements));
     // now check the conflicts with what we desire to update/install and what is already installed.
     //I2CE::raiseError("Checking for conflicts in " . implode(" ", $requirements) . "\nagainst enabled: " . implode(" " , $enabled_modules));
     $ret = $this->checkForConflicts($requirements, $enabled_modules);
     if (is_string($ret)) {
         I2CE::raiseError("Found conflict in: {$ret}\nEnabled: " . implode(" ", $enabled_modules) . "\nRequired by update:" . implode(" ", $requirements));
         //OK.  there is a conflict. Let see if the conflict is against an optional module.  this is tedious/slow.
         //first we build a list of modules that are enabled bu not required.
         $modules_optional_enabled = array();
         foreach ($enabled_modules as $e) {
             if ($this->moduleRequires($e, $available->{$e}->version, $required_modules)) {
                 continue;
             }
             $modules_optional_enabled[] = $e;
         }
         if (count($modules_optional_enabled) == 0) {
             I2CE::raiseError("All currently enabled modules are required");
             return array('failure' => $ret);
         }
         I2CE::raiseError("The following enabled modules are optional.  Will try to disable some of them:\n" . implode(" ", $modules_optional_enabled));
         $allowed_optional_enabled = array();
         $optional_disables = array();
         $non_optional_enabled = array_diff($enabled_modules, $modules_optional_enabled);
         while (count($modules_optional_enabled) > 0) {
             $optional_disable = array_pop($modules_optional_enabled);
             if (is_string($t_ret = $this->checkForConflicts($requirements, array_merge($non_optional_enabled, array($optional_disable))))) {
                 //there is a conflict when trying to enable $optional_disable
                 $optional_disables[] = $optional_disable;
                 I2CE::raiseError("WARNING: The module {$optional_disable} is not allowed");
             } else {
                 I2CE::raiseError("The module {$optional_disable} is allowed");
             }
         }
         I2CE::raiseError('Adding enabled optional modules to removal list: ' . implode(" ", $optional_disables));
         $tmp_removals = array();
         $avails = $available->getKeys();
         while (count($optional_disables) > 0) {
             $r = array_pop($optional_disables);
             if (array_key_exists($r, $tmp_removals) && $tmp_removals[$r]) {
                 continue;
             }
             /*
              * start removing anything that is required by this
              * guy.  work your way up the requirement tree.  we
              * will use the avialable rather than currently
              * installed information as we want the status at the
              * _end_ of the process to be correct.  we are going to
              * make sure everything that could be possibly
              * installed is on the removal list.  we will check
              * against this list as we process the depndencies for
              * updates and installs to make sure there are no
              * conflicts
              */
             $t_removals = array($r);
             while (count($t_removals) > 0) {
                 $remove = array_pop($t_removals);
                 if (!is_string($remove)) {
                     return array('failure' => "bad removals: " . print_r($remove, true));
                 }
                 if (in_array($remove, $t_removals)) {
                     return array('failure' => "Recursion in dependency " . "list for removal on {$remove}:\n" . implode(',', $t_removals));
                 }
                 $tmp_removals[$remove] = true;
                 foreach ($avails as $avail) {
                     // should we check versions here?
                     if (isset($available->{$avail}->requirement->{$remove})) {
                         array_push($t_removals, $avail);
                     }
                 }
             }
         }
         $optional_disables = $tmp_removals;
         $xpath = new DOMXPath($requirementsDOM);
         foreach (array_keys($tmp_removals) as $r) {
             $this->removeRequirement($r, $requirementsDOM);
         }
         I2CE::raiseError('Adding enabled optional modules to removal list (with dependent modules): ' . implode(" ", array_keys($optional_disables)));
         $removals = array_merge($removals, $optional_disables);
         $enabled_modules = array_diff($enabled_modules, array_keys($optional_disables));
         $requirements = array_diff($requirements, array_keys($optional_disables));
     }
     // now we check to see if there are any modules that are requested to be enabled.
     $enable_list = array();
     foreach (array_diff($requirements, array_keys($removals)) as $req) {
         $es = array();
         $available->setIfIsSet($es, "{$req}/enable", true);
         foreach ($es as $e) {
             if (in_array($e, $requirements)) {
                 continue;
             }
             $enable_list[$e] = true;
         }
     }
     $enable_list = array_keys($enable_list);
     if (count($enable_list) > 0) {
         //I2CE::raiseError("Processing main dependecy for optional modules:". implode(',',$enable_list));
         while (count($enable_list) > 0) {
             $e = array_shift($enable_list);
             //I2CE::raiseError("Doing $e from enable list");
             //I2CE::raiseError("Current optionals on $e=" . implode(" ", $optionals));
             $t_requirementsDOM = new DOMDocument('1.0', 'UTF-8');
             for ($i = 0; $i < $requirementsDOM->childNodes->length; $i++) {
                 $child = $requirementsDOM->childNodes->item($i);
                 $clone = $child->cloneNode(true);
                 $clone = $t_requirementsDOM->importNode($clone, true);
                 $t_requirementsDOM->appendChild($clone);
             }
             //try to see if we can  add in $e as an optional module will cause a conflict.
             $ret = $this->processRequirementsDOM($e, $t_requirementsDOM, true);
             if (is_string($ret)) {
                 // we don't really care if this fails but lets put
                 // a notice anyways
                 I2CE::raiseError("Internal Configuration Error for {$e} -- Could not enable requested optional module: {$ret}");
                 continue;
             }
             //now check that the new requirements added don't have conflicts with existing enabled modules, any requireed modules.  skip the modules set to be removed
             $conflict = $this->checkForConflicts($requirements, array_merge(array($e), $ret, $enabled_modules));
             //$conflict = $this->checkForConflicts(array_merge(array($e),$ret), array_diff(array_unique(array_merge($enabled_modules, $requirements)),array_keys($removals)));
             if (is_string($conflict)) {
                 I2CE::raiseError("Could not enable {$e} as there was a conflict: {$conflict}");
                 //skip to the next enable request
                 continue;
             }
             // check to see if any of the newly added modules are  on the removal list
             foreach ($ret as $nreq) {
                 if (array_key_exists($nreq, $removals)) {
                     I2CE::raiseError("Could not enable module {$e} as it's " . "requirement {$nreq} is set to be removed");
                     continue 2;
                     //skip to the next enable request
                 }
             }
             //if we made it here, all was good.
             $requirementsDOM = $t_requirementsDOM;
             list($requirements, $optionals) = $this->flattenRequirements($t_requirementsDOM);
             I2CE::raiseError("Current requirements on {$e}=" . implode(" ", $requirements));
             I2CE::raiseError("Current optionals on {$e}=" . implode(" ", $optionals));
             foreach ($ret as $r) {
                 $es = array();
                 $available->setIfIsSet($es, "{$r}/enable", true);
                 foreach ($es as $e) {
                     if (in_array($e, $requirements) || in_array($e, $enable_list)) {
                         continue;
                     }
                     $enable_list[] = $e;
                 }
             }
         }
     }
     // list out all the module requirements. separated by
     // optional and required
     $optional = array();
     $xpath = new DOMXPath($requirementsDOM);
     $modules = $xpath->query("//module[@optional='1']");
     for ($i = 0; $i < $modules->length; $i++) {
         $opt_file = false;
         $opt_mod = $modules->item($i)->getAttribute('name');
         if (!$opt_mod) {
             continue;
         }
         if ($available->setIfIsSet($opt_file, "{$opt_mod}/file")) {
             $optional[$opt_mod] = $opt_file;
         }
         //$optional[ ] = $available->$req->file;
     }
     $reqs = array();
     foreach ($requirements as $req) {
         $reqs[$req] = $available->{$req}->file;
     }
     //for any already
     //we need the optional module to be enabled in reverse order that they are in the dom.
     $optional = array_reverse($optional);
     //get the list of enabled and up to date modules.
     $ignore_modules = array_diff($enabled_modules, $updates);
     //I2CE::raiseError("Requirements:" . implode(" ", array_keys($reqs)));
     //I2CE::raiseError("Optional:" . implode(" ", array_keys($optional)));
     //I2CE::raiseError("The following modules are being ignored as they are up to date and enabled " . implode(" ", $ignore_modules));
     foreach ($ignore_modules as $mod) {
         if (array_key_exists($mod, $optional)) {
             unset($optional[$mod]);
         }
         if (array_key_exists($mod, $reqs)) {
             unset($reqs[$mod]);
         }
     }
     //I2CE::raiseError("Needed Up Enable/Update Requirements:" . implode(" ", array_keys($reqs)));
     //I2CE::raiseError("Need to Enable/Update  Optional:" . implode(" ", array_keys($optional)));
     return array('requirements' => $reqs, 'removals' => $removals, 'optional' => $optional, 'moved' => $this->moved);
 }