Test Failed
Push — master ( 8c47c2...3acf9f )
by Steve
12:37
created

engine/lib/elgglib.php (1 issue)

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

1
<?php
2
3
use Elgg\Filesystem\Directory;
4
use Elgg\Http\ResponseBuilder;
5
6
/**
7
 * Bootstrapping and helper procedural code available for use in Elgg core and plugins.
8
 *
9
 * @package Elgg.Core
10
 * @todo These functions can't be subpackaged because they cover a wide mix of
11
 * purposes and subsystems.  Many of them should be moved to more relevant files.
12
 */
13
14
/**
15
 * Get a reference to the global Application object
16
 *
17
 * @return Elgg\Application
18
 * @since 2.0.0
19
 */
20
function elgg() {
21 15
	return Elgg\Application::$_instance;
22
}
23
24
/**
25
 * Register a PHP file as a library.
26
 *
27
 * @see elgg_load_library
28
 *
29
 * @param string $name     The name of the library
30
 * @param string $location The location of the file
31
 *
32
 * @return void
33
 * @since 1.8.0
34
 */
35
function elgg_register_library($name, $location) {
36
	$libraries = _elgg_config()->libraries;
37
	if ($libraries === null) {
38
		$libraries = [];
39
	}
40
	$libraries[$name] = $location;
41
	_elgg_config()->libraries = $libraries;
42
}
43
44
/**
45
 * Load a PHP library.
46
 *
47
 * @see elgg_register_library
48
 *
49
 * @param string $name The name of the library
50
 *
51
 * @return void
52
 * @throws InvalidParameterException
53
 * @since 1.8.0
54
 */
55
function elgg_load_library($name) {
56
	static $loaded_libraries = [];
57
58
	if (in_array($name, $loaded_libraries)) {
59
		return;
60
	}
61
62
	$libraries = _elgg_config()->libraries;
63
64
	if (!isset($libraries[$name])) {
65
		$error = "$name is not a registered library";
66
		throw new \InvalidParameterException($error);
67
	}
68
69
	if (!include_once($libraries[$name])) {
70
		$error = "Could not load the $name library from {$libraries[$name]}";
71
		throw new \InvalidParameterException($error);
72
	}
73
74
	$loaded_libraries[] = $name;
75
}
76
77
/**
78
 * Forward to $location.
79
 *
80
 * Sends a 'Location: $location' header and exits.  If headers have already been sent, throws an exception.
81
 *
82
 * @param string $location URL to forward to browser to. This can be a path
83
 *                         relative to the network's URL.
84
 * @param string $reason   Short explanation for why we're forwarding. Set to
85
 *                         '404' to forward to error page. Default message is
86
 *                         'system'.
87
 *
88
 * @return void
89
 * @throws SecurityException|InvalidParameterException
90
 */
91
function forward($location = "", $reason = 'system') {
92
	if (headers_sent($file, $line)) {
93
		throw new \SecurityException("Redirect could not be issued due to headers already being sent. Halting execution for security. "
94
			. "Output started in file $file at line $line. Search http://learn.elgg.org/ for more information.");
95
	}
96
97
	_elgg_services()->responseFactory->redirect($location, $reason);
98
	exit;
99
}
100
101
/**
102
 * Set a response HTTP header
103
 *
104
 * @see header
105
 *
106
 * @param string $header  Header
107
 * @param bool   $replace Replace existing header
108
 * @return void
109
 * @since 2.3
110
 */
111
function elgg_set_http_header($header, $replace = true) {
112
	if (headers_sent($file, $line)) {
113
		_elgg_services()->logger->error("Cannot modify header information - headers already sent by
114 17
			(output started at $file:$line)");
115 17
	} else {
116 17
		header($header, $replace);
117
	}
118
119
	if (!preg_match('~^HTTP/\\d\\.\\d~', $header)) {
120
		list($name, $value) = explode(':', $header, 2);
121 17
		_elgg_services()->responseFactory->setHeader($name, ltrim($value), $replace);
122 17
	}
123 17
}
124
125 17
/**
126
 * Register a JavaScript file for inclusion
127
 *
128
 * This function handles adding JavaScript to a web page. If multiple
129
 * calls are made to register the same JavaScript file based on the $id
130
 * variable, only the last file is included. This allows a plugin to add
131
 * JavaScript from a view that may be called more than once. It also handles
132
 * more than one plugin adding the same JavaScript.
133
 *
134
 * jQuery plugins often have filenames such as jquery.rating.js. A best practice
135
 * is to base $name on the filename: "jquery.rating". It is recommended to not
136
 * use version numbers in the name.
137
 *
138
 * The JavaScript files can be local to the server or remote (such as
139
 * Google's CDN).
140
 *
141
 * @note Since 2.0, scripts with location "head" will also be output in the footer, but before
142
 *       those with location "footer".
143
 *
144
 * @param string $name     An identifier for the JavaScript library
145
 * @param string $url      URL of the JavaScript file
146
 * @param string $location Page location: head or footer. (default: head)
147
 * @param int    $priority Priority of the JS file (lower numbers load earlier)
148
 *
149
 * @return bool
150
 * @since 1.8.0
151
 */
152
function elgg_register_js($name, $url, $location = 'head', $priority = null) {
153
	return elgg_register_external_file('js', $name, $url, $location, $priority);
154
}
155
156
/**
157
 * Defines a JS lib as an AMD module. This is useful for shimming
158
 * traditional JS or for setting the paths of AMD modules.
159
 *
160
 * Calling multiple times for the same name will:
161
 *     * set the preferred path to the last call setting a path
162
 *     * overwrite the shimmed AMD modules with the last call setting a shimmed module
163
 *
164
 * Use elgg_require_js($name) to load on the current page.
165
 *
166
 * Calling this function is not needed if your JS are in views named like `module/name.js`
167
 * Instead, simply call elgg_require_js("module/name").
168
 *
169
 * @note The configuration is cached in simplecache, so logic should not depend on user-
170
 *       specific values like get_language().
171
 *
172
 * @param string $name   The module name
173
 * @param array  $config An array like the following:
174
 *                       array  'deps'    An array of AMD module dependencies
175
 *                       string 'exports' The name of the exported module
176
 *                       string 'src'     The URL to the JS. Can be relative.
177
 *
178
 * @return void
179
 */
180
function elgg_define_js($name, $config) {
181
	$src = elgg_extract('src', $config);
182
183
	if ($src) {
184
		$url = elgg_normalize_url($src);
185
		_elgg_services()->amdConfig->addPath($name, $url);
186
	}
187
188
	// shimmed module
189
	if (isset($config['deps']) || isset($config['exports'])) {
190
		_elgg_services()->amdConfig->addShim($name, $config);
191
	}
192
}
193
194
/**
195
 * Unregister a JavaScript file
196
 *
197
 * @param string $name The identifier for the JavaScript library
198
 *
199
 * @return bool
200
 * @since 1.8.0
201
 */
202
function elgg_unregister_js($name) {
203
	return elgg_unregister_external_file('js', $name);
204
}
205
206
/**
207
 * Load a JavaScript resource on this page
208
 *
209
 * This must be called before elgg_view_page(). It can be called before the
210
 * script is registered. If you do not want a script loaded, unregister it.
211
 *
212
 * @param string $name Identifier of the JavaScript resource
213
 *
214
 * @return void
215
 * @since 1.8.0
216
 */
217
function elgg_load_js($name) {
218
	elgg_load_external_file('js', $name);
219
}
220
221
222
/**
223
 * Request that Elgg load an AMD module onto the page.
224
 *
225
 * @param string $name The AMD module name.
226
 * @return void
227
 * @since 1.9.0
228
 */
229
function elgg_require_js($name) {
230
	_elgg_services()->amdConfig->addDependency($name);
231
}
232
233
/**
234
 * Cancel a request to load an AMD module onto the page.
235
 *
236
 * @note The elgg, jquery, and jquery-ui modules cannot be cancelled.
237
 *
238
 * @param string $name The AMD module name.
239
 * @return void
240
 * @since 2.1.0
241
 */
242
function elgg_unrequire_js($name) {
243
	_elgg_services()->amdConfig->removeDependency($name);
244
}
245
246
/**
247
 * Get the JavaScript URLs that are loaded
248
 *
249
 * @param string $location 'head' or 'footer'
250
 *
251
 * @return array
252
 * @since 1.8.0
253
 */
254
function elgg_get_loaded_js($location = 'head') {
255
	return elgg_get_loaded_external_files('js', $location);
256
}
257
258
/**
259
 * Register a CSS file for inclusion in the HTML head
260
 *
261
 * @param string $name     An identifier for the CSS file
262
 * @param string $url      URL of the CSS file
263
 * @param int    $priority Priority of the CSS file (lower numbers load earlier)
264
 *
265
 * @return bool
266
 * @since 1.8.0
267
 */
268
function elgg_register_css($name, $url, $priority = null) {
269
	return elgg_register_external_file('css', $name, $url, 'head', $priority);
270
}
271
272
/**
273
 * Unregister a CSS file
274
 *
275
 * @param string $name The identifier for the CSS file
276
 *
277
 * @return bool
278
 * @since 1.8.0
279
 */
280
function elgg_unregister_css($name) {
281
	return elgg_unregister_external_file('css', $name);
282
}
283
284
/**
285
 * Load a CSS file for this page
286
 *
287
 * This must be called before elgg_view_page(). It can be called before the
288
 * CSS file is registered. If you do not want a CSS file loaded, unregister it.
289
 *
290
 * @param string $name Identifier of the CSS file
291
 *
292
 * @return void
293
 * @since 1.8.0
294
 */
295
function elgg_load_css($name) {
296
	elgg_load_external_file('css', $name);
297
}
298
299
/**
300
 * Get the loaded CSS URLs
301
 *
302
 * @return array
303
 * @since 1.8.0
304
 */
305
function elgg_get_loaded_css() {
306
	return elgg_get_loaded_external_files('css', 'head');
307
}
308
309
/**
310
 * Core registration function for external files
311
 *
312
 * @param string $type     Type of external resource (js or css)
313
 * @param string $name     Identifier used as key
314
 * @param string $url      URL
315
 * @param string $location Location in the page to include the file
316
 * @param int    $priority Loading priority of the file
317
 *
318
 * @return bool
319
 * @since 1.8.0
320
 */
321
function elgg_register_external_file($type, $name, $url, $location, $priority = 500) {
322
	return _elgg_services()->externalFiles->register($type, $name, $url, $location, $priority);
323
}
324
325
/**
326
 * Unregister an external file
327
 *
328
 * @param string $type Type of file: js or css
329
 * @param string $name The identifier of the file
330
 *
331
 * @return bool
332
 * @since 1.8.0
333
 */
334
function elgg_unregister_external_file($type, $name) {
335
	return _elgg_services()->externalFiles->unregister($type, $name);
336
}
337
338
/**
339
 * Load an external resource for use on this page
340
 *
341
 * @param string $type Type of file: js or css
342
 * @param string $name The identifier for the file
343
 *
344
 * @return void
345
 * @since 1.8.0
346
 */
347
function elgg_load_external_file($type, $name) {
348
	_elgg_services()->externalFiles->load($type, $name);
349
}
350
351
/**
352
 * Get external resource descriptors
353
 *
354
 * @param string $type     Type of file: js or css
355
 * @param string $location Page location
356
 *
357
 * @return array
358
 * @since 1.8.0
359
 */
360
function elgg_get_loaded_external_files($type, $location) {
361
	return _elgg_services()->externalFiles->getLoadedFiles($type, $location);
362
}
363
364
/**
365
 * Returns a list of files in $directory.
366
 *
367
 * Only returns files.  Does not recurse into subdirs.
368
 *
369
 * @param string $directory  Directory to look in
370
 * @param array  $exceptions Array of filenames to ignore
371
 * @param array  $list       Array of files to append to
372
 * @param mixed  $extensions Array of extensions to allow, null for all. Use a dot: array('.php').
373
 *
374
 * @return array Filenames in $directory, in the form $directory/filename.
375
 */
376 View Code Duplication
function elgg_get_file_list($directory, $exceptions = [], $list = [], $extensions = null) {
377
378
	$directory = sanitise_filepath($directory);
379
	if ($handle = opendir($directory)) {
380
		while (($file = readdir($handle)) !== false) {
381
			if (!is_file($directory . $file) || in_array($file, $exceptions)) {
382
				continue;
383
			}
384
385
			if (is_array($extensions)) {
386
				if (in_array(strrchr($file, '.'), $extensions)) {
387
					$list[] = $directory . $file;
388
				}
389
			} else {
390
				$list[] = $directory . $file;
391
			}
392
		}
393
		closedir($handle);
394
	}
395
396
	return $list;
397
}
398
399
/**
400
 * Sanitise file paths ensuring that they begin and end with slashes etc.
401
 *
402
 * @param string $path         The path
403
 * @param bool   $append_slash Add tailing slash
404
 *
405
 * @return string
406
 */
407 View Code Duplication
function sanitise_filepath($path, $append_slash = true) {
408
	// Convert to correct UNIX paths
409
	$path = str_replace('\\', '/', $path);
410
	$path = str_replace('../', '/', $path);
411
	// replace // with / except when preceeded by :
412
	$path = preg_replace("/([^:])\/\//", "$1/", $path);
413
414
	// Sort trailing slash
415
	$path = trim($path);
416
	// rtrim defaults plus /
417
	$path = rtrim($path, " \n\t\0\x0B/");
418
419
	if ($append_slash) {
420
		$path = $path . '/';
421
	}
422
423
	return $path;
424
}
425
426
/**
427
 * Counts the number of messages, either globally or in a particular register
428
 *
429
 * @param string $register Optionally, the register
430
 *
431
 * @return integer The number of messages
432
 */
433
function count_messages($register = "") {
434
	return _elgg_services()->systemMessages->count($register);
435
}
436
437
/**
438
 * Display a system message on next page load.
439
 *
440
 * @param string|array $message Message or messages to add
441
 *
442
 * @return bool
443
 */
444
function system_message($message) {
445
	_elgg_services()->systemMessages->addSuccessMessage($message);
446
	return true;
447 9
}
448 9
449
/**
450
 * Display an error on next page load.
451
 *
452
 * @param string|array $error Error or errors to add
453
 *
454
 * @return bool
455
 */
456
function register_error($error) {
457
	_elgg_services()->systemMessages->addErrorMessage($error);
458
	return true;
459 17
}
460 17
461
/**
462
 * Get a copy of the current system messages.
463
 *
464
 * @return \Elgg\SystemMessages\RegisterSet
465
 * @since 2.1
466
 */
467
function elgg_get_system_messages() {
468
	return _elgg_services()->systemMessages->loadRegisters();
469
}
470
471
/**
472
 * Set the system messages. This will overwrite the state of all messages and errors!
473
 *
474
 * @param \Elgg\SystemMessages\RegisterSet $set Set of messages
475
 * @return void
476
 * @since 2.1
477
 */
478
function elgg_set_system_messages(\Elgg\SystemMessages\RegisterSet $set) {
479
	_elgg_services()->systemMessages->saveRegisters($set);
480
}
481
482
/**
483
 * Register a callback as an Elgg event handler.
484
 *
485
 * Events are emitted by Elgg when certain actions occur.  Plugins
486
 * can respond to these events or halt them completely by registering a handler
487
 * as a callback to an event.  Multiple handlers can be registered for
488
 * the same event and will be executed in order of $priority.
489
 *
490
 * For most events, any handler returning false will halt the execution chain and
491
 * cause the event to be "cancelled". For After Events, the return values of the
492
 * handlers will be ignored and all handlers will be called.
493
 *
494
 * This function is called with the event name, event type, and handler callback name.
495
 * Setting the optional $priority allows plugin authors to specify when the
496
 * callback should be run.  Priorities for plugins should be 1-1000.
497
 *
498
 * The callback is passed 3 arguments when called: $event, $type, and optional $params.
499
 *
500
 * $event is the name of event being emitted.
501
 * $type is the type of event or object concerned.
502
 * $params is an optional parameter passed that can include a related object.  See
503
 * specific event documentation for details on which events pass what parameteres.
504
 *
505
 * @tip If a priority isn't specified it is determined by the order the handler was
506
 * registered relative to the event and type.  For plugins, this generally means
507
 * the earlier the plugin is in the load order, the earlier the priorities are for
508
 * any event handlers.
509
 *
510
 * @tip $event and $object_type can use the special keyword 'all'.  Handler callbacks registered
511
 * with $event = all will be called for all events of type $object_type.  Similarly,
512
 * callbacks registered with $object_type = all will be called for all events of type
513
 * $event, regardless of $object_type.  If $event and $object_type both are 'all', the
514
 * handler callback will be called for all events.
515
 *
516
 * @tip Event handler callbacks are considered in the follow order:
517
 *  - Specific registration where 'all' isn't used.
518
 *  - Registration where 'all' is used for $event only.
519
 *  - Registration where 'all' is used for $type only.
520
 *  - Registration where 'all' is used for both.
521
 *
522
 * @warning If you use the 'all' keyword, you must have logic in the handler callback to
523
 * test the passed parameters before taking an action.
524
 *
525
 * @tip When referring to events, the preferred syntax is "event, type".
526
 *
527
 * Internal note: Events are stored in $CONFIG->events as:
528
 * <code>
529
 * $CONFIG->events[$event][$type][$priority] = $callback;
530
 * </code>
531
 *
532
 * @param string $event       The event type
533
 * @param string $object_type The object type
534
 * @param string $callback    The handler callback
535
 * @param int    $priority    The priority - 0 is default, negative before, positive after
536
 *
537
 * @return bool
538
 * @example documentation/events/basic.php
539
 * @example documentation/events/advanced.php
540
 * @example documentation/events/all.php
541
 */
542
function elgg_register_event_handler($event, $object_type, $callback, $priority = 500) {
543
	return _elgg_services()->events->registerHandler($event, $object_type, $callback, $priority);
544
}
545 93
546
/**
547
 * Unregisters a callback for an event.
548
 *
549
 * @param string $event       The event type
550
 * @param string $object_type The object type
551
 * @param string $callback    The callback. Since 1.11, static method callbacks will match dynamic methods
552
 *
553
 * @return bool true if a handler was found and removed
554
 * @since 1.7
555
 */
556
function elgg_unregister_event_handler($event, $object_type, $callback) {
557
	return _elgg_services()->events->unregisterHandler($event, $object_type, $callback);
558
}
559
560
/**
561
 * Clears all callback registrations for a event.
562
 *
563
 * @param string $event       The name of the event
564
 * @param string $object_type The objecttype of the event
565
 *
566
 * @return void
567
 * @since 2.3
568
 */
569
function elgg_clear_event_handlers($event, $object_type) {
570
	_elgg_services()->events->clearHandlers($event, $object_type);
571
}
572
573
/**
574
 * Trigger an Elgg Event and attempt to run all handler callbacks registered to that
575
 * event, type.
576
 *
577
 * This function attempts to run all handlers registered to $event, $object_type or
578
 * the special keyword 'all' for either or both. If a handler returns false, the
579
 * event will be cancelled (no further handlers will be called, and this function
580
 * will return false).
581
 *
582
 * $event is usually a verb: create, update, delete, annotation.
583
 *
584
 * $object_type is usually a noun: object, group, user, annotation, relationship, metadata.
585
 *
586
 * $object is usually an Elgg* object associated with the event.
587
 *
588
 * @warning Elgg events should only be triggered by core.  Plugin authors should use
589
 * {@link trigger_elgg_plugin_hook()} instead.
590
 *
591
 * @tip When referring to events, the preferred syntax is "event, type".
592
 *
593
 * @note Internal: Only rarely should events be changed, added, or removed in core.
594
 * When making changes to events, be sure to first create a ticket on Github.
595
 *
596
 * @note Internal: @tip Think of $object_type as the primary namespace element, and
597
 * $event as the secondary namespace.
598
 *
599
 * @param string $event       The event type
600
 * @param string $object_type The object type
601
 * @param string $object      The object involved in the event
602
 *
603
 * @return bool False if any handler returned false, otherwise true.
604
 * @example documentation/examples/events/trigger.php
605
 */
606
function elgg_trigger_event($event, $object_type, $object = null) {
607
	return _elgg_services()->events->trigger($event, $object_type, $object);
608
}
609 2
610
/**
611
 * Trigger a "Before event" indicating a process is about to begin.
612
 *
613
 * Like regular events, a handler returning false will cancel the process and false
614
 * will be returned.
615
 *
616
 * To register for a before event, append ":before" to the event name when registering.
617
 *
618
 * @param string $event       The event type. The fired event type will be appended with ":before".
619
 * @param string $object_type The object type
620
 * @param string $object      The object involved in the event
621
 *
622
 * @return bool False if any handler returned false, otherwise true
623
 *
624
 * @see elgg_trigger_event
625
 * @see elgg_trigger_after_event
626
 */
627
function elgg_trigger_before_event($event, $object_type, $object = null) {
628
	return _elgg_services()->events->triggerBefore($event, $object_type, $object);
629
}
630 85
631
/**
632
 * Trigger an "After event" indicating a process has finished.
633
 *
634
 * Unlike regular events, all the handlers will be called, their return values ignored.
635
 *
636
 * To register for an after event, append ":after" to the event name when registering.
637
 *
638
 * @param string $event       The event type. The fired event type will be appended with ":after".
639
 * @param string $object_type The object type
640
 * @param string $object      The object involved in the event
641
 *
642
 * @return true
643
 *
644
 * @see elgg_trigger_before_event
645
 */
646
function elgg_trigger_after_event($event, $object_type, $object = null) {
647
	return _elgg_services()->events->triggerAfter($event, $object_type, $object);
648
}
649 86
650
/**
651
 * Trigger an event normally, but send a notice about deprecated use if any handlers are registered.
652
 *
653
 * @param string $event       The event type
654
 * @param string $object_type The object type
655
 * @param string $object      The object involved in the event
656
 * @param string $message     The deprecation message
657
 * @param string $version     Human-readable *release* version: 1.9, 1.10, ...
658
 *
659
 * @return bool
660
 *
661
 * @see elgg_trigger_event
662
 */
663
function elgg_trigger_deprecated_event($event, $object_type, $object = null, $message = null, $version = null) {
664
	return _elgg_services()->events->triggerDeprecated($event, $object_type, $object, $message, $version);
665
}
666
667
/**
668
 * Register a callback as a plugin hook handler.
669
 *
670
 * Plugin hooks allow developers to losely couple plugins and features by
671
 * responding to and emitting {@link elgg_trigger_plugin_hook()} customizable hooks.
672
 * Handler callbacks can respond to the hook, change the details of the hook, or
673
 * ignore it.
674
 *
675
 * Multiple handlers can be registered for a plugin hook, and each callback
676
 * is called in order of priority.  If the return value of a handler is not
677
 * null, that value is passed to the next callback in the call stack.  When all
678
 * callbacks have been run, the final value is passed back to the caller
679
 * via {@link elgg_trigger_plugin_hook()}.
680
 *
681
 * Similar to Elgg Events, plugin hook handler callbacks are registered by passing
682
 * a hook, a type, and a priority.
683
 *
684
 * The callback is passed 4 arguments when called: $hook, $type, $value, and $params.
685
 *
686
 *  - str $hook The name of the hook.
687
 *  - str $type The type of hook.
688
 *  - mixed $value The return value of the last handler or the default
689
 *  value if no other handlers have been called.
690
 *  - mixed $params An optional array of parameters.  Used to provide additional
691
 *  information to plugins.
692
 *
693
 * @note Internal: Plugin hooks are stored in $CONFIG->hooks as:
694
 * <code>
695
 * $CONFIG->hooks[$hook][$type][$priority] = $callback;
696
 * </code>
697
 *
698
 * @tip Plugin hooks are similar to Elgg Events in that Elgg emits
699
 * a plugin hook when certain actions occur, but a plugin hook allows you to alter the
700
 * parameters, as well as halt execution.
701
 *
702
 * @tip If a priority isn't specified it is determined by the order the handler was
703
 * registered relative to the event and type.  For plugins, this generally means
704
 * the earlier the plugin is in the load order, the earlier the priorities are for
705
 * any event handlers.
706
 *
707
 * @tip Like Elgg Events, $hook and $type can use the special keyword 'all'.
708
 * Handler callbacks registered with $hook = all will be called for all hooks
709
 * of type $type.  Similarly, handlers registered with $type = all will be
710
 * called for all hooks of type $event, regardless of $object_type.  If $hook
711
 * and $type both are 'all', the handler will be called for all hooks.
712
 *
713
 * @tip Plugin hooks are sometimes used to gather lists from plugins.  This is
714
 * usually done by pushing elements into an array passed in $params.  Be sure
715
 * to append to and then return $value so you don't overwrite other plugin's
716
 * values.
717
 *
718
 * @warning Unlike Elgg Events, a handler that returns false will NOT halt the
719
 * execution chain.
720
 *
721
 * @param string   $hook     The name of the hook
722
 * @param string   $type     The type of the hook
723
 * @param callable $callback The name of a valid function or an array with object and method
724
 * @param int      $priority The priority - 500 is default, lower numbers called first
725
 *
726
 * @return bool
727
 *
728
 * @example hooks/register/basic.php Registering for a plugin hook and examining the variables.
729
 * @example hooks/register/advanced.php Registering for a plugin hook and changing the params.
730
 * @since 1.8.0
731
 */
732
function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority = 500) {
733
	return _elgg_services()->hooks->registerHandler($hook, $type, $callback, $priority);
734
}
735 94
736
/**
737
 * Unregister a callback as a plugin hook.
738
 *
739
 * @param string   $hook        The name of the hook
740
 * @param string   $entity_type The name of the type of entity (eg "user", "object" etc)
741
 * @param callable $callback    The PHP callback to be removed. Since 1.11, static method
742
 *                              callbacks will match dynamic methods
743
 *
744
 * @return void
745
 * @since 1.8.0
746
 */
747
function elgg_unregister_plugin_hook_handler($hook, $entity_type, $callback) {
748
	_elgg_services()->hooks->unregisterHandler($hook, $entity_type, $callback);
749
}
750 1
751 1
/**
752
 * Clears all callback registrations for a plugin hook.
753
 *
754
 * @param string $hook The name of the hook
755
 * @param string $type The type of the hook
756
 *
757
 * @return void
758
 * @since 2.0
759
 */
760
function elgg_clear_plugin_hook_handlers($hook, $type) {
761
	_elgg_services()->hooks->clearHandlers($hook, $type);
762
}
763
764
/**
765
 * Trigger a Plugin Hook and run all handler callbacks registered to that hook:type.
766
 *
767
 * This function runs all handlers registered to $hook, $type or
768
 * the special keyword 'all' for either or both.
769
 *
770
 * Use $params to send additional information to the handler callbacks.
771
 *
772
 * $returnvalue is the initial value to pass to the handlers, which can
773
 * change it by returning non-null values. It is useful to use $returnvalue
774
 * to set defaults. If no handlers are registered, $returnvalue is immediately
775
 * returned.
776
 *
777
 * Handlers that return null (or with no explicit return or return value) will
778
 * not change the value of $returnvalue.
779
 *
780
 * $hook is usually a verb: import, get_views, output.
781
 *
782
 * $type is usually a noun: user, ecml, page.
783
 *
784
 * @tip Like Elgg Events, $hook and $type can use the special keyword 'all'.
785
 * Handler callbacks registered with $hook = all will be called for all hooks
786
 * of type $type.  Similarly, handlers registered with $type = all will be
787
 * called for all hooks of type $event, regardless of $object_type.  If $hook
788
 * and $type both are 'all', the handler will be called for all hooks.
789
 *
790
 * @tip It's not possible for a plugin hook to change a non-null $returnvalue
791
 * to null.
792
 *
793
 * @note Internal: The checks for $hook and/or $type not being equal to 'all' is to
794
 * prevent a plugin hook being registered with an 'all' being called more than
795
 * once if the trigger occurs with an 'all'. An example in core of this is in
796
 * actions.php:
797
 * elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', ...)
798
 *
799
 * @see elgg_register_plugin_hook_handler()
800
 *
801
 * @param string $hook        The name of the hook to trigger ("all" will
802
 *                            trigger for all $types regardless of $hook value)
803
 * @param string $type        The type of the hook to trigger ("all" will
804
 *                            trigger for all $hooks regardless of $type value)
805
 * @param mixed  $params      Additional parameters to pass to the handlers
806
 * @param mixed  $returnvalue An initial return value
807
 *
808
 * @return mixed|null The return value of the last handler callback called
809
 *
810
 * @example hooks/trigger/basic.php    Trigger a hook that determines if execution
811
 *                                     should continue.
812
 * @example hooks/trigger/advanced.php Trigger a hook with a default value and use
813
 *                                     the results to populate a menu.
814
 * @example hooks/basic.php            Trigger and respond to a basic plugin hook.
815
 *
816
 * @since 1.8.0
817
 */
