Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

engine/lib/entities.php (6 issues)

1
<?php
2
/**
3
 * Procedural code for creating, loading, and modifying \ElggEntity objects.
4
 */
5
6
/**
7
 * Return the class name registered as a constructor for an entity of a given type and subtype
8
 *
9
 * @see elgg_set_entity_type()
10
 *
11
 * @param string $type    The type
12
 * @param string $subtype The subtype
13
 *
14
 * @return string
15
 */
16
function elgg_get_entity_class($type, $subtype) {
17 293
	return _elgg_services()->entityTable->getEntityClass($type, $subtype);
18
}
19
20
/**
21
 * Sets class constructor name for entities with given type and subtype
22
 *
23
 * By default entities are loaded as one of the 4 parent objects:
24
 *  - site: ElggSite
25
 *  - user: ElggUser
26
 *  - object: ElggObject
27
 *  - group: ElggGroup
28
 *
29
 * Entity classes for subtypes should extend the base class for entity type,
30
 * e.g. ElggBlog must extend ElggObject
31
 *
32
 * @param string $type    Entity type
33
 * @param string $subtype Entity subtype
34
 * @param string $class   Class name for the object
35
 *                        Can be empty to reset previously declared class name
36
 *
37
 * @return void
38
 */
39
function elgg_set_entity_class($type, $subtype, $class = "") {
40 4791
	_elgg_services()->entityTable->setEntityClass($type, $subtype, $class);
41 4791
}
42
43
44
/**
45
 * Returns a database row from the entities table.
46
 *
47
 * @tip Use get_entity() to return the fully loaded entity.
48
 *
49
 * @warning This will only return results if a) it exists, b) you have access to it.
50
 * see {@link _elgg_get_access_where_sql()}.
51
 *
52
 * @param int $guid The GUID of the object to extract
53
 *
54
 * @return \stdClass|false
55
 * @see entity_row_to_elggstar()
56
 * @access private
57
 */
58
function get_entity_as_row($guid) {
59
	return _elgg_services()->entityTable->getRow($guid);
60
}
61
62
/**
63
 * Create an Elgg* object from a given entity row.
64
 *
65
 * Handles loading all tables into the correct class.
66
 *
67
 * @param \stdClass $row The row of the entry in the entities table.
68
 *
69
 * @return \ElggEntity|false
70
 * @see get_entity_as_row()
71
 * @see get_entity()
72
 * @access private
73
 *
74
 * @throws ClassException|InstallationException
75
 */
76
function entity_row_to_elggstar($row) {
77 424
	return _elgg_services()->entityTable->rowToElggStar($row);
78
}
79
80
/**
81
 * Loads and returns an entity object from a guid.
82
 *
83
 * @param int $guid The GUID of the entity
84
 *
85
 * @return \ElggEntity The correct Elgg or custom object based upon entity type and subtype
86
 */
87
function get_entity($guid) {
88 648
	if ($guid == 1) {
89 13
		return _elgg_config()->site;
90
	}
91 642
	return _elgg_services()->entityTable->get($guid);
92
}
93
94
/**
95
 * Does an entity exist?
96
 *
97
 * This function checks for the existence of an entity independent of access
98
 * permissions. It is useful for situations when a user cannot access an entity
99
 * and it must be determined whether entity has been deleted or the access level
100
 * has changed.
101
 *
102
 * @param int $guid The GUID of the entity
103
 *
104
 * @return bool
105
 * @since 1.8.0
106
 */
107
function elgg_entity_exists($guid) {
108
	return _elgg_services()->entityTable->exists($guid);
109
}
110
111
/**
112
 * Enable an entity.
113
 *
114
 * @param int  $guid      GUID of entity to enable
115
 * @param bool $recursive Recursively enable all entities disabled with the entity?
116
 *
117
 * @return bool
118
 * @since 1.9.0
119
 */
120
function elgg_enable_entity($guid, $recursive = true) {
121
	return _elgg_services()->entityTable->enable($guid, $recursive);
122
}
123
124
/**
125
 * Get the current site entity
126
 *
127
 * @return \ElggSite
128
 * @since 1.8.0
129
 */
