Issues (2473)

Branch: master

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

engine/lib/entities.php (7 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

Code
1
<?php
2
/**
3
 * Procedural code for creating, loading, and modifying \ElggEntity objects.
4
 *
5
 * @package Elgg.Core
6
 * @subpackage DataModel.Entities
7
 */
8
9
/**
10
 * Cache entities in memory once loaded.
11
 *
12
 * @global \ElggEntity[] $ENTITY_CACHE
13
 * @access private
14
 */
15
global $ENTITY_CACHE;
16
$ENTITY_CACHE = array();
17
18
/**
19
 * GUIDs of entities banned from the entity cache (during this request)
20
 *
21
 * @global array $ENTITY_CACHE_DISABLED_GUIDS
22
 * @access private
23
 */
24
global $ENTITY_CACHE_DISABLED_GUIDS;
25
$ENTITY_CACHE_DISABLED_GUIDS = array();
26
27
/**
28
 * Remove this entity from the entity cache and make sure it is not re-added
29
 *
30
 * @param int $guid The entity guid
31
 *
32
 * @access private
33
 * @todo this is a workaround until #5604 can be implemented
34
 */
35
function _elgg_disable_caching_for_entity($guid) {
36
	global $ENTITY_CACHE_DISABLED_GUIDS;
37
38
	_elgg_invalidate_cache_for_entity($guid);
39
	$ENTITY_CACHE_DISABLED_GUIDS[$guid] = true;
40
}
41
42
/**
43
 * Allow this entity to be stored in the entity cache
44
 *
45
 * @param int $guid The entity guid
46
 *
47
 * @access private
48
 */
49
function _elgg_enable_caching_for_entity($guid) {
50
	global $ENTITY_CACHE_DISABLED_GUIDS;
51
52
	unset($ENTITY_CACHE_DISABLED_GUIDS[$guid]);
53
}
54
55
/**
56
 * Invalidate this class's entry in the cache.
57
 *
58
 * @param int $guid The entity guid
59
 *
60
 * @return void
61
 * @access private
62
 */
63
function _elgg_invalidate_cache_for_entity($guid) {
64
	global $ENTITY_CACHE;
65
66
	$guid = (int)$guid;
67
68
	if (isset($ENTITY_CACHE[$guid])) {
69
		unset($ENTITY_CACHE[$guid]);
70
71
		// Purge separate metadata cache. Original idea was to do in entity destructor, but that would
72
		// have caused a bunch of unnecessary purges at every shutdown. Doing it this way we have no way
73
		// to know that the expunged entity will be GCed (might be another reference living), but that's
74
		// OK; the metadata will reload if necessary.
75
		_elgg_services()->metadataCache->clear($guid);
76
	}
77
}
78
79
/**
80
 * Cache an entity.
81
 *
82
 * Stores an entity in $ENTITY_CACHE;
83
 *
84
 * @param \ElggEntity $entity Entity to cache
85
 *
86
 * @return void
87
 * @see _elgg_retrieve_cached_entity()
88
 * @see _elgg_invalidate_cache_for_entity()
89
 * @access private
90
 * @todo Use an \ElggCache object
91
 */
92
function _elgg_cache_entity(\ElggEntity $entity) {
93
	global $ENTITY_CACHE, $ENTITY_CACHE_DISABLED_GUIDS;
94
95
	// Don't cache non-plugin entities while access control is off, otherwise they could be
96
	// exposed to users who shouldn't see them when control is re-enabled.
97
	if (!($entity instanceof \ElggPlugin) && elgg_get_ignore_access()) {
98
		return;
99
	}
100
101
	$guid = $entity->getGUID();
102
	if (isset($ENTITY_CACHE_DISABLED_GUIDS[$guid])) {
103
		return;
104
	}
105
106
	// Don't store too many or we'll have memory problems
107
	// @todo Pick a less arbitrary limit
108
	if (count($ENTITY_CACHE) > 256) {
109
		_elgg_invalidate_cache_for_entity(array_rand($ENTITY_CACHE));
110
	}
111
112
	$ENTITY_CACHE[$guid] = $entity;
113
}
114
115
/**
116
 * Retrieve a entity from the cache.
117
 *
118
 * @param int $guid The guid
119
 *
120
 * @return \ElggEntity|bool false if entity not cached, or not fully loaded
121
 * @see _elgg_cache_entity()
122
 * @see _elgg_invalidate_cache_for_entity()
123
 * @access private
124
 */
125
function _elgg_retrieve_cached_entity($guid) {
126
	global $ENTITY_CACHE;
127
128
	if (isset($ENTITY_CACHE[$guid])) {
129
		if ($ENTITY_CACHE[$guid]->isFullyLoaded()) {
130
			return $ENTITY_CACHE[$guid];
131
		}
132
	}
133
134
	return false;
135
}
136
137
/**
138
 * Return the id for a given subtype.
139
 *
140
 * \ElggEntity objects have a type and a subtype.  Subtypes
141
 * are defined upon creation and cannot be changed.
142
 *
143
 * Plugin authors generally don't need to use this function
144
 * unless writing their own SQL queries.  Use {@link \ElggEntity::getSubtype()}
145
 * to return the string subtype.
146
 *
147
 * @internal Subtypes are stored in the entity_subtypes table.  There is a foreign
148
 * key in the entities table.
149
 *
150
 * @param string $type    Type
151
 * @param string $subtype Subtype
152
 *
153
 * @return int Subtype ID
154
 * @see get_subtype_from_id()
155
 * @access private
156
 */
157
function get_subtype_id($type, $subtype) {
158
	return _elgg_services()->subtypeTable->getId($type, $subtype);
159
}
160
161
/**
162
 * Gets the denormalized string for a given subtype ID.
163
 *
164
 * @param int $subtype_id Subtype ID from database
165
 * @return string|false Subtype name, false if subtype not found
166
 * @see get_subtype_id()
167
 * @access private
168
 */
169
function get_subtype_from_id($subtype_id) {
170
	return _elgg_services()->subtypeTable->getSubtype($subtype_id);
171
}
172
173
/**
174
 * Retrieve subtype from the cache.
175
 *
176
 * @param string $type
177
 * @param string $subtype
178
 * @return \stdClass|null
179
 *
180
 * @access private
181
 */
182
function _elgg_retrieve_cached_subtype($type, $subtype) {
183
	return _elgg_services()->subtypeTable->retrieveFromCache($type, $subtype);
184
}
185
186
/**
187
 * Fetch all suptypes from DB to local cache.
188
 *
189
 * @access private
190
 */
191
function _elgg_populate_subtype_cache() {
192
	return _elgg_services()->subtypeTable->populateCache();
193
}
194
195
/**
196
 * Return the class name for a registered type and subtype.
197
 *
198
 * Entities can be registered to always be loaded as a certain class
199
 * with add_subtype() or update_subtype(). This function returns the class
200
 * name if found and null if not.
201
 *
202
 * @param string $type    The type
203
 * @param string $subtype The subtype
204
 *
205
 * @return string|null a class name or null
206
 * @see get_subtype_from_id()
207
 * @see get_subtype_class_from_id()
208
 * @access private
209
 */
210
function get_subtype_class($type, $subtype) {
211
	return _elgg_services()->subtypeTable->getClass($type, $subtype);
212
}
213
214
/**
215
 * Returns the class name for a subtype id.
216
 *
217
 * @param int $subtype_id The subtype id
218
 *
219
 * @return string|null
220
 * @see get_subtype_class()
221
 * @see get_subtype_from_id()
222
 * @access private
223
 */
224
function get_subtype_class_from_id($subtype_id) {
225
	return _elgg_services()->subtypeTable->getClassFromId($subtype_id);
226
}
227
228
/**
229
 * Register \ElggEntities with a certain type and subtype to be loaded as a specific class.
230
 *
231
 * By default entities are loaded as one of the 4 parent objects: site, user, object, or group.
232
 * If you subclass any of these you can register the classname with add_subtype() so
233
 * it will be loaded as that class automatically when retrieved from the database with
234
 * {@link get_entity()}.
235
 *
236
 * @warning This function cannot be used to change the class for a type-subtype pair.
237
 * Use update_subtype() for that.
238
 *
239
 * @param string $type    The type you're subtyping (site, user, object, or group)
240
 * @param string $subtype The subtype
241
 * @param string $class   Optional class name for the object
242
 *
243
 * @return int
244
 * @see update_subtype()
245
 * @see remove_subtype()
246
 * @see get_entity()
247
 */
248
function add_subtype($type, $subtype, $class = "") {
249
	return _elgg_services()->subtypeTable->add($type, $subtype, $class);
250
}
251
252
/**
253
 * Removes a registered \ElggEntity type, subtype, and classname.
254
 *
255
 * @warning You do not want to use this function. If you want to unregister
256
 * a class for a subtype, use update_subtype(). Using this function will
257
 * permanently orphan all the objects created with the specified subtype.
258
 *
259
 * @param string $type    Type
260
 * @param string $subtype Subtype
261
 *
262
 * @return bool
263
 * @see add_subtype()
264
 * @see update_subtype()
265
 */
266
function remove_subtype($type, $subtype) {
267
	return _elgg_services()->subtypeTable->remove($type, $subtype);
268
}
269
270
/**
271
 * Update a registered \ElggEntity type, subtype, and class name
272
 *
273
 * @param string $type    Type
274
 * @param string $subtype Subtype
275
 * @param string $class   Class name to use when loading this entity
276
 *
277
 * @return bool
278
 */
279
function update_subtype($type, $subtype, $class = '') {
280
	return _elgg_services()->subtypeTable->update($type, $subtype, $class);
281
}
282
283
/**
284
 * Determine if a given user can write to an entity container.
285
 *
286
 * An entity can be a container for any other entity by setting the
287
 * container_guid.  container_guid can differ from owner_guid.
288
 *
289
 * A plugin hook container_permissions_check:$entity_type is emitted to allow granular
290
 * access controls in plugins.
291
 *
292
 * @param int    $user_guid      The user guid, or 0 for logged in user.
293
 * @param int    $container_guid The container, or 0 for the current page owner.
294
 * @param string $type           The type of entity we want to create (default: 'all')
295
 * @param string $subtype        The subtype of the entity we want to create (default: 'all')
296
 *
297
 * @return bool
298
 */
299
function can_write_to_container($user_guid = 0, $container_guid = 0, $type = 'all', $subtype = 'all') {
300
	$container_guid = (int)$container_guid;
301
	if (!$container_guid) {
302
		$container_guid = elgg_get_page_owner_guid();
303
	}
304
305
	$container = get_entity($container_guid);
306
307
	$user_guid = (int)$user_guid;
308
	if ($user_guid == 0) {
309
		$user = elgg_get_logged_in_user_entity();
310
		$user_guid = elgg_get_logged_in_user_guid();
311
	} else {
312
		$user = get_user($user_guid);
313
		if (!$user) {
314
			return false;
315
		}
316
	}
317
318
	$return = false;
319
	if ($container) {
320
		// If the user can edit the container, they can also write to it
321
		if ($container->canEdit($user_guid)) {
322
			$return = true;
323
		}
324
	}
325
326
	// See if anyone else has anything to say
327
	return elgg_trigger_plugin_hook(
328
			'container_permissions_check',
329
			$type,
330
			array(
331
				'container' => $container,
332
				'user' => $user,
333
				'subtype' => $subtype
334
			),
335
			$return);
336
}
337
338
/**
339
 * Returns a database row from the entities table.
340
 *
341
 * @tip Use get_entity() to return the fully loaded entity.
342
 *
343
 * @warning This will only return results if a) it exists, b) you have access to it.
344
 * see {@link _elgg_get_access_where_sql()}.
345
 *
346
 * @param int $guid The GUID of the object to extract
347
 *
348
 * @return \stdClass|false
349
 * @see entity_row_to_elggstar()
350
 * @access private
351
 */
352
function get_entity_as_row($guid) {
353
	return _elgg_services()->entityTable->getRow($guid);
354
}
355
356
/**
357
 * Create an Elgg* object from a given entity row.
358
 *
359
 * Handles loading all tables into the correct class.
360
 *
361
 * @param \stdClass $row The row of the entry in the entities table.
362
 *
363
 * @return \ElggEntity|false
364
 * @see get_entity_as_row()
365
 * @see add_subtype()
366
 * @see get_entity()
367
 * @access private
368
 *
369
 * @throws ClassException|InstallationException
370
 */
371
function entity_row_to_elggstar($row) {
372
	return _elgg_services()->entityTable->rowToElggStar($row);
373
}
374
375
/**
376
 * Loads and returns an entity object from a guid.
377
 *
378
 * @param int $guid The GUID of the entity
379
 *
380
 * @return \ElggEntity The correct Elgg or custom object based upon entity type and subtype
381
 */
382
function get_entity($guid) {
383 1
	return _elgg_services()->entityTable->get($guid);
384
}
385
386
/**
387
 * Does an entity exist?
388
 *
389
 * This function checks for the existence of an entity independent of access
390
 * permissions. It is useful for situations when a user cannot access an entity
391
 * and it must be determined whether entity has been deleted or the access level
392
 * has changed.
393
 *
394
 * @param int $guid The GUID of the entity
395
 *
396
 * @return bool
397
 * @since 1.8.0
398
 */
399
function elgg_entity_exists($guid) {
400
	return _elgg_services()->entityTable->exists($guid);
401
}
402
403
/**
404
 * Enable an entity.
405
 *
406
 * @param int  $guid      GUID of entity to enable
407
 * @param bool $recursive Recursively enable all entities disabled with the entity?
408
 *
409
 * @return bool
410
 * @since 1.9.0
411
 */
412
function elgg_enable_entity($guid, $recursive = true) {
413
	return _elgg_services()->entityTable->enable($guid, $recursive);
414
}
415
416
/**
417
 * Returns an array of entities with optional filtering.
418
 *
419
 * Entities are the basic unit of storage in Elgg.  This function
420
 * provides the simplest way to get an array of entities.  There
421
 * are many options available that can be passed to filter
422
 * what sorts of entities are returned.
423
 *
424
 * @tip To output formatted strings of entities, use {@link elgg_list_entities()} and
425
 * its cousins.
426
 *
427
 * @tip Plural arguments can be written as singular if only specifying a
428
 * single element.  ('type' => 'object' vs 'types' => array('object')).
429
 *
430
 * @param array $options Array in format:
431
 *
432
 * 	types => null|STR entity type (type IN ('type1', 'type2')
433
 *           Joined with subtypes by AND. See below)
434
 *
435
 * 	subtypes => null|STR entity subtype (SQL: subtype IN ('subtype1', 'subtype2))
436
 *              Use ELGG_ENTITIES_NO_VALUE to match the default subtype.
437
 *              Use ELGG_ENTITIES_ANY_VALUE to match any subtype.
438
 *
439
 * 	type_subtype_pairs => null|ARR (array('type' => 'subtype'))
440
 *                        array(
441
 *                            'object' => array('blog', 'file'), // All objects with subtype of 'blog' or 'file'
442
 *                            'user' => ELGG_ENTITY_ANY_VALUE, // All users irrespective of subtype
443
 *                        );
444
 *
445
 *	guids => null|ARR Array of entity guids
446
 *
447
 * 	owner_guids => null|ARR Array of owner guids
448
 *
449
 * 	container_guids => null|ARR Array of container_guids
450
 *
451
 * 	site_guids => null (current_site)|ARR Array of site_guid
452
 *
453
 * 	order_by => null (time_created desc)|STR SQL order by clause
454
 *
455
 *  reverse_order_by => BOOL Reverse the default order by clause
456
 *
457
 * 	limit => null (from settings)|INT SQL limit clause (0 means no limit)
458
 *
459
 * 	offset => null (0)|INT SQL offset clause
460
 *
461
 * 	created_time_lower => null|INT Created time lower boundary in epoch time
462
 *
463
 * 	created_time_upper => null|INT Created time upper boundary in epoch time
464
 *
465
 * 	modified_time_lower => null|INT Modified time lower boundary in epoch time
466
 *
467
 * 	modified_time_upper => null|INT Modified time upper boundary in epoch time
468
 *
469
 * 	count => true|false return a count instead of entities
470
 *
471
 * 	wheres => array() Additional where clauses to AND together
472
 *
473
 * 	joins => array() Additional joins
474
 *
475
 * 	preload_owners => bool (false) If set to true, this function will preload
476
 * 					  all the owners of the returned entities resulting in better
477
 * 					  performance when displaying entities owned by several users
478
 *
479
 * 	callback => string A callback function to pass each row through
480
 *
481
 * 	distinct => bool (true) If set to false, Elgg will drop the DISTINCT clause from
482
 *				the MySQL query, which will improve performance in some situations.
483
 *				Avoid setting this option without a full understanding of the underlying
484
 *				SQL query Elgg creates.
485
 *
486
 * @return mixed If count, int. If not count, array. false on errors.
487
 * @since 1.7.0
488
 * @see elgg_get_entities_from_metadata()
489
 * @see elgg_get_entities_from_relationship()
490
 * @see elgg_get_entities_from_access_id()
491
 * @see elgg_get_entities_from_annotations()
492
 * @see elgg_list_entities()
493
 */
494
function elgg_get_entities(array $options = array()) {
495
	return _elgg_services()->entityTable->getEntities($options);
496
}
497
498
/**
499
 * Return entities from an SQL query generated by elgg_get_entities.
500
 *
501
 * @param string    $sql
502
 * @param \ElggBatch $batch
503
 * @return \ElggEntity[]
504
 *
505
 * @access private
506
 * @throws LogicException
507
 */
508
function _elgg_fetch_entities_from_sql($sql, \ElggBatch $batch = null) {
509
	return _elgg_services()->entityTable->fetchFromSql($sql, $batch);
510
}
511
512
/**
513
 * Returns SQL where clause for type and subtype on main entity table
514
 *
515
 * @param string     $table    Entity table prefix as defined in SELECT...FROM entities $table
516
 * @param null|array $types    Array of types or null if none.
517
 * @param null|array $subtypes Array of subtypes or null if none
518
 * @param null|array $pairs    Array of pairs of types and subtypes
519
 *
520
 * @return false|string
521
 * @since 1.7.0
522
 * @access private
523
 */
524
function _elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pairs) {
525
	return _elgg_services()->entityTable->getEntityTypeSubtypeWhereSql($table, $types, $subtypes, $pairs);
526
}
527
528
/**
529
 * Returns SQL where clause for owner and containers.
530
 *
531
 * @param string     $column Column name the guids should be checked against. Usually
532
 *                           best to provide in table.column format.
533
 * @param null|array $guids  Array of GUIDs.
534
 *
535
 * @return false|string
536
 * @since 1.8.0
537
 * @access private
538
 */
539
function _elgg_get_guid_based_where_sql($column, $guids) {
540
	return _elgg_services()->entityTable->getGuidBasedWhereSql($column, $guids);
541
}
542
543
/**
544
 * Returns SQL where clause for entity time limits.
545
 *
546
 * @param string   $table              Entity table prefix as defined in
547
 *                                     SELECT...FROM entities $table
548
 * @param null|int $time_created_upper Time created upper limit
549
 * @param null|int $time_created_lower Time created lower limit
550
 * @param null|int $time_updated_upper Time updated upper limit
551
 * @param null|int $time_updated_lower Time updated lower limit
552
 *
553
 * @return false|string false on fail, string on success.
554
 * @since 1.7.0
555
 * @access private
556
 */
557
function _elgg_get_entity_time_where_sql($table, $time_created_upper = null,
558
		$time_created_lower = null, $time_updated_upper = null, $time_updated_lower = null) {
559
	return _elgg_services()->entityTable->getEntityTimeWhereSql($table,
560
		$time_created_upper, $time_created_lower, $time_updated_upper, $time_updated_lower);
561
}
562
563
/**
564
 * Returns a string of rendered entities.
565
 *
566
 * Displays list of entities with formatting specified by the entity view.
567
 *
568
 * @tip Pagination is handled automatically.
569
 *
570
 * @note Internal: This also provides the views for elgg_view_annotation().
571
 *
572
 * @note Internal: If the initial COUNT query returns 0, the $getter will not be called again.
573
 *
574
 * @param array    $options Any options from $getter options plus:
575
 *                   item_view => STR Optional. Alternative view used to render list items
576
 *                   full_view => BOOL Display full view of entities (default: false)
577
 *                   list_type => STR 'list' or 'gallery'
578
 *                   list_type_toggle => BOOL Display gallery / list switch
579
 *                   pagination => BOOL Display pagination links
580
 *                   no_results => STR|Closure Message to display when there are no entities
581
 *
582
 * @param callback $getter  The entity getter function to use to fetch the entities.
583
 * @param callback $viewer  The function to use to view the entity list.
584
 *
585
 * @return string
586
 * @since 1.7
587
 * @see elgg_get_entities()
588
 * @see elgg_view_entity_list()
589
 */
590
function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entities',
591
	$viewer = 'elgg_view_entity_list') {
592
593
	global $autofeed;
594
	$autofeed = true;
595
596
	$offset_key = isset($options['offset_key']) ? $options['offset_key'] : 'offset';
597
598
	$defaults = array(
599
		'offset' => (int) max(get_input($offset_key, 0), 0),
600
		'limit' => (int) max(get_input('limit', elgg_get_config('default_limit')), 0),
601
		'full_view' => false,
602
		'list_type_toggle' => false,
603
		'pagination' => true,
604
		'no_results' => '',
605
	);
606
607
	$options = array_merge($defaults, $options);
608
609
	// backward compatibility
610 View Code Duplication
	if (isset($options['view_type_toggle'])) {
611
		elgg_deprecated_notice("Option 'view_type_toggle' deprecated by 'list_type_toggle' in elgg_list* functions", 1.9);
612
		$options['list_type_toggle'] = $options['view_type_toggle'];
613
	}
614
615
	$options['count'] = true;
616
	$count = call_user_func($getter, $options);
617
618
	if ($count > 0) {
619
		$options['count'] = false;
620
		$entities = call_user_func($getter, $options);
621
	} else {
622
		$entities = array();
623
	}
624
625
	$options['count'] = $count;
626
627
	return call_user_func($viewer, $entities, $options);
628
}
629
630
/**
631
 * Gets entities based upon attributes in secondary tables.
632
 * Also accepts all options available to elgg_get_entities(),
633
 * elgg_get_entities_from_metadata(), and elgg_get_entities_from_relationship().
634
 *
635
 * @warning requires that the entity type be specified and there can only be one
636
 * type.
637
 *
638
 * @see elgg_get_entities
639
 * @see elgg_get_entities_from_metadata
640
 * @see elgg_get_entities_from_relationship
641
 *
642
 * @param array $options Array in format:
643
 *
644
 * 	attribute_name_value_pairs => ARR (
645
 *                                   'name' => 'name',
646
 *                                   'value' => 'value',
647
 *                                   'operand' => '=', (optional)
648
 *                                   'case_sensitive' => false (optional)
649
 *                                  )
650
 * 	                             If multiple values are sent via
651
 *                               an array ('value' => array('value1', 'value2')
652
 *                               the pair's operand will be forced to "IN".
653
 *
654
 * 	attribute_name_value_pairs_operator => null|STR The operator to use for combining
655
 *                                        (name = value) OPERATOR (name = value); default is AND
656
 *
657
 * @return \ElggEntity[]|mixed If count, int. If not count, array. false on errors.
658
 * @since 1.9.0
659
 * @throws InvalidArgumentException
660
 * @todo Does not support ordering by attributes or using an attribute pair shortcut like this ('title' => 'foo')
661
 */
662
function elgg_get_entities_from_attributes(array $options = array()) {
663
	return _elgg_services()->entityTable->getEntitiesFromAttributes($options);
664
}
665
666
/**
667
 * Get the join and where clauses for working with entity attributes
668
 *
669
 * @return false|array False on fail, array('joins', 'wheres')
670
 * @since 1.9.0
671
 * @access private
672
 * @throws InvalidArgumentException
673
 */
674
function _elgg_get_entity_attribute_where_sql(array $options = array()) {
675
	return _elgg_services()->entityTable->getEntityAttributeWhereSql($options);
676
}
677
678
/**
679
 * Returns a list of months in which entities were updated or created.
680
 *
681
 * @tip Use this to generate a list of archives by month for when entities were added or updated.
682
 *
683
 * @todo document how to pass in array for $subtype
684
 *
685
 * @warning Months are returned in the form YYYYMM.
686
 *
687
 * @param string $type           The type of entity
688
 * @param string $subtype        The subtype of entity
689
 * @param int    $container_guid The container GUID that the entities belong to
690
 * @param int    $site_guid      The site GUID
691
 * @param string $order_by       Order_by SQL order by clause
692
 *
693
 * @return array|false Either an array months as YYYYMM, or false on failure
694
 */
695
function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $site_guid = 0,
696
		$order_by = 'time_created') {
697
	return _elgg_services()->entityTable->getDates(
698
		$type, $subtype, $container_guid, $site_guid, $order_by);
699
}
700
701
/**
702
 * Registers an entity type and subtype as a public-facing entity that should
703
 * be shown in search and by {@link elgg_list_registered_entities()}.
704
 *
705
 * @warning Entities that aren't registered here will not show up in search.
706
 *
707
 * @tip Add a language string item:type:subtype to make sure the items are display properly.
708
 *
709
 * @param string $type    The type of entity (object, site, user, group)
710
 * @param string $subtype The subtype to register (may be blank)
711
 *
712
 * @return bool Depending on success
713
 * @see get_registered_entity_types()
714
 */
