/** * 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; } }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }