/** * Updates an existing node if the $tree-$branch-$node tuple already exists * in the db, or creates a new node if it doesn't. * * @version 1.0 * @since 1.0 * * @param string $tree | Tree name * @param string $branch | Branch name * @param string $node | Node name. * @param mixed $val | Value to store to node * @param string $filter | The filter function to validate data stored to node * @param array $ctrl | Filter function's control args * * @return int | Exception on failure. Number of db rows changed on success. */ public function writeNode($tree, $branch, $node, $val, $filter, $ctrl = null) { $db = new FOX_db(); $struct = $this->_struct(); // Trap empty keys, and keys that map to ints. We can't use ints as keys // or strings that map to keys due to the way PHP handles array indexing // in our trie structures. For example, if we had an array with the keys // "red", "green", "blue", and (string)"1"; PHP would *overwrite* the // "green" key when we tried to save key (string)"1", because it converts // it to (int)1 and addresses the array in indexed mode instead of associative // mode. There is no way to override this behavior in PHP. if ($tree == (int) $tree || !is_string($tree) || empty($tree) || ($branch == (int) $branch || !is_string($branch) || empty($branch)) || ($node == (int) $node || !is_string($node) || empty($node))) { throw new FOX_exception(array('numeric' => 1, 'text' => "Tree, Branch, and Node keys must be valid strings that do not map to ints", 'data' => array('tree' => $tree, 'branch' => $branch, 'node' => $node), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null)); } // Trap attempting to use a nonexistent filter $cls = new FOX_sanitize(); if (!method_exists($cls, $filter)) { throw new FOX_exception(array('numeric' => 2, 'text' => "Filter method doesn't exist", 'data' => array('tree' => $tree, 'branch' => $branch, 'node' => $node, 'filter' => $filter), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null)); } // Run node value through the specified filter // ===================================================== $filter_valid = null; $filter_error = null; // Passed by reference try { $processed_val = $cls->{$filter}($val, $ctrl, $filter_valid, $filter_error); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 3, 'text' => "Error in filter function", 'data' => array('filter' => $filter, 'val' => $val, 'ctrl' => $ctrl, 'filter_valid' => $filter_valid, 'filter_error' => $filter_error), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } if (!$filter_valid) { throw new FOX_exception(array('numeric' => 4, 'text' => "Filter function reports value data isn't valid", 'data' => array('filter' => $filter, 'val' => $val, 'ctrl' => $ctrl, 'filter_valid' => $filter_valid, 'filter_error' => $filter_error), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null)); } // Lock the cache // =========================================================== try { $cache_image = self::lockCache(); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 5, 'text' => "Error locking cache", 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } // Update the database // =========================================================== $args = array("tree" => $tree, "branch" => $branch, "node" => $node, "val" => $processed_val, "filter" => $filter, "ctrl" => $ctrl); $columns = null; try { $rows_changed = $db->runIndateQuery($struct, $args, $columns); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 6, 'text' => "Error writing to database", 'data' => array('args' => $args, 'columns' => $columns, 'ctrl' => $ctrl), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } // Rebuild the cache image // =========================================================== $cache_image["keys"][$tree][$branch][$node]["val"] = $processed_val; $cache_image["keys"][$tree][$branch][$node]["filter"] = $filter; $cache_image["keys"][$tree][$branch][$node]["ctrl"] = $ctrl; // 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' => 7, 'text' => "Cache write error", 'data' => array('cache_image' => $cache_image), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } // Update the class cache $this->cache = $cache_image; return (int) $rows_changed; }
/** * Creates or updates one or more keys. * * @version 1.0 * @since 1.0 * * @param array $data | Array of row arrays * => ARR @param int '' | Individual row array * => VAL @param int $user_id | user id * => VAL @param int $tree | tree id * => VAL @param int $branch | branch id * => VAL @param string $key | key name * => VAL @param bool/int/float/string/array/obj $val | key value * @return bool | False on failure. True on success. */ public function setKeyMulti($data) { global $rad; $db = new FOX_db(); //var_dump($data); $cache_update = array(); $insert_data = array(); // Process each row // =========================================================== foreach ($data as $row) { if (empty($row["user_id"])) { throw new FOX_exception(array('numeric' => 1, 'text' => "Empty user_id", 'data' => $row, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null)); } if (empty($row["tree"])) { throw new FOX_exception(array('numeric' => 2, 'text' => "Empty tree name", 'data' => $row, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null)); } if (empty($row["branch"])) { throw new FOX_exception(array('numeric' => 3, 'text' => "Empty branch name", 'data' => $row, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null)); } if (empty($row["key"])) { throw new FOX_exception(array('numeric' => 4, 'text' => "Empty key name", 'data' => $row, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => null)); } // Expand the key into a heirarchical array $update_data[$row["user_id"]][$row["tree"]][$row["branch"]][$row["key"]] = $row["val"]; } unset($row); // Load the persistent cache records for all the module_id's into the temp class cache array try { $update_cache = $rad->cache->getMulti("FOX_uData", array_keys($update_data)); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 5, 'text' => "Cache get error", 'data' => $update_data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child)); } // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ try { $begin_ok = $db->beginTransaction(); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 6, 'text' => "Couldn't initiate transaction", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child)); } foreach ($update_data as $user_id => $trees) { foreach ($trees as $tree => $branches) { foreach ($branches as $branch => $keys) { foreach ($keys as $key => $val) { $insert_data = array("user_id" => $user_id, "tree" => $tree, "branch" => $branch, "node" => $key, "val" => $val); try { $query_ok = $db->runIndateQuery(self::$struct, $insert_data, $columns = null); } catch (FOX_exception $child) { try { $db->rollbackTransaction(); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 8, 'text' => "rollbackTransaction", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child)); } throw new FOX_exception(array('numeric' => 7, 'text' => "Error while writing to the database", 'data' => $insert_data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child)); } // Overwrite the class cache array with the data set in the db query $update_cache[$user_id]["keys"][$tree][$branch][$key] = $val; } unset($key, $val); } unset($branch, $key); } unset($tree, $branches); } unset($user_id, $trees); try { $commit_ok = $db->commitTransaction(); } catch (FOX_exception $child) { // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ throw new FOX_exception(array('numeric' => 8, 'text' => "Error commiting transaction to database", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child)); } // Overwrite the persistent cache records with the temp class cache array items try { $cache_ok = $rad->cache->setMulti("FOX_uData", $update_cache); } catch (BPN_exception $child) { throw new FOX_exception(array('numeric' => 9, 'text' => "Cache set error", 'data' => $data, 'file' => __FILE__, 'line' => __LINE__, 'method' => __METHOD__, 'child' => $child)); } // Write the temp class cache array items to the class cache array foreach ($update_cache as $key => $val) { $this->cache[$key] = $val; } unset($key, $val); return true; }
/** * Creates or updates one or more keys. * * @version 1.0 * @since 1.0 * * @param array $data | Array of row arrays * => ARR @param int '' | Individual row array * => VAL @param int $module_id | module id * => VAL @param string $type_id | type_id id * => VAL @param string $branch_id | key type * => VAL @param int $key_id | key id * => VAL @param bool/int/float/string/array/obj $ctrl_val | key value * * @return int | Exception on failure. Int number of rows changed on success. */ public function setKeyMulti($data) { $db = new FOX_db(); $struct = $this->_struct(); $update_data = array(); $insert_data = array(); if (!is_array($data) || count($data) < 1) { throw new FOX_exception(array('numeric' => 1, 'text' => "Empty data array", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null)); } // Process each row // =========================================================== foreach ($data as $row) { if (empty($row["module_id"])) { throw new FOX_exception(array('numeric' => 2, 'text' => "Empty module_id", 'data' => $row, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null)); } if (empty($row["type_id"])) { throw new FOX_exception(array('numeric' => 4, 'text' => "Empty type_id name", 'data' => $row, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null)); } if (empty($row["branch_id"])) { throw new FOX_exception(array('numeric' => 5, 'text' => "Empty key type", 'data' => $row, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null)); } if (empty($row["key_id"])) { throw new FOX_exception(array('numeric' => 6, 'text' => "Empty key id", 'data' => $row, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => null)); } // Expand the key into a heirarchical array $update_data[$row["module_id"]][$row["type_id"]][$row["branch_id"]][$row["key_id"]] = $row["ctrl_val"]; } unset($row); // CASE 1: Transactions aren't required. // =========================================================== if (count($data) == 1) { $row = $data[0]; // Lock and load the cache key try { $page_image = self::lockCachePage($row["module_id"]); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 7, 'text' => "Cache lock error", 'data' => $row, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } try { $rows_changed = $db->runIndateQuery($struct, $row, $columns = null); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 8, 'text' => "Error while writing to the database", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } // Update the key's value in the cache page $page_image["keys"][$row["type_id"]][$row["branch_id"]][$row["key_id"]] = $row["ctrl_val"]; // Write the updated image back to the cache (which also releases our lock) try { self::writeCachePage(array($row["module_id"] => $page_image)); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 9, 'text' => "Cache set error", 'data' => $this->cache[$row["module_id"]], 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } // If the persistent cache write was successful, write the new page image // to the class cache $this->cache[$row["module_id"]] = $page_image; return (int) $rows_changed; } else { // Lock and load the persistent cache records for all the module_id's // into the temp class cache array try { $update_cache = self::lockCachePage(array_keys($update_data)); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 10, 'text' => "Cache get error", 'data' => $update_data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } // @@@@@@ BEGIN TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ try { $db->beginTransaction(); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 11, 'text' => "Couldn't initiate transaction", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } $rows_changed = 0; foreach ($update_data as $module_id => $type_ids) { foreach ($type_ids as $type_id => $branch_ids) { foreach ($branch_ids as $branch_id => $key_ids) { foreach ($key_ids as $key_id => $ctrl_val) { $insert_data = array("module_id" => $module_id, "type_id" => $type_id, "branch_id" => $branch_id, "key_id" => $key_id, "ctrl_val" => $ctrl_val); try { $rows_changed += (int) $db->runIndateQuery($struct, $insert_data, $columns = null); } catch (FOX_exception $child) { try { $db->rollbackTransaction(); } catch (FOX_exception $child_2) { throw new FOX_exception(array('numeric' => 12, 'text' => "Error while writing to the database. Error rolling back.", 'data' => array('rollback_exception' => $child_2), 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } throw new FOX_exception(array('numeric' => 13, 'text' => "Error while writing to the database. Successful rollback.", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } // Overwrite the temp class cache array with the data we set in the db query $update_cache[$module_id]["keys"][$type_id][$branch_id][$key_id] = $ctrl_val; } unset($key_id, $ctrl_val); } unset($branch_id, $key_ids); } unset($type_id, $branch_ids); } unset($module_id, $type_ids); // @@@@@@ END TRANSACTION @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ try { $db->commitTransaction(); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 14, 'text' => "Error commiting transaction to database", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } // Overwrite the persistent cache records with the temp class cache array items try { self::writeCachePage($update_cache); } catch (FOX_exception $child) { throw new FOX_exception(array('numeric' => 15, 'text' => "Cache set error", 'data' => $data, 'file' => __FILE__, 'class' => __CLASS__, 'function' => __FUNCTION__, 'line' => __LINE__, 'child' => $child)); } // Write the temp class cache array items to the class cache array foreach ($update_cache as $module_id => $module_data) { $this->cache[$module_id] = $module_data; } unset($module_id, $module_data); return (int) $rows_changed; } }