130
function elgg_get_site_entity() {
131 237
	return _elgg_config()->site;
132
}
133
134
/**
135
 * Fetches/counts entities or performs a calculation on their properties
136
 *
137
 * Note that you can use singulars for most options, e.g. $options['type'] will be normalized to $options['types']
138
 *
139
 * ------------------------
140
 * TYPE SUBTYPE CONSTRAINTS
141
 * ------------------------
142
 *
143
 * Filter entities by their type and subtype
144
 *
145
 * @option string[] $types
146
 * @option string[] $subtypes
147
 * @option string[] $type_subtype_pairs
148
 *
149
 * <code>
150
 * $options['types'] = ['object'];
151
 * $options['subtypes'] = ['blog', 'file'];
152
 * $options['type_subtype_pairs'] = [
153
 *     'object' => ['blog', 'file'],
154
 *     'group' => [], // all group subtypes
155
 *     'user' => null, // all user subtypes
156
 * ];
157
 * </code>
158
 *
159
 * ----------------
160
 * GUID CONSTRAINTS
161
 * ----------------
162
 *
163
 * Filter entities by their guid, owner or container
164
 *
165
 * @option int[]|ElggEntity[] $guids
166
 * @option int[]|ElggEntity[] $owner_guids
167
 * @option int[]|ElggEntity[] $container_guids
168
 *
169
 * ----------------
170
 * TIME CONSTRAINTS
171
 * ----------------
172
 *
173
 * Filter entities that were created, updated or last acted on within certain bounds
174
 *
175
 * @option DateTime|string|int $created_after
176
 * @option DateTime|string|int $created_before
177
 * @option DateTime|string|int $updated_after
178
 * @option DateTime|string|int $updated_before
179
 * @option DateTime|string|int $last_action_after
180
 * @option DateTime|string|int $last_action_before
181
 *
182
 * <code>
183
 * $options['created_after'] = '-1 year';
184
 * $options['created_before'] = 'now';
185
 * </code>
186
 *
187
 * ------------------
188
 * ACCESS CONSTRAINTS
189
 * ------------------
190
 *
191
 * Filter entities by their access_id attribute. Note that this filter apply to entities that the user has access to.
192
 * You can ignore access system using {@link elgg_set_ignore_access()}
193
 *
194
 * @option int[] $access_id
195
 *
196
 * ----------------
197
 * LIMIT AND OFFSET
198
 * ----------------
199
 *
200
 * This options are used for paginating lists of entities
201
 *
202
 * @option int $limit
203
 * @option int $offset
204
 *
205
 * --------------------
206
 * METADATA CONSTRAINTS
207
 * --------------------
208
 *
209
 * Filter entities by their metadata and attributes
210
 *
211
 * The following options will be merged and applied as a metadata pair to @options['metadata_name_value_pairs']
212
 * Note metadata names can contain attributes names and will be resolved automatically during query building.
213
 * @option int[]                $metadata_ids
214
 * @option string[]             $metadata_names
215
 * @option mixed                $metadata_values
216
 * @option DateTime|string|int  $metadata_created_after
217
 * @option DateTime|string|int  $metadata_created_before
218
 * @option bool                 $metadata_case_sensitive
219
 *
220
 * Metadata name value pairs will be joined by the boolean specified in $metadata_name_value_pairs_operator
221
 * @option array                $metadata_name_value_pairs
222
 * @option string               $metadata_name_value_pairs_operator
223
 *
224
 * In addition to metadata name value pairs, you can specify search pair, which will be merged using OR boolean
225
 * and will filter entities regardless of metadata name value pairs and their operator
226
 *
227
 * @option array                $search_name_value_pairs
228
 *
229
 * <code>
230
 * // Search for entities with:
231
 * // status of draft or unsaved_draft
232
 * // AND index greater than 5
233
 * // AND (title/description containing the word hello OR tags containing the word world)
234
 * $options['metadata_name_value_pairs'] = [
235
 *    [
236
 *       'name' => 'status',
237
 *       'value' => ['draft', 'unsaved_draft'],
238
 *       'operand' => 'IN',
239
 *       'created_after' => '-1 day',
240
 *    ],
241
 *    [
242
 *        'name' => 'index',
243
 *        'value' => 5,
244
 *        'operand' => '>=',
245
 *        'type' => ELGG_VALUE_INTEGER,
246
 *    ]
247
 * ];
248
 * $options['search_name_value_pairs'] = [
249
 *    [
250
 *       'name' => ['title', 'description'],
251
 *       'value' => '%hello%',
252
 *       'operand' => 'LIKE',
253
 *       'case_sensitive' => false,
254
 *    ],
255
 *    [
256
 *       'name' => 'tags',
257
 *       'value' => '%world%',
258
 *       'operand' => 'LIKE',
259
 *       'case_sensitive' => false,
260
 *    ],
261
 * ];
262
 * </code>
263
 *
264
 * ----------------------
265
 * ANNOTATION CONSTRAINTS
266
 * ----------------------
267
 *
268
 * Filter entities by their annotations
269
 *
270
 * The following options will be merged and applied as an annotation pair to @options['annotation_name_value_pairs']
271
 * @option int[]                $annotation_ids
272
 * @option string[]             $annotation_names
273
 * @option mixed                $annotation_values
274
 * @option bool                 $annotation_case_sensitive
275
 * @option DateTime|string|int  $annotation_created_after
276
 * @option DateTime|string|int  $annotation_created_before
277
 * @option int[]|ElggEntity[]   $annotation_owner_guids
278
 * @option int[]|ElggEntity[]   $annotation_access_ids
279
 *
280
 * Annotation name value pairs will be joined by the boolean specified in $annotation_name_value_pairs_operator
281
 * @option array                $annotation_name_value_pairs
282
 * @option string               $annotation_name_value_pairs_operator
283
 **
284
 * <code>
285
 * $options['annotation_name_value_pairs'] = [
286
 *    [
287
 *       'name' => 'likes',
288
 *       'created_after' => '-1 day',
289
 *    ],
290
 *    [
291
 *        'name' => 'rating',
292
 *        'value' => 5,
293
 *        'operand' => '>=',
294
 *        'type' => ELGG_VALUE_INTEGER,
295
 *    ],
296
 *    [
297
 *        'name' => 'review',
298
 *        'value' => '%awesome%',
299
 *        'operand' => 'LIKE',
300
 *        'type' => ELGG_VALUE_STRING,
301
 *    ]
302
 * ];
303
 * </code>
304
 *
305
 * ------------------------
306
 * RELATIONSHIP CONSTRAINTS
307
 * ------------------------
308
 *
309
 * Filter entities by their relationships
310
 *
311
 * The following options will be merged and applied as a relationship pair to $options['relationship_name_value_pairs']
312
 * @option int[]                $relationship_ids
313
 * @option string[]             $relationship
314
 * @option int[]|ElggEntity[]   $relationship_guid
315
 * @option bool                 $inverse_relationship
316
 * @option DateTime|string|int  $relationship_created_after
317
 * @option DateTime|string|int  $relationship_created_before
318
 * @option string               $relationship_join_on Column name in the name main table
319
 *
320
 * @option array                $relationship_pairs
321
 *
322
 * <code>
323
 * // Get all entities that user with guid 25 has friended or been friended by
324
 * $options['relationship_pairs'] = [
325
 *    [
326
 *       'relationship' => 'friend',
327
 *       'relationship_guid' => 25,
328
 *       'inverse_relationship' => true,
329
 *    ],
330
 *    [
331
 *       'relationship' => 'friend',
332
 *       'relationship_guid' => 25,
333
 *       'inverse_relationship' => false,
334
 *    ],
335
 * ];
336
 * </code>
337
 *
338
 * ----------------------------
339
 * PRIVATE SETTINGS CONSTRAINTS
340
 * ----------------------------
341
 *
342
 * Filter entities by their private settings
343
 *
344
 * The following options will be merged and applied as a private_setting pair to
345
 * $options['private_setting_name_value_pairs']
346
 * @option int[]                $private_setting_ids
347
 * @option string[]             $private_setting_names
348
 * @option mixed                $private_setting_values
349
 * @option bool                 $private_setting_case_sensitive
350
 *
351
 * Private name value pairs will be joined by the boolean specified in $private_setting_name_value_pairs_operator
352
 * @option array                $private_setting_name_value_pairs
353
 * @option string               $private_setting_name_value_pairs_operator
354
 *
355
 * Setting names in all pairs can be namespaced using the prefix
356
 * @option string               $private_setting_name_prefix
357
 *
358
 * <code>
359
 * $options['private_setting_name_value_pairs'] = [
360
 *    [
361
 *       'name' => 'handler',
362
 *       'value' => ['admin', 'dashboard'],
363
 *       'operand' => 'IN',
364
 *    ],
365
 * ];
366
 * </code>
367
 *
368
 * -------
369
 * SORTING
370
 * -------
371
 *
372
 * You can specify sorting options using ONE of the following options
373
 *
374
 * Order by a calculation performed on annotation name value pairs
375
 * $option array annotation_sort_by_calculation e.g. avg, max, min, sum
376
 *
377
 * Order by value of a specific annotation
378
 * @option array $order_by_annotation
379
 *
380
 * Order by value of a speicifc metadata/attribute
381
 * @option array $order_by_metadata
382
 *
383
 * Order by arbitrary clauses
384
 * @option array $order_by
385
 *
386
 * <code>
387
 * $options['order_by_metadata'] = [
388
 *     'name' => 'priority',
389
 *     'direction' => 'DESC',
390
 *     'as' => 'integer',
391
 * ];
392
 * $options['order_by_annotation'] = [
393
 *     'name' => 'priority',
394
 *     'direction' => 'DESC',
395
 *     'as' => 'integer',
396
 * ];
397
 *
398
 * $sort_by = new \Elgg\Database\Clauses\EntitySortByClause();
399
 * $sort_by->property = 'private';
400
 * $sort_by->property_type = 'private_setting';
401
 * $sort_by->join_type = 'left';
402
 *
403
 * $fallback = new \Elgg\Database\Clauses\OrderByClause('e.time_created', 'desc');
404
 *
405
 * $options['order_by'] = [
406
 *     $sort_by,
407
 *     $fallback,
408
 * ];
409
 * </code>
410
 *
411
 * -----------------
412
 * COUNT/CALCULATION
413
 * -----------------
414
 *
415
 * Performs a calculation on a set of entities that match all of the criteria
416
 * If any of these are specific, the return of this function will be int or float
417
 *
418
 * Return total number of entities
419
 * @option bool $count
420
 *
421
 * Perform a calculation on a set of entity's annotations using a numeric sql function
422
 * If specified, the number of annotation name value pairs can not be more than 1, or they must be merged using OR
423
 * operator
424
 * @option string $annotation_calculation e.g. avg, max, min, sum
425
 *
426
 * Perform a calculation on a set of entity's metadat using a numeric sql function
427
 * If specified, the number of metadata name value pairs can not be more than 1, or they must be merged using OR
428
 * operator
429
 * @option string $metadata_calculation e.g. avg, max, min, sum
430
 *
431
 * ----------
432
 * SQL SELECT
433
 * ----------
434
 *
435
 * @option array $selects
436
 * <code>
437
 * $options['selects'] = [
438
 *    'e.last_action AS last_action',
439
 *    function(QueryBulder $qb, $main_alias) {
440
 *        $joined_alias = $qb->joinMetadataTable($main_alias, 'guid', 'status');
441
 *        return "$joined_alias.value AS status";
442
 *    }
443
 * ];
444
 * </code>
445
 *
446
 * --------
447
 * SQL JOIN
448
 * --------
449
 *
450
 * @option array $joins
451
 * <code>
452
 * $on = function(QueryBuilder $qb, $joined_alias, $main_alias) {
453
 *     return $qb->compare("$joined_alias.user_guid", '=', "$main_alias.guid");
454
 * };
455
 * $options['joins'] = [
456
 *     new JoinClause('access_collections_membership', 'acm', $on);
457
 * ];
458
 * </code>
459
 *
460
 * ----------
461
 * SQL GROUPS
462
 * ----------
463
 *
464
 * @option array $group_by
465
 * @option array $having
466
 *
467
 * <code>
468
 * $options['group_by'] = [
469
 *      function(QueryBuilder $qb, $main_alias) {
470
 *          return "$main_alias.guid";
471
 *      }
472
 * ];
473
 * $options['having'] = [
474
 *      function(QueryBuilder $qb, $main_alias) {
475
 *          return $qb->compare("$main_alias.guid", '>=', 50, ELGG_VALUE_INTEGER);
476
 *      }
477
 * ];
478
 * </code>
479
 *
480
 * ---------
481
 * SQL WHERE
482
 * ---------
483
 *
484
 * @option array $where
485
 * <code>
486
 * $options['wheres'] = [
487
 *      function(QueryBuilder $qb, $main_alias) {
488
 *          return $qb->merge([
489
 *              $qb->compare("$main_alias.guid", '>=', 50, ELGG_VALUE_INTEGER),
490
 *              $qb->compare("$main_alias.guid", '<=', 250, ELGG_VALUE_INTEGER),
491
 *          ], 'OR');
492
 *      }
493
 * ];
494
 * </code>
495
 *
496
 * --------------
497
 * RESULT OPTIONS
498
 * --------------
499
 *
500
 * @option bool $distinct           If set to false, Elgg will drop the DISTINCT clause from
501
 *                                  the MySQL query, which will improve performance in some situations.
502
 *                                  Avoid setting this option without a full understanding of the underlying
503
 *                                  SQL query Elgg creates.
504
 *                                  Default: true
505
 * @option callable|false $callback A callback function to pass each row through
506
 *                                  Default: entity_row_to_elggstar
507
 * @option bool $preload_owners     If set to true, this function will preload
508
 *                                  all the owners of the returned entities resulting in better
509
 *                                  performance when displaying entities owned by several users
510
 *                                  Default: false
511
 * @option bool $preload_containers If set to true, this function will preload
512
 *                                  all the containers of the returned entities resulting in better
513
 *                                  performance when displaying entities contained by several users/groups
514
 *                                  Default: false
515
 * @option bool $batch              If set to true, an Elgg\BatchResult object will be returned instead of an array.
516
 *                                  Default: false
517
 * @option bool $batch_inc_offset   If "batch" is used, this tells the batch to increment the offset
518
 *                                  on each fetch. This must be set to false if you delete the batched results.
519
 *                                  Default: true
520
 * @option int  $batch_size         If "batch" is used, this is the number of entities/rows to pull in before
521
 *                                  requesting more.
522
 *                                  Default: 25
523
 *
524
 *
525
 * @see    elgg_list_entities()
526
 * @see    \Elgg\Database\LegacyQueryOptionsAdapter
527
 *
528
 * @param array $options Options
529
 *
530
 * @return \ElggEntity[]|int|mixed If count, int. Otherwise an array or an Elgg\BatchResult. false on errors.
531
 *
532
 * @since 1.7.0
533
 */
