From 2b08c9d03b803c8227a99a4c3fce3ecb84ed6fc4 Mon Sep 17 00:00:00 2001 From: Brett Profitt Date: Tue, 18 Dec 2012 15:55:42 -0500 Subject: Fixes #3940. Cleaned up caching for access functions. Access caches are now cleared on ignore access change. --- engine/classes/ElggStaticVariableCache.php | 4 +- engine/lib/access.php | 153 ++++++++++++++++++----------- engine/tests/api/access_collections.php | 22 +++++ 3 files changed, 120 insertions(+), 59 deletions(-) (limited to 'engine') diff --git a/engine/classes/ElggStaticVariableCache.php b/engine/classes/ElggStaticVariableCache.php index 787d35a32..17d849400 100644 --- a/engine/classes/ElggStaticVariableCache.php +++ b/engine/classes/ElggStaticVariableCache.php @@ -21,8 +21,8 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache { * This function creates a variable cache in a static variable in * memory, optionally with a given namespace (to avoid overlap). * - * @param string $namespace The namespace for this cache to write to - * note, namespaces of the same name are shared! + * @param string $namespace The namespace for this cache to write to. + * @note namespaces of the same name are shared! */ function __construct($namespace = 'default') { $this->setNamespace($namespace); diff --git a/engine/lib/access.php b/engine/lib/access.php index 3b2b7aeaa..f7d3bf7ea 100644 --- a/engine/lib/access.php +++ b/engine/lib/access.php @@ -11,6 +11,26 @@ * @link http://docs.elgg.org/Access */ +/** + * Return an ElggCache static variable cache for the access caches + * + * @staticvar ElggStaticVariableCache $access_cache + * @return \ElggStaticVariableCache + * @access private + */ +function _elgg_get_access_cache() { + /** + * A default filestore cache using the dataroot. + */ + static $access_cache; + + if (!$access_cache) { + $access_cache = new ElggStaticVariableCache('access'); + } + + return $access_cache; +} + /** * Return a string of access_ids for $user_id appropriate for inserting into an SQL IN clause. * @@ -29,10 +49,10 @@ */ function get_access_list($user_id = 0, $site_id = 0, $flush = false) { global $CONFIG, $init_finished; - static $access_list; - - if (!isset($access_list)) { - $access_list = array(); + $cache = _elgg_get_access_cache(); + + if ($flush) { + $cache->clear(); } if ($user_id == 0) { @@ -45,20 +65,20 @@ function get_access_list($user_id = 0, $site_id = 0, $flush = false) { $user_id = (int) $user_id; $site_id = (int) $site_id; - if (isset($access_list[$user_id]) && $flush == false) { - return $access_list[$user_id]; - } + $hash = $user_id . $site_id . 'get_access_list'; - $access = "(" . implode(",", get_access_array($user_id, $site_id, $flush)) . ")"; + if ($cache[$hash]) { + return $cache[$hash]; + } + + $access_array = get_access_array($user_id, $site_id, $flush); + $access = "(" . implode(",", $access_array) . ")"; - // only cache if done with init and access is enabled (unless admin user) - // session is loaded before init is finished, so don't need to check for user session - if ($init_finished && (elgg_is_admin_logged_in() || !elgg_get_ignore_access())) { - $access_list[$user_id] = $access; - return $access_list[$user_id]; - } else { - return $access; + if ($init_finished) { + $cache[$hash] = $access; } + + return $access; } /** @@ -86,9 +106,11 @@ function get_access_list($user_id = 0, $site_id = 0, $flush = false) { function get_access_array($user_id = 0, $site_id = 0, $flush = false) { global $CONFIG, $init_finished; - // @todo everything from the db is cached. - // this cache might be redundant. But db cache is flushed on every db write. - static $access_array = array(); + $cache = _elgg_get_access_cache(); + + if ($flush) { + $cache->clear(); + } if ($user_id == 0) { $user_id = elgg_get_logged_in_user_guid(); @@ -101,35 +123,41 @@ function get_access_array($user_id = 0, $site_id = 0, $flush = false) { $user_id = (int) $user_id; $site_id = (int) $site_id; - if (empty($access_array[$user_id]) || $flush == true) { - $tmp_access_array = array(ACCESS_PUBLIC); + $hash = $user_id . $site_id . 'get_access_array'; + + if ($cache[$hash]) { + $access_array = $cache[$hash]; + } else { + $access_array = array(ACCESS_PUBLIC); // The following can only return sensible data if the user is logged in. if (elgg_is_logged_in()) { - $tmp_access_array[] = ACCESS_LOGGED_IN; + $access_array[] = ACCESS_LOGGED_IN; // Get ACL memberships $query = "SELECT am.access_collection_id" . " FROM {$CONFIG->dbprefix}access_collection_membership am" . " LEFT JOIN {$CONFIG->dbprefix}access_collections ag ON ag.id = am.access_collection_id" - . " WHERE am.user_guid = {$user_id} AND (ag.site_guid = {$site_id} OR ag.site_guid = 0)"; + . " WHERE am.user_guid = $user_id AND (ag.site_guid = $site_id OR ag.site_guid = 0)"; - if ($collections = get_data($query)) { + $collections = get_data($query); + if ($collections) { foreach ($collections as $collection) { if (!empty($collection->access_collection_id)) { - $tmp_access_array[] = (int)$collection->access_collection_id; + $access_array[] = (int)$collection->access_collection_id; } } } // Get ACLs owned. $query = "SELECT ag.id FROM {$CONFIG->dbprefix}access_collections ag "; - $query .= "WHERE ag.owner_guid = {$user_id} AND (ag.site_guid = {$site_id} OR ag.site_guid = 0)"; + $query .= "WHERE ag.owner_guid = $user_id AND (ag.site_guid = $site_id OR ag.site_guid = 0)"; - if ($collections = get_data($query)) { + $collections = get_data($query); + if ($collections) { foreach ($collections as $collection) { if (!empty($collection->id)) { - $tmp_access_array[] = (int)$collection->id; + $access_array[] = (int)$collection->id; } } } @@ -137,21 +165,21 @@ function get_access_array($user_id = 0, $site_id = 0, $flush = false) { $ignore_access = elgg_check_access_overrides($user_id); if ($ignore_access == true) { - $tmp_access_array[] = ACCESS_PRIVATE; + $access_array[] = ACCESS_PRIVATE; } + } - // only cache if done with init and access is enabled (unless admin user) - // session is loaded before init is finished, so don't need to check for user session - if ($init_finished && (elgg_is_admin_logged_in() || !elgg_get_ignore_access())) { - $access_array[$user_id] = $tmp_access_array; - } + if ($init_finished) { + $cache[$hash] = $access_array; } - } else { - $tmp_access_array = $access_array[$user_id]; } - $options = array('user_id' => $user_id, 'site_id' => $site_id); - return elgg_trigger_plugin_hook('access:collections:read', 'user', $options, $tmp_access_array); + $options = array( + 'user_id' => $user_id, + 'site_id' => $site_id + ); + + return elgg_trigger_plugin_hook('access:collections:read', 'user', $options, $access_array); } /** @@ -397,9 +425,12 @@ function has_access_to_entity($entity, $user = null) { * @link http://docs.elgg.org/Access */ function get_write_access_array($user_id = 0, $site_id = 0, $flush = false) { - global $CONFIG; - //@todo this is probably not needed since caching happens at the DB level. - static $access_array; + global $CONFIG, $init_finished; + $cache = _elgg_get_access_cache(); + + if ($flush) { + $cache->clear(); + } if ($user_id == 0) { $user_id = elgg_get_logged_in_user_guid(); @@ -412,37 +443,41 @@ function get_write_access_array($user_id = 0, $site_id = 0, $flush = false) { $user_id = (int) $user_id; $site_id = (int) $site_id; - if (empty($access_array[$user_id]) || $flush == true) { - $query = "SELECT ag.* FROM {$CONFIG->dbprefix}access_collections ag "; - $query .= " WHERE (ag.site_guid = {$site_id} OR ag.site_guid = 0)"; - $query .= " AND (ag.owner_guid = {$user_id})"; - // ACCESS_PRIVATE through ACCESS_PUBLIC take 0 through 2 - // @todo this AND clause is unnecessary because of id starts at 3 for table - $query .= " AND ag.id >= 3"; + $hash = $user_id . $site_id . 'get_write_access_array'; - $tmp_access_array = array( + if ($cache[$hash]) { + $access_array = $cache[$hash]; + } else { + // @todo is there such a thing as public write access? + $access_array = array( ACCESS_PRIVATE => elgg_echo("PRIVATE"), ACCESS_FRIENDS => elgg_echo("access:friends:label"), ACCESS_LOGGED_IN => elgg_echo("LOGGED_IN"), ACCESS_PUBLIC => elgg_echo("PUBLIC") ); + + $query = "SELECT ag.* FROM {$CONFIG->dbprefix}access_collections ag "; + $query .= " WHERE (ag.site_guid = $site_id OR ag.site_guid = 0)"; + $query .= " AND (ag.owner_guid = $user_id)"; + $collections = get_data($query); if ($collections) { foreach ($collections as $collection) { - $tmp_access_array[$collection->id] = $collection->name; + $access_array[$collection->id] = $collection->name; } } - $access_array[$user_id] = $tmp_access_array; - } else { - $tmp_access_array = $access_array[$user_id]; + if ($init_finished) { + $cache[$hash] = $access_array; + } } - $options = array('user_id' => $user_id, 'site_id' => $site_id); - $tmp_access_array = elgg_trigger_plugin_hook('access:collections:write', 'user', - $options, $tmp_access_array); - - return $tmp_access_array; + $options = array( + 'user_id' => $user_id, + 'site_id' => $site_id + ); + return elgg_trigger_plugin_hook('access:collections:write', 'user', + $options, $access_array); } /** @@ -871,6 +906,8 @@ function get_readable_access_level($entity_access_id) { * @tip Use this to access entities in automated scripts * when no user is logged in. * + * @note This clears the access cache. + * * @warning This will not show disabled entities. * Use {@link access_show_hidden_entities()} to access disabled entities. * @@ -882,6 +919,8 @@ function get_readable_access_level($entity_access_id) { * @see elgg_get_ignore_access() */ function elgg_set_ignore_access($ignore = true) { + $cache = _elgg_get_access_cache(); + $cache->clear(); $elgg_access = elgg_get_access_object(); return $elgg_access->setIgnoreAccess($ignore); } diff --git a/engine/tests/api/access_collections.php b/engine/tests/api/access_collections.php index bea995a6e..ebcd7d318 100644 --- a/engine/tests/api/access_collections.php +++ b/engine/tests/api/access_collections.php @@ -268,4 +268,26 @@ class ElggCoreAccessCollectionsTest extends ElggCoreUnitTest { $group->delete(); } + + public function testAccessCaching() { + // create a new user to check against + $user = new ElggUser(); + $user->username = 'access_test_user'; + $user->save(); + + foreach (array('get_access_list', 'get_access_array') as $func) { + $cache = _elgg_get_access_cache(); + $cache->clear(); + + // admin users run tests, so disable access + elgg_set_ignore_access(true); + $access = $func($user->getGUID()); + + elgg_set_ignore_access(false); + $access2 = $func($user->getGUID()); + $this->assertNotEqual($access, $access2, "Access test for $func"); + } + + $user->delete(); + } } -- cgit v1.2.3