diff options
Diffstat (limited to 'engine/lib/entities.php')
| -rw-r--r-- | engine/lib/entities.php | 139 | 
1 files changed, 108 insertions, 31 deletions
| diff --git a/engine/lib/entities.php b/engine/lib/entities.php index 4875b2c2f..a50567d9f 100644 --- a/engine/lib/entities.php +++ b/engine/lib/entities.php @@ -30,7 +30,7 @@ $SUBTYPE_CACHE = NULL;   *   * @param int $guid The entity guid   * - * @return void + * @return null   * @access private   */  function invalidate_cache_for_entity($guid) { @@ -39,6 +39,8 @@ function invalidate_cache_for_entity($guid) {  	$guid = (int)$guid;  	unset($ENTITY_CACHE[$guid]); + +	elgg_get_metadata_cache()->clear($guid);  }  /** @@ -48,14 +50,27 @@ function invalidate_cache_for_entity($guid) {   *   * @param ElggEntity $entity Entity to cache   * - * @return void + * @return null   * @see retrieve_cached_entity()   * @see invalidate_cache_for_entity()   * @access private + * TODO(evan): Use an ElggCache object   */  function cache_entity(ElggEntity $entity) {  	global $ENTITY_CACHE; +	// Don't cache entities while access control is off, otherwise they could be +	// exposed to users who shouldn't see them when control is re-enabled. +	if (elgg_get_ignore_access()) { +		return; +	} + +	// Don't store too many or we'll have memory problems +	// TODO(evan): Pick a less arbitrary limit +	if (count($ENTITY_CACHE) > 256) { +		unset($ENTITY_CACHE[array_rand($ENTITY_CACHE)]); +	} +  	$ENTITY_CACHE[$entity->guid] = $entity;  } @@ -64,7 +79,7 @@ function cache_entity(ElggEntity $entity) {   *   * @param int $guid The guid   * - * @return void + * @return ElggEntity|bool false if entity not cached, or not fully loaded   * @see cache_entity()   * @see invalidate_cache_for_entity()   * @access private @@ -313,6 +328,10 @@ function add_subtype($type, $subtype, $class = "") {  /**   * Removes a registered ElggEntity type, subtype, and classname.   * + * @warning You do not want to use this function. If you want to unregister + * a class for a subtype, use update_subtype(). Using this function will + * permanently orphan all the objects created with the specified subtype. + *   * @param string $type    Type   * @param string $subtype Subtype   * @@ -331,7 +350,7 @@ function remove_subtype($type, $subtype) {  }  /** - * Update a registered ElggEntity type, subtype, and classname + * Update a registered ElggEntity type, subtype, and class name   *   * @param string $type    Type   * @param string $subtype Subtype @@ -340,7 +359,7 @@ function remove_subtype($type, $subtype) {   * @return bool   */  function update_subtype($type, $subtype, $class = '') { -	global $CONFIG; +	global $CONFIG, $SUBTYPE_CACHE;  	if (!$id = get_subtype_id($type, $subtype)) {  		return FALSE; @@ -348,10 +367,16 @@ function update_subtype($type, $subtype, $class = '') {  	$type = sanitise_string($type);  	$subtype = sanitise_string($subtype); -	return update_data("UPDATE {$CONFIG->dbprefix}entity_subtypes +	$result = update_data("UPDATE {$CONFIG->dbprefix}entity_subtypes  		SET type = '$type', subtype = '$subtype', class = '$class'  		WHERE id = $id  	"); + +	if ($result && isset($SUBTYPE_CACHE[$id])) { +		$SUBTYPE_CACHE[$id]->class = $class; +	} + +	return $result;  }  /** @@ -583,12 +608,14 @@ function get_entity_as_row($guid) {   *   * @param stdClass $row The row of the entry in the entities table.   * - * @return object|false + * @return ElggEntity|false   * @link http://docs.elgg.org/DataModel/Entities   * @see get_entity_as_row()   * @see add_subtype()   * @see get_entity()   * @access private + * + * @throws ClassException|InstallationException   */  function entity_row_to_elggstar($row) {  	if (!($row instanceof stdClass)) { @@ -666,29 +693,53 @@ function entity_row_to_elggstar($row) {   * @link http://docs.elgg.org/DataModel/Entities   */  function get_entity($guid) { -	static $newentity_cache; -	$new_entity = false; +	// This should not be a static local var. Notice that cache writing occurs in a completely +	// different instance outside this function. +	// @todo We need a single Memcache instance with a shared pool of namespace wrappers. This function would pull an instance from the pool. +	static $shared_cache;  	// We could also use: if (!(int) $guid) { return FALSE },   	// but that evaluates to a false positive for $guid = TRUE.  	// This is a bit slower, but more thorough.  	if (!is_numeric($guid) || $guid === 0 || $guid === '0') { -		return FALSE; +		return false; +	} +	 +	// Check local cache first +	$new_entity = retrieve_cached_entity($guid); +	if ($new_entity) { +		return $new_entity;  	} -	if ((!$newentity_cache) && (is_memcache_available())) { -		$newentity_cache = new ElggMemcache('new_entity_cache'); +	// Check shared memory cache, if available +	if (null === $shared_cache) { +		if (is_memcache_available()) { +			$shared_cache = new ElggMemcache('new_entity_cache'); +		} else { +			$shared_cache = false; +		}  	} -	if ($newentity_cache) { -		$new_entity = $newentity_cache->load($guid); +	// until ACLs in memcache, DB query is required to determine access +	$entity_row = get_entity_as_row($guid); +	if (!$entity_row) { +		return false;  	} -	if ($new_entity) { -		return $new_entity; +	if ($shared_cache) { +		$cached_entity = $shared_cache->load($guid); +		// @todo store ACLs in memcache http://trac.elgg.org/ticket/3018#comment:3 +		if ($cached_entity) { +			// @todo use ACL and cached entity access_id to determine if user can see it +			return $cached_entity; +		}  	} -	return entity_row_to_elggstar(get_entity_as_row($guid)); +	$new_entity = entity_row_to_elggstar($entity_row); +	if ($new_entity) { +		cache_entity($new_entity); +	} +	return $new_entity;  }  /** @@ -915,11 +966,11 @@ function elgg_get_entities(array $options = array()) {  	}  	if (!$options['count']) { -		if ($options['group_by'] = sanitise_string($options['group_by'])) { +		if ($options['group_by']) {  			$query .= " GROUP BY {$options['group_by']}";  		} -		if ($options['order_by'] = sanitise_string($options['order_by'])) { +		if ($options['order_by']) {  			$query .= " ORDER BY {$options['order_by']}";  		} @@ -930,6 +981,26 @@ function elgg_get_entities(array $options = array()) {  		}  		$dt = get_data($query, $options['callback']); +		if ($dt) { +			// populate entity and metadata caches +			$guids = array(); +			foreach ($dt as $item) { +				// A custom callback could result in items that aren't ElggEntity's, so check for them +				if ($item instanceof ElggEntity) { +					cache_entity($item); +					// plugins usually have only settings +					if (!$item instanceof ElggPlugin) { +						$guids[] = $item->guid; +					} +				} +			} +			// @todo Without this, recursive delete fails. See #4568 +			reset($dt); + +			if ($guids) { +				elgg_get_metadata_cache()->populateFromEntities($guids); +			} +		}  		return $dt;  	} else {  		$total = get_data_row($query); @@ -1103,7 +1174,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair   *                           best to provide in table.column format.   * @param NULL|array $guids  Array of GUIDs.   * - * @return false|str + * @return false|string   * @since 1.8.0   * @access private   */ @@ -1152,7 +1223,7 @@ function elgg_get_guid_based_where_sql($column, $guids) {   * @param NULL|int $time_updated_upper Time updated upper limit   * @param NULL|int $time_updated_lower Time updated lower limit   * - * @return FALSE|str FALSE on fail, string on success. + * @return FALSE|string FALSE on fail, string on success.   * @since 1.7.0   * @access private   */ @@ -1254,7 +1325,7 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti   * @param string $subtype        The subtype of entity   * @param int    $container_guid The container GUID that the entinties belong to   * @param int    $site_guid      The site GUID - * @param str    $order_by       Order_by SQL order by clause + * @param string $order_by       Order_by SQL order by clause   *   * @return array|false Either an array months as YYYYMM, or false on failure   */ @@ -1402,6 +1473,7 @@ function disable_entity($guid, $reason = "", $recursive = true) {  				$entity->disableMetadata();  				$entity->disableAnnotations(); +				invalidate_cache_for_entity($guid);  				$res = update_data("UPDATE {$CONFIG->dbprefix}entities  					SET enabled = 'no' @@ -1452,6 +1524,7 @@ function enable_entity($guid, $recursive = true) {  						'relationship' => 'disabled_with',  						'relationship_guid' => $entity->guid,  						'inverse_relationship' => true, +						'limit' => 0,  					));  					foreach ($disabled_with_it as $e) { @@ -1597,7 +1670,7 @@ function delete_entity($guid, $recursive = true) {   * @param string $returnvalue Return value from previous hook   * @param array  $params      The parameters, passed 'guid' and 'varname'   * - * @return void + * @return ElggMetadata|null   * @elgg_plugin_hook_handler volatile metadata   * @todo investigate more.   * @access private @@ -1642,6 +1715,8 @@ function volatile_data_export_plugin_hook($hook, $entity_type, $returnvalue, $pa   * @elgg_event_handler export all   * @return mixed   * @access private + * + * @throws InvalidParameterException|InvalidClassException   */  function export_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {  	// Sanity check values @@ -1684,6 +1759,8 @@ function export_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {   * @return ElggEntity the unsaved entity which should be populated by items.   * @todo Remove this.   * @access private + * + * @throws ClassException|InstallationException|ImportException   */  function oddentity_to_elggentity(ODDEntity $element) {  	$class = $element->getAttribute('class'); @@ -1755,6 +1832,8 @@ function oddentity_to_elggentity(ODDEntity $element) {   * @elgg_plugin_hook_handler import all   * @todo document   * @access private + * + * @throws ImportException   */  function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {  	$element = $params['element']; @@ -1767,7 +1846,7 @@ function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {  		if ($tmp) {  			// Make sure its saved  			if (!$tmp->save()) { -				elgg_echo('ImportException:ProblemSaving', array($element->getAttribute('uuid'))); +				$msg = elgg_echo('ImportException:ProblemSaving', array($element->getAttribute('uuid')));  				throw new ImportException($msg);  			} @@ -1801,8 +1880,6 @@ function import_entity_plugin_hook($hook, $entity_type, $returnvalue, $params) {   * @link http://docs.elgg.org/Entities/AccessControl   */  function can_edit_entity($entity_guid, $user_guid = 0) { -	global $CONFIG; -  	$user_guid = (int)$user_guid;  	$user = get_entity($user_guid);  	if (!$user) { @@ -1926,7 +2003,7 @@ function get_entity_url($entity_guid) {   * @param string $entity_subtype The entity subtype   * @param string $function_name  The function to register   * - * @return true|false Depending on success + * @return bool Depending on success   * @see get_entity_url()   * @see ElggEntity::getURL()   * @since 1.8.0 @@ -1962,7 +2039,7 @@ function elgg_register_entity_url_handler($entity_type, $entity_subtype, $functi   * @param string $type    The type of entity (object, site, user, group)   * @param string $subtype The subtype to register (may be blank)   * - * @return true|false Depending on success + * @return bool Depending on success   * @see get_registered_entity_types()   * @link http://docs.elgg.org/Search   * @link http://docs.elgg.org/Tutorials/Search @@ -1999,7 +2076,7 @@ function elgg_register_entity_type($type, $subtype = null) {   * @param string $type    The type of entity (object, site, user, group)   * @param string $subtype The subtype to register (may be blank)   * - * @return true|false Depending on success + * @return bool Depending on success   * @see elgg_register_entity_type()   */  function unregister_entity_type($type, $subtype) { @@ -2066,7 +2143,7 @@ function get_registered_entity_types($type = null) {   * @param string $type    The type of entity (object, site, user, group)   * @param string $subtype The subtype (may be blank)   * - * @return true|false Depending on whether or not the type has been registered + * @return bool Depending on whether or not the type has been registered   */  function is_registered_entity_type($type, $subtype = null) {  	global $CONFIG; @@ -2266,7 +2343,7 @@ function entities_gc() {  /**   * Runs unit tests for the entity objects.   * - * @param sting  $hook   unit_test + * @param string  $hook   unit_test   * @param string $type   system   * @param mixed  $value  Array of tests   * @param mixed  $params Params | 