534
function elgg_get_entities(array $options = []) {
535 1159
	return \Elgg\Database\Entities::find($options);
536
}
537
538
/**
539
 * Returns a string of rendered entities.
540
 *
541
 * Displays list of entities with formatting specified by the entity view.
542
 *
543
 * @tip Pagination is handled automatically.
544
 *
545
 * @note Internal: This also provides the views for elgg_view_annotation().
546
 *
547
 * @note Internal: If the initial COUNT query returns 0, the $getter will not be called again.
548
 *
549
 * @param array    $options Any options from $getter options plus:
550
 *                   item_view => STR Optional. Alternative view used to render list items
551
 *                   full_view => BOOL Display full view of entities (default: false)
552
 *                   list_type => STR 'list', 'gallery', or 'table'
553
 *                   columns => ARR instances of Elgg\Views\TableColumn if list_type is "table"
554
 *                   list_type_toggle => BOOL Display gallery / list switch
555
 *                   pagination => BOOL Display pagination links
556
 *                   no_results => STR|Closure Message to display when there are no entities
557
 *
558
 * @param callable $getter  The entity getter function to use to fetch the entities.
559
 * @param callable $viewer  The function to use to view the entity list.
560
 *
561
 * @return string
562
 * @since 1.7
563
 * @see elgg_get_entities()
564
 * @see elgg_view_entity_list()
565
 */