818
function elgg_trigger_plugin_hook($hook, $type, $params = null, $returnvalue = null) {
819
	return _elgg_services()->hooks->trigger($hook, $type, $params, $returnvalue);
820
}
821 119
822
/**
823
 * Returns an ordered array of hook handlers registered for $hook and $type.
824
 *
825
 * @param string $hook Hook name
826
 * @param string $type Hook type
827
 *
828
 * @return array
829
 *
830
 * @since 2.0.0
831
 */
832
function elgg_get_ordered_hook_handlers($hook, $type) {
833
	return _elgg_services()->hooks->getOrderedHandlers($hook, $type);
834
}
835
836
/**
837
 * Returns an ordered array of event handlers registered for $event and $type.
838
 *
839
 * @param string $event Event name
840
 * @param string $type  Object type
841
 *
842
 * @return array
843
 *
844
 * @since 2.0.0
845
 */
846
function elgg_get_ordered_event_handlers($event, $type) {
847
	return _elgg_services()->events->getOrderedHandlers($event, $type);
848
}
849
850
/**
851
 * Intercepts, logs, and displays uncaught exceptions.
852
 *
853
 * To use a viewtype other than failsafe, create the views:
854
 *  <viewtype>/messages/exceptions/admin_exception
855
 *  <viewtype>/messages/exceptions/exception
856
 * See the json viewtype for an example.
857
 *
858
 * @warning This function should never be called directly.
859
 *
860
 * @see http://www.php.net/set-exception-handler
861
 *
862
 * @param Exception $exception The exception being handled
863
 *
864
 * @return void
865
 * @access private
866
 */
867
function _elgg_php_exception_handler($exception) {
868
	$timestamp = time();
869
	error_log("Exception at time $timestamp: $exception");
870
871
	// Wipe any existing output buffer
872
	ob_end_clean();
873
874
	// make sure the error isn't cached
875
	header("Cache-Control: no-cache, must-revalidate", true);
876
	header('Expires: Fri, 05 Feb 1982 00:00:00 -0500', true);
877
878
	$CONFIG = _elgg_services()->config->getStorageObject();
879
880
	try {
881
		// allow custom scripts to trigger on exception
882
		// $CONFIG->exception_include can be set locally in settings.php
883
		// value should be a system path to a file to include
884
		if (!empty($CONFIG->exception_include) && is_file($CONFIG->exception_include)) {
885
			ob_start();
886
887
			// don't isolate, these scripts may use the local $exception var.
888
			include $CONFIG->exception_include;
889
890
			$exception_output = ob_get_clean();
891
			
892
			// if content is returned from the custom handler we will output
893
			// that instead of our default failsafe view
894
			if (!empty($exception_output)) {
895
				echo $exception_output;
896
				exit;
897
			}
898
		}
899
900
		if (elgg_is_xhr()) {
901
			elgg_set_viewtype('json');
902
			$response = new \Symfony\Component\HttpFoundation\JsonResponse(null, 500);
903
		} else {
904
			elgg_set_viewtype('failsafe');
905
			$response = new \Symfony\Component\HttpFoundation\Response('', 500);
906
		}
907
908
		if (elgg_is_admin_logged_in()) {
909
			$body = elgg_view("messages/exceptions/admin_exception", [
910
				'object' => $exception,
911
				'ts' => $timestamp
912
			]);
913
		} else {
914
			$body = elgg_view("messages/exceptions/exception", [
915
				'object' => $exception,
916
				'ts' => $timestamp
917
			]);
918
		}
919
920
		$response->setContent(elgg_view_page(elgg_echo('exception:title'), $body));
921
		$response->send();
922
	} catch (Exception $e) {
923
		$timestamp = time();
924
		$message = $e->getMessage();
925
		http_response_code(500);
926
		echo "Fatal error in exception handler. Check log for Exception at time $timestamp";
927
		error_log("Exception at time $timestamp : fatal error in exception handler : $message");
928
	}
929
}
930
931
/**
932
 * Intercepts catchable PHP errors.
933
 *
934
 * @warning This function should never be called directly.
935
 *
936
 * @internal
937
 * For catchable fatal errors, throws an Exception with the error.
938
 *
939
 * For non-fatal errors, depending upon the debug settings, either
940
 * log the error or ignore it.
941
 *
942
 * @see http://www.php.net/set-error-handler
943
 *
944
 * @param int    $errno    The level of the error raised
945
 * @param string $errmsg   The error message
946
 * @param string $filename The filename the error was raised in
947
 * @param int    $linenum  The line number the error was raised at
948
 * @param array  $vars     An array that points to the active symbol table where error occurred
949
 *
950
 * @return true
951
 * @throws Exception
952
 * @access private
953
 */
954
function _elgg_php_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
0 ignored issues
show
The parameter $vars is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
955
956
	$error = date("Y-m-d H:i:s (T)") . ": \"$errmsg\" in file $filename (line $linenum)";
957
958
	switch ($errno) {
959
		case E_USER_ERROR:
960
			if (!elgg_log("PHP: $error", 'ERROR')) {
961
				error_log("PHP ERROR: $error");
962
			}
963
			register_error("ERROR: $error");
964
965
			// Since this is a fatal error, we want to stop any further execution but do so gracefully.
966
			throw new \Exception($error);
967
			break;
968
969
		case E_WARNING :
970
		case E_USER_WARNING :
971
		case E_RECOVERABLE_ERROR: // (e.g. type hint violation)
972
			
973
			// check if the error wasn't suppressed by the error control operator (@)
974
			if (error_reporting() && !elgg_log("PHP: $error", 'WARNING')) {
975
				error_log("PHP WARNING: $error");
976
			}
977
			break;
978
979
		default:
980
			$CONFIG = _elgg_services()->config->getStorageObject();
981
			if (isset($CONFIG->debug) && $CONFIG->debug === 'NOTICE') {
982
				if (!elgg_log("PHP (errno $errno): $error", 'NOTICE')) {
983
					error_log("PHP NOTICE: $error");
984
				}
985
			}
986
	}
987
988
	return true;
989
}
990
991
992
/**
993
 * Display or log a message.
994
 *
995
 * If $level is >= to the debug setting in {@link $CONFIG->debug}, the
996
 * message will be sent to {@link elgg_dump()}.  Messages with lower
997
 * priority than {@link $CONFIG->debug} are ignored.
998
 *
999
 * Outputs all levels but NOTICE to screen by default.
1000
 *
1001
 * @note No messages will be displayed unless debugging has been enabled.
1002
 *
1003
 * @param string $message User message
1004
 * @param string $level   NOTICE | WARNING | ERROR
1005
 *
1006
 * @return bool
1007
 * @since 1.7.0
1008
 */
1009
function elgg_log($message, $level = 'NOTICE') {
1010
	static $levels = [
1011
		'INFO' => 200,
1012 2
		'NOTICE' => 250,
1013
		'WARNING' => 300,
1014
		'ERROR' => 400,
1015
	];
1016
1017
	if (!isset($levels[$level])) {
1018
		throw new \InvalidArgumentException("Invalid \$level value");
1019 2
	}
1020
1021
	$level = $levels[$level];
1022
	return _elgg_services()->logger->log($message, $level);
1023 2
}
1024 2
1025
/**
1026
 * Logs or displays $value.
1027
 *
1028
 * If $to_screen is true, $value is displayed to screen.  Else,
1029
 * it is handled by PHP's {@link error_log()} function.
1030
 *
1031
 * A {@elgg_plugin_hook debug log} is called.  If a handler returns
1032
 * false, it will stop the default logging method.
1033
 *
1034
 * @param mixed $value     The value
1035
 * @param bool  $to_screen Display to screen?
1036
 * @return void
1037
 * @since 1.7.0
1038
 */
1039
function elgg_dump($value, $to_screen = true) {
1040
	_elgg_services()->logger->dump($value, $to_screen);
1041
}
1042
1043
/**
1044
 * Get the current Elgg version information
1045
 *
1046
 * @param bool $human_readable Whether to return a human readable version (default: false)
1047
 *
1048
 * @return string|false Depending on success
1049
 * @since 1.9
1050
 */
1051
function elgg_get_version($human_readable = false) {
1052
	static $version, $release;
1053
	
1054
	if (!isset($version) || !isset($release)) {
1055
		$path = \Elgg\Application::elggDir()->getPath('version.php');
1056
		if (!is_file($path)) {
1057
			return false;
1058
		}
1059
		include $path;
1060
	}
1061
	
1062
	return $human_readable ? $release : $version;
1063
}
1064
1065
/**
1066
 * Log a notice about deprecated use of a function, view, etc.
1067
 *
1068
 * @param string $msg             Message to log
1069
 * @param string $dep_version     Human-readable *release* version: 1.7, 1.8, ...
1070
 * @param int    $backtrace_level How many levels back to display the backtrace.
1071
 *                                Useful if calling from functions that are called
1072
 *                                from other places (like elgg_view()). Set to -1
1073
 *                                for a full backtrace.
1074
 *
1075
 * @return bool
1076
 * @since 1.7.0
1077
 */
1078
function elgg_deprecated_notice($msg, $dep_version, $backtrace_level = 1) {
1079
	$backtrace_level += 1;
1080
	return _elgg_services()->deprecation->sendNotice($msg, $dep_version, $backtrace_level);
1081 3
}
1082 3
1083
/**
1084
 * Builds a URL from the a parts array like one returned by {@link parse_url()}.
1085
 *
1086
 * @note If only partial information is passed, a partial URL will be returned.
1087
 *
1088
 * @param array $parts       Associative array of URL components like parse_url() returns
1089
 *                           'user' and 'pass' parts are ignored because of security reasons
1090
 * @param bool  $html_encode HTML Encode the url?
1091
 *
1092
 * @see https://github.com/Elgg/Elgg/pull/8146#issuecomment-91544585
1093
 * @return string Full URL
1094
 * @since 1.7.0
1095
 */
