function test_arrayPrune()
 {
     $source_data = array("1" => array("1" => array(), "2" => array("1" => array("A" => "test_val", "B" => "test_val"), "2" => array("A" => "test_val", "B" => "test_val"))), "2" => array("2" => array("1" => array())), "3" => array("1" => array("2" => array("A" => "test_val"), "3" => array("C" => ""), "4" => array("C" => "test_val"))));
     $check_data = array("1" => array("2" => array("1" => array("A" => "test_val", "B" => "test_val"), "2" => array("A" => "test_val", "B" => "test_val"))), "3" => array("1" => array("2" => array("A" => "test_val"), "3" => array("C" => ""), "4" => array("C" => "test_val"))));
     $result = FOX_sUtil::arrayPrune($source_data, 3);
     $this->assertEquals($check_data, $result);
 }
Example #2
0
 /**
  * Removes all empty walks from the class cache for a specific user_id
  *
  * @version 1.0
  * @since 1.0
  * @param int $user_id | user_id to compact cache for
  * @return null
  */
 public function compactCache($user_id)
 {
     if (FOX_sUtil::keyExists($user_id, $this->cache)) {
         $this->cache[$user_id]["keys"] = FOX_sUtil::arrayPrune($this->cache[$user_id]["keys"], 2);
     }
 }
 /**
  * Deletes one or more trees from the datastore
  *
  * @version 1.0
  * @since 1.0
  *
  * @param string/array $trees | Single tree as string. Multiple trees as array of string.
  * @return int | Exception on failure. Number of db rows changed on success.
  */
 public function dropTree($trees)
 {
     $db = new FOX_db();
     $struct = $this->_struct();
     // Lock the cache
     // ===========================================================
     try {
         $cache_image = self::lockCache();
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Error locking cache", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     // Update the database
     // ===========================================================
     $args = array(array("col" => "tree", "op" => "=", "val" => $trees));
     try {
         $rows_changed = $db->runDeleteQuery($struct, $args);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 2, 'text' => "Error deleting from database", 'data' => array('args' => $args), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     if (!is_array($trees)) {
         $trees = array($trees);
     }
     // Rebuild the cache image
     // ===========================================================
     foreach ($trees as $tree) {
         unset($cache_image["keys"][$tree]);
         unset($cache_image["branches"][$tree]);
         unset($cache_image["trees"][$tree]);
     }
     unset($tree);
     // Clear empty walks
     $cache_image["branches"] = FOX_sUtil::arrayPrune($cache_image["branches"], 1);
     $cache_image["keys"] = FOX_sUtil::arrayPrune($cache_image["keys"], 2);
     // Write the image back to the persistent cache, releasing our lock
     // ===========================================================
     try {
         self::writeCache($cache_image);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Cache write error", 'data' => array('cache_image' => $cache_image), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     // Update the class cache
     $this->cache = $cache_image;
     return (int) $rows_changed;
 }
 /**
  * Drops multiple L5->L1 walks from the datastore
  *
  * @version 1.0
  * @since 1.0
  *
  * [MATRIX MODE] 
  * @param array $data | Array of row arrays 
  *	=> ARR @param int '' | Individual row array
  *	    => VAL @param int/string $L5 | Single L5 id as int/string
  *	    => VAL @param int/string $L4 | Single L4 id as int/string
  *	    => VAL @param int/string $L3 | Single L3 id as int/string
  *	    => VAL @param int/string $L2 | Single L2 id as int/string
  *	    => VAL @param int/string $L1 | Single L1 id as int/string
  * 
  * [TRIE MODE]
  * @param array $data | array of L5's in the form "L5_id"=>"L4s"	
  *	=> ARR @param array $L4s | array of L4's in the form "L4_id"=>"L3s"	 
  *	    => ARR @param array $L3s | array of L3's in the form "L3_id"=>"L2s"
  *		=> ARR @param array $L2s | array of L2's in the form "L2_id"=>"L1s"
  *		    => ARR @param array $L1s | array of L1's in the form "L1_id"=>"L1_value"
  *			=> KEY @param int/string | L1 id
  *			    => VAL @param NULL	 
  * 
  * @param array $ctrl | Control parameters
  *	=> VAL @param bool $validate | Validate keys
  *	=> VAL @param string $mode | Operation mode 'matrix' | 'trie'
  * 
  * @return int | Exception on failure. Int number of rows changed on success.
  */
 public function dropMulti($data, $ctrl = null)
 {
     if (!$this->init) {
         throw new FOX_exception(array('numeric' => 0, 'text' => "Descendent class must call init() before using class methods", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "method_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     // Add default control params
     // ==========================
     $ctrl_default = array('validate' => true, 'mode' => 'trie', 'trap_*' => true);
     $ctrl = FOX_sUtil::parseArgs($ctrl, $ctrl_default);
     if (!is_array($data) || count($data) < 1) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Invalid data array", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     // Validate data array
     // ===========================================================
     $struct = $this->_struct();
     $del_data = array();
     if ($ctrl['mode'] == 'matrix') {
         if ($ctrl['validate'] != false) {
             // Performance optimization (saves 1 op per key)
             if ($this->debug_on) {
                 extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "matrix_validate_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
             }
             $row_valid = false;
             try {
                 $validator = new FOX_dataStore_validator($struct);
                 $row_ctrl = array('end_node_format' => 'scalar');
                 foreach ($data as $row) {
                     $row_valid = $validator->validateMatrixRow($row, $row_ctrl);
                     if ($row_valid !== true) {
                         break;
                     }
                 }
                 unset($row);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "Error in validator", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
             if ($row_valid !== true) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "Invalid row in data array", 'data' => $row_valid, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
             }
             if ($this->debug_on) {
                 extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "matrix_validate_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
             }
         }
         // Loft the individual rows into a trie, to merge overlapping entries, then clip
         // the tree to get the highest order cache LUT's affected by the delete
         $columns = array($this->L5_col, $this->L4_col, $this->L3_col, $this->L2_col, $this->L1_col);
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "matrix_transform_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
         $trie = FOX_trie::loftMatrix($data, $columns, null);
         $del_data = FOX_trie::clipAssocTrie($trie, $columns, null);
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "matrix_transform_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
     } elseif ($ctrl['mode'] == 'trie') {
         if ($ctrl['validate'] != false) {
             // Validate the $data array
             if ($this->debug_on) {
                 extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "trie_validate_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
             }
             try {
                 $validator = new FOX_dataStore_validator($struct);
                 $val_ctrl = array('order' => 5, 'mode' => 'control', 'allow_wildcard' => false);
                 $tree_valid = $validator->validateTrie($data, $val_ctrl);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 4, 'text' => "Error in validator", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
             if ($tree_valid !== true) {
                 throw new FOX_exception(array('numeric' => 5, 'text' => "Invalid key in data array", 'data' => $tree_valid, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
             }
             if ($this->debug_on) {
                 extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "trie_validate_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
             }
         }
         $del_data = $data;
     } else {
         throw new FOX_exception(array('numeric' => 6, 'text' => "Invalid ctrl['mode'] parameter", 'data' => $ctrl, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     // Trap "DELETE * WHERE TRUE"
     // ===========================================================
     if ($ctrl['trap_*'] == true) {
         if (!array_keys($del_data)) {
             // @see http://en.wikipedia.org/wiki/Universal_set
             $error_msg = "INTERLOCK TRIP: One or more of the conditions set in the \$data array reduces to the universal set, ";
             $error_msg .= "which is equivalent to 'WHERE 1 = 1'. Running this command would have cleared the entire datastore. ";
             $error_msg .= "If this is actually your design intent, set \$ctrl['trap_*'] = false to disable this interlock.";
             throw new FOX_exception(array('numeric' => 7, 'text' => "{$error_msg}", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         }
     }
     // Lock affected cache pages
     // ===========================================================
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_lock_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     try {
         $cache_pages = self::lockCachePage(array_keys($del_data));
         $update_cache = $cache_pages;
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 8, 'text' => "Error locking cache", 'data' => $del_data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_lock_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     // Build db insert array and updated cache pages array
     // ===========================================================
     $dead_pages = array();
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "build_data_array_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     foreach ($del_data as $L5 => $L4s) {
         // Handle "true", "null" etc end nodes. The algorithm is implemented this
         // way to avoid excessive if-else nesting indentation. We know that any
         // non-array keys are valid end nodes because the trie passed validation
         // at the beginning of the class method
         if (!is_array($L4s)) {
             $L4s = array();
         }
         if (count($L4s) == 0) {
             // If the trie has no L4 structures, delete the
             // entire cache page from the class cache, and flag
             $dead_pages[] = $L5;
             // the page to be flushed from the persistent cache
             unset($update_cache[$L5]);
             continue;
         }
         foreach ($L4s as $L4 => $L3s) {
             if (!is_array($L3s)) {
                 $L3s = array();
             }
             if (count($L3s) == 0) {
                 // If the L4 structure has no L3 structures,
                 // delete its descendents' cache entries
                 unset($update_cache[$L5][$this->L4_col][$L4]);
                 unset($update_cache[$L5][$this->L3_col][$L4]);
                 unset($update_cache[$L5][$this->L2_col][$L4]);
                 unset($update_cache[$L5]["keys"][$L4]);
             }
             foreach ($L3s as $L3 => $L2s) {
                 if (!is_array($L2s)) {
                     $L2s = array();
                 }
                 if (count($L2s) == 0) {
                     // If the L3 structure has no L2 structures,
                     // delete its descendents' cache entries
                     unset($update_cache[$L5][$this->L3_col][$L4][$L3]);
                     unset($update_cache[$L5][$this->L2_col][$L4][$L3]);
                     unset($update_cache[$L5]["keys"][$L4][$L3]);
                 }
                 foreach ($L2s as $L2 => $L1s) {
                     if (!is_array($L1s)) {
                         $L1s = array();
                     }
                     if (count($L1s) == 0) {
                         // If the L2 structure has no L1 structures,
                         // delete its descendents' cache entries
                         unset($update_cache[$L5][$this->L2_col][$L4][$L3][$L2]);
                         unset($update_cache[$L5]["keys"][$L4][$L3][$L2]);
                     }
                     foreach ($L1s as $L1 => $val) {
                         unset($update_cache[$L5]["keys"][$L4][$L3][$L2][$L1]);
                     }
                     unset($L1, $val);
                 }
                 unset($L2, $L1s);
             }
             unset($L3, $L2s);
         }
         unset($L4, $L3s);
         // Clear empty walks from the keystore and LUT's
         // ==========================================================================
         $update_cache[$L5]['keys'] = FOX_sUtil::arrayPrune($update_cache[$L5]['keys'], 4);
         $update_cache[$L5][$this->L2_col] = FOX_sUtil::arrayPrune($update_cache[$L5][$this->L2_col], 3);
         $update_cache[$L5][$this->L3_col] = FOX_sUtil::arrayPrune($update_cache[$L5][$this->L3_col], 2);
         $update_cache[$L5][$this->L4_col] = FOX_sUtil::arrayPrune($update_cache[$L5][$this->L4_col], 1);
         if (count($update_cache[$L5]['keys']) == 0) {
             // If a cache page is empty after being pruned, delete
             // the entire cache page from the class cache, and flag
             $dead_pages[] = $L5;
             // the page to be flushed from the persistent cache
             unset($update_cache[$L5]);
         }
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "build_data_array_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
     }
     unset($L5, $L4s);
     // Clear the specified structures from the DB
     // ===========================================================
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "db_delete_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     $args = array('key_col' => array($this->L5_col, $this->L4_col, $this->L3_col, $this->L2_col, $this->L1_col), 'args' => $data);
     $del_ctrl = array('args_format' => $ctrl['mode'], 'hash_key_vals' => false);
     try {
         $rows_changed = $this->db->runDeleteQuery($struct, $args, $del_ctrl);
     } catch (FOX_exception $child) {
         // Try to unlock the cache pages we locked
         try {
             self::writeCachePage($cache_pages);
         } catch (FOX_exception $child_2) {
             throw new FOX_exception(array('numeric' => 9, 'text' => "Error while writing to the database. Error unlocking cache pages.", 'data' => array('cache_exception' => $child_2, 'cache_pages' => $cache_pages, 'del_args' => $args, 'del_ctrl' => $del_ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         throw new FOX_exception(array('numeric' => 10, 'text' => "Error while writing to the database. Successfully unlocked cache pages.", 'data' => array('del_args' => $args, 'del_ctrl' => $del_ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "db_delete_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     // NOTE: we *must* update the class cache before the persistent cache so that if
     // the persistent cache write fails, the class cache will still in the correct
     // state. If we failed to do this, the class cache could end up with 'ghost' pages
     // that no longer exist in the db. If the persistent cache throws an error during
     // the write operation, any pages that fail to update will remain locked, causing
     // them to be purged on the next read operation. This guarantees cache coherency.
     // Write updated cache page images to class cache
     // ===========================================================
     foreach ($update_cache as $L5 => $page_image) {
         $this->cache[$L5] = $page_image;
     }
     unset($L5, $page_image);
     // Flush dead pages from the class cache
     // ===========================================================
     foreach ($dead_pages as $L5) {
         unset($this->cache[$L5]);
     }
     unset($L5);
     // Write updated cache page images to persistent cache
     // ===========================================================
     if ($update_cache) {
         // Trap deleting nothing but L5's
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_write_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
         try {
             self::writeCachePage($update_cache);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 11, 'text' => "Error writing to cache", 'data' => $update_cache, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_write_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
     }
     // Flush any dead pages from the cache
     // ===========================================================
     if ($dead_pages) {
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_flush_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
         try {
             self::flushCachePage($dead_pages);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 12, 'text' => "Error flushing pages from cache", 'data' => $dead_pages, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "persistent_cache_flush_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
     }
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "method_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     return (int) $rows_changed;
 }
 /**
  * Drops all keys from the database and cache for one or more type_ids belonging
  * to a single module id.
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int $module_id | ID of the module
  * @param string/array $type_ids | Single type_id as string. Multiple type_ids as array of string.
  *
  * @return bool | Exception on failure. True on success. False on nonexistent.
  */
 public function dropType($module_id, $type_ids)
 {
     $db = new FOX_db();
     $struct = $this->_struct();
     if (empty($module_id) || empty($type_ids)) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Empty control parameter", 'data' => array("module_id" => $module_id, "type_id" => $type_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if (is_array($module_id)) {
         throw new FOX_exception(array('numeric' => 2, 'text' => "Attempted to pass multiple module id's", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     // Lock and load the module_id's cache page
     // ===========================================================
     try {
         $update_cache = self::lockCachePage($module_id);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Error locking cache page", 'data' => array("module_id" => $module_id), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     // Drop affected type_ids from the db
     // ===========================================================
     $args = array(array("col" => "module_id", "op" => "=", "val" => $module_id), array("col" => "type_id", "op" => "=", "val" => $type_ids));
     try {
         $rows_changed = $db->runDeleteQuery($struct, $args, $ctrl = null);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 4, 'text' => "Error while deleting from database", 'data' => array("module_id" => $module_id, "type_id" => $type_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     // Rebuild the module_id's cache page image
     // ===========================================================
     if (!is_array($type_ids)) {
         $type_ids = array($type_ids);
     }
     foreach ($type_ids as $type_id) {
         unset($update_cache["keys"][$type_id]);
         unset($update_cache["branch_id"][$type_id]);
         unset($update_cache["type_id"][$type_id]);
     }
     unset($type_id);
     $update_cache["keys"] = FOX_sUtil::arrayPrune($update_cache["keys"], 2);
     $update_cache["branch_id"] = FOX_sUtil::arrayPrune($update_cache["branch_id"], 1);
     // Overwrite the module_id's cache page, releasing our lock
     // ===========================================================
     try {
         self::writeCachePage(array($module_id => $update_cache));
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 5, 'text' => "Cache set error", 'data' => array("module_id" => $module_id, "update_cache" => $update_cache), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     $this->cache[$module_id] = $update_cache;
     return (bool) $rows_changed;
 }