566
function elgg_list_entities(array $options = [], $getter = 'elgg_get_entities', $viewer = 'elgg_view_entity_list') {
567
568 4
	elgg_register_rss_link();
569
570 4
	$offset_key = isset($options['offset_key']) ? $options['offset_key'] : 'offset';
571
572
	$defaults = [
573 4
		'offset' => (int) max(get_input($offset_key, 0), 0),
574 4
		'limit' => (int) max(get_input('limit', _elgg_config()->default_limit), 0),
575
		'full_view' => false,
576
		'list_type_toggle' => false,
577
		'pagination' => true,
578 4
		'no_results' => '',
579
	];
580
581 4
	$options = array_merge($defaults, $options);
582
583 4
	$entities = [];
584
	
585 4
	if (!$options['pagination']) {
586 3
		$options['count'] = false;
587 3
		$entities = call_user_func($getter, $options);
588 3
		unset($options['count']);
589
	} else {
590 1
		$options['count'] = true;
591 1
		$count = call_user_func($getter, $options);
592
	
593 1
		if ($count > 0) {
594
			$options['count'] = false;
595
			$entities = call_user_func($getter, $options);
596
		}
597
598 1
		$options['count'] = $count;
599
	}
600
	
601 4
	return call_user_func($viewer, $entities, $options);
602
}
603
604
/**
605
 * Returns a list of months in which entities were updated or created.
606
 *
607
 * @tip Use this to generate a list of archives by month for when entities were added or updated.
608
 *
609
 * @todo document how to pass in array for $subtype
610
 *
611
 * @warning Months are returned in the form YYYYMM.
612
 *
613
 * @param string $type           The type of entity
614
 * @param string $subtype        The subtype of entity
615
 * @param int    $container_guid The container GUID that the entities belong to
616
 * @param int    $ignored        Ignored parameter
617
 * @param string $order_by       Order_by SQL order by clause
618
 *
619
 * @return array|false Either an array months as YYYYMM, or false on failure
620
 */