1096
function elgg_http_build_url(array $parts, $html_encode = true) {
1097
	// build only what's given to us.
1098
	$scheme = isset($parts['scheme']) ? "{$parts['scheme']}://" : '';
1099
	$host = isset($parts['host']) ? "{$parts['host']}" : '';
1100 94
	$port = isset($parts['port']) ? ":{$parts['port']}" : '';
1101 94
	$path = isset($parts['path']) ? "{$parts['path']}" : '';
1102 94
	$query = isset($parts['query']) ? "?{$parts['query']}" : '';
1103 94
	$fragment = isset($parts['fragment']) ? "#{$parts['fragment']}" : '';
1104 94
1105 94
	$string = $scheme . $host . $port . $path . $query . $fragment;
1106
1107 94
	if ($html_encode) {
1108
		return htmlspecialchars($string, ENT_QUOTES, 'UTF-8', false);
1109 94
	} else {
1110 22
		return $string;
1111
	}
1112 72
}
1113
1114
/**
1115
 * Adds action tokens to URL
1116
 *
1117
 * As of 1.7.0 action tokens are required on all actions.
1118
 * Use this function to append action tokens to a URL's GET parameters.
1119
 * This will preserve any existing GET parameters.
1120
 *
1121
 * @note If you are using {@elgg_view input/form} you don't need to
1122
 * add tokens to the action.  The form view automatically handles
1123
 * tokens.
1124
 *
1125
 * @param string $url         Full action URL
1126
 * @param bool   $html_encode HTML encode the url? (default: false)
1127
 *
1128
 * @return string URL with action tokens
1129
 * @since 1.7.0
1130
 */
1131
function elgg_add_action_tokens_to_url($url, $html_encode = false) {
1132
	$url = elgg_normalize_url($url);
1133
	$components = parse_url($url);
1134
1135
	if (isset($components['query'])) {
1136
		$query = elgg_parse_str($components['query']);
1137
	} else {
1138
		$query = [];
1139
	}
1140
1141
	if (isset($query['__elgg_ts']) && isset($query['__elgg_token'])) {
1142
		return $url;
1143
	}
1144
1145
	// append action tokens to the existing query
1146
	$query['__elgg_ts'] = time();
1147
	$query['__elgg_token'] = generate_action_token($query['__elgg_ts']);
1148
	$components['query'] = http_build_query($query);
1149
1150
	// rebuild the full url
1151
	return elgg_http_build_url($components, $html_encode);
1152
}
1153
1154
/**
1155
 * Removes an element from a URL's query string.
1156
 *
1157
 * @note You can send a partial URL string.
1158
 *
1159
 * @param string $url     Full URL
1160
 * @param string $element The element to remove
1161
 *
1162
 * @return string The new URL with the query element removed.
1163
 * @since 1.7.0
1164
 */
1165
function elgg_http_remove_url_query_element($url, $element) {
1166
	return elgg_http_add_url_query_elements($url, [$element => null]);
1167
}
1168 20
1169
/**
1170
 * Sets elements in a URL's query string.
1171
 *
1172
 * @param string $url      The URL
1173
 * @param array  $elements Key/value pairs to set in the URL. If the value is null, the
1174
 *                         element is removed from the URL.
1175
 *
1176
 * @return string The new URL with the query strings added
1177
 * @since 1.7.0
1178
 */
1179
function elgg_http_add_url_query_elements($url, array $elements) {
1180
	$url_array = parse_url($url);
1181
1182 72
	if (isset($url_array['query'])) {
1183
		$query = elgg_parse_str($url_array['query']);
1184 72
	} else {
1185 35
		$query = [];
1186
	}
1187 37
1188
	foreach ($elements as $k => $v) {
1189
		if ($v === null) {
1190 72
			unset($query[$k]);
1191 52
		} else {
1192 24
			$query[$k] = $v;
1193
		}
1194 52
	}
1195
1196
	// why check path? A: if no path, this may be a relative URL like "?foo=1". In this case,
1197
	// the output "" would be interpreted the current URL, so in this case we *must* set
1198
	// a query to make sure elements are removed.
1199
	if ($query || empty($url_array['path'])) {
1200
		$url_array['query'] = http_build_query($query);
1201 72
	} else {
1202 47
		unset($url_array['query']);
1203
	}
1204 25
	$string = elgg_http_build_url($url_array, false);
1205
1206 72
	// Restore relative protocol to url if missing and is provided as part of the initial url (see #9874)
1207
	if (!isset($url['scheme']) && (substr($url, 0, 2) == '//')) {
1208
		$string = "//{$string}";
1209 72
	}
1210 3
	
1211
	return $string;
1212
}
1213 72
1214
/**
1215
 * Test if two URLs are functionally identical.
1216
 *
1217
 * @tip If $ignore_params is used, neither the name nor its value will be considered when comparing.
1218
 *
1219
 * @tip The order of GET params doesn't matter.
1220
 *
1221
 * @param string $url1          First URL
1222
 * @param string $url2          Second URL
1223
 * @param array  $ignore_params GET params to ignore in the comparison
1224
 *
1225
 * @return bool
1226
 * @since 1.8.0
1227
 */
1228
function elgg_http_url_is_identical($url1, $url2, $ignore_params = ['offset', 'limit']) {
1229
	$url1 = elgg_normalize_url($url1);
1230
	$url2 = elgg_normalize_url($url2);
1231 29
1232 29
	// @todo - should probably do something with relative URLs
1233
1234
	if ($url1 == $url2) {
1235
		return true;
1236 29
	}
1237 24
1238
	$url1_info = parse_url($url1);
1239
	$url2_info = parse_url($url2);
1240 5
1241 5
	if (isset($url1_info['path'])) {
1242
		$url1_info['path'] = trim($url1_info['path'], '/');
1243 5
	}
1244 5
	if (isset($url2_info['path'])) {
1245
		$url2_info['path'] = trim($url2_info['path'], '/');
1246 5
	}
1247 5
1248
	// compare basic bits
1249
	$parts = ['scheme', 'host', 'path'];
1250
1251 5
	foreach ($parts as $part) {
1252
		if ((isset($url1_info[$part]) && isset($url2_info[$part]))
1253 5
		&& $url1_info[$part] != $url2_info[$part]) {
1254 5
			return false;
1255 5
		} elseif (isset($url1_info[$part]) && !isset($url2_info[$part])) {
1256
			return false;
1257 5
		} elseif (!isset($url1_info[$part]) && isset($url2_info[$part])) {
1258
			return false;
1259 5
		}
1260 5
	}
1261
1262
	// quick compare of get params
1263
	if (isset($url1_info['query']) && isset($url2_info['query'])
1264
	&& $url1_info['query'] == $url2_info['query']) {
1265 5
		return true;
1266 5
	}
1267
1268
	// compare get params that might be out of order
1269
	$url1_params = [];
1270
	$url2_params = [];
1271 5
1272 5
	if (isset($url1_info['query'])) {
1273
		if ($url1_info['query'] = html_entity_decode($url1_info['query'])) {
1274 5
			$url1_params = elgg_parse_str($url1_info['query']);
1275 5
		}
1276 5
	}
1277
1278
	if (isset($url2_info['query'])) {
1279
		if ($url2_info['query'] = html_entity_decode($url2_info['query'])) {
1280 5
			$url2_params = elgg_parse_str($url2_info['query']);
1281 5
		}
1282 5
	}
1283
1284
	// drop ignored params
1285
	foreach ($ignore_params as $param) {
1286
		if (isset($url1_params[$param])) {
1287 5
			unset($url1_params[$param]);
1288 4
		}
1289 3
		if (isset($url2_params[$param])) {
1290
			unset($url2_params[$param]);
1291 4
		}
1292 4
	}
1293
1294
	// array_diff_assoc only returns the items in arr1 that aren't in arrN
1295
	// but not the items that ARE in arrN but NOT in arr1
1296
	// if arr1 is an empty array, this function will return 0 no matter what.
1297
	// since we only care if they're different and not how different,
1298
	// add the results together to get a non-zero (ie, different) result
1299
	$diff_count = count(array_diff_assoc($url1_params, $url2_params));
1300
	$diff_count += count(array_diff_assoc($url2_params, $url1_params));
1301 5
	if ($diff_count > 0) {
1302 5
		return false;
1303 5
	}
1304 2
1305
	return true;
1306
}
1307 3
1308
/**
1309
 * Signs provided URL with a SHA256 HMAC key
1310
 *
1311
 * @note Signed URLs do not offer CSRF protection and should not be used instead of action tokens.
1312
 *
1313
 * @param string $url     URL to sign
1314
 * @param string $expires Expiration time
1315
 *                        A string suitable for strtotime()
1316
 *                        Falsey values indicate non-expiring URL
1317
 * @return string
1318
 */
1319
function elgg_http_get_signed_url($url, $expires = false) {
1320
	return _elgg_services()->urlSigner->sign($url, $expires);
1321
}
1322
1323
/**
1324
 * Validates if the HMAC signature of the URL is valid
1325
 *
1326
 * @param string $url URL to validate
1327
 * @return bool
1328
 */
1329
function elgg_http_validate_signed_url($url) {
1330
	return _elgg_services()->urlSigner->isValid($url);
1331
}
1332
1333
/**
1334
 * Validates if the HMAC signature of the current request is valid
1335
 * Issues 403 response if signature is inalid
1336
 * @return void
1337
 */