715
function elgg_register_entity_type($type, $subtype = null) {
716
	global $CONFIG;
717
718
	$type = strtolower($type);
719
	if (!in_array($type, $CONFIG->entity_types)) {
720
		return false;
721
	}
722
723
	if (!isset($CONFIG->registered_entities)) {
724
		$CONFIG->registered_entities = array();
725
	}
726
727
	if (!isset($CONFIG->registered_entities[$type])) {
728
		$CONFIG->registered_entities[$type] = array();
729
	}
730
731
	if ($subtype) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $subtype of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
732
		$CONFIG->registered_entities[$type][] = $subtype;
733
	}
734
735
	return true;
736
}
737
738
/**
739
 * Unregisters an entity type and subtype as a public-facing type.
740
 *
741
 * @warning With a blank subtype, it unregisters that entity type including
742
 * all subtypes. This must be called after all subtypes have been registered.
743
 *
744
 * @param string $type    The type of entity (object, site, user, group)
745
 * @param string $subtype The subtype to register (may be blank)
746
 *
747
 * @return bool Depending on success
748
 * @see elgg_register_entity_type()
749
 */
750
function elgg_unregister_entity_type($type, $subtype = null) {
751
	global $CONFIG;
752
753
	$type = strtolower($type);
754
	if (!in_array($type, $CONFIG->entity_types)) {
755
		return false;
756
	}
757
758
	if (!isset($CONFIG->registered_entities)) {
759
		return false;
760
	}
761
762
	if (!isset($CONFIG->registered_entities[$type])) {
763
		return false;
764
	}
765
766
	if ($subtype) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $subtype of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
767
		if (in_array($subtype, $CONFIG->registered_entities[$type])) {
768
			$key = array_search($subtype, $CONFIG->registered_entities[$type]);
769
			unset($CONFIG->registered_entities[$type][$key]);
770
		} else {
771
			return false;
772
		}
773
	} else {
774
		unset($CONFIG->registered_entities[$type]);
775
	}