621
function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $ignored = 0, $order_by = 'time_created') {
622
	return _elgg_services()->entityTable->getDates($type, $subtype, $container_guid, $order_by);
623
}
624
625
/**
626
 * Registers an entity type and subtype as a public-facing entity that should
627
 * be shown in search and by {@link elgg_list_registered_entities()}.
628
 *
629
 * @warning Entities that aren't registered here will not show up in search.
630
 *
631
 * @tip Add a language string item:type:subtype to make sure the items are display properly.
632
 *
633
 * @param string $type    The type of entity (object, site, user, group)
634
 * @param string $subtype The subtype to register (may be blank)
635
 *
636
 * @return bool Depending on success
637
 * @see get_registered_entity_types()
638
 */
639
function elgg_register_entity_type($type, $subtype = null) {
640 59
	$type = strtolower($type);
641 59
	if (!in_array($type, \Elgg\Config::getEntityTypes())) {
642
		return false;
643
	}
644
645 59
	$entities = _elgg_config()->registered_entities;
646 59
	if (!$entities) {
647 40
		$entities = [];
648
	}
649
650 59
	if (!isset($entities[$type])) {
651 40
		$entities[$type] = [];
652
	}
653
654 59
	if ($subtype) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $subtype of type null|string 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...
655 59
		$entities[$type][] = $subtype;
656
	}