1338
function elgg_signed_request_gatekeeper() {
1339
	if (!elgg_http_validate_signed_url(current_page_url())) {
1340
		register_error(elgg_echo('invalid_request_signature'));
1341
		forward('', '403');
1342
	}
1343
}
1344
1345
/**
1346
 * Checks for $array[$key] and returns its value if it exists, else
1347
 * returns $default.
1348
 *
1349
 * Shorthand for $value = (isset($array['key'])) ? $array['key'] : 'default';
1350
 *
1351
 * @param string $key     Key to check in the source array
1352
 * @param array  $array   Source array
1353
 * @param mixed  $default Value to return if key is not found
1354
 * @param bool   $strict  Return array key if it's set, even if empty. If false,
1355
 *                        return $default if the array key is unset or empty.
1356
 *
1357
 * @return mixed
1358
 * @since 1.8.0
1359
 */
1360
function elgg_extract($key, $array, $default = null, $strict = true) {
1361
	if (!is_array($array)) {
1362
		return $default;
1363 213
	}
1364
1365
	if ($strict) {
1366
		return (isset($array[$key])) ? $array[$key] : $default;
1367 213
	} else {
1368 213
		return (isset($array[$key]) && !empty($array[$key])) ? $array[$key] : $default;
1369
	}
1370 48
}
1371
1372
/**
1373
 * Extract class names from an array with key "class", optionally merging into a preexisting set.
1374
 *
1375
 * @param array           $array    Source array
1376
 * @param string|string[] $existing Existing name(s)
1377
 * @return string[]
1378
 *
1379
 * @since 2.3.0
1380
 */
1381
function elgg_extract_class(array $array, $existing = []) {
1382
	$existing = empty($existing) ? [] : (array) $existing;
1383
1384 3
	$merge = (array) elgg_extract('class', $array, []);
1385
1386 3
	array_splice($existing, count($existing), 0, $merge);
1387
1388 3
	return array_values(array_unique($existing));
1389
}
1390 3
1391
/**
1392
 * Sorts a 3d array by specific element.
1393
 *
1394
 * @warning Will re-index numeric indexes.
1395
 *
1396
 * @note This operates the same as the built-in sort functions.
1397
 * It sorts the array and returns a bool for success.
1398
 *
1399
 * Do this: elgg_sort_3d_array_by_value($my_array);
1400
 * Not this: $my_array = elgg_sort_3d_array_by_value($my_array);
1401
 *
1402
 * @param array  &$array     Array to sort
1403
 * @param string $element    Element to sort by
1404
 * @param int    $sort_order PHP sort order
1405
 *                           {@link http://us2.php.net/array_multisort}
1406
 * @param int    $sort_type  PHP sort type
1407
 *                           {@link http://us2.php.net/sort}
1408
 *
1409
 * @return bool
1410
 */
1411
function elgg_sort_3d_array_by_value(&$array, $element, $sort_order = SORT_ASC, $sort_type = SORT_LOCALE_STRING) {
1412
1413
	$sort = [];
1414
1415
	foreach ($array as $v) {
1416
		if (isset($v[$element])) {
1417
			$sort[] = strtolower($v[$element]);
1418
		} else {
1419
			$sort[] = null;
1420
		}
1421
	};
1422
1423
	return array_multisort($sort, $sort_order, $sort_type, $array);
1424
}
1425
1426
/**
1427
 * Return the state of a php.ini setting as a bool
1428
 *
1429
 * @warning Using this on ini settings that are not boolean
1430
 * will be inaccurate!
1431
 *
1432
 * @param string $ini_get_arg The INI setting
1433
 *
1434
 * @return bool Depending on whether it's on or off
1435
 */
1436
function ini_get_bool($ini_get_arg) {
1437
	$temp = strtolower(ini_get($ini_get_arg));
1438
1439
	if ($temp == '1' || $temp == 'on' || $temp == 'true') {
1440
		return true;
1441
	}
1442
	return false;
1443
}
1444
1445
/**
1446
 * Returns a PHP INI setting in bytes.
1447
 *
1448
 * @tip Use this for arithmetic when determining if a file can be uploaded.
1449
 *
1450
 * @param string $setting The php.ini setting
1451
 *
1452
 * @return int
1453
 * @since 1.7.0
1454
 * @link http://www.php.net/manual/en/function.ini-get.php
1455
 */
1456
function elgg_get_ini_setting_in_bytes($setting) {
1457
	// retrieve INI setting
1458
	$val = ini_get($setting);
1459
1460
	// convert INI setting when shorthand notation is used
1461
	$last = strtolower($val[strlen($val) - 1]);
1462
	if (in_array($last, ['g', 'm', 'k'])) {
1463
		$val = substr($val, 0, -1);
1464
	}
1465
	$val = (int) $val;
1466
	switch ($last) {
1467
		case 'g':
1468
			$val *= 1024;
1469
			// fallthrough intentional
1470
		case 'm':
1471
			$val *= 1024;
1472
			// fallthrough intentional
1473
		case 'k':
1474
			$val *= 1024;
1475
	}
1476
1477
	// return byte value
1478
	return $val;
1479
}
1480
1481
/**
1482
 * Returns true is string is not empty, false, or null.
1483
 *
1484
 * Function to be used in array_filter which returns true if $string is not null.
1485
 *
1486
 * @param string $string The string to test
1487
 *
1488
 * @return bool
1489
 * @todo This is used once in metadata.php.  Use a lambda function instead.
1490
 */
1491
function is_not_null($string) {
1492
	if (($string === '') || ($string === false) || ($string === null)) {
1493
		return false;
1494 1
	}
1495
1496
	return true;
1497
}
1498 1
1499
/**
1500
 * Normalise the singular keys in an options array to plural keys.
1501
 *
1502
 * Used in elgg_get_entities*() functions to support shortcutting plural
1503
 * names by singular names.
1504
 *
1505
 * @param array $options   The options array. $options['keys'] = 'values';
1506
 * @param array $singulars A list of singular words to pluralize by adding 's'.
1507
 *
1508
 * @return array
1509
 * @since 1.7.0
1510
 * @access private
1511
 */
1512
function _elgg_normalize_plural_options_array($options, $singulars) {
1513
	foreach ($singulars as $singular) {
1514
		$plural = $singular . 's';
1515 99
1516 99
		if (array_key_exists($singular, $options)) {
1517
			if ($options[$singular] === ELGG_ENTITIES_ANY_VALUE) {
1518 99
				$options[$plural] = $options[$singular];
1519 99
			} else {
1520
				// Test for array refs #2641
1521
				if (!is_array($options[$singular])) {
1522
					$options[$plural] = [$options[$singular]];
1523 99
				} else {
1524 99
					$options[$plural] = $options[$singular];
1525
				}
1526
			}
1527
		}
1528
1529
		unset($options[$singular]);
1530
	}
1531 99
1532
	return $options;
1533
}
1534 99
1535
/**
1536
 * Emits a shutdown:system event upon PHP shutdown, but before database connections are dropped.
1537
 *
1538
 * @tip Register for the shutdown:system event to perform functions at the end of page loads.
1539
 *
1540
 * @warning Using this event to perform long-running functions is not very
1541
 * useful.  Servers will hold pages until processing is done before sending
1542
 * them out to the browser.
1543
 *
1544
 * @see http://www.php.net/register-shutdown-function
1545
 *
1546
 * @internal This is registered in \Elgg\Application::create()
1547
 *
1548
 * @return void
1549
 * @see register_shutdown_hook()
1550
 * @access private
1551
 */
1552
function _elgg_shutdown_hook() {
1553
	try {
1554
		_elgg_services()->logger->setDisplay(false);
1555
		elgg_trigger_event('shutdown', 'system');
1556
1557
		$time = (float) (microtime(true) - $GLOBALS['START_MICROTIME']);
1558
		$uri = _elgg_services()->request->server->get('REQUEST_URI', 'CLI');
1559
		// demoted to NOTICE from DEBUG so javascript is not corrupted
1560
		elgg_log("Page {$uri} generated in $time seconds", 'INFO');
1561
	} catch (Exception $e) {
1562
		$message = 'Error: ' . get_class($e) . ' thrown within the shutdown handler. ';
1563
		$message .= "Message: '{$e->getMessage()}' in file {$e->getFile()} (line {$e->getLine()})";
1564
		error_log($message);
1565
		error_log("Exception trace stack: {$e->getTraceAsString()}");
1566
	}
1567
1568
	// Prevent an APC session bug: https://bugs.php.net/bug.php?id=60657
1569
	session_write_close();
1570
}
1571
1572
/**
1573
 * Serve individual views for Ajax.
1574
 *
1575
 * /ajax/view/<view_name>?<key/value params>
1576
 * /ajax/form/<action_name>?<key/value params>
1577
 *
1578
 * @param string[] $segments URL segments (not including "ajax")
1579
 * @return ResponseBuilder
1580
 *
1581
 * @see elgg_register_ajax_view()
1582
 * @elgg_pagehandler ajax
1583
 * @access private
1584
 */