776
777
	return true;
778
}
779
780
/**
781
 * Returns registered entity types and subtypes
782
 *
783
 * @param string $type The type of entity (object, site, user, group) or blank for all
784
 *
785
 * @return array|false Depending on whether entities have been registered
786
 * @see elgg_register_entity_type()
787
 */
788
function get_registered_entity_types($type = null) {
789
	global $CONFIG;
790
791
	if (!isset($CONFIG->registered_entities)) {
792
		return false;
793
	}
794
	if ($type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
795
		$type = strtolower($type);
796
	}
797
	if (!empty($type) && empty($CONFIG->registered_entities[$type])) {
798
		return false;
799
	}
800
801
	if (empty($type)) {
802
		return $CONFIG->registered_entities;
803
	}
804
805
	return $CONFIG->registered_entities[$type];
806
}
807
808
/**
809
 * Returns if the entity type and subtype have been registered with {@link elgg_register_entity_type()}.
810
 *
811
 * @param string $type    The type of entity (object, site, user, group)
812
 * @param string $subtype The subtype (may be blank)
813
 *
814
 * @return bool Depending on whether or not the type has been registered
815
 */
816
function is_registered_entity_type($type, $subtype = null) {
817
	global $CONFIG;
818
819
	if (!isset($CONFIG->registered_entities)) {
820
		return false;
821
	}
822
823
	$type = strtolower($type);
824
825
	// @todo registering a subtype implicitly registers the type.
826
	// see #2684
827
	if (!isset($CONFIG->registered_entities[$type])) {
828
		return false;
829
	}
830
831
	if ($subtype && !in_array($subtype, $CONFIG->registered_entities[$type])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $subtype of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
832
		return false;
833
	}
834
	return true;
835
}
836
837
/**
838
 * Returns a viewable list of entities based on the registered types.
839
 *
840
 * @see elgg_view_entity_list
841
 *
842
 * @param array $options Any elgg_get_entity() options plus:
843
 *
844
 * 	full_view => BOOL Display full view entities
845
 *
846
 * 	list_type_toggle => BOOL Display gallery / list switch
847
 *
848
 * 	allowed_types => true|ARRAY True to show all types or an array of valid types.
849
 *
850
 * 	pagination => BOOL Display pagination links
851
 *
852
 * @return string A viewable list of entities
853
 * @since 1.7.0
854
 */