657
658 59
	_elgg_config()->registered_entities = $entities;
659
660 59
	return true;
661
}
662
663
/**
664
 * Unregisters an entity type and subtype as a public-facing type.
665
 *
666
 * @warning With a blank subtype, it unregisters that entity type including
667
 * all subtypes. This must be called after all subtypes have been registered.
668
 *
669
 * @param string $type    The type of entity (object, site, user, group)
670
 * @param string $subtype The subtype to register (may be blank)
671
 *
672
 * @return bool Depending on success
673
 * @see elgg_register_entity_type()
674
 */
675
function elgg_unregister_entity_type($type, $subtype = null) {
676
	$type = strtolower($type);
677
	if (!in_array($type, \Elgg\Config::getEntityTypes())) {
678
		return false;
679
	}
680
681
	$entities = _elgg_config()->registered_entities;
682
	if (!$entities) {
683
		return false;
684
	}
685
686
	if (!isset($entities[$type])) {
687
		return false;
688
	}
689
690
	if ($subtype) {
691
		if (in_array($subtype, $entities[$type])) {
692
			$key = array_search($subtype, $entities[$type]);
693
			unset($entities[$type][$key]);
694
		} else {
695
			return false;
696
		}
697
	} else {
698
		unset($entities[$type]);
699
	}
700
701
	_elgg_config()->registered_entities = $entities;
702
	return true;
703
}
704
705
/**
706
 * Returns registered entity types and subtypes
707
 *
708
 * @param string $type The type of entity (object, site, user, group) or blank for all
709
 *
710
 * @return array|false Depending on whether entities have been registered
711
 * @see elgg_register_entity_type()
712
 */