1585
function _elgg_ajax_page_handler($segments) {
1586
	elgg_ajax_gatekeeper();
1587
1588 17
	if (count($segments) < 2) {
1589
		return false;
1590 17
	}
1591
1592
	if ($segments[0] === 'view' || $segments[0] === 'form') {
1593
		if ($segments[0] === 'view') {
1594 17
			// ignore 'view/'
1595 17
			$view = implode('/', array_slice($segments, 1));
1596
		} else {
1597 13
			// form views start with "forms", not "form"
1598
			$view = 'forms/' . implode('/', array_slice($segments, 1));
1599
		}
1600 4
1601
		$ajax_api = _elgg_services()->ajax;
1602
		$allowed_views = $ajax_api->getViews();
1603 17
		
1604 17
		// cacheable views are always allowed
1605
		if (!in_array($view, $allowed_views) && !_elgg_services()->views->isCacheableView($view)) {
1606
			return elgg_error_response("Ajax view '$view' was not registered", REFERRER, ELGG_HTTP_FORBIDDEN);
1607 17
		}
1608 4
1609
		// pull out GET parameters through filter
1610
		$vars = [];
1611
		foreach (_elgg_services()->request->query->keys() as $name) {
1612 13
			$vars[$name] = get_input($name);
1613 13
		}
1614 7
1615
		if (isset($vars['guid'])) {
1616
			$vars['entity'] = get_entity($vars['guid']);
1617 13
		}
1618
1619
		$content_type = '';
1620
		if ($segments[0] === 'view') {
1621 13
			$output = elgg_view($view, $vars);
1622 13
1623 11
			// Try to guess the mime-type
1624
			switch ($segments[1]) {
1625
				case "js":
1626 11
					$content_type = 'text/javascript;charset=utf-8';
1627 11
					break;
1628 2
				case "css":
1629 2
					$content_type = 'text/css;charset=utf-8';
1630 9
					break;
1631 2
				default :
1632 2
					if (_elgg_services()->views->isCacheableView($view)) {
1633
						$file = _elgg_services()->views->findViewFile($view, elgg_get_viewtype());
1634 7
						$content_type = (new \Elgg\Filesystem\MimeTypeDetector())->getType($file, 'text/html');
1635 2
					}
1636 2
					break;
1637
			}
1638 11
		} else {
1639
			$action = implode('/', array_slice($segments, 1));
1640
			$output = elgg_view_form($action, [], $vars);
1641 2
		}
1642 2
1643
		if ($content_type) {
1644
			elgg_set_http_header("Content-Type: $content_type");
1645 13
		}
1646 6
		
1647
		return elgg_ok_response($output);
1648
	}
1649 13
1650
	return false;
1651
}
1652
1653
/**
1654
 * Handle requests for /favicon.ico
1655
 *
1656
 * @param string[] $segments The URL segments
1657
 * @return bool
1658
 * @access private
1659
 * @since 1.10
1660
 */
1661
function _elgg_favicon_page_handler($segments) {
1662
	header("HTTP/1.1 404 Not Found", true, 404);
1663
1664
	header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+1 week")), true);
1665
	header("Pragma: public", true);
1666
	header("Cache-Control: public", true);
1667
1668
	header('Content-Type: image/x-icon');
1669
	echo elgg_view('graphics/favicon.ico');
1670
1671
	return true;
1672
}
1673
1674
/**
1675
 * Reverses the ordering in an ORDER BY clause.  This is achived by replacing
1676
 * asc with desc, or appending desc to the end of the clause.
1677
 *
1678
 * This is used mostly for elgg_get_entities() and other similar functions.
1679
 *
1680
 * @param string $order_by An order by clause
1681
 * @access private
1682
 * @return string
1683
 * @access private
1684
 */
1685
function _elgg_sql_reverse_order_by_clause($order_by) {
1686
	$order_by = strtolower($order_by);
1687
1688
	if (strpos($order_by, ' asc') !== false) {
1689
		$return = str_replace(' asc', ' desc', $order_by);
1690
	} elseif (strpos($order_by, ' desc') !== false) {
1691
		$return = str_replace(' desc', ' asc', $order_by);
1692
	} else {
1693
		// no order specified, so default to desc since mysql defaults to asc
1694
		$return = $order_by . ' desc';
1695
	}
1696
1697
	return $return;
1698
}
1699
1700
/**
1701
 * Enable objects with an enable() method.
1702
 *
1703
 * Used as a callback for \ElggBatch.
1704
 *
1705
 * @todo why aren't these static methods on \ElggBatch?
1706
 *
1707
 * @param object $object The object to enable
1708
 * @return bool
1709
 * @access private
1710
 */
1711
function elgg_batch_enable_callback($object) {
1712
	// our db functions return the number of rows affected...
1713
	return $object->enable() ? true : false;
1714
}
1715
1716
/**
1717
 * Disable objects with a disable() method.
1718
 *
1719
 * Used as a callback for \ElggBatch.
1720
 *
1721
 * @param object $object The object to disable
1722
 * @return bool
1723
 * @access private
1724
 */
1725
function elgg_batch_disable_callback($object) {
1726
	// our db functions return the number of rows affected...
1727
	return $object->disable() ? true : false;
1728
}
1729
1730
/**
1731
 * Delete objects with a delete() method.
1732
 *
1733
 * Used as a callback for \ElggBatch.
1734
 *
1735
 * @param object $object The object to disable
1736
 * @return bool
1737
 * @access private
1738
 */
1739
function elgg_batch_delete_callback($object) {
1740
	// our db functions return the number of rows affected...
1741
	return $object->delete() ? true : false;
1742
}
1743
1744
/**
1745
 * Checks if there are some constraints on the options array for
1746
 * potentially dangerous operations.
1747
 *
1748
 * @param array  $options Options array
1749
 * @param string $type    Options type: metadata, annotation or river
1750
 * @return bool
1751
 * @access private
1752
 */
1753
function _elgg_is_valid_options_for_batch_operation($options, $type) {
1754
	if (!$options || !is_array($options)) {
1755
		return false;
1756 2
	}
1757
1758
	// at least one of these is required.
1759
	$required = [
1760
		// generic restraints
1761
		'guid', 'guids'
1762
	];
1763 2
1764
	switch ($type) {
1765
		case 'metadata':
1766
			$metadata_required = [
1767 2
				'metadata_owner_guid', 'metadata_owner_guids',
1768
				'metadata_name', 'metadata_names',
1769 1
				'metadata_value', 'metadata_values'
1770
			];
1771
1772
			$required = array_merge($required, $metadata_required);
1773
			break;
1774 1
1775 1
		case 'annotations':
1776
		case 'annotation':
1777 2
			$annotations_required = [
1778 2
				'annotation_owner_guid', 'annotation_owner_guids',
1779
				'annotation_name', 'annotation_names',
1780 2
				'annotation_value', 'annotation_values'
1781
			];
1782
1783
			$required = array_merge($required, $annotations_required);
1784
			break;
1785 2
1786 2
		case 'river':
1787
			// overriding generic restraints as guids isn't supported in river
1788 1
			$required = [
1789
				'id', 'ids',
1790
				'subject_guid', 'subject_guids',
1791 1
				'object_guid', 'object_guids',
1792
				'target_guid', 'target_guids',
1793
				'annotation_id', 'annotation_ids',
1794
				'view', 'views',
1795
			];
1796
			break;
1797
		
1798 1
		default:
1799
			return false;
1800
	}
1801
1802
	foreach ($required as $key) {
1803
		// check that it exists and is something.
1804 2
		if (isset($options[$key]) && $options[$key]) {
1805
			return true;
1806 2
		}
1807 2
	}
1808
1809
	return false;
1810
}
1811
1812
/**
1813
 * Intercepts the index page when Walled Garden mode is enabled.
1814
 *
1815
 * @return ResponseBuilder
1816
 * @access private
1817
 */
1818
function _elgg_walled_garden_index() {
1819
	return elgg_ok_response(elgg_view_resource('walled_garden'));
1820
}
1821
1822
/**
1823
 * Checks the status of the Walled Garden and forwards to a login page
1824
 * if required.
1825
 *
1826
 * If the site is in Walled Garden mode, all page except those registered as
1827
 * plugin pages by {@elgg_hook public_pages walled_garden} will redirect to
1828
 * a login page.
1829
 *
1830
 * @since 1.8.0
1831
 * @elgg_event_handler init system
1832
 * @return void
1833
 * @access private
1834
 */
1835
function _elgg_walled_garden_init() {
1836
	if (!elgg_get_config('walled_garden')) {
1837
		return;
1838
	}
1839
1840
	elgg_register_css('elgg.walled_garden', elgg_get_simplecache_url('walled_garden.css'));
1841
1842
	elgg_register_plugin_hook_handler('register', 'menu:walled_garden', '_elgg_walled_garden_menu');
1843
1844
	elgg_register_page_handler('walled_garden', '_elgg_walled_garden_ajax_handler');
1845
1846
	if (elgg_get_config('default_access') == ACCESS_PUBLIC) {
1847
		elgg_set_config('default_access', ACCESS_LOGGED_IN);
1848
	}
1849
1850
	elgg_register_plugin_hook_handler('access:collections:write', 'all', '_elgg_walled_garden_remove_public_access', 9999);
1851
1852
	if (!elgg_is_logged_in()) {
1853
		// override the front page
1854
		elgg_register_page_handler('', '_elgg_walled_garden_index');
1855
	}
1856
}
1857
1858
/**
1859
 * Adds home link to walled garden menu
1860
 *
1861
 * @param string $hook         'register'
1862
 * @param string $type         'menu:walled_garden'
1863
 * @param array  $return_value Current menu items
1864
 * @param array  $params       Optional menu parameters
1865
 *
1866
 * @return array
1867
 *
1868
 * @access private
1869
 */
1870
function _elgg_walled_garden_menu($hook, $type, $return_value, $params) {
1871
	
1872
	if (current_page_url() === elgg_get_site_url()) {
1873
		return;
1874
	}
1875
	
1876
	$return_value[] = \ElggMenuItem::factory([
1877
		'name' => 'home',
1878
		'href' => '/',
1879
		'text' => elgg_echo('walled_garden:home'),
1880
		'priority' => 10,
1881
	]);
1882
1883
	return $return_value;
1884
}
1885
1886
/**
1887
 * Remove public access for walled gardens
1888
 *
1889
 * @param string $hook
1890
 * @param string $type
1891
 * @param array $accesses
1892
 * @return array
1893
 * @access private
1894
 */
