/**
  * Recursively builds a clipped tree structure
  *
  * @version 1.0
  * @since 1.0
  *
  * @return array | Exception on failure. Trie structure on success.
  */
 function render()
 {
     // CASE 1: This is a root node or intermediate node
     // ===============================================================
     if ($this->depth < $this->base->max_depth) {
         if (!is_array($this->trie) || FOX_sUtil::keyExists($this->base->null_token, $this->trie)) {
             return array();
         } else {
             foreach ($this->trie as $key => $data) {
                 try {
                     $child_node = new FOX_trie_clip_iterator(array('base' => $this->base, 'trie' => $data, 'depth' => $this->depth + 1));
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 1, 'text' => "Error creating child node", 'data' => array('current_node' => array('trie' => $this->trie, 'depth' => $this->depth), 'child_node' => array('args' => $data, 'depth' => $this->depth + 1, "value" => $key)), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
                 }
                 try {
                     $reduced = $child_node->render();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 2, 'text' => "Error reducing child node", 'data' => array('current_node' => array('trie' => $this->trie, 'depth' => $this->depth), 'child_node' => array('args' => $data, 'depth' => $this->depth + 1, "value" => $key)), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
                 }
                 $this->children[$key] = $reduced;
             }
             unset($key, $data);
         }
         return $this->children;
     } else {
         return array();
     }
 }
 public function __construct($args = null)
 {
     $args_default = array('max_offset' => 2147483646, 'server' => array('ip' => '127.0.0.1', 'port' => 6379, 'database' => 15, 'alias' => 'first'));
     $args = FOX_sUtil::parseArgs($args, $args_default);
     foreach ($args as $key => $var) {
         $this->{$key} = $var;
     }
     unset($key, $var);
     // Handle process-id binding
     // ===========================================================
     if (FOX_sUtil::keyExists('process_id', $args)) {
         // Binding to a reference is important. It makes the cache engine $process_id
         // update if the FOX_mCache is changed, which we do during unit testing.
         $this->process_id =& $args['process_id'];
     } else {
         global $fox;
         $this->process_id = $fox->process_id;
     }
     $this->has_libs = true;
     if ($this->enable == true) {
         require_once FOX_PATH_BASE . '/lib/predis/autoload.php';
         $this->engine = new Predis\Client($this->server);
         $this->is_active = true;
     }
 }
 public function __construct($args = null)
 {
     // Handle dependency-injection for unit tests
     if (FOX_sUtil::keyExists('process_id', $args)) {
         // Binding to a reference is important. It makes the cache engine $process_id
         // update if the FOX_mCache is changed, which we do during unit testing.
         $this->process_id =& $args['process_id'];
     } else {
         global $fox;
         $this->process_id = $fox->process_id;
     }
     if (FOX_sUtil::keyExists('max_offset', $args)) {
         $this->max_offset = $args['max_offset'];
     } else {
         $this->max_offset = 2147483646;
         // (32-bit maxint)
     }
     if (function_exists("apc_store")) {
         $this->has_libs = true;
         if ($this->enable == true) {
             $this->is_active = true;
         }
     } else {
         $this->has_libs = false;
         $this->is_active = false;
     }
 }
 public function __construct($args = null)
 {
     // Handle process-id binding
     // ===========================================================
     if (FOX_sUtil::keyExists('process_id', $args)) {
         // Binding to a reference is important. It makes the cache engine $process_id
         // update if the FOX_mCache is changed, which we do during unit testing.
         $this->process_id =& $args['process_id'];
     } else {
         global $fox;
         $this->process_id = $fox->process_id;
     }
 }
 /**
  * Guarantees the value is either true, false, or NULL.
  *
  * @version 1.0
  * @since 1.0
  *
  * @param string $input | input string to process
  * @param array $ctrl | control parameters
  * 
  * @return bool | the processed string, cast as (bool)
  */
 public function bool($input, $ctrl = null)
 {
     $null_strategy = FOX_sUtil::keyExists('null_strategy ', $ctrl) ? $ctrl['null_strategy'] : 'convert';
     // CASE 1 - NULL VALUE PASSED TO SYSTEM
     // ===================================================================
     if ($input === null || $input === '') {
         switch ($null_strategy) {
             // Converts the NULL to (bool)false
             // ==========================================================
             case "convert":
                 return array('status' => true, 'val' => false);
                 // Passes the null back out to the front end code
                 // ==========================================================
             // Passes the null back out to the front end code
             // ==========================================================
             case "passthru":
                 return array('status' => true, 'val' => $input);
                 // Traps invalid data
                 // ==========================================================
             // Traps invalid data
             // ==========================================================
             case "trap":
                 return array('status' => 'false', 'val' => $input);
         }
     }
     // CASE 2 - Values that can be converted to bool
     // ===================================================================
     if ($input === 0 || $input === '0') {
         return array('status' => true, 'val' => false);
     } else {
         if ($input === 1 || $input === '1') {
             return array('status' => true, 'val' => true);
         }
     }
     // CASE 3 - Values that can't be converted.
     // ===================================================================
     return array('status' => false, 'val' => $input);
 }
 public function event($data)
 {
     foreach ($this->events as $event) {
         // This is probably one of the few situations in all of modern computer science where
         // a massive "elseif" chain is the optimal solution.
         // Ordered by frequency of use, for best performance
         if (FOX_sUtil::keyExists('pid', $event) && $event['pid'] != $data['pid']) {
             continue;
         } elseif (FOX_sUtil::keyExists('text', $event) && $event['text'] != $data['text']) {
             continue;
         } elseif (FOX_sUtil::keyExists('class', $event) && $event['class'] != $data['class']) {
             continue;
         } elseif (FOX_sUtil::keyExists('function', $event) && $event['function'] != $data['function']) {
             continue;
         } elseif (FOX_sUtil::keyExists('file', $event) && $event['file'] != $data['file']) {
             continue;
         } elseif (FOX_sUtil::keyExists('line', $event) && $event['line'] != $data['line']) {
             continue;
         } else {
             if ($event['type'] == 'log') {
                 $this->log_buffer[] = array('pid' => $data['pid'], 'file' => $data['file'], 'class' => $data['class'], 'function' => $data['function'], 'line' => $data['line'], 'text' => $data['text']);
                 return array();
             } else {
                 // Events are emitted by class instances. Pass the class instance that emitted the event, and all
                 // currently defined variables within the *function inside the class instance* that emitted the
                 // event, to the function sent in as the event handler; then take the results (if any) returned
                 // by the event handler function and use them to overwrite the variables within the function
                 // inside the class instance that emitted the event.
                 return $event['modifier']($data['parent'], $data['vars']);
             }
         }
     }
     unset($event);
     // Handle not matching on any events
     return array();
 }
 /**
  * Fetches a key_id, branch_id, type_id, or an entire module's keystore from the key cache.
  *
  * If an object is not in the cache yet, it will be retrieved from the database
  * and added to the cache. Multiple items in the *lowest level group* specified
  * can be retrieved in a single query by passing their names or id's as an array.
  *
  * WRONG: get( $module_id=1, $type_ids = array(3, 6, 7, 2),  $branch_ids="foo")
  *                    ^^^^^^^^^^^^^^^^^^^^^^^^
  * RIGHT: get( $module_id=1, $type_ids=7, $branch_ids=array("foo","bar","baz")
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int/array $module_ids | Single $module_id as int. Multiple as array of ints.
  * @param int/array $type_ids | Single $type_id as int. Multiple as array of ints.
  * @param string/array $branch_ids | Single key type as string. Multiple key types as array of strings.
  * @param bool &$valid | True if the object exists. False if not.
  *
  * @return bool | Exception on failure. True on success.
  */
 public function get($module_id, $type_ids = null, $branch_ids = null, $key_ids = null, &$valid = null)
 {
     // CASE 1: One or more $key_ids
     // =====================================================================
     if ($module_id && $type_ids && $branch_ids && $key_ids) {
         if (is_array($module_id) || is_array($type_ids) || is_array($branch_ids)) {
             throw new FOX_exception(array('numeric' => 1, 'text' => "Attempted to pass multiple module id's, type_ids, or branch_id's when specifying key_id", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         }
         // If the module_id is not present in the class cache array, try to load it
         // from the persistent cache
         if (!FOX_sUtil::keyExists($module_id, $this->cache)) {
             try {
                 $this->cache[$module_id] = self::readCache($module_id);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "Cache get error", 'data' => array("module_id" => $module_id, "type_id" => $type_ids, "branch_id" => $branch_ids, "key_id" => $key_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         // Single key
         if (!is_array($key_ids)) {
             $single = true;
             $key_ids = array($key_ids);
         } else {
             $single = false;
         }
         // Find all the keys that have been requested but are not in the cache
         $missing_keys = array();
         $module_cached = FOX_sUtil::keyTrue("all_cached", $this->cache[$module_id]);
         $type_id_cached = FOX_sUtil::keyTrue($type_ids, $this->cache[$module_id]["type_id"]);
         $type_cached = FOX_sUtil::keyTrue($branch_ids, $this->cache[$module_id]["branch_id"][$type_ids]);
         if (!$module_cached && !$type_id_cached && !$type_cached) {
             foreach ($key_ids as $key_id) {
                 if (!FOX_sUtil::keyExists($key_id, $this->cache[$module_id]["keys"][$type_ids][$branch_ids])) {
                     $missing_keys[] = $key_id;
                 }
             }
             unset($key_id);
         }
         // Load missing keys
         if (count($missing_keys) != 0) {
             try {
                 $this->load($module_id, $type_ids, $branch_ids, $missing_keys, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "Load error", 'data' => array("module_id" => $module_id, "type_id" => $type_ids, "branch_id" => $branch_ids, "missing_keys" => $missing_keys), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested keys
         foreach ($key_ids as $key_id) {
             if (FOX_sUtil::keyExists($key_id, $this->cache[$module_id]["keys"][$type_ids][$branch_ids])) {
                 $result[$key_id] = $this->cache[$module_id]["keys"][$type_ids][$branch_ids][$key_id];
             } else {
                 $result[$key_id] = null;
             }
         }
         unset($key_id);
         // Only set the $valid flag true if every requested key was successfully fetched
         if (count($result) == count($key_ids)) {
             $valid = true;
         } else {
             $valid = false;
         }
         // If only one key was requested, and the key was successfully retrieved from the db,
         // lift the result array up one level
         if ($single == true && count($result) == 1) {
             $result = $result[$key_ids[0]];
         }
         return $result;
     }
     // CASE 2: One or more $branch_ids
     // =====================================================================
     if ($module_id && $type_ids && $branch_ids && !$key_ids) {
         if (is_array($module_id) || is_array($type_ids)) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "Attempted to pass multiple module id's, or type_ids when specifying branch_id", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         }
         // If the module_id is not present in the class cache array, try to load it
         // from the persistent cache
         if (!FOX_sUtil::keyExists($module_id, $this->cache)) {
             try {
                 $this->cache[$module_id] = self::readCache($module_id);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 5, 'text' => "Cache get error", 'data' => array("module_id" => $module_id, "type_id" => $type_ids, "branch_id" => $branch_ids, "key_id" => $key_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         // Single key
         if (!is_array($branch_ids)) {
             $single = true;
             $branch_ids = array($branch_ids);
         } else {
             $single = false;
         }
         // Find all the branch_ids that have been requested but are not in the cache
         $missing_types = array();
         $module_cached = FOX_sUtil::keyTrue("all_cached", $this->cache[$module_id]);
         $type_id_cached = FOX_sUtil::keyTrue($type_ids, $this->cache[$module_id]["type_id"]);
         $type_cached = FOX_sUtil::keyTrue($branch_ids, $this->cache[$module_id]["branch_id"][$type_ids]);
         if (!$module_cached && !$type_id_cached && !$type_cached) {
             foreach ($branch_ids as $branch_id) {
                 if (!FOX_sUtil::keyExists($branch_id, $this->cache[$module_id]["keys"][$type_ids])) {
                     $missing_types[] = $branch_id;
                 }
             }
             unset($branch_id);
         }
         // Load missing keys
         if (count($missing_types) != 0) {
             try {
                 $this->load($module_id, $type_ids, $missing_types, null, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 6, 'text' => "Load error", 'data' => array("module_id" => $module_id, "type_id" => $type_ids, "branch_id" => $branch_ids, "missing_types" => $missing_types), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested keys
         foreach ($branch_ids as $branch_id) {
             if (FOX_sUtil::keyExists($branch_id, $this->cache[$module_id]["keys"][$type_ids])) {
                 $result[$branch_id] = $this->cache[$module_id]["keys"][$type_ids][$branch_id];
             } else {
                 $result[$branch_id] = null;
             }
         }
         unset($branch_id);
         // Only set the $valid flag true if every requested key was successfully fetched
         if (count($result) == count($branch_ids)) {
             $valid = true;
         } else {
             $valid = false;
         }
         // If only one key was requested, and the key was successfully retrieved from the db,
         // lift the result array up one level
         if ($single == true && count($result) == 1) {
             $result = $result[$branch_ids[0]];
         }
         return $result;
     } elseif ($module_id && $type_ids && !$branch_ids && !$key_ids) {
         if (is_array($module_id)) {
             throw new FOX_exception(array('numeric' => 7, 'text' => "Attempted to pass multiple module id's when specifying type_id", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         }
         // If the module_id is not present in the class cache, try to load it
         // from the persistent cache
         if (!FOX_sUtil::keyExists($module_id, $this->cache)) {
             try {
                 $this->cache[$module_id] = self::readCache($module_id);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 8, 'text' => "Cache get error", 'data' => array("module_id" => $module_id, "type_id" => $type_ids, "branch_id" => $branch_ids, "key_id" => $key_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         // Single type_id
         if (!is_array($type_ids)) {
             $single = true;
             $type_ids = array($type_ids);
         } else {
             $single = false;
         }
         // Find all the type_ids that have been requested but are not in the cache
         $missing_type_ids = array();
         foreach ($type_ids as $type_id) {
             $module_cached = FOX_sUtil::keyTrue("all_cached", $this->cache[$module_id]);
             $type_id_cached = FOX_sUtil::keyTrue($type_id, $this->cache[$module_id]["type_id"]);
             if (!$module_cached && !$type_id_cached) {
                 $missing_type_ids[] = $type_id;
             }
         }
         unset($type_id);
         // Load missing type_ids
         if (count($missing_type_ids) != 0) {
             try {
                 self::load($module_id, $missing_type_ids, null, null, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 9, 'text' => "Load error", 'data' => array("module_id" => $module_id, "type_id" => $type_ids, "branch_id" => $branch_ids, "key_id" => $key_ids, "missing_type_ids" => $missing_type_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested type_ids
         foreach ($type_ids as $type_id) {
             if (FOX_sUtil::keyExists($type_id, $this->cache[$module_id]["keys"])) {
                 $result[$type_id] = $this->cache[$module_id]["keys"][$type_id];
             } else {
                 $result[$type_id] = null;
             }
         }
         unset($type_id);
         // Only set the $valid flag true if every requested type_id was successfully fetched
         if (count($result) == count($type_ids)) {
             $valid = true;
         } else {
             $valid = false;
         }
         // If only one type_id was requested, and the type_id was successfully retrieved from the db,
         // lift the result array up one level
         if ($single == true && count($result) == 1) {
             $result = $result[$type_ids[0]];
         }
         return $result;
     } elseif ($module_id && !$type_ids && !$branch_ids && !$key_ids) {
         if (!is_array($module_id)) {
             $single = true;
             $module_id = array($module_id);
         } else {
             $single = false;
         }
         // Find all the module_id's that have been requested but are not in the cache
         $missing_module_ids = array();
         foreach ($module_id as $current_module_id) {
             if (!FOX_sUtil::keyTrue("all_cached", $this->cache[$current_module_id])) {
                 $missing_module_ids[] = $current_module_id;
             }
         }
         unset($current_module_id);
         // Load missing module_id's
         if (count($missing_module_ids) != 0) {
             try {
                 self::load($missing_module_ids, null, null, null, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 13, 'text' => "Load error", 'data' => array("missing_module_ids" => $missing_module_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested module_ids
         foreach ($module_id as $current_module_id) {
             if (FOX_sUtil::keyExists($current_module_id, $this->cache)) {
                 $result[$current_module_id] = $this->cache[$current_module_id]["keys"];
             } else {
                 $result[$current_module_id] = null;
             }
         }
         unset($current_module_id);
         // Only set the $valid flag true if every requested module_id was successfully fetched
         if (count($result) == count($module_id)) {
             $valid = true;
         } else {
             $valid = false;
         }
         // If only one module_id was requested, and the module_id was successfully retrieved from the db,
         // lift the result array up one level
         if (($single = true) && count($result) == 1) {
             $result = $result[$module_id[0]];
         }
         return $result;
     } else {
         throw new FOX_exception(array('numeric' => 14, 'text' => "Bad input args", 'data' => array("module_id" => $module_id, "type_id" => $type_ids, "branch_id" => $branch_ids, "key_id" => $key_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
 }
 /**
  * Releases an exclusive PID lock on the class' namespace. If released by a different
  * PID than the one that set the lock, the class namespace will be flushed to maintain
  * data integrity.
  *
  * @version 1.0
  * @since 1.0
  * 
  * @param array $args | Control args
  *	=> VAL @param array $engine | Class cache engines array
  *	=> VAL @param string $namespace | Class namespace
  *	=> VAL @param int $process_id | Process ID to use as owner   
  * 
  * @return int | Exception on failure. Int current offset on success.
  */
 public function unlockNamespace($args = null)
 {
     // Heavily error-check. Bad parameters passed to this method could cause
     // difficult-to-debug intermittent errors throughout the plugin
     // =====================================================================
     if (!FOX_sUtil::keyExists('engine', $args) || empty($args['engine'])) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Invalid engine parameter", 'data' => $args, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if (!FOX_sUtil::keyExists('namespace', $args) || empty($args['namespace'])) {
         throw new FOX_exception(array('numeric' => 2, 'text' => "Invalid namespace parameter", 'data' => $args, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if (!FOX_sUtil::keyExists('process_id', $args) || !is_int($args['process_id'])) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Invalid process_id parameter", 'data' => $args, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     // Find the first active cache engine specified by the class
     // =====================================================================
     $engine = false;
     foreach ($args['engine'] as $engine_name) {
         // Check engine exists
         if (FOX_sUtil::keyExists($engine_name, $this->engines)) {
             // Check engine is active
             if ($this->engines[$engine_name]->isActive()) {
                 $engine =& $this->engines[$engine_name];
                 break;
             }
         } else {
             throw new FOX_exception(array('numeric' => 4, 'text' => "Specified engine name doesn't exist", 'data' => $engine_name, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
     }
     unset($engine_name);
     if (!$engine) {
         // If none of the requested engines are active,
         // use the loopback engine
         $engine =& $this->engines['loopback'];
     }
     try {
         $result = $engine->unlockNamespace($args['namespace']);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 5, 'text' => "Error in engine->unlockNamespace()", 'data' => $args, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     return $result;
 }
 /**
  * Replaces multiple L4 trie structures which MAY OR MAY NOT ALREADY EXIST in the datastore,
  * deleting all L4->L1 walks for each L5->L4 intersect structure passed in the $data array, 
  * then adding the new L4->L1 walks contained in the intersect structure. 
  *
  * @version 1.0
  * @since 1.0
  *
  * @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 bool/int/float/string/array/obj $val | key value
  * 
  * @param array $ctrl | Control parameters
  *	=> VAL @param bool $validate | Validate keys	 
  *
  * @return int | Exception on failure. Int number of rows SET on success.
  */
 public function replaceL4_multi($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);
     $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));
     }
     $struct = $this->_struct();
     // Validate data array
     // ===========================================================
     if ($ctrl['validate'] == true) {
         if ($this->debug_on) {
             extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "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' => $this->order, 'mode' => 'data', 'clip_order' => 4);
             $tree_valid = $validator->validateTrie($data, $val_ctrl);
         } 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 ($tree_valid !== true) {
             throw new FOX_exception(array('numeric' => 3, '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' => "validate_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
         }
     }
     // Lock all L5 cache pages in the $data array
     // ===========================================================
     $L5_ids = array_keys($data);
     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 {
         $page_images = self::lockCachePage($L5_ids);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 4, 'text' => "Error locking cache pages", 'data' => array("pages" => $L5_ids), '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())))));
     }
     // 1) Build $insert_data array
     // 2) Calculate $del_args
     // 3) Rebuild cache page images
     // ================================================================
     $insert_data = array();
     $del_args = array();
     $page_images = $this->cache;
     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 ($data as $L5 => $L4s) {
         // Avoid creating redundant LUT entries
         if (FOX_sUtil::keyExists('all_cached', $page_images[$L5])) {
             $parent_has_auth = true;
         } else {
             $parent_has_auth = false;
         }
         foreach ($L4s as $L4 => $L3s) {
             // Clear all objects currently inside the L4
             unset($page_images[$L5]["keys"][$L4]);
             // Clear the LUT entries for all the L2's and L3's
             // that were inside the L4
             unset($page_images[$L5][$this->L2_col][$L4]);
             unset($page_images[$L5][$this->L3_col][$L4]);
             $del_args[] = array($this->L5_col => $L5, $this->L4_col => $L4);
             // Don't set a LUT entry if the parent has authority, or the
             // the node has no children
             if (!$parent_has_auth && !empty($L3s)) {
                 $page_images[$L5][$this->L4_col][$L4] = true;
             }
             foreach ($L3s as $L3 => $L2s) {
                 foreach ($L2s as $L2 => $L1s) {
                     foreach ($L1s as $L1 => $val) {
                         $page_images[$L5]["keys"][$L4][$L3][$L2][$L1] = $val;
                         $insert_data[] = array($this->L5_col => $L5, $this->L4_col => $L4, $this->L3_col => $L3, $this->L2_col => $L2, $this->L1_col => $L1, $this->L0_col => $val);
                     }
                     unset($L1, $val);
                 }
                 unset($L2, $L1s);
             }
             unset($L3, $L2s);
         }
         unset($L4, $L3s);
     }
     unset($L5, $L4s);
     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())))));
     }
     // Update the database
     // ===========================================================
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "db_transaction_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     try {
         $this->db->beginTransaction();
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 5, 'text' => "Couldn't initiate transaction", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     // Clear all L5->L4 intersects from the db
     // ===========================================================
     $args = array('key_col' => array($this->L5_col, $this->L4_col), 'args' => $del_args);
     $del_ctrl = array('args_format' => 'matrix');
     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())))));
     }
     try {
         $this->db->runDeleteQuery($struct, $args, $del_ctrl);
     } catch (FOX_exception $child) {
         try {
             $this->db->rollbackTransaction();
         } catch (FOX_exception $child_2) {
             throw new FOX_exception(array('numeric' => 6, 'text' => "Error while deleting from the database. Error rolling back.", 'data' => array('rollback_exception' => $child_2, 'args' => $args, 'del_ctrl' => $del_ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         throw new FOX_exception(array('numeric' => 7, 'text' => "Error while deleting from the database. Successful rollback.", 'data' => array('args' => $args), '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())))));
     }
     // Insert updated walks
     // ===========================================================
     $insert_cols = null;
     $insert_ctrl = null;
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "db_insert_start", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     try {
         $rows_set = $this->db->runInsertQueryMulti($struct, $insert_data, $insert_cols, $insert_ctrl);
     } catch (FOX_exception $child) {
         try {
             $this->db->rollbackTransaction();
         } catch (FOX_exception $child_2) {
             throw new FOX_exception(array('numeric' => 8, 'text' => "Error while writing to the database. Error rolling back.", 'data' => array('insert_data' => $insert_data, 'rollback_exception' => $child_2), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         throw new FOX_exception(array('numeric' => 9, 'text' => "Error while writing to the database. Successful rollback.", 'data' => $insert_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' => "db_insert_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     try {
         $this->db->commitTransaction();
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 10, 'text' => "Error commiting transaction to database", 'data' => $insert_data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     if ($this->debug_on) {
         extract($this->debug_handler->event(array('pid' => $this->process_id, 'text' => "db_transaction_end", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'parent' => $this, 'vars' => compact(array_keys(get_defined_vars())))));
     }
     // Overwrite the locked L5 cache pages, releasing our lock
     // ===========================================================
     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($page_images);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 11, 'text' => "Cache set error", 'data' => $page_images, '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())))));
     }
     $this->cache = $page_images;
     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_set;
 }
 /**
  * Deletes an item from the cache
  *
  * @version 1.0
  * @since 1.0
  * 
  * @param string $key | Name of key
  * @return bool | False on failure. True on success.
  */
 public function delete($key)
 {
     if (FOX_sUtil::keyExists($key, $this->cache)) {
         unset($this->cache[$key]);
         return true;
     } else {
         return false;
     }
 }
Example #11
0
 /**
  * Fetches a key, branch, tree, or an entire user's keystore from the key cache.
  *
  * If an object is not in the cache yet, it will be retrieved from the database
  * and added to the cache. Multiple items in the *lowest level group* specified
  * can be retrieved in a single query by passing their names or id's as an array.
  *
  * WRONG: get( $id=1, $tree = array(3, 6, 7, 2), $branch=7, $key="gender")
  *                    ^^^^^^^^^^^^^^^^^^^^^^^^
  * RIGHT: get( $id=1, $tree=2, $branch=7, $key=array("fans","likes","gender")
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int/array $user_id | Single $user_id as int. Multiple as array of ints.
  * @param int/array $tree | Single $tree as int. Multiple as array of ints.
  * @param int/array $branch | Single $branch as int. Multiple as array of ints.
  * @param string/array $key | Single key name as string. Multiple as array of strings.
  * @param bool &$valid | True if the object exists. False if not.
  * @param array &$error | Array containing numeric and text error information
  * @return bool | False on failure. True on success.
  */
 public function get($user_id, $tree = null, $branch = null, $key = null, &$valid = null)
 {
     global $rad;
     $db = new FOX_db();
     $args = array();
     // Key or group of keys
     // =============================
     if ($user_id && $tree && $branch && $key) {
         if (is_array($user_id) || is_array($tree) || is_array($branch)) {
             throw new FOX_exception(array('numeric' => 1, 'text' => "Attempted to pass multiple user id's, tree's, or branches when specifying key", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
         }
         // If the user_id is not present in the class cache array, try to load it
         // from the persistent cache
         if (!$this->cache[$user_id]) {
             try {
                 $this->cache[$user_id] = $rad->cache->get("FOX_uData", $user_id, $valid);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "Cache get exception", 'data' => array("user_id" => $user_id, "tree" => $tree, "branch" => $branch, "key" => $key), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         }
         // Single key
         if (!is_array($key)) {
             $single = true;
             $key = array($key);
         } else {
             $single = false;
         }
         // Find all the keys that have been requested but are not in the cache
         $missing_keys = array();
         $branch_cached = FOX_sUtil::keyTrue($branch, $this->cache[$user_id]["branch"][$tree]);
         $tree_cached = FOX_sUtil::keyTrue($tree, $this->cache[$user_id]["tree"]);
         $user_cached = FOX_sUtil::keyTrue("all_cached", $this->cache[$user_id]);
         if (!$branch_cached && !$tree_cached && !$user_cached) {
             foreach ($key as $key_name) {
                 if (!FOX_sUtil::keyExists($key_name, $this->cache[$user_id]["keys"][$tree][$branch])) {
                     $missing_keys[] = $key_name;
                 }
             }
             unset($key_name);
         }
         // Load missing keys
         if (count($missing_keys) != 0) {
             try {
                 $this->load($user_id, $tree, $branch, $missing_keys, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "Load exception", 'data' => array("user_id" => $user_id, "tree" => $tree, "branch" => $branch, "missing_keys" => $missing_keys), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested keys
         foreach ($key as $key_name) {
             if (FOX_sUtil::keyExists($key_name, $this->cache[$user_id]["keys"][$tree][$branch])) {
                 $result[$key_name] = $this->cache[$user_id]["keys"][$tree][$branch][$key_name];
             } else {
                 $result[$key_name] = null;
             }
         }
         unset($key_name);
         // Only set the $valid flag true if every requested key was successfully fetched
         if (count($result) == count($key)) {
             $valid = true;
         } else {
             $valid = false;
         }
         // If only one key was requested, and the key was successfully retrieved from the db,
         // lift the result array up one level
         if ($single == true && count($result) == 1) {
             $result = $result[$key[0]];
         }
         return $result;
     } elseif ($user_id && $tree && $branch && !$key) {
         if (is_array($user_id) || is_array($tree)) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "Attempted to pass multiple user id's or tree's when specifying branch", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
         }
         // If the user_id is not present in the class cache array, try to load it
         // from the persistent cache
         if (!$this->cache[$user_id]) {
             try {
                 $this->cache[$user_id] = $rad->cache->get("FOX_uData", $user_id, $valid);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 5, 'text' => "Cache get exception", 'data' => array("user_id" => $user_id, "tree" => $tree, "branch" => $branch), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         }
         // Single branch
         if (!is_array($branch)) {
             $single = true;
             $branch = array($branch);
         } else {
             $single = false;
         }
         // Find all the branches that have been requested but are not in the cache
         $missing_branches = array();
         foreach ($branch as $branch_name) {
             $branch_cached = FOX_sUtil::keyTrue($branch_name, $this->cache[$user_id]["branch"][$tree]);
             $tree_cached = FOX_sUtil::keyTrue($tree_name, $this->cache[$user_id]["tree"]);
             $user_cached = FOX_sUtil::keyTrue("all_cached", $this->cache[$user_id]);
             if (!$branch_cached && !$tree_cached && !$user_cached) {
                 $missing_branches[] = $branch_name;
             }
         }
         unset($branch_name);
         // Load missing branches
         if (count($missing_branches) != 0) {
             try {
                 $this->load($user_id, $tree, $missing_branches, null, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 6, 'text' => "Load exception", 'data' => array("user_id" => $user_id, "tree" => $tree, "missing_branches" => $missing_branches), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested branches
         foreach ($branch as $branch_name) {
             if (FOX_sUtil::keyExists($branch_name, $this->cache[$user_id]["keys"][$tree])) {
                 $result[$branch_name] = $this->cache[$user_id]["keys"][$tree][$branch_name];
             } else {
                 $result[$branch_name] = null;
             }
         }
         unset($branch_name);
         // Only set the $valid flag true if every requested branch was successfully fetched
         if (count($result) == count($branch)) {
             $valid = true;
         } else {
             $valid = false;
         }
         // If only one branch was requested, and the branch was successfully retrieved from the db,
         // lift the result array up one level
         if ($single == true && count($result) == 1) {
             $result = $result[$branch[0]];
         }
         return $result;
     } elseif ($user_id && $tree && !$branch && !$key) {
         if (is_array($user_id)) {
             throw new FOX_exception(array('numeric' => 7, 'text' => "Attempted to pass multiple user id's when specifying tree", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
         }
         // If the user_id is not present in the class cache array, try to load it
         // from the persistent cache
         if (!$this->cache[$user_id]) {
             try {
                 $this->cache[$user_id] = $rad->cache->get("FOX_uData", $user_id, $valid);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 8, 'text' => "Cache get exception", 'data' => array("user_id" => $user_id, "tree" => $tree), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         }
         // Single tree
         if (!is_array($tree)) {
             $single = true;
             $tree = array($tree);
         } else {
             $single = false;
         }
         // Find all the trees that have been requested but are not in the cache
         $missing_trees = array();
         foreach ($tree as $tree_name) {
             $tree_cached = FOX_sUtil::keyTrue($tree_name, $this->cache[$user_id]["tree"]);
             $user_cached = FOX_sUtil::keyTrue("all_cached", $this->cache[$user_id]);
             if (!$tree_cached && !$user_cached) {
                 $missing_trees[] = $tree_name;
             }
         }
         unset($tree_name);
         // Load missing trees
         if (count($missing_trees) != 0) {
             try {
                 $this->load($user_id, $missing_trees, null, null, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 9, 'text' => "Load exception", 'data' => array("user_id" => $user_id, "missing_trees" => $missing_trees), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested trees
         foreach ($tree as $tree_name) {
             if (FOX_sUtil::keyExists($tree_name, $this->cache[$user_id]["keys"])) {
                 $result[$tree_name] = $this->cache[$user_id]["keys"][$tree_name];
             } else {
                 $result[$tree_name] = null;
             }
         }
         unset($tree_name);
         // Only set the $valid flag true if every requested tree was successfully fetched
         if (count($result) == count($tree)) {
             $valid = true;
         } else {
             $valid = false;
         }
         // If only one tree was requested, and the tree was successfully retrieved from the db,
         // lift the result array up one level
         if ($single == true && count($result) == 1) {
             $result = $result[$tree[0]];
         }
         return $result;
     } elseif ($user_id && !$tree && !$branch && !$key) {
         if (!is_array($user_id)) {
             $single = true;
             $user_id = array($user_id);
         } else {
             $single = false;
         }
         // Find all the user_id's that have been requested but are not in the cache
         $missing_trees = array();
         foreach ($user_id as $current_user_id) {
             if (!FOX_sUtil::keyTrue("all_cached", $this->cache[$current_user_id])) {
                 $missing_users[] = $current_user_id;
             }
         }
         unset($current_user_id);
         // Load missing user_id's
         if (count($missing_users) != 0) {
             try {
                 $this->load($missing_users, null, null, null, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 10, 'text' => "Load exception", 'data' => array("missing_users" => $missing_users), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested users
         foreach ($user_id as $current_user_id) {
             if (FOX_sUtil::keyExists($current_user_id, $this->cache)) {
                 $result[$current_user_id] = $this->cache[$current_user_id]["keys"];
             } else {
                 $result[$current_user_id] = null;
             }
         }
         unset($current_user_id);
         // Only set the $valid flag true if every requested tree was successfully fetched
         if (count($result) == count($user_id)) {
             $valid = true;
         } else {
             $valid = false;
         }
         // If only one user_id was requested, and the user_id was successfully retrieved from the db,
         // lift the result array up one level
         if (($single = true) && count($result) == 1) {
             $result = $result[$user_id[0]];
         }
         return $result;
     } else {
         throw new FOX_exception(array('numeric' => 10, 'text' => "Bad input args", 'data' => array("user_id" => $user_id, "tree" => $tree, "branch" => $branch, "key" => $key), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
 }
 /**
  * Checks if a group grants a specific key
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int $group_id | id of the group
  * @param int $key_id | id of the key
  * @return bool | True if group has key. False if group does not have key.
  */
 public function hasKey($group_id, $key_id)
 {
     if (FOX_sUtil::keyExists($key_id, $this->cache["keys"][$group_id])) {
         return $this->cache["keys"][$group_id][$key_id];
     } else {
         try {
             $this->load($group_id, $key_id);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 1, 'text' => "FOX_uGroupKeyRing load exception", 'data' => array("group_id" => $group_id, "key_id" => $key_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         return $this->cache["keys"][$group_id][$key_id];
     }
 }
 /**
  * Validates a trie structure 
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $data | trie structure to validate
  *  
  * @param array $ctrl | Control parameters
  *	=> VAL @param int $order | Order of trie structure
  *	=> VAL @param string $mode | Operation mode 'control' | 'data'
  *	=> VAL @param bool $allow_wildcard | Allow wildcards (universal selector) to be used in control tries
  *	=> VAL @param string $wildcard_token | String to use as wildcard token
  *	=> VAL @param int $clip_order | Order to clip keys at when in 'data' mode	 
  * 
  * @return bool/array | Exception on failure, (bool)true on success, (array)data_array on failure.
  */
 public function validateTrie($data, $ctrl = null)
 {
     $ctrl_default = array('order' => $this->order, 'mode' => 'control', 'allow_wildcard' => false, 'wildcard_token' => '*', 'clip_order' => false);
     $ctrl = FOX_sUtil::parseArgs($ctrl, $ctrl_default);
     if ($ctrl['order'] > $this->order || $ctrl['order'] < 0) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Specified order is not valid", 'data' => array("order" => $ctrl['order'], "class_order" => $this->order), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if ($ctrl['mode'] != 'data' && $ctrl['mode'] != 'control') {
         throw new FOX_exception(array('numeric' => 2, 'text' => "Invalid 'mode' parameter", 'data' => array('ctrl' => $ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if ($ctrl['mode'] == 'data' && !is_int($ctrl['clip_order'])) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "The 'clip_order' parameter must be set when operating in 'data' mode", 'data' => array('ctrl' => $ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if ($ctrl['mode'] == 'data' && $ctrl['allow_wildcard'] != false) {
         throw new FOX_exception(array('numeric' => 4, 'text' => "Wildcard selectors cannot be used in 'data' mode", 'data' => array('ctrl' => $ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     try {
         if ($ctrl['order'] == 0) {
             // We're at L0 in a walk
             return true;
         }
         if (is_array($data) && !empty($data)) {
             foreach ($data as $parent_id => $children) {
                 if (!($ctrl['allow_wildcard'] == true && $parent_id == $ctrl['wildcard_token'])) {
                     $check_result = self::validateKey(array('type' => $this->order_dict[$ctrl['order']]['type'], 'format' => 'scalar', 'var' => $parent_id));
                     if ($check_result !== true) {
                         return array('numeric' => 1, 'message' => $check_result, 'key' => $ctrl['order'], 'val' => $parent_id);
                     }
                 }
                 $child_result = self::validateTrie($children, array('order' => $ctrl['order'] - 1, 'mode' => $ctrl['mode'], 'allow_wildcard' => $ctrl['allow_wildcard'], 'clip_order' => $ctrl['clip_order']));
                 if ($child_result !== true) {
                     if (FOX_sUtil::keyExists('trace', $child_result)) {
                         $trace = array_merge($child_result['trace'], array($ctrl['order'] => $parent_id));
                     } else {
                         $trace = array($ctrl['order'] => $parent_id);
                     }
                     return array('numeric' => 2, 'message' => $child_result['message'], 'key' => $ctrl['order'], 'val' => $parent_id, 'trace' => $trace);
                 }
             }
             unset($parent_id, $children);
         } else {
             if ($ctrl['mode'] == 'control') {
                 if (!is_array($data) && (!is_bool($data) || $data === false)) {
                     $message = "Each walk in a control trie must terminate with either (bool)true, ";
                     $message .= "or an empty array, or must extend to the L1 key.";
                     return array('numeric' => 3, 'message' => $message, 'key' => $ctrl['order'], 'val' => $parent_id);
                 } else {
                     return true;
                 }
             } elseif ($ctrl['mode'] == 'data') {
                 if ($ctrl['order'] == $ctrl['clip_order'] - 1) {
                     if (!is_array($data) && (!is_bool($data) || $data === false)) {
                         $message = "Each walk in a data trie, that terminates at the clip_order, must ";
                         $message .= "terminate with an empty array.";
                         return array('numeric' => 4, 'message' => $message, 'key' => $ctrl['order'], 'val' => $parent_id);
                     } else {
                         return true;
                     }
                 } else {
                     $message = "Each walk in a data trie must terminate either at the clip_order ";
                     $message .= "or at the L1 key.";
                     return array('numeric' => 5, 'message' => $message, 'key' => $ctrl['order'], 'val' => $parent_id);
                 }
             }
         }
         // ENDOF: if( is_array($data) && !empty($data) )
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 5, 'text' => "Error in validator", 'data' => array("columns" => $this->order_dict), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     return true;
 }
 /**
  * Processes config variables passed via an HTML form
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $post | HTML form array
  * @return int | Exception on failure. Number of rows changed on success.
  */
 public function processHTMLForm($post)
 {
     if (empty($post['key_names'])) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "No key names posted with form", 'data' => $post, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
     $san = new FOX_sanitize();
     // Explode fields array into individual key names
     $options = explode(',', stripslashes($post['key_names']));
     // Convert the encoded full name into its corresponding $tree, $branch,
     // and $key, then add the group to the query array
     // ====================================================================
     $query_keys = array();
     foreach ($options as $option) {
         $full_name = explode($this->key_delimiter, $option);
         // Process the raw form strings into proper key names
         $raw_plugin = trim($full_name[0]);
         $raw_tree = trim($full_name[1]);
         $raw_branch = trim($full_name[2]);
         $raw_key = trim($full_name[3]);
         // Passed by reference
         $plugin_valid = null;
         $tree_valid = null;
         $branch_valid = null;
         $key_valid = null;
         $plugin_error = null;
         $tree_error = null;
         $branch_error = null;
         $key_error = null;
         try {
             $plugin = $san->keyName($raw_plugin, null, $plugin_valid, $plugin_error);
             $tree = $san->keyName($raw_tree, null, $tree_valid, $tree_error);
             $branch = $san->keyName($raw_branch, null, $branch_valid, $branch_error);
             $key = $san->keyName($raw_key, null, $key_valid, $key_error);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "Error in sanitizer function", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if (!$plugin_valid) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "Called with invalid plugin name", 'data' => array('raw_tree' => $raw_plugin, 'san_error' => $plugin_error), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
         } elseif (!$tree_valid) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "Called with invalid tree name", 'data' => array('raw_tree' => $raw_tree, 'san_error' => $tree_error), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
         } elseif (!$branch_valid) {
             throw new FOX_exception(array('numeric' => 5, 'text' => "Called with invalid branch name", 'data' => array('raw_branch' => $raw_branch, 'san_error' => $branch_error), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
         } elseif (!$key_valid) {
             throw new FOX_exception(array('numeric' => 6, 'text' => "Called with invalid node name", 'data' => array('raw_key' => $raw_key, 'san_error' => $key_error), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
         }
         $query_keys[$plugin][$tree][$branch][$key] = true;
         unset($option, $plugin, $raw_plugin, $tree, $raw_tree, $branch, $raw_branch, $key, $raw_key);
         unset($plugin_error, $tree_error, $branch_error, $key_error, $plugin_valid, $tree_valid, $branch_valid, $key_valid);
     }
     if (count(array_keys($query_keys)) > 1) {
         throw new FOX_exception(array('numeric' => 7, 'text' => "Attempting to set nodes for multiple plugins", 'data' => array_keys($query_keys), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
     $get_ctrl = array('validate' => false, 'q_mode' => 'trie', 'r_mode' => 'trie', 'trap_*' => true);
     try {
         // We do not use getMulti's $valid flag to trap nonexistent keys
         // because we want to throw an error listing the key names
         $current_keys = parent::getMulti($query_keys, $get_ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 8, 'text' => "Error calling parent::getMulti()", 'data' => $query_keys, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     // Iterate through all of the requested keys, cast the submitted form
     // data for each key to the format specified in the key's db entry
     // ====================================================================
     $rows_changed = 0;
     $invalid_nodes = array();
     $update_keys = array();
     foreach ($query_keys as $plugin => $trees) {
         foreach ($trees as $tree => $branches) {
             foreach ($branches as $branch => $nodes) {
                 foreach ($nodes as $node => $fake_var) {
                     if (!FOX_sUtil::keyExists($node, $current_keys[$plugin][$tree][$branch])) {
                         $invalid_nodes[] = "P: {$plugin} T: {$tree} B: {$branch} N: {$node}";
                         continue;
                     }
                     $filter = $current_keys[$plugin][$tree][$branch][$node]["filter"];
                     $filter_ctrl = $current_keys[$plugin][$tree][$branch][$node]["filter_ctrl"];
                     // Remove any escaping PHP has added to the posted form value
                     $post_key = $plugin . $this->key_delimiter . $tree . $this->key_delimiter;
                     $post_key .= $branch . $this->key_delimiter . $node;
                     $post[$post_key] = FOX_sUtil::formVal($post[$post_key]);
                     // Run key value through the specified filter
                     // =====================================================
                     $filter_valid = null;
                     $filter_error = null;
                     // Passed by reference
                     try {
                         $new_val = $san->{$filter}($post[$post_key], $filter_ctrl, $filter_valid, $filter_error);
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 9, 'text' => "Error in filter function", 'data' => array('filter' => $filter, 'val' => $post[$post_key], 'ctrl' => $ctrl, 'filter_valid' => $filter_valid, 'filter_error' => $filter_error), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     if (!$filter_valid) {
                         throw new FOX_exception(array('numeric' => 10, 'text' => "Filter function reports value data isn't valid", 'data' => array('filter' => $filter, 'val' => $post[$post_key], 'ctrl' => $ctrl, 'filter_valid' => $filter_valid, 'filter_error' => $filter_error), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
                     }
                     // If the new value doesn't match the stored value, update the key
                     // (using === because null == int )
                     // ====================================================================
                     if ($new_val !== $current_keys[$plugin][$tree][$branch][$node]["val"]) {
                         $update_keys[$plugin][$tree][$branch][$node] = array('filter' => $filter, 'filter_ctrl' => $filter_ctrl, 'val' => $new_val);
                     }
                 }
                 // ENDOF: foreach($nodes as $node => $fake_var)
                 unset($node, $fake_var);
             }
             // ENDOF: foreach($branches as $branch => $nodes)
             unset($branch, $nodes);
         }
         // ENDOF: foreach($trees as $tree => $branches)
         unset($tree, $branches);
     }
     // ENDOF: foreach($query_keys as $plugin => $trees)
     unset($plugin, $trees);
     if ($invalid_nodes) {
         throw new FOX_exception(array('numeric' => 11, 'text' => "Trying to set one or more nonexistent nodes", 'data' => $invalid_nodes, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
     if ($update_keys) {
         $set_ctrl = array('validate' => false, 'mode' => 'trie');
         try {
             $rows_changed = parent::setMulti($update_keys, $set_ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 12, 'text' => "Error in parent::setMulti()", 'data' => array('update_keys' => $update_keys, 'set_ctrl' => $set_ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
     }
     return (int) $rows_changed;
 }
 /**
  * Builds a delete query for processing by $wpdb->prepare().
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $struct | Structure of the db table, @see class FOX_db header for examples
  *
  * @param array $args | Args in the form: array("col"=>column_name, "op" => "<, >, =, !=", "val" => "int | string | array()")
  *	=> ARR @param int '' | Array index
  *	    => VAL @param string $col | Name of the column in the db table this key describes
  *	    => VAL @param string $op | SQL comparison operator to use: ">=" | "<=" | ">" | "<" | "=" | "!=" | "<>"
  *	    => VAL @param int/string/array $val | Value or values to test against. Single value as int/string. Multiple values as array.
  *
  * @param array $ctrl | Control parameters for the query
  *	=> VAL @param string $args_format | "default" to use standard format, "multi", or "matrix"
  * 
  * @return array | Exception on failure. Query array on success.
  */
 public function buildDeleteQuery($struct, $args, $ctrl = null)
 {
     $ctrl_default = array('args_format' => 'default');
     $ctrl = FOX_sUtil::parseArgs($ctrl, $ctrl_default);
     // Switch between unit test mode (pass as array) and
     // normal mode (pass as class name)
     // ====================================================
     if (is_string($struct)) {
         $struct = call_user_func(array($struct, '_struct'));
     }
     // ====================================================
     // Build WHERE statement
     // ######################################################
     $where = "1 = 1";
     // We need to pre-load it with a value in case the user adds zero 'where' conditions
     // Primary table args. The "if" clause handles the case where the user passes no args because
     // they do not want to constrain the query using a column in the primary table.
     if ($args) {
         try {
             switch ($ctrl['args_format']) {
                 case "multi":
                     $result = self::buildWhereMulti($struct, $args, __FUNCTION__, $prefix = null);
                     break;
                 case "matrix":
                     // Forward the 'hash_token_vals' flag, if set
                     if (FOX_sUtil::keyExists('hash_token_vals', $args)) {
                         $matrix_ctrl = array('hash_token_vals' => $args['hash_token_vals']);
                     } else {
                         $matrix_ctrl = null;
                     }
                     $result = self::buildWhereMatrix($struct, $args["key_col"], $args["args"], $matrix_ctrl);
                     break;
                 case "trie":
                     // Forward the 'hash_token_vals' flag, if set
                     if (FOX_sUtil::keyExists('hash_token_vals', $args)) {
                         $trie_ctrl = array('hash_token_vals' => $args['hash_token_vals']);
                     } else {
                         $trie_ctrl = null;
                     }
                     $result = self::buildWhereTrie($struct, $args["key_col"], $args["args"], $trie_ctrl);
                     break;
                 default:
                     $result = self::buildWhere($struct, $args, __FUNCTION__, $prefix = null);
                     break;
             }
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 1, 'text' => "Error in buildWhere generator", 'data' => array("struct" => $struct, "args" => $args, "ctrl" => $ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         $where .= $result['where'];
         $params_list = $result['params'];
         unset($result);
     } else {
         // From the MySQL site: "DELETE FROM tbl_name does not regenerate the table but instead deletes all rows, one by one."
         // @link http://dev.mysql.com/doc/refman/5.5/en/innodb-restrictions.html
         $text = "\nSAFTEY INTERLOCK TRIP [DESTROY TABLE] - buildDeleteQuery() called with no WHERE args. Running this query would delete ";
         $text .= " ALL rows in the target table. If this is what you really want to do, use buildTruncateTable().\n";
         throw new FOX_exception(array('numeric' => 2, 'text' => $text, 'data' => array("struct" => $struct, "args" => $args, "ctrl" => $ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     $query = "DELETE FROM " . $this->base_prefix . $struct["table"] . " WHERE " . $where;
     // Merge all return data into an array
     // ###############################################################
     $result = array('query' => $query, 'params' => $params_list);
     return $result;
 }
 /**
  * Saves pages in a paged class cache array to the persistent cache, and broadcasts the 
  * update to any classes that are listening for it
  *
  * @version 1.0
  * @since 1.0
  * @param string/array $pages | Single page as string. Multiple pages as array of strings.
  * @return bool | Exception on failure. True on success.
  */
 public function saveCachePage($pages)
 {
     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())))));
     }
     $struct = $this->_struct();
     if ($struct['cache_strategy'] != 'paged') {
         throw new FOX_exception(array('numeric' => 1, 'text' => "This method can only be used on classes that use a paged cache", 'data' => array('struct' => $struct), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if (!is_array($pages)) {
         $pages = array($pages);
     }
     $processed_pages = array();
     foreach ($pages as $page_name) {
         if (FOX_sUtil::keyExists($page_name, $this->cache)) {
             $processed_pages[$page_name] = $this->cache[$page_name];
         } else {
             throw new FOX_exception(array('numeric' => 2, 'text' => "Called with key name that doesn't exist in the class cache", 'data' => array("faulting_page" => $page_name, "cache" => $this->cache), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         }
     }
     try {
         self::writeCachePage($processed_pages);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Error in self::writeCachePage()", 'data' => $processed_pages, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
     do_action('fox_saveCachePage_' . $struct["cache_namespace"], $processed_pages);
     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 true;
 }
 /**
  * Checks if a single user has a single key. If the key is not already cached, it will
  * be added to the class cache and the persistent cache.
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int $user_id | user_id to check
  * @param int $key_id | key_id to check for
  * @return bool | True if user_id has key. False if not.
  */
 public function hasKey($user_id, $key_id)
 {
     global $fox;
     $result = array();
     // If the key has an entry in the class cache array, return its value (true
     // if the user has the key, false if they don't)
     if (FOX_sUtil::keyExists($key_id, $this->cache[$user_id]["keys"])) {
         $result = $this->cache[$user_id]["keys"][$key_id];
     } elseif ($this->cache[$user_id]["all_cached"] == true) {
         $result = false;
     } else {
         $this->cache[$user_id] = $fox->cache->get("FOX_uKeyRing", $user_id);
         if (FOX_sUtil::keyExists($key_id, $this->cache[$user_id]["keys"])) {
             $result = $this->cache[$user_id]["keys"][$key_id];
         } elseif ($this->cache[$user_id]["all_cached"] == true) {
             $result = false;
         } else {
             self::load($user_id, $key_id, $skip_load = true);
             $result = $this->cache[$user_id]["keys"][$key_id];
         }
     }
     return $result;
 }
 /**
  * Fetches one or more ids. 
  *
  * @version 1.0
  * @since 1.0
  * @param int/array $ids | Single id as int. Multiple ids as array of int.
  * @return string/array | Exception on failure. String (single id). Array of string (multiple id's).
  */
 public function getId($ids)
 {
     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 (is_null($ids)) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "null parameter passed as ids exception", 'data' => array("tokens" => $ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     if (!is_array($ids)) {
         $ids = array($ids);
         $single = true;
     } else {
         $single = false;
     }
     // Fetch all ids currently stored in the class cache
     // ======================================================
     $result = array();
     $missing_ids = array();
     foreach ($ids as $id) {
         if (FOX_sUtil::keyExists($id, $this->cache["ids"])) {
             $result[$id] = $this->cache["ids"][$id];
         } else {
             $missing_ids[] = $id;
         }
     }
     unset($id);
     // Try to fetch missing ids from the persistent cache
     // ======================================================
     if (count($missing_ids)) {
         try {
             $cache_ids = $this->cacheFetchId($missing_ids);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "Error in self::cacheFetchId()", 'data' => array("ids" => $missing_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         if ($cache_ids) {
             $result = $result + $cache_ids;
         }
         $missing_ids = array_diff($missing_ids, array_keys($cache_ids));
     }
     // Try to fetch missing ids from the database
     // ======================================================
     if (count($missing_ids)) {
         try {
             $db_ids = $this->dbFetchId($missing_ids);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "Error in self::dbFetchId()", 'data' => array("ids" => $missing_ids), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         if ($db_ids) {
             $result = $result + $db_ids;
         }
     }
     if ($single == true) {
         return $result[$ids[0]];
     } else {
         return $result;
     }
 }
 /**
  * Removes one or more targets from the database and cache.
  *
  * @version 1.0
  * @since 1.0
  * @param string $location | Single location as string.
  * @param int/string/array targets | Single target as int or string. Multiple targets as array of int/string.
  * @return int | Exception on failure. Int number of db rows changed on success.
  */
 public function dropTarget($location, $targets)
 {
     $db = new FOX_db();
     $struct = self::_struct();
     if (empty($location) || !is_string($location)) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Invalid location parameter", 'data' => array($location, $targets), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
     if (empty($targets)) {
         throw new FOX_exception(array('numeric' => 2, 'text' => "Invalid targets parameter", 'data' => array($location, $targets), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
     if (!is_array($targets)) {
         // Handle single int as input
         $targets = array($targets);
     }
     // Lock the cache
     // ===========================================================
     try {
         $cache_image = self::lockCache();
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Error locking cache", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // Update the database
     // ===========================================================
     $args = array(array("col" => "location", "op" => "=", "val" => $location), array("col" => "target", "op" => "=", "val" => $targets));
     try {
         $rows_changed = $db->runDeleteQuery($struct, $args, $ctrl = null);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 4, 'text' => "Error deleting from database", 'data' => array('args' => $args), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // Rebuild the cache image
     // ===========================================================
     foreach ($targets as $target) {
         if (FOX_sUtil::keyExists($target, $cache_image["data"][$location])) {
             unset($cache_image["data"][$location][$target]);
             // If deleting the target makes its parent location empty, remove
             // the parent location from the cache as well
             if (count($cache_image["data"][$location]) == 0) {
                 unset($cache_image["data"][$location]);
                 unset($cache_image["locations"][$location]);
             }
         }
     }
     unset($target);
     // 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' => 5, 'text' => "Cache write error", 'data' => array('cache_image' => $cache_image), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // Update the class cache
     $this->cache = $cache_image;
     return (int) $rows_changed;
 }
 /**
  * Prepares a SQL query for safe execution. Uses sprintf()-like syntax.
  * 
  * @version 1.0
  * @since 1.0
  *
  * @param array $args | Query args array
  *	=> VAL @param string [0] | First key in array is query string in vsprintf() format
  *	=> VAL @param mixed  [N] | Each successive key is a var referred to in the query string
  *
  * @return string | Prepared query string	 
  */
 function prepare($query, $params = null)
 {
     // Force floats to be locale unaware
     $query = preg_replace('|(?<!%)%f|', '%F', $query);
     // Quote the strings, avoiding escaped strings like %%s
     $query = preg_replace('|(?<!%)%s|', "'%s'", $query);
     // Replace our %r raw string token with an unquoted %s
     $query = preg_replace('|(?<!%)%r|', "%s", $query);
     $escaped_params = array();
     if ($params) {
         $cast = new FOX_cast();
         foreach ($params as $param) {
             if (!FOX_sUtil::keyExists('escape', $param) || !FOX_sUtil::keyExists('val', $param) || !FOX_sUtil::keyExists('php', $param) || !FOX_sUtil::keyExists('sql', $param)) {
                 $text = "SAFETY INTERLOCK TRIP [ANTI SQL-INJECTION] - All data objects passed to the ";
                 $text .= "database driver must include 'val', 'escape', 'php', and 'sql' parameters. This ";
                 $text .= "interlock cannot be disabled.";
                 throw new FOX_exception(array('numeric' => 1, 'text' => $text, 'data' => $param, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
             }
             try {
                 $cast_val = $cast->PHPToSQL($param['val'], $param['php'], $param['sql']);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "Error while casting parameter", 'data' => array("val" => $param['val'], "php" => $param['php'], "sql" => $param['sql']), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
             if ($param['escape'] !== false) {
                 // NOTE: parameters are in reverse order from mysqli_real_escape_string()
                 $escaped_params[] = mysql_real_escape_string($cast_val, $this->dbh);
             } else {
                 $escaped_params[] = $cast_val;
             }
         }
         unset($param);
     }
     $result = vsprintf($query, $escaped_params);
     return $result;
 }
Example #21
0
 /**
  * Creates a single group
  *
  * @version 1.0
  * @since 1.0
  *
  * @param string $name | name of group (max 32 chars)
  * @param string $title | title of group (max 128 chars)
  * @param string $caption | caption of group
  * @param bool $is_default | if set true, this group is the default group
  * @param int $icon | media_id of image to use as icon for the group
  * @return bool | False on failure. Created group's id on success.
  */
 public function createGroup($data)
 {
     if (!FOX_sUtil::keyExists('name', $data) || empty($data['name'])) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Missing or invalid 'name' key", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
     if (!FOX_sUtil::keyExists('is_default', $data) || $data['is_default'] === null) {
         $data['is_default'] = false;
     }
     // IMPORTANT: Default group flag rules
     // ==========================================
     // 1) Only one group can be the default group
     // 2) There must always be a default group
     $db = new FOX_db();
     $ctrl = array("format" => "var", "count" => true);
     try {
         $groups_count = $db->runSelectQuery(self::$struct, $args = null, $columns = null, $ctrl);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "DB select exception", 'data' => array("ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // CASE 1: No other groups exist
     // =============================================================================
     if ($groups_count == 0) {
         $data["is_default"] = true;
         // Force $is_default flag to be true
         // Insert new group into the db
         try {
             $insert_ok = $db->runInsertQuery(self::$struct, $data, $columns = null);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "DB insert exception", 'data' => array("data" => $data), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         $new_group_id = $db->insert_id;
         if ($insert_ok && $new_group_id) {
             $result = true;
         } else {
             $result = false;
         }
     } elseif ($data['is_default'] == true && $groups_count != 0) {
         // Find $group_id of current default group
         $args = array(array("col" => "is_default", "op" => "=", "val" => "1"));
         $columns = array("mode" => "include", "col" => "group_id");
         $ctrl = array("format" => "var");
         try {
             $old_default = $db->runSelectQuery(self::$struct, $args, $columns, $ctrl);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 3, 'text' => "DB select exception", 'data' => array("args" => $args, "columns" => $columns, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
         try {
             $started_transaction = $db->beginTransaction();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 2, 'text' => "beginTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($started_transaction) {
             // Insert new group into the db
             try {
                 $insert_ok = $db->runInsertQuery(self::$struct, $data, $columns = null);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "DB insert exception", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             $new_group_id = $db->insert_id;
             // Remove remove default flag from old group
             $unset_default = array("is_default" => false);
             try {
                 $unset_ok = $db->runUpdateQueryCol(self::$struct, $unset_default, "group_id", "=", $old_default);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 4, 'text' => "DB update exception", 'data' => array("data" => $unset_default, "col" => "group_id", "op" => "=", "val" => $old_default), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // If all queries were successful, commit the transaction
             if ($insert_ok && $new_group_id && $unset_ok) {
                 try {
                     $result = $db->commitTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 5, 'text' => "commitTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
             } else {
                 //echo "\ninsert_ok:$insert_ok new_group_id: $new_group_id old_default:$old_default unset_ok:$unset_ok";
                 try {
                     $db->rollbackTransaction();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 6, 'text' => "rollbackTransaction exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 $result = false;
             }
         } else {
             $result = false;
         }
         // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
     } else {
         // Insert new group into the db
         try {
             $insert_ok = $db->runInsertQuery(self::$struct, $data, $columns = null);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 7, 'text' => "DB insert exception", 'data' => array("data" => $data), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         $new_group_id = $db->insert_id;
         if ($insert_ok && $new_group_id) {
             $result = true;
         } else {
             $result = false;
         }
     }
     // Update the cache
     // =========================================
     if ($new_group_id && $result) {
         try {
             self::loadCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "loadCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         $this->cache["ids"][$data['name']] = $new_group_id;
         if ($data['is_default']) {
             $this->cache["default_id"] = $new_group_id;
         }
         try {
             $cache_ok = self::saveCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 5, 'text' => "saveCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($cache_ok) {
             return $new_group_id;
         } else {
             return false;
         }
     } else {
         return false;
     }
 }
 /**
  * Fetches a node, branch, tree, or all nodes from the cache or database
  *
  * If an object is not in the cache yet, it will be retrieved from the database
  * and added to the cache. Multiple items in the *lowest level group* specified
  * can be retrieved in a single query by passing their names or id's as an array.
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int/array $tree | Single $tree as int. Multiple as array of ints.
  * @param int/array $branch | Single $branch as int. Multiple as array of ints.
  * @param string/array $node | Single node name as string. Multiple as array of strings.
  * @param bool &$valid | True if all requested objects exist in the db. False if not.
  *
  * @return mixed | Exception on failure. Requested objects on success.
  */
 public function get($trees = null, $branches = null, $nodes = null, &$valid = null)
 {
     $valid = true;
     // CASE 1: One or more $nodes
     // =====================================================================
     if ($trees && $branches && $nodes) {
         // Trap attempting to pass array of tree's, or branches when specifying node
         if (is_array($trees) || is_array($branches)) {
             throw new FOX_exception(array('numeric' => 1, 'text' => "Attempted to specify multiple trees or branches when specifying nodes", 'data' => array('trees' => $trees, 'branch' => $branches, 'nodes' => $nodes), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         }
         if (!is_array($nodes)) {
             $nodes = array($nodes);
         }
         // Find all the nodes that have been requested but are not in the cache
         $missing_nodes = array();
         $cache_loaded = false;
         foreach ($nodes as $node) {
             $all_cached = FOX_sUtil::keyTrue("all_cached", $this->cache);
             $tree_cached = FOX_sUtil::keyTrue($trees, $this->cache["trees"]);
             $branch_cached = FOX_sUtil::keyTrue($branch, $this->cache["branches"][$trees]);
             $node_exists = FOX_sUtil::keyExists($node, $this->cache["keys"][$trees][$branches]);
             // If the node doesn't exist in the class cache array, fetch it from the persistent cache
             if (!$all_cached && !$tree_cached && !$branch_cached && !$node_exists && !$cache_loaded) {
                 try {
                     self::loadCache();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 2, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
                 }
                 $all_cached = FOX_sUtil::keyTrue("all_cached", $this->cache);
                 $tree_cached = FOX_sUtil::keyTrue($trees, $this->cache["trees"]);
                 $branch_cached = FOX_sUtil::keyTrue($branch, $this->cache["branches"][$trees]);
                 $node_exists = FOX_sUtil::keyExists($node, $this->cache["keys"][$trees][$branches]);
                 $cache_loaded = true;
             }
             // If the requested node doesn't exist in the persistent cache, add it to the missing nodes array
             if (!$all_cached && !$tree_cached && !$branch_cached && !$node_exists) {
                 $missing_nodes[] = $node;
             }
         }
         unset($node);
         // Load all missing keys in a single query
         if (count($missing_nodes) > 0) {
             try {
                 $valid = self::load($trees, $branches, $missing_nodes, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "Error in self::load()", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested nodes
         foreach ($nodes as $node) {
             $result[$node] = $this->cache["keys"][$trees][$branches][$node];
         }
         unset($node);
         return $result;
     } elseif ($trees && $branches && !$nodes) {
         // Trap attempting to pass array of tree's when specifying branch
         if (is_array($trees) || is_array($branches)) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "Attempted to specify multiple trees when specifying branch", 'data' => array('trees' => $trees, 'branch' => $branches, 'nodes' => $nodes), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         }
         if (!is_array($branches)) {
             $branches = array($branches);
         }
         // Find all the branches that have been requested but are not in the cache
         $missing_branches = array();
         $cache_loaded = false;
         foreach ($branches as $branch) {
             $all_cached = FOX_sUtil::keyTrue("all_cached", $this->cache);
             $tree_cached = FOX_sUtil::keyTrue($trees, $this->cache["trees"]);
             $branch_cached = FOX_sUtil::keyTrue($branch, $this->cache["branches"][$trees]);
             // If the branch doesn't exist in the class cache array, fetch it from the persistent cache
             if (!$all_cached && !$tree_cached && !$branch_cached && !$cache_loaded) {
                 try {
                     self::loadCache();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 5, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
                 }
                 $all_cached = FOX_sUtil::keyTrue("all_cached", $this->cache);
                 $tree_cached = FOX_sUtil::keyTrue($trees, $this->cache["trees"]);
                 $branch_cached = FOX_sUtil::keyTrue($branch, $this->cache["branches"][$trees]);
                 $cache_loaded = true;
             }
             // If the branch doesn't exist in the persistent cache, add it to the missing branches array
             if (!$all_cached && !$tree_cached && !$branch_cached) {
                 $missing_branches[] = $branch;
             }
         }
         unset($branch);
         // Load all missing branches in a single query
         if (count($missing_branches) != 0) {
             try {
                 $valid = self::load($trees, $missing_branches, null, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 6, 'text' => "Error in self::load()", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested branches
         foreach ($branches as $branch) {
             $result[$branch] = $this->cache["keys"][$trees][$branch];
         }
         unset($branch);
         return $result;
     } elseif ($trees && !$branches && !$nodes) {
         if (!is_array($trees)) {
             $trees = array($trees);
         }
         // Find all the trees that have been requested but are not in the cache
         $missing_trees = array();
         $cache_loaded = false;
         foreach ($trees as $tree) {
             $all_cached = FOX_sUtil::keyTrue("all_cached", $this->cache);
             $tree_cached = FOX_sUtil::keyTrue($tree, $this->cache["trees"]);
             // If the tree doesn't exist in the class cache array, fetch it from the persistent cache
             if (!$all_cached && !$tree_cached && !$cache_loaded) {
                 try {
                     self::loadCache();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 7, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
                 }
                 $all_cached = FOX_sUtil::keyTrue("all_cached", $this->cache);
                 $tree_cached = FOX_sUtil::keyTrue($tree, $this->cache["trees"]);
                 $cache_loaded = true;
             }
             // If the tree doesn't exist in the persistent cache, add it to the missing branches array
             if (!$all_cached && !$tree_cached) {
                 $missing_trees[] = $tree;
             }
         }
         unset($tree);
         // Load all missing branches in a single query
         if (count($missing_trees) != 0) {
             try {
                 $valid = self::load($missing_trees, null, null, $skip_load = true);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 8, 'text' => "Error in self::load()", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
         }
         $result = array();
         // Build an array of the requested branches
         foreach ($tree as $tree) {
             $result[$tree] = $this->cache["keys"][$tree];
         }
         unset($tree);
         return $result;
     } elseif (!$trees && !$branches && !$nodes) {
         $all_cached = FOX_sUtil::keyTrue("all_cached", $this->cache);
         // If the tree doesn't exist in the class cache array, fetch it from the persistent cache
         if (!$all_cached) {
             try {
                 self::loadCache();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 9, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
             $all_cached = FOX_sUtil::keyTrue("all_cached", $this->cache);
         }
         if (!$all_cached) {
             $valid = self::load(null, null, null, $skip_load = true);
         }
         return $this->cache["keys"];
     } else {
         throw new FOX_exception(array('numeric' => 10, 'text' => "Bad input format", 'data' => array('trees' => $trees, 'branches' => $branches, 'nodes' => $nodes), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
     }
 }
 /**
  * Returns all type_ids owned by a module
  *
  * @version 1.0
  * @since 1.0
  * @param int $module_id| Single module_id as int.
  * @return array | Exception on failure. False on nonexistent. Array of type_ids on success.
  */
 public function getTypes($module_id)
 {
     $db = new FOX_db();
     $result = array();
     // If the module_id doesn't exist in the class cache array, fetch it from the persistent cache
     if (!FOX_sUtil::keyExists($module_id, $this->cache["module_id_types"])) {
         try {
             self::loadCache();
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 1, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
         // If the module_id doesn't exist in the persistent cache, load it from the db
         if (!FOX_sUtil::keyExists($module_id, $this->cache["module_id_types"])) {
             $columns = array("mode" => "include", "col" => array("type_id"));
             $ctrl = array("format" => "col");
             try {
                 $db_result = $db->runSelectQueryCol($this->_struct(), "module_id", "=", $module_id, $columns, $ctrl);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "Error reading from database", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
             }
             // Update the cache
             // ==============================
             if ($db_result) {
                 try {
                     $cache_image = self::readCache();
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 3, 'text' => "Cache read error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
                 }
                 // Rebuild the cache image
                 foreach ($db_result as $type_id) {
                     $result[] = $type_id;
                     $cache_image["module_id_types"][$module_id][$type_id] = true;
                 }
                 unset($type_id);
                 try {
                     self::writeCache($cache_image);
                 } catch (FOX_exception $child) {
                     throw new FOX_exception(array('numeric' => 4, 'text' => "Cache write error", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
                 }
                 // Overwrite the class cache
                 $this->cache = $cache_image;
             } else {
                 // The module_id doesn't exist
                 return false;
             }
         }
     }
     // Build the result array
     $type_ids = $this->cache["module_id_types"][$module_id];
     $result = array_keys($type_ids);
     return $result;
 }
 /**
  * Given a *minimum spanning trie* of objects to check, returns a minimum spanning trie
  * of objects in the original trie which don't have authority in the class cache.
  * 
  * @see FOX_trie::clipAssocTrie()
  *
  * @version 1.0
  * @since 1.0
  * 
  * @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 $cache_image | cache image to check against
  * 
  * @return array | Exception on failure. Data array on success.
  */
 public function notInClassCache($data, $cache_image)
 {
     //  1) If an object has authority in the class cache skip it
     //  2) If the object doesn't have authority and has no descendents, flag it
     //  3) Otherwise, repeat the algorithm on the object's descendents
     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())))));
     }
     $result = array();
     foreach ($data as $L5 => $L4s) {
         $L5_has_auth = FOX_sUtil::keyTrue('all_cached', $cache_image[$L5]);
         if (!$L5_has_auth) {
             // 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
             // in the caller method
             if (!is_array($L4s)) {
                 $L4s = array();
             }
             if (count($L4s) == 0) {
                 $result[$L5] = array();
             }
             foreach ($L4s as $L4 => $L3s) {
                 $L4_has_auth = FOX_sUtil::keyTrue($L4, $cache_image[$L5][$this->L4_col]);
                 if (!$L4_has_auth) {
                     if (!is_array($L3s)) {
                         // Handle "true", "null" etc end nodes
                         $L3s = array();
                     }
                     if (count($L3s) == 0) {
                         $result[$L5][$L4] = array();
                     }
                     foreach ($L3s as $L3 => $L2s) {
                         $L3_has_auth = FOX_sUtil::keyTrue($L3, $cache_image[$L5][$this->L3_col][$L4]);
                         if (!$L3_has_auth) {
                             if (!is_array($L2s)) {
                                 // Handle "true", "null" etc end nodes
                                 $L2s = array();
                             }
                             if (count($L2s) == 0) {
                                 $result[$L5][$L4][$L3] = array();
                             }
                             foreach ($L2s as $L2 => $L1s) {
                                 $L2_has_auth = FOX_sUtil::keyTrue($L2, $cache_image[$L5][$this->L2_col][$L4][$L3]);
                                 if (!$L2_has_auth) {
                                     if (!is_array($L1s)) {
                                         // Handle "true", "null" etc end nodes
                                         $L1s = array();
                                     }
                                     if (count($L1s) == 0) {
                                         $result[$L5][$L4][$L3][$L2] = array();
                                     }
                                     foreach ($L1s as $L1 => $val) {
                                         if (!FOX_sUtil::keyExists($L1, $cache_image[$L5]["keys"][$L4][$L3][$L2])) {
                                             $result[$L5][$L4][$L3][$L2][$L1] = true;
                                         }
                                     }
                                     unset($L1, $val);
                                 }
                             }
                             unset($L2, $L1s);
                         }
                     }
                     unset($L3, $L2s);
                 }
             }
             unset($L4, $L3s);
         }
     }
     unset($L5, $L4s);
     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 $result;
 }
Example #25
0
 /**
  * Adds a page to the site's navigation tree by injecting it into the BP
  * active components array using the 'bp_core_get_directory_page_ids' action
  *
  * @version 1.0
  * @since 1.0
  * @return bool $result | Exception on failure. True on success.
  */
 public function injectSite($pages)
 {
     // Avoid hooking on admin screens
     // =======================================================================
     // BuddyPress runs their screen router on all WordPress screens including back-end
     // admin screens. This wastes resorces and causes *epic* debugging problems with
     // page module installers. If the code in this method gets run on the "system tools"
     // screen, it will interfere with the "reset system to default" button.
     // The isLoginScreen() check handles the case where the admin manages to corrupt the
     // routing table, causing the router to throw an exception on every front-end page,
     // including the login page (preventing the admin from logging in and fixing it)
     if (FOX_wp::isAdminScreen() || FOX_wp::isLoginScreen()) {
         return $pages;
     }
     // Fetch all the page modules in the system that have 'site page'
     // as their target
     // ==============================================================================
     $locations = "page";
     try {
         $targets = $this->target_class->getLocation($locations);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 1, 'text' => "Error in target class getLocation() method", 'data' => array('locations' => $locations), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // If no targets were found, quit, returning the unmodified $pages array
     // =======================================================================
     if (!$targets) {
         return $pages;
     }
     // Fetch active page modules
     // =======================================================================
     try {
         $active_page_modules = $this->page_modules_class->getActiveModules();
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 2, 'text' => "Error loading active page modules", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     // Intersect the page module id's that are registered on the target with
     // the page module id's that are currently active on the system
     // =======================================================================
     $valid_modules = array();
     $module_ids = array();
     foreach ($targets as $page_id => $target_data) {
         if (FOX_sUtil::keyExists($target_data["module_id"], $active_page_modules)) {
             $valid_modules[$page_id] = $active_page_modules[$target_data["module_id"]];
             $module_ids[] = $target_data["module_id"];
         } else {
             // TODO: remove the module's page from the WP menu system
         }
     }
     unset($page_id, $target_data);
     // If there are no valid modules, quit, returning the unmodified $pages array
     // =======================================================================
     if (!$valid_modules) {
         return $pages;
     }
     // Fetch policies for the modules, then intersect the policies with the user's
     // keyring to determine if the user is allowed to access the page module. This
     // is necessary in order to be able to hide modules that the user should not
     // have access to from the WordPress menu tree
     // =======================================================================
     try {
         $module_policies = $this->policy_class->getL5($module_ids);
     } catch (FOX_exception $child) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Error loading module policies", 'data' => array('module_ids' => $module_ids), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
     }
     foreach ($valid_modules as $page_id => $module_data) {
         $user_keyring = $this->user_class->loggedin_user->keyring;
         $module_policy = $module_policies[$module_data['module_id']];
         try {
             $can_access = self::canAccess($module_policy, $user_keyring);
         } catch (FOX_exception $child) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "Error in self::canAccess() method", 'data' => array('module_policy' => $module_policy, 'user_keyring' => $user_keyring), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
         }
         if ($can_access) {
             // Add the page module's class files to the PHP include path
             // ==============================================================================
             try {
                 $plugin_path = WP_PLUGIN_DIR . '/' . $module_data['plugin'] . '/modules/page/';
                 $module_slug = $module_data['slug'];
                 $this->page_modules_class->includeModule($plugin_path, $module_slug);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "Error in page modules loadModule() method", 'data' => $page_module_data[$data["php_class"]]["slug"], 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Add the page module to the BP pages array as "class_name" => "page_id", which
             // causes BuddyPress to set $bp->current_component to "class_name" whenever
             // the user loads the URL that "page_id" is assigned to.
             // ==============================================================================
             $pages[$module_data["php_class"]] = $page_id;
             // Create an anonymous function that instantiates the page module's class
             // and runs its screen method whenever the page module is the $bp->current_component
             // ==============================================================================
             $function_body = 'global $bp;';
             $function_body .= 'if($bp->current_component == "' . $module_data["php_class"] . '"){';
             $function_body .= '	$cls = new ' . $module_data["php_class"] . '();';
             $function_body .= '	$cls->screen();';
             $function_body .= '}';
             $function_name = create_function('', $function_body);
             // Attach the anonymous function to the bp_init action
             add_action('bp_screens', $function_name);
         } else {
             // TODO: remove the module's page from the WP menu system
         }
     }
     return $pages;
 }
 /**
  * Loads a monolithic class cache array from the persistent cache and locks the class 
  * namespace until the timeout expires or the PID releases the lock by writing to the 
  * cache. Read requests in the namespace will throw an exception until the lock expires. 
  * Write and delete requests will remove the lock and clear/update the namespace.
  *
  * @version 1.0
  * @since 1.0
  * 
  * @param string/array $keys | Single key as string. Multiple keys as array of strings.
  * 
  * @param array $args | Control args
  *	=> VAL @param string $namespace | Class namespace
  *	=> VAL @param int $seconds |  Time in seconds from present time until lock expires	  
  *	=> VAL @param string/array $pages | Single page as string. Multiple pages as array of string.	 
  * 
  * @return int &$offset | Current namespace offset	 
  * @return mixed | Exception on failure. Mixed on success.
  */
 public function lockCachePage($args, &$offset = null)
 {
     if (!is_array($args['pages'])) {
         $args['pages'] = array($args['pages']);
     }
     try {
         $cache_result = $this->getMulti($args["namespace"], $args['pages'], $offset);
     } catch (FOX_exception $child) {
         if ($child->data['numeric'] == 4) {
             throw new FOX_exception(array('numeric' => 1, 'text' => "Cache namespace is currently locked by another PID", 'data' => $child->data['data'], 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         } else {
             throw new FOX_exception(array('numeric' => 2, 'text' => "Error in descendent::getMulti()", 'data' => $args, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
     }
     $processed_result = array();
     $locked_pages = array();
     foreach ($args['pages'] as $page) {
         // Page has no cache entry
         // =============================================================
         if (!FOX_sUtil::keyExists($page, $cache_result)) {
             // Write an empty array to the result
             $processed_result[$page] = array();
         } elseif (!FOX_sUtil::keyExists("lock", $cache_result[$page])) {
             $processed_result[$page] = $cache_result[$page];
         } else {
             $expiry_time = $cache_result[$page]['lock']['expire'];
             $current_time = microtime(true);
             if ($current_time > $expiry_time) {
                 // If the lock has expired, the cache contents are no longer
                 // valid. Return an empty cache array
                 $processed_result[$page] = array();
             } elseif ($cache_result[$page]['lock']['pid'] == $this->process_id) {
                 // If the lock is owned by the current PID, just write back the lock array to the cache
                 // with an updated timestamp, refreshing the lock. This provides important functionality,
                 // letting a PID that has a lock on the page extend its lock time incrementally as it
                 // works through a complex processing job. If the PID had to release and reset the lock
                 // each time, the data would be venerable to being overwritten by other PID's.
                 unset($cache_result[$page]['lock']);
                 $processed_result[$page] = $cache_result[$page];
             } else {
                 // Othewise, the lock is still valid, so flag the key
                 $locked_pages[$page] = $cache_result[$page]['lock'];
             }
         }
     }
     unset($page);
     // If any of the pages were already locked, throw an exception
     if (count($locked_pages) != 0) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "One or more requested pages are currently locked", 'data' => $locked_pages, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
     }
     // Build a cache image with the lock array added to each cache
     // page, and write it to the cache
     // =============================================================
     $cache_image = array();
     $lock_array = array('pid' => $this->process_id, 'expire' => microtime(true) + $args['seconds']);
     foreach ($processed_result as $page => $data) {
         // FoxFire data classes always use an array as their storage variable and
         // store scalar values into it as keys. They never use a single scalar
         // variable as their storage object. So the line below is valid.
         $data['lock'] = $lock_array;
         $cache_image[$page] = $data;
     }
     unset($page, $data);
     try {
         $this->setMulti($args["namespace"], $cache_image, $offset);
     } catch (FOX_exception $child) {
         if ($child->data['numeric'] == 5) {
             throw new FOX_exception(array('numeric' => 4, 'text' => "Namespace was flushed by another PID during page locking sequence.", 'data' => $child->data['data'], 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         } elseif ($child->data['numeric'] == 4) {
             throw new FOX_exception(array('numeric' => 5, 'text' => "Namespace was locked by another PID during page locking sequence.", 'data' => $child->data['data'], 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null));
         } else {
             throw new FOX_exception(array('numeric' => 6, 'text' => "Error in descendent::setMulti()", 'data' => $cache_image, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child));
         }
     }
     return $processed_result;
 }
 function __construct($args = null)
 {
     // Process ID
     // ====================================================================================
     if (FOX_sUtil::keyExists('pid', $args)) {
         $this->process_id = $args['pid'];
     } else {
         global $fox;
         $this->process_id = $fox->process_id;
     }
     // Debug events
     // ====================================================================================
     if (FOX_sUtil::keyExists('debug_on', $args) && $args['debug_on'] == true) {
         $this->debug_on = true;
         if (FOX_sUtil::keyExists('debug_handler', $args)) {
             $this->debug_handler =& $args['debug_handler'];
         } else {
             global $fox;
             $this->debug_handler =& $fox->debug_handler;
         }
     } else {
         $this->debug_on = false;
     }
     // Trap manually setting sql_api to anything other than 'mysql' when using
     // the 'bind_wp' database handle mode, as a safety measure.
     // ======================================================================================
     if (FOX_sUtil::keyExists('sql_api', $args) && $args['sql_api'] != 'mysql') {
         // 'sql_api' is not 'mysql'
         if (!FOX_sUtil::keyExists('dbh_mode', $args) || FOX_sUtil::keyExists('dbh_mode', $args) && $args['dbh_mode'] == 'bind_wp') {
             // manually set to 'bind_wp'
             throw new FOX_exception(array('numeric' => 1, 'text' => "Attempting to use a sql_api other than 'mysql' when using the 'bind_wp' dbh_mode", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
         }
     }
     // Set default args
     // ====================================================================================
     global $table_prefix;
     // WordPress global
     $args_default = array('dbh_mode' => 'bind_fox', 'db_host' => DB_HOST, 'db_name' => DB_NAME, 'db_user' => DB_USER, 'db_pass' => DB_PASSWORD, 'charset' => defined('DB_CHARSET') ? DB_CHARSET : 'utf8', 'collate' => defined('DB_COLLATE') ? DB_COLLATE : 'utf8_general_ci', 'base_prefix' => $table_prefix, 'sql_api' => function_exists('mysqli_connect') ? 'mysqli' : 'mysql');
     $args = FOX_sUtil::parseArgs($args, $args_default);
     // Force sql_api to be 'mysql' when using the 'bind_wp' dbh mode
     if ($args['dbh_mode'] == 'bind_wp') {
         $args['sql_api'] = 'mysql';
     }
     $this->dbh_mode = $args['dbh_mode'];
     $this->db_host = $args['db_host'];
     $this->db_name = $args['db_name'];
     $this->db_user = $args['db_user'];
     $this->db_pass = $args['db_pass'];
     $this->charset = $args['charset'];
     $this->collate = $args['collate'];
     $this->base_prefix = $args['base_prefix'];
     // Trap invalid or inactive SQL API drivers
     // ====================================================================================
     if ($args['sql_api'] != 'mysql' && $args['sql_api'] != 'mysqli') {
         throw new FOX_exception(array('numeric' => 2, 'text' => "Invalid SQL API driver name", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
     if ($args['sql_api'] == 'mysql' && !function_exists('mysql_connect') || $args['sql_api'] == 'mysqli' && !function_exists('mysqli_connect')) {
         throw new FOX_exception(array('numeric' => 3, 'text' => "Requested SQL API isn't installed on this server", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
     }
     $dbh = false;
     // CASE 1: Bind to an existing database handle
     // ==========================================================================================
     if ($args['dbh_mode'] == 'bind_wp' || $args['dbh_mode'] == 'bind_fox') {
         // CASE 1A: 'bind_wp' mode
         // ---------------------------------------------------------------
         if ($args['dbh_mode'] == 'bind_wp') {
             if (FOX_sUtil::keyExists('dbh', $args)) {
                 $dbh =& $args['dbh'];
             } else {
                 global $wpdb;
                 $dbh =& $wpdb->dbh;
             }
             if (!$dbh) {
                 throw new FOX_exception(array('numeric' => 4, 'text' => "Attempting to bind to invalid wpdb database handle", 'data' => array('args' => $args, 'dbh' => $dbh), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
             }
         } else {
             if (FOX_sUtil::keyExists('dbh', $args)) {
                 $dbh =& $args['dbh'];
                 if (!$dbh) {
                     throw new FOX_exception(array('numeric' => 5, 'text' => "Attempting to bind to invalid fox database handle", 'data' => array('args' => $args, 'dbh' => $dbh), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null));
                 }
             } else {
                 global $fox;
                 // Note that if this is the first db call, the $fox->dbh global
                 // might not be initialized
                 if (property_exists($fox, 'dbh') && isset($fox->dbh)) {
                     $dbh =& $fox->dbh;
                 }
             }
         }
         // Bind to the database handle (if valid)
         // ---------------------------------------------------------------
         if ($dbh) {
             switch ($args["sql_api"]) {
                 case "mysql":
                     try {
                         $this->driver = new FOX_db_driver_mysql(array('dbh' => $dbh, 'charset' => $this->charset));
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 6, 'text' => "Error binding 'mysql' SQL driver to database handle", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     break;
                 case "mysqli":
                     try {
                         $this->driver = new FOX_db_driver_mysqli(array('dbh' => $dbh, 'charset' => $this->charset));
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 7, 'text' => "Error binding 'mysqli' SQL driver to database handle", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     break;
             }
         }
     }
     // CASE 2: Generate a new database handle
     // ==========================================================================================
     if (!$dbh) {
         switch ($args["sql_api"]) {
             case "mysql":
                 try {
                     $this->driver = new FOX_db_driver_mysql(array('db_host' => $this->db_host, 'db_name' => $this->db_name, 'db_user' => $this->db_user, 'db_pass' => $this->db_pass, 'charset' => $this->charset, 'collate' => $this->collate));
                 } catch (FOX_exception $child) {
                     // SECURITY: Overwrite the DB connection parameters to prevent them from
                     // being leaked if an exception is thrown on a production site, and for
                     // some reason error messages haven't been disabled
                     $args['db_user'] = '******';
                     $args['db_pass'] = '******';
                     $args['db_host'] = '[********]';
                     $args['db_name'] = '[********]';
                     throw new FOX_exception(array('numeric' => 8, 'text' => "Error in 'mysql' SQL driver", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 break;
             case "mysqli":
                 try {
                     $this->driver = new FOX_db_driver_mysqli(array('db_host' => $this->db_host, 'db_name' => $this->db_name, 'db_user' => $this->db_user, 'db_pass' => $this->db_pass, 'charset' => $this->charset, 'collate' => $this->collate));
                 } catch (FOX_exception $child) {
                     // Overwrite the DB user and pass parameters to prevent them from being leaked in the
                     // event of an exception
                     $args['db_user'] = '******';
                     $args['db_pass'] = '******';
                     throw new FOX_exception(array('numeric' => 9, 'text' => "Error in 'mysqli' SQL driver", 'data' => $args, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                 }
                 break;
         }
         // If we're generating a new dbh and we're in 'bind_fox' mode, initialize
         // the global $fox->dbh singleton with the handle
         if ($args['dbh_mode'] == 'bind_fox') {
             global $fox;
             $fox->dbh = $this->driver->dbh;
         }
     }
     // Allow the query builder and query runner to be injected
     // if needed for testing
     if (FOX_sUtil::keyExists('builder', $args)) {
         $this->builder =& $args['builder'];
     } else {
         $this->builder = new FOX_queryBuilder($this);
     }
     if (FOX_sUtil::keyExists('runner', $args)) {
         $this->runner =& $args['runner'];
     } else {
         $this->runner = new FOX_queryRunner($this);
     }
     // Because our local stats variables are bound by reference to the host
     // db instance, they automatically update
     $this->rows_affected =& $this->driver->rows_affected;
     $this->insert_id =& $this->driver->insert_id;
 }
 /**
  * Checks if a user is a member of a group
  *
  * @version 1.0
  * @since 1.0
  *
  * @param int $user_id | Single $user_id as int.
  * @param int $group_id | Single $group_id as int.
  * @return bool | True if user is a member of the group. False if not.
  */
 public function inGroup($user_id, $group_id)
 {
     global $fox;
     // If the user-group pair has an entry in the class cache array, return its value (true
     // if the user has the key, false if they don't)
     if (FOX_sUtil::keyExists($group_id, $this->cache[$user_id]["keys"])) {
         $result = $this->cache[$user_id]["keys"][$group_id];
     } elseif ($this->cache[$user_id]["all_cached"] == true) {
         $result = false;
     } else {
         $this->cache[$user_id] = $fox->cache->get("FOX_uGroupMember", $user_id);
         if (FOX_sUtil::keyExists($group_id, $this->cache[$user_id]["keys"])) {
             $result = $this->cache[$user_id]["keys"][$group_id];
         } elseif ($this->cache[$user_id]["all_cached"] == true) {
             $result = false;
         } else {
             try {
                 self::load($user_id, $group_id);
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 1, 'text' => "FOX_uGroupMember load exception", 'data' => array("user_id" => $user_id, "group_id" => $group_id), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             $result = $this->cache[$user_id]["keys"][$group_id];
         }
     }
     return $result;
 }
 /**
  * Fetches the ID's of one or more keys.
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $keys | one or more arrays, each descrrribing a key to retrieve
  *	=> ARR @param int '' | Array index
  *	    => VAL @param string $tree | tree name for key
  *	    => VAL @param string $branch | branch name for key
  *	    => VAL @param string/array $name | Single name as string. Multiple as array of strings.
  *
  * @param bool &$valid | True if all requested keys were found in the database. False if not.
  * @return bool/array | False on failure. Array of key ID's on success
  */
 public function getKeyIDMulti($keys, &$valid = null)
 {
     global $fox;
     $valid = true;
     // Build a list of keys we need to fetch from the db. Remember, the class cache
     // array is loaded from the persistent cache every time the class is instantiated,
     // so if the requested keys are not in the class cache at this point, they will
     // have to be fetched from the db
     // ================================================================================
     $missing_keys = array();
     foreach ($keys as $key) {
         $tree = $key["tree"];
         // This is easier to understand than using extract($key)
         $branch = $key["branch"];
         // because it shows the variable names we're using
         $name = $key["name"];
         if (!is_array($name)) {
             $name = array($name);
         }
         foreach ($name as $key_name) {
             if (!FOX_sUtil::keyExists($key_name, $this->cache["keys"][$tree][$branch])) {
                 $missing_keys[$tree][$branch][$key_name] = true;
             }
         }
     }
     unset($key, $tree, $branch, $name, $key_name);
     // Load any missing keys from the database
     // =======================================
     if (count($missing_keys) > 0) {
         $db = new FOX_db();
         $key_ids = array();
         foreach ($missing_keys as $tree_name => $branches) {
             foreach ($branches as $branch_name => $key_arrays) {
                 foreach ($key_arrays as $key_name => $fake_var) {
                     $args = array(array("col" => "tree", "op" => "=", "val" => $tree_name), array("col" => "branch", "op" => "=", "val" => $branch_name), array("col" => "name", "op" => "=", "val" => $key_name));
                     $columns = array("mode" => "include", "col" => array("key_id"));
                     $ctrl = array("format" => "var");
                     try {
                         $result = $db->runSelectQuery(self::$struct, $args, $columns, $ctrl);
                     } catch (FOX_exception $child) {
                         throw new FOX_exception(array('numeric' => 1, 'text' => "DB select exception", 'data' => array("args" => $args, "columns" => $columns, "ctrl" => $ctrl), 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
                     }
                     if ($result) {
                         $key_ids[$tree_name][$branch_name][$key_name] = $result;
                     } else {
                         // If the user has requested a non-existent key, set the
                         // valid flag to false to indicate a problem
                         $valid = false;
                     }
                 }
             }
         }
         unset($tree_name, $branches, $branch_name, $key_arrays, $key_names);
         // Update the cache
         // ================
         if (count($key_ids) > 0) {
             // Update the class cache from the persistent cache
             try {
                 self::loadCache();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 2, 'text' => "loadCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
             // Add keys fetched from the database to it
             foreach ($key_ids as $tree_name => $branches) {
                 foreach ($branches as $branch_name => $key_names) {
                     foreach ($key_names as $key_name => $val) {
                         $this->cache["keys"][$tree_name][$branch_name][$key_name] = $val;
                     }
                 }
             }
             unset($tree_name, $branches, $branch_name, $key_names, $key_name, $val);
             // Write the updated class cache array to the persistent cache
             try {
                 self::saveCache();
             } catch (FOX_exception $child) {
                 throw new FOX_exception(array('numeric' => 3, 'text' => "daveCache exception", 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child));
             }
         }
     }
     // Build the results array
     // =======================
     $result = array();
     foreach ($keys as $key) {
         $tree = $key["tree"];
         // This is easier to understand than using extract($key)
         $branch = $key["branch"];
         // because it shows the variable names we're using
         $name = $key["name"];
         $result[$tree][$branch][$name] = $this->cache["keys"][$tree][$branch][$name];
     }
     unset($key, $tree, $branch, $name);
     return $result;
 }
Example #30
0
 /**
  * Lofts a flat matrix into a trie structure, while hashing its keys
  *
  * @version 1.0
  * @since 1.0
  *
  * @param array $rows | Flat matrix
  * @param array $columns | Array of column names
  * @param array $ctrl | Control args
  * 
  * @return array | Exception on failure. Flattened trie structure on success.
  */
 public static function loftMatrixHash($rows, $columns, &$hash_table, $ctrl = null)
 {
     $ctrl_default = array('null_token' => '*');
     $ctrl = FOX_sUtil::parseArgs($ctrl, $ctrl_default);
     $result = array();
     // If a single column name is passed as a string, use the more efficient
     // direct assignment algorithm to build a 1 level trie
     if (!is_array($columns)) {
         foreach ($rows as $row) {
             // Convert row object into array
             foreach ($row as $tokens) {
                 $result[$hash_table->set($tokens[$columns])] = true;
             }
             unset($tokens);
         }
         unset($row);
     } else {
         foreach ($rows as $row) {
             // Since there is no functionality in PHP for creating a new array key
             // based on a name stored in a variable ( $$ variable variable syntax does
             // not work for multidimensional arrays), we have to build a string of PHP
             // code and use eval() to run it
             $eval_str = "\$result";
             foreach ($columns as $keyname) {
                 if (FOX_sUtil::keyExists($keyname, $row)) {
                     $eval_str .= '["' . $hash_table->set($row[$keyname]) . '"]';
                 } else {
                     $eval_str .= '["' . $ctrl['null_token'] . '"]';
                 }
             }
             unset($keyname);
             $eval_str .= " = true;";
             // Run the PHP string we have built
             eval($eval_str);
         }
         unset($row);
     }
     return $result;
 }