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) {
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);
0 ignored issues
show
It seems like $site defined by elgg_get_site_entity() on line 1920 can also be of type false; however, Elgg\Http\WebAppManifestResource::__construct() does only seem to accept object<ElggSite>, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
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