713
function get_registered_entity_types($type = null) {
714 3
	$registered_entities = _elgg_config()->registered_entities;
715 3
	if (!$registered_entities) {
716 1
		return false;
717
	}
718
719 2
	if ($type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type null|string 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...
720
		$type = strtolower($type);
721
	}
722
723 2
	if (!empty($type) && !isset($registered_entities[$type])) {
724
		return false;
725
	}
726
727 2
	if (empty($type)) {
728 2
		return $registered_entities;
729
	}
730
731
	return $registered_entities[$type];
732
}
733
734
/**
735
 * Returns if the entity type and subtype have been registered with {@link elgg_register_entity_type()}.
736
 *
737
 * @param string $type    The type of entity (object, site, user, group)
738
 * @param string $subtype The subtype (may be blank)
739
 *
740
 * @return bool Depending on whether or not the type has been registered
741
 */
742
function is_registered_entity_type($type, $subtype = null) {
743
	$registered_entities = _elgg_config()->registered_entities;
744
	if (!$registered_entities) {
745
		return true;
746
	}
747
748
	$type = strtolower($type);
749
750
	// @todo registering a subtype implicitly registers the type.
751
	// see #2684
752
	if (!isset($registered_entities[$type])) {
753
		return false;
754
	}
755
756
	if ($subtype && !in_array($subtype, $registered_entities[$type])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $subtype of type null|string 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...
757
		return false;
758
	}
759
	return true;
760
}
761
762
/**
763
 * Returns a viewable list of entities based on the registered types.
764
 *
765
 * @see elgg_view_entity_list()
766
 *
767
 * @param array $options Any elgg_get_entity() options plus:
768
 *
769
 * 	full_view => BOOL Display full view entities
770
 *
771
 * 	list_type_toggle => BOOL Display gallery / list switch
772
 *
773
 * 	allowed_types => true|ARRAY True to show all types or an array of valid types.
774
 *
775
 * 	pagination => BOOL Display pagination links
776
 *
777
 * @return string A viewable list of entities
778
 * @since 1.7.0
779
 */
780
function elgg_list_registered_entities(array $options = []) {
781
	elgg_register_rss_link();
782
783
	$defaults = [
784
		'full_view' => false,
785
		'allowed_types' => true,
786
		'list_type_toggle' => false,
787
		'pagination' => true,
788
		'offset' => 0,
789
		'types' => [],
790
		'type_subtype_pairs' => [],
791
	];
792
793
	$options = array_merge($defaults, $options);
794
795
	$types = get_registered_entity_types();
796
797
	foreach ($types as $type => $subtype_array) {
1 ignored issue
show
The expression $types of type false is not traversable.
Loading history...
798
		if (in_array($type, $options['allowed_types']) || $options['allowed_types'] === true) {
799
			// you must explicitly register types to show up in here and in search for objects
800
			if ($type == 'object') {
801
				if (is_array($subtype_array) && count($subtype_array)) {
802
					$options['type_subtype_pairs'][$type] = $subtype_array;
803
				}
804
			} else {
805
				if (is_array($subtype_array) && count($subtype_array)) {
806
					$options['type_subtype_pairs'][$type] = $subtype_array;
807
				} else {
808
					$options['type_subtype_pairs'][$type] = ELGG_ENTITIES_ANY_VALUE;
809
				}
810
			}
811
		}
812
	}
813
814
	if (!empty($options['type_subtype_pairs'])) {
815
		$count = elgg_get_entities(array_merge(['count' => true], $options));
816
		if ($count > 0) {
817
			$entities = elgg_get_entities($options);
818
		} else {
819
			$entities = [];
820
		}
821
	} else {
822
		$count = 0;
823
		$entities = [];
824
	}
825
826
	$options['count'] = $count;
827
	return elgg_view_entity_list($entities, $options);
828
}
829
830
/**
831
 * Checks if $entity is an \ElggEntity and optionally for type and subtype.
832
 *
833
 * @tip Use this function in actions and views to check that you are dealing
834
 * with the correct type of entity.
835
 *
836
 * @param mixed  $entity  Entity
837
 * @param string $type    Entity type
838
 * @param string $subtype Entity subtype
839
 *
840
 * @return bool
841
 * @since 1.8.0
842
 */