855
function elgg_list_registered_entities(array $options = array()) {
856
	global $autofeed;
857
	$autofeed = true;
858
859
	$defaults = array(
860
		'full_view' => false,
861
		'allowed_types' => true,
862
		'list_type_toggle' => false,
863
		'pagination' => true,
864
		'offset' => 0,
865
		'types' => array(),
866
		'type_subtype_pairs' => array(),
867
	);
868
869
	$options = array_merge($defaults, $options);
870
871
	// backward compatibility
872 View Code Duplication
	if (isset($options['view_type_toggle'])) {
873
		elgg_deprecated_notice("Option 'view_type_toggle' deprecated by 'list_type_toggle' in elgg_list* functions", 1.9);
874
		$options['list_type_toggle'] = $options['view_type_toggle'];
875
	}
876
877
	$types = get_registered_entity_types();
878
879
	foreach ($types as $type => $subtype_array) {
880
		if (in_array($type, $options['allowed_types']) || $options['allowed_types'] === true) {
881
			// you must explicitly register types to show up in here and in search for objects
882
			if ($type == 'object') {
883
				if (is_array($subtype_array) && count($subtype_array)) {
884
					$options['type_subtype_pairs'][$type] = $subtype_array;
885
				}
886
			} else {
887
				if (is_array($subtype_array) && count($subtype_array)) {
888
					$options['type_subtype_pairs'][$type] = $subtype_array;
889
				} else {
890
					$options['type_subtype_pairs'][$type] = ELGG_ENTITIES_ANY_VALUE;
891
				}
892
			}
893
		}
894
	}
895
896
	if (!empty($options['type_subtype_pairs'])) {
897
		$count = elgg_get_entities(array_merge(array('count' => true), $options));
898
		if ($count > 0) {
899
			$entities = elgg_get_entities($options);
900
		} else {
901
			$entities = array();
902
		}
903
	} else {
904
		$count = 0;
905
		$entities = array();
906
	}
907
908
	$options['count'] = $count;
909
	return elgg_view_entity_list($entities, $options);
910
}
911
912
/**
913
 * Checks if $entity is an \ElggEntity and optionally for type and subtype.
914
 *
915
 * @tip Use this function in actions and views to check that you are dealing
916
 * with the correct type of entity.
917
 *
918
 * @param mixed  $entity  Entity
919
 * @param string $type    Entity type
920
 * @param string $subtype Entity subtype
921
 * @param string $class   Class name
922
 *
923
 * @return bool
924
 * @since 1.8.0
925
 */