1895
function _elgg_walled_garden_remove_public_access($hook, $type, $accesses) {
1896
	if (isset($accesses[ACCESS_PUBLIC])) {
1897
		unset($accesses[ACCESS_PUBLIC]);
1898
	}
1899
	return $accesses;
1900
}
1901
1902
/**
1903
 * Elgg's main init.
1904
 *
1905
 * Handles core actions for comments, the JS pagehandler, and the shutdown function.
1906
 *
1907
 * @elgg_event_handler init system
1908
 * @return void
1909
 * @access private
1910
 */
1911
function _elgg_init() {
1912
	elgg_register_action('entity/delete');
1913
	elgg_register_action('comment/save');
1914
	elgg_register_action('comment/delete');
1915
1916
	elgg_register_page_handler('ajax', '_elgg_ajax_page_handler');
1917
	elgg_register_page_handler('favicon.ico', '_elgg_favicon_page_handler');
1918
1919
	elgg_register_page_handler('manifest.json', function() {
1920
		$site = elgg_get_site_entity();
1921
		$resource = new \Elgg\Http\WebAppManifestResource($site);
1922
		header('Content-Type: application/json;charset=utf-8');
1923
		echo json_encode($resource->get());
1924
		return true;
1925
	});
1926
1927
	elgg_register_plugin_hook_handler('head', 'page', function($hook, $type, array $result) {
1928
		$result['links']['manifest'] = [
1929
			'rel' => 'manifest',
1930
			'href' => elgg_normalize_url('/manifest.json'),
1931
		];
1932
1933
		return $result;
1934
	});
1935
1936
	if (_elgg_services()->config->getVolatile('enable_profiling')) {
1937
		/**
1938
		 * @see \Elgg\Profiler::handlePageOutput
1939
		 */
1940
		elgg_register_plugin_hook_handler('output', 'page', [\Elgg\Profiler::class, 'handlePageOutput'], 999);
1941
	}
1942
}
1943
1944
/**
1945
 * Adds unit tests for the general API.
1946
 *
1947
 * @param string $hook   unit_test
1948
 * @param string $type   system
1949
 * @param array  $value  array of test files
1950
 * @param array  $params empty
1951
 *
1952
 * @elgg_plugin_hook unit_tests system
1953
 * @return array
1954
 * @access private
1955
 */
1956
function _elgg_api_test($hook, $type, $value, $params) {
1957
	global $CONFIG;
1958
	$value[] = $CONFIG->path . 'engine/tests/ElggTravisInstallTest.php';
1959
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreHelpersTest.php';
1960
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreRegressionBugsTest.php';
1961
	$value[] = $CONFIG->path . 'engine/tests/ElggBatchTest.php';
1962
	return $value;
1963
}
1964
1965
/**#@+
1966
 * Controls access levels on \ElggEntity entities, metadata, and annotations.
1967
 *
1968
 * @warning ACCESS_DEFAULT is a place holder for the input/access view. Do not
1969
 * use it when saving an entity.
1970
 *
1971
 * @var int
1972
 */
1973
define('ACCESS_DEFAULT', -1);
1974
define('ACCESS_PRIVATE', 0);
1975
define('ACCESS_LOGGED_IN', 1);
1976
define('ACCESS_PUBLIC', 2);
1977
define('ACCESS_FRIENDS', -2);
1978
/**#@-*/
1979
1980
/**
1981
 * Constant to request the value of a parameter be ignored in elgg_get_*() functions
1982
 *
1983
 * @see elgg_get_entities()
1984
 * @var null
1985
 * @since 1.7
1986
 */
1987
define('ELGG_ENTITIES_ANY_VALUE', null);
1988
1989
/**
1990
 * Constant to request the value of a parameter be nothing in elgg_get_*() functions.
1991
 *
1992
 * @see elgg_get_entities()
1993
 * @var int 0
1994
 * @since 1.7
1995
 */
1996
define('ELGG_ENTITIES_NO_VALUE', 0);
1997
1998
/**
1999
 * Used in calls to forward() to specify the browser should be redirected to the
2000
 * referring page.
2001
 *
2002
 * @see forward
2003
 * @var int -1
2004
 */
2005
define('REFERRER', -1);
2006
2007
/**
2008
 * Alternate spelling for REFERRER.  Included because of some bad documentation
2009
 * in the original HTTP spec.
2010
 *
2011
 * @see forward()
2012
 * @link http://en.wikipedia.org/wiki/HTTP_referrer#Origin_of_the_term_referer
2013
 * @var int -1
2014
 */
2015
define('REFERER', -1);
2016
2017
/**
2018
 * HTTP Response codes
2019
 */
2020
define('ELGG_HTTP_CONTINUE', 100);
2021
define('ELGG_HTTP_SWITCHING_PROTOCOLS', 101);
2022
define('ELGG_HTTP_PROCESSING', 102);// RFC2518
2023
define('ELGG_HTTP_OK', 200);
2024
define('ELGG_HTTP_CREATED', 201);
2025
define('ELGG_HTTP_ACCEPTED', 202);
2026
define('ELGG_HTTP_NON_AUTHORITATIVE_INFORMATION', 203);
2027
define('ELGG_HTTP_NO_CONTENT', 204);
2028
define('ELGG_HTTP_RESET_CONTENT', 205);
2029
define('ELGG_HTTP_PARTIAL_CONTENT', 206);
2030
define('ELGG_HTTP_MULTI_STATUS', 207); // RFC4918
2031
define('ELGG_HTTP_ALREADY_REPORTED', 208); // RFC5842
2032
define('ELGG_HTTP_IM_USED', 226); // RFC3229
2033
define('ELGG_HTTP_MULTIPLE_CHOICES', 300);
2034
define('ELGG_HTTP_MOVED_PERMANENTLY', 301);
2035
define('ELGG_HTTP_FOUND', 302);
2036
define('ELGG_HTTP_SEE_OTHER', 303);
2037
define('ELGG_HTTP_NOT_MODIFIED', 304);
2038
define('ELGG_HTTP_USE_PROXY', 305);
2039
define('ELGG_HTTP_RESERVED', 306);
2040
define('ELGG_HTTP_TEMPORARY_REDIRECT', 307);
2041
define('ELGG_HTTP_PERMANENTLY_REDIRECT', 308); // RFC7238
2042
define('ELGG_HTTP_BAD_REQUEST', 400);
2043
define('ELGG_HTTP_UNAUTHORIZED', 401);
2044
define('ELGG_HTTP_PAYMENT_REQUIRED', 402);
2045
define('ELGG_HTTP_FORBIDDEN', 403);
2046
define('ELGG_HTTP_NOT_FOUND', 404);
2047
define('ELGG_HTTP_METHOD_NOT_ALLOWED', 405);
2048
define('ELGG_HTTP_NOT_ACCEPTABLE', 406);
2049
define('ELGG_HTTP_PROXY_AUTHENTICATION_REQUIRED', 407);
2050
define('ELGG_HTTP_REQUEST_TIMEOUT', 408);
2051
define('ELGG_HTTP_CONFLICT', 409);
2052
define('ELGG_HTTP_GONE', 410);
2053
define('ELGG_HTTP_LENGTH_REQUIRED', 411);
2054
define('ELGG_HTTP_PRECONDITION_FAILED', 412);
2055
define('ELGG_HTTP_REQUEST_ENTITY_TOO_LARGE', 413);
2056
define('ELGG_HTTP_REQUEST_URI_TOO_LONG', 414);
2057
define('ELGG_HTTP_UNSUPPORTED_MEDIA_TYPE', 415);
2058
define('ELGG_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE', 416);
2059
define('ELGG_HTTP_EXPECTATION_FAILED', 417);
2060
define('ELGG_HTTP_I_AM_A_TEAPOT', 418); // RFC2324
2061
define('ELGG_HTTP_UNPROCESSABLE_ENTITY', 422);// RFC4918
2062
define('ELGG_HTTP_LOCKED', 423); // RFC4918
2063
define('ELGG_HTTP_FAILED_DEPENDENCY', 424); // RFC4918
2064
define('ELGG_HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL', 425); // RFC2817
2065
define('ELGG_HTTP_UPGRADE_REQUIRED', 426);// RFC2817
2066
define('ELGG_HTTP_PRECONDITION_REQUIRED', 428); // RFC6585
2067
define('ELGG_HTTP_TOO_MANY_REQUESTS', 429); // RFC6585
2068
define('ELGG_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE', 431); // RFC6585
2069
define('ELGG_HTTP_INTERNAL_SERVER_ERROR', 500);
2070
define('ELGG_HTTP_NOT_IMPLEMENTED', 501);
2071
define('ELGG_HTTP_BAD_GATEWAY', 502);
2072
define('ELGG_HTTP_SERVICE_UNAVAILABLE', 503);
2073
define('ELGG_HTTP_GATEWAY_TIMEOUT', 504);
2074
define('ELGG_HTTP_VERSION_NOT_SUPPORTED', 505);
2075
define('ELGG_HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL', 506);// RFC2295
2076
define('ELGG_HTTP_INSUFFICIENT_STORAGE', 507);// RFC4918
2077
define('ELGG_HTTP_LOOP_DETECTED', 508); // RFC5842
2078
define('ELGG_HTTP_NOT_EXTENDED', 510);// RFC2774
2079
define('ELGG_HTTP_NETWORK_AUTHENTICATION_REQUIRED', 511); // RFC6585
2080
2081
/**
2082
 * Default JSON encoding
2083
 */
2084
define('ELGG_JSON_ENCODING', JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
2085
2086
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
2087
	$events->registerHandler('cache:flush', 'system', function () {
2088
		_elgg_services()->boot->invalidateCache();
2089
	});
2090
2091
	$events->registerHandler('init', 'system', '_elgg_init');
2092
	$events->registerHandler('init', 'system', '_elgg_walled_garden_init', 1000);
2093
2094
	$hooks->registerHandler('unit_test', 'system', '_elgg_api_test');
2095
};
2096