aboutsummaryrefslogtreecommitdiff
path: root/engine/lib/entities.php
diff options
context:
space:
mode:
Diffstat (limited to 'engine/lib/entities.php')
-rw-r--r--engine/lib/entities.php139
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