926
function elgg_instanceof($entity, $type = null, $subtype = null, $class = null) {
927 3
	$return = ($entity instanceof \ElggEntity);
928
929 3
	if ($type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
930
		/* @var \ElggEntity $entity */
931
		$return = $return && ($entity->getType() == $type);
932
	}
933
934 3
	if ($subtype) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $subtype of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
935
		$return = $return && ($entity->getSubtype() == $subtype);
936
	}
937
938 3
	if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
939
		$return = $return && ($entity instanceof $class);
940
	}
941
942 3
	return $return;
943
}
944
945
/**
946
 * Update the last_action column in the entities table for $guid.
947
 *
948
 * @warning This is different to time_updated.  Time_updated is automatically set,
949
 * while last_action is only set when explicitly called.
950
 *
951
 * @param int $guid   Entity annotation|relationship action carried out on
952
 * @param int $posted Timestamp of last action
953
 *
954
 * @return bool
955
 * @access private
956
 */
957
function update_entity_last_action($guid, $posted = null) {
958
	return _elgg_services()->entityTable->updateLastAction($guid, $posted);
959
}
960
961
/**
962
 * Runs unit tests for the entity objects.
963
 *
964
 * @param string $hook   unit_test
965
 * @param string $type   system
966
 * @param array  $value  Array of tests
967
 *
968
 * @return array
969
 * @access private
970
 */
971
function _elgg_entities_test($hook, $type, $value) {
972
	global $CONFIG;
973
	$value[] = $CONFIG->path . 'engine/tests/ElggEntityTest.php';
974
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreAttributeLoaderTest.php';
975
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesTest.php';
976
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromAnnotationsTest.php';
977
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromMetadataTest.php';
978
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromPrivateSettingsTest.php';
979
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromRelationshipTest.php';
980
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromAttributesTest.php';
981
	$value[] = $CONFIG->path . 'engine/tests/ElggEntityPreloaderIntegrationTest.php';
982
	return $value;
983
}
984
985
/**
986
 * Entities init function; establishes the default entity page handler
987
 *
988
 * @return void
989
 * @elgg_event_handler init system
990
 * @access private
991
 */
992
function _elgg_entities_init() {
993
	elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_entities_test');
994
}
995
996
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
997
	$events->registerHandler('init', 'system', '_elgg_entities_init');
998
};
999