/**
  * Saves this definition into database
  */
 public function save(&$preventLoop = array())
 {
     // Load ID and parents before saving, as the definition may be deleted next
     $parents = $this['parents'];
     $peType = $this['pe_type'];
     $peID = $this['pe_id'];
     $key = $peType . '-' . $peID;
     $st = IACLStorage::get('SD');
     if (!$this->data['rules']) {
         // Delete definition
         $clean = $this->clean();
         $delRules = $clean['rules'] ? $clean['rules'] : array();
         $addRules = array();
         $st->deleteRules(array(array('pe_type' => $peType, 'pe_id' => $peID)));
     } else {
         // Update definition
         list($delRules, $addRules) = $this->diffRulesAssoc();
         if ($delRules) {
             $st->deleteRules(self::expandRuleArray($delRules));
         }
         if ($addRules) {
             $st->addRules(self::expandRuleArray($addRules));
         }
     }
     // Invalidate userCan() cache (FIXME - in fact our parents don't need to do it...)
     if (isset($delRules[IACL::PE_ALL_USERS]) || isset($delRules[IACL::PE_REG_USERS]) || isset($addRules[IACL::PE_ALL_USERS]) || isset($addRules[IACL::PE_REG_USERS])) {
         self::$userCache = array();
         self::$userCacheLoaded = array();
     } else {
         if (isset($delRules[IACL::PE_USER])) {
             foreach ($delRules[IACL::PE_USER] as $userID => $rule) {
                 unset(self::$userCache[$userID]);
                 unset(self::$userCacheLoaded[$userID]);
             }
         }
         if (isset($addRules[IACL::PE_USER])) {
             foreach ($addRules[IACL::PE_USER] as $userID => $rule) {
                 unset(self::$userCache[$userID]);
                 unset(self::$userCacheLoaded[$userID]);
             }
         }
     }
     // Invalidate 'parents' field for children
     foreach ($delRules as $peType => $ids) {
         foreach ($ids as $peID => $rules) {
             if (!empty(self::$clean["{$peType}-{$peID}"])) {
                 unset(self::$clean["{$peType}-{$peID}"]->data['parents']);
             }
         }
     }
     foreach ($addRules as $peType => $ids) {
         foreach ($ids as $peID => $rules) {
             if (!empty(self::$clean["{$peType}-{$peID}"])) {
                 unset(self::$clean["{$peType}-{$peID}"]->data['parents']);
             }
         }
     }
     // Commit new state into the object cache (FIXME - is the object cache needed at all?)
     unset(self::$dirty[$key]);
     $this->rw = false;
     if ($this->data['rules']) {
         self::$clean[$key] = $this;
     } else {
         self::$clean[$key] = false;
     }
     // Invalidate parents - they will do the same recursively for their parents and so on
     $preventLoop[$key] = true;
     foreach ($parents as $p) {
         if (!isset($preventLoop[$p['key']])) {
             $p->save($preventLoop);
         }
     }
 }