843
function elgg_instanceof($entity, $type = null, $subtype = null) {
844 227
	$return = ($entity instanceof \ElggEntity);
845
846 227
	if ($type) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $type of type null|string 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...
847
		/* @var \ElggEntity $entity */
848 227
		$return = $return && ($entity->getType() == $type);
849
	}
850
851 227
	if ($subtype) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $subtype of type null|string 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...
852 7
		$return = $return && ($entity->getSubtype() == $subtype);
853
	}
854
855 227
	return $return;
856
}
857
858
/**
859
 * Checks options for the existing of site_guid or site_guids contents and reports a warning if found
860
 *
861
 * @param array $options array of options to check
862
 *
863
 * @return void
864
 */
865
function _elgg_check_unsupported_site_guid(array $options = []) {
866 1301
	$site_guid = elgg_extract('site_guid', $options, elgg_extract('site_guids', $options));
867 1301
	if ($site_guid === null) {
868 1301
		return;
869
	}
870
	
871
	$backtrace = debug_backtrace();
872
	// never show this call.
873
	array_shift($backtrace);
874
875
	if (!empty($backtrace[0]['class'])) {
876
		$warning = "Passing site_guid or site_guids to the method {$backtrace[0]['class']}::{$backtrace[0]['file']} is not supported.";
877
		$warning .= "Please update your usage of the method.";
878
	} else {
879
		$warning = "Passing site_guid or site_guids to the function {$backtrace[0]['function']} in {$backtrace[0]['file']} is not supported.";
880
		$warning .= "Please update your usage of the function.";
881
	}
882
883
	_elgg_services()->logger->warn($warning);
884
}
885
886
/**
887
 * Runs unit tests for the entity objects.
888
 *
889
 * @param string $hook  'unit_test'
890
 * @param string $type  'system'
891
 * @param array  $value Array of tests
892
 *
893
 * @return array
894
 * @access private
895
 * @codeCoverageIgnore
896
 */
897
function _elgg_entities_test($hook, $type, $value) {
898
	$value[] = ElggEntityUnitTest::class;
899
	$value[] = ElggCoreGetEntitiesFromAnnotationsTest::class;
900
	$value[] = ElggCoreGetEntitiesFromMetadataTest::class;
901
	$value[] = ElggCoreGetEntitiesFromPrivateSettingsTest::class;
902
	$value[] = ElggCoreGetEntitiesFromRelationshipTest::class;
903
	$value[] = ElggEntityPreloaderIntegrationTest::class;
904
	$value[] = ElggCoreObjectTest::class;
905
	return $value;
906
}
907
908
/**
909
 * Entities init function; establishes the default entity page handler
910
 *
911
 * @return void
912
 * @elgg_event_handler init system
913
 * @access private
914
 */
915
function _elgg_entities_init() {
916 31
	elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_entities_test');
917 31
}
918
919
/**
920
 * @see \Elgg\Application::loadCore Do not do work here. Just register for events.
921
 */
922
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
923 18
	$events->registerHandler('init', 'system', '_elgg_entities_init');
924
};
925