Issues (2473)

Branch: master

Security Analysis    no vulnerabilities found

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

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

engine/lib/elgglib.php (10 issues)

Upgrade to new PHP Analysis Engine

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

Code
1
<?php
2
/**
3
 * Bootstrapping and helper procedural code available for use in Elgg core and plugins.
4
 *
5
 * @package Elgg.Core
6
 * @todo These functions can't be subpackaged because they cover a wide mix of
7
 * purposes and subsystems.  Many of them should be moved to more relevant files.
8
 */
9
10
/**
11
 * Register a PHP file as a library.
12
 *
13
 * @see elgg_load_library
14
 *
15
 * @param string $name     The name of the library
16
 * @param string $location The location of the file
17
 *
18
 * @return void
19
 * @since 1.8.0
20
 */
21
function elgg_register_library($name, $location) {
22
	$config = _elgg_services()->config;
23
24
	$libraries = $config->get('libraries');
25
	if ($libraries === null) {
26
		$libraries = array();
27
	}
28
	$libraries[$name] = $location;
29
	$config->set('libraries', $libraries);
30
}
31
32
/**
33
 * Load a PHP library.
34
 *
35
 * @see elgg_register_library
36
 *
37
 * @param string $name The name of the library
38
 *
39
 * @return void
40
 * @throws InvalidParameterException
41
 * @since 1.8.0
42
 */
43
function elgg_load_library($name) {
44
	static $loaded_libraries = array();
45
46
	if (in_array($name, $loaded_libraries)) {
47
		return;
48
	}
49
50
	$libraries = _elgg_services()->config->get('libraries');
51
52
	if (!isset($libraries[$name])) {
53
		$error = "$name is not a registered library";
54
		throw new \InvalidParameterException($error);
55
	}
56
57
	if (!include_once($libraries[$name])) {
58
		$error = "Could not load the $name library from {$libraries[$name]}";
59
		throw new \InvalidParameterException($error);
60
	}
61
62
	$loaded_libraries[] = $name;
63
}
64
65
/**
66
 * Forward to $location.
67
 *
68
 * Sends a 'Location: $location' header and exists.  If headers have
69
 * already been sent, throws an exception.
70
 *
71
 * @param string $location URL to forward to browser to. This can be a path
72
 *                         relative to the network's URL.
73
 * @param string $reason   Short explanation for why we're forwarding. Set to
74
 *                         '404' to forward to error page. Default message is
75
 *                         'system'.
76
 *
77
 * @return void
78
 * @throws SecurityException
79
 */
80
function forward($location = "", $reason = 'system') {
81
	if (!headers_sent($file, $line)) {
82
		if ($location === REFERER) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $location (string) and REFERER (integer) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
83
			$location = _elgg_services()->request->headers->get('Referer');
84
		}
85
86
		$location = elgg_normalize_url($location);
0 ignored issues
show
It seems like $location can also be of type array<integer,string> or null; however, elgg_normalize_url() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

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

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
905
906
		case E_WARNING :
907
		case E_USER_WARNING :
908
		case E_RECOVERABLE_ERROR: // (e.g. type hint violation)
909
			
910
			// check if the error wasn't suppressed by the error control operator (@)
911
			if (error_reporting()) {
912
				error_log("PHP WARNING: $error");
913
			}
914
			break;
915
916
		default:
917
			global $CONFIG;
918
			if (isset($CONFIG->debug) && $CONFIG->debug === 'NOTICE') {
919
				error_log("PHP NOTICE: $error");
920
			}
921
	}
922
923
	return true;
924
}
925
926
927
/**
928
 * Display or log a message.
929
 *
930
 * If $level is >= to the debug setting in {@link $CONFIG->debug}, the
931
 * message will be sent to {@link elgg_dump()}.  Messages with lower
932
 * priority than {@link $CONFIG->debug} are ignored.
933
 *
934
 * Outputs all levels but NOTICE to screen by default.
935
 *
936
 * @note No messages will be displayed unless debugging has been enabled.
937
 *
938
 * @param string $message User message
939
 * @param string $level   NOTICE | WARNING | ERROR
940
 *
941
 * @return bool
942
 * @since 1.7.0
943
 */
944
function elgg_log($message, $level = 'NOTICE') {
945
	static $levels = array(
946
		'INFO' => 200,
947
		'NOTICE' => 250,
948
		'WARNING' => 300,
949
		'DEBUG' => 300,
950
		'ERROR' => 400,
951 1
	);
952
953 1
	if ($level == 'DEBUG') {
954
		elgg_deprecated_notice("The 'DEBUG' level for logging has been deprecated.", 1.9);
955
	}
956
957 1
	$level = $levels[$level];
958 1
	return _elgg_services()->logger->log($message, $level);
959
}
960
961
/**
962
 * Logs or displays $value.
963
 *
964
 * If $to_screen is true, $value is displayed to screen.  Else,
965
 * it is handled by PHP's {@link error_log()} function.
966
 *
967
 * A {@elgg_plugin_hook debug log} is called.  If a handler returns
968
 * false, it will stop the default logging method.
969
 *
970
 * @param mixed $value     The value
971
 * @param bool  $to_screen Display to screen?
972
 * @return void
973
 * @since 1.7.0
974
 */
975
function elgg_dump($value, $to_screen = true) {
976
	_elgg_services()->logger->dump($value, $to_screen);
977
}
978
979
/**
980
 * Get the current Elgg version information
981
 *
982
 * @param bool $human_readable Whether to return a human readable version (default: false)
983
 *
984
 * @return string|false Depending on success
985
 * @since 1.9
986
 */
987
function elgg_get_version($human_readable = false) {
988
	global $CONFIG;
989
990
	static $version, $release;
991
992
	if (isset($CONFIG->path)) {
993
		if (!isset($version) || !isset($release)) {
994
			if (!include($CONFIG->path . "version.php")) {
995
				return false;
996
			}
997
		}
998
		return $human_readable ? $release : $version;
999
	}
1000
1001
	return false;
1002
}
1003
1004
/**
1005
 * Log a notice about deprecated use of a function, view, etc.
1006
 *
1007
 * @param string $msg             Message to log
1008
 * @param string $dep_version     Human-readable *release* version: 1.7, 1.8, ...
1009
 * @param int    $backtrace_level How many levels back to display the backtrace.
1010
 *                                Useful if calling from functions that are called
1011
 *                                from other places (like elgg_view()). Set to -1
1012
 *                                for a full backtrace.
1013
 *
1014
 * @return bool
1015
 * @since 1.7.0
1016
 */
1017
function elgg_deprecated_notice($msg, $dep_version, $backtrace_level = 1) {
1018
	$backtrace_level += 1;
1019
	return _elgg_services()->deprecation->sendNotice($msg, $dep_version, $backtrace_level);
1020
}
1021
1022
/**
1023
 * Builds a URL from the a parts array like one returned by {@link parse_url()}.
1024
 *
1025
 * @note If only partial information is passed, a partial URL will be returned.
1026
 *
1027
 * @param array $parts       Associative array of URL components like parse_url() returns
1028
 *                           'user' and 'pass' parts are ignored because of security reasons
1029
 * @param bool  $html_encode HTML Encode the url?
1030
 *
1031
 * @see https://github.com/Elgg/Elgg/pull/8146#issuecomment-91544585
1032
 * @return string Full URL
1033
 * @since 1.7.0
1034
 */
1035
function elgg_http_build_url(array $parts, $html_encode = true) {
1036
	// build only what's given to us.
1037 3
	$scheme = isset($parts['scheme']) ? "{$parts['scheme']}://" : '';
1038 3
	$host = isset($parts['host']) ? "{$parts['host']}" : '';
1039 3
	$port = isset($parts['port']) ? ":{$parts['port']}" : '';
1040 3
	$path = isset($parts['path']) ? "{$parts['path']}" : '';
1041 3
	$query = isset($parts['query']) ? "?{$parts['query']}" : '';
1042 3
	$fragment = isset($parts['fragment']) ? "#{$parts['fragment']}" : '';
1043
1044 3
	$string = $scheme . $host . $port . $path . $query . $fragment;
1045
1046 3
	if ($html_encode) {
1047
		return elgg_format_url($string);
1048
	} else {
1049 3
		return $string;
1050
	}
1051
}
1052
1053
/**
1054
 * Adds action tokens to URL
1055
 *
1056
 * As of 1.7.0 action tokens are required on all actions.
1057
 * Use this function to append action tokens to a URL's GET parameters.
1058
 * This will preserve any existing GET parameters.
1059
 *
1060
 * @note If you are using {@elgg_view input/form} you don't need to
1061
 * add tokens to the action.  The form view automatically handles
1062
 * tokens.
1063
 *
1064
 * @param string $url         Full action URL
1065
 * @param bool   $html_encode HTML encode the url? (default: false)
1066
 *
1067
 * @return string URL with action tokens
1068
 * @since 1.7.0
1069
 */
1070
function elgg_add_action_tokens_to_url($url, $html_encode = false) {
1071
	$url = elgg_normalize_url($url);
1072
	$components = parse_url($url);
1073
1074
	if (isset($components['query'])) {
1075
		$query = elgg_parse_str($components['query']);
1076
	} else {
1077
		$query = array();
1078
	}
1079
1080
	if (isset($query['__elgg_ts']) && isset($query['__elgg_token'])) {
1081
		return $url;
1082
	}
1083
1084
	// append action tokens to the existing query
1085
	$query['__elgg_ts'] = time();
1086
	$query['__elgg_token'] = generate_action_token($query['__elgg_ts']);
1087
	$components['query'] = http_build_query($query);
1088
1089
	// rebuild the full url
1090
	return elgg_http_build_url($components, $html_encode);
0 ignored issues
show
It seems like $components defined by parse_url($url) on line 1072 can also be of type false; however, elgg_http_build_url() does only seem to accept array, 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...
1091
}
1092
1093
/**
1094
 * Removes an element from a URL's query string.
1095
 *
1096
 * @note You can send a partial URL string.
1097
 *
1098
 * @param string $url     Full URL
1099
 * @param string $element The element to remove
1100
 *
1101
 * @return string The new URL with the query element removed.
1102
 * @since 1.7.0
1103
 */
1104
function elgg_http_remove_url_query_element($url, $element) {
1105 1
	return elgg_http_add_url_query_elements($url, array($element => null));
1106
}
1107
1108
/**
1109
 * Sets elements in a URL's query string.
1110
 *
1111
 * @param string $url      The URL
1112
 * @param array  $elements Key/value pairs to set in the URL. If the value is null, the
1113
 *                         element is removed from the URL.
1114
 *
1115
 * @return string The new URL with the query strings added
1116
 * @since 1.7.0
1117
 */
1118
function elgg_http_add_url_query_elements($url, array $elements) {
1119 3
	$url_array = parse_url($url);
1120
1121 3
	if (isset($url_array['query'])) {
1122 3
		$query = elgg_parse_str($url_array['query']);
1123 3
	} else {
1124 3
		$query = array();
1125
	}
1126
1127 3
	foreach ($elements as $k => $v) {
1128 2
		if ($v === null) {
1129 1
			unset($query[$k]);
1130 1
		} else {
1131 2
			$query[$k] = $v;
1132
		}
1133 3
	}
1134
1135
	// why check path? A: if no path, this may be a relative URL like "?foo=1". In this case,
1136
	// the output "" would be interpreted the current URL, so in this case we *must* set
1137
	// a query to make sure elements are removed.
1138 3
	if ($query || empty($url_array['path'])) {
1139 3
		$url_array['query'] = http_build_query($query);
1140 3
	} else {
1141 2
		unset($url_array['query']);
1142
	}
1143 3
	$string = elgg_http_build_url($url_array, false);
0 ignored issues
show
It seems like $url_array defined by parse_url($url) on line 1119 can also be of type false; however, elgg_http_build_url() does only seem to accept array, 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...
1144
1145 3
	return $string;
1146
}
1147
1148
/**
1149
 * Test if two URLs are functionally identical.
1150
 *
1151
 * @tip If $ignore_params is used, neither the name nor its value will be considered when comparing.
1152
 *
1153
 * @tip The order of GET params doesn't matter.
1154
 *
1155
 * @param string $url1          First URL
1156
 * @param string $url2          Second URL
1157
 * @param array  $ignore_params GET params to ignore in the comparison
1158
 *
1159
 * @return bool
1160
 * @since 1.8.0
1161
 */
1162
function elgg_http_url_is_identical($url1, $url2, $ignore_params = array('offset', 'limit')) {
1163
	$url1 = elgg_normalize_url($url1);
1164
	$url2 = elgg_normalize_url($url2);
1165
1166
	// @todo - should probably do something with relative URLs
1167
1168
	if ($url1 == $url2) {
1169
		return true;
1170
	}
1171
1172
	$url1_info = parse_url($url1);
1173
	$url2_info = parse_url($url2);
1174
1175
	if (isset($url1_info['path'])) {
1176
		$url1_info['path'] = trim($url1_info['path'], '/');
1177
	}
1178
	if (isset($url2_info['path'])) {
1179
		$url2_info['path'] = trim($url2_info['path'], '/');
1180
	}
1181
1182
	// compare basic bits
1183
	$parts = array('scheme', 'host', 'path');
1184
1185
	foreach ($parts as $part) {
1186
		if ((isset($url1_info[$part]) && isset($url2_info[$part]))
1187
		&& $url1_info[$part] != $url2_info[$part]) {
1188
			return false;
1189
		} elseif (isset($url1_info[$part]) && !isset($url2_info[$part])) {
1190
			return false;
1191
		} elseif (!isset($url1_info[$part]) && isset($url2_info[$part])) {
1192
			return false;
1193
		}
1194
	}
1195
1196
	// quick compare of get params
1197
	if (isset($url1_info['query']) && isset($url2_info['query'])
1198
	&& $url1_info['query'] == $url2_info['query']) {
1199
		return true;
1200
	}
1201
1202
	// compare get params that might be out of order
1203
	$url1_params = array();
1204
	$url2_params = array();
1205
1206
	if (isset($url1_info['query'])) {
1207
		if ($url1_info['query'] = html_entity_decode($url1_info['query'])) {
1208
			$url1_params = elgg_parse_str($url1_info['query']);
1209
		}
1210
	}
1211
1212
	if (isset($url2_info['query'])) {
1213
		if ($url2_info['query'] = html_entity_decode($url2_info['query'])) {
1214
			$url2_params = elgg_parse_str($url2_info['query']);
1215
		}
1216
	}
1217
1218
	// drop ignored params
1219
	foreach ($ignore_params as $param) {
1220
		if (isset($url1_params[$param])) {
1221
			unset($url1_params[$param]);
1222
		}
1223
		if (isset($url2_params[$param])) {
1224
			unset($url2_params[$param]);
1225
		}
1226
	}
1227
1228
	// array_diff_assoc only returns the items in arr1 that aren't in arrN
1229
	// but not the items that ARE in arrN but NOT in arr1
1230
	// if arr1 is an empty array, this function will return 0 no matter what.
1231
	// since we only care if they're different and not how different,
1232
	// add the results together to get a non-zero (ie, different) result
1233
	$diff_count = count(array_diff_assoc($url1_params, $url2_params));
1234
	$diff_count += count(array_diff_assoc($url2_params, $url1_params));
1235
	if ($diff_count > 0) {
1236
		return false;
1237
	}
1238
1239
	return true;
1240
}
1241
1242
/**
1243
 * Checks for $array[$key] and returns its value if it exists, else
1244
 * returns $default.
1245
 *
1246
 * Shorthand for $value = (isset($array['key'])) ? $array['key'] : 'default';
1247
 *
1248
 * @param string $key     The key to check.
1249
 * @param array  $array   The array to check against.
1250
 * @param mixed  $default Default value to return if nothing is found.
1251
 * @param bool   $strict  Return array key if it's set, even if empty. If false,
1252
 *                        return $default if the array key is unset or empty.
1253
 *
1254
 * @return mixed
1255
 * @since 1.8.0
1256
 */
1257
function elgg_extract($key, $array, $default = null, $strict = true) {
1258 7
	if (!is_array($array)) {
1259
		return $default;
1260
	}
1261
1262 7
	if ($strict) {
1263 7
		return (isset($array[$key])) ? $array[$key] : $default;
1264
	} else {
1265
		return (isset($array[$key]) && !empty($array[$key])) ? $array[$key] : $default;
1266
	}
1267
}
1268
1269
/**
1270
 * Sorts a 3d array by specific element.
1271
 *
1272
 * @warning Will re-index numeric indexes.
1273
 *
1274
 * @note This operates the same as the built-in sort functions.
1275
 * It sorts the array and returns a bool for success.
1276
 *
1277
 * Do this: elgg_sort_3d_array_by_value($my_array);
1278
 * Not this: $my_array = elgg_sort_3d_array_by_value($my_array);
1279
 *
1280
 * @param array  &$array     Array to sort
1281
 * @param string $element    Element to sort by
1282
 * @param int    $sort_order PHP sort order
1283
 *                           {@link http://us2.php.net/array_multisort}
1284
 * @param int    $sort_type  PHP sort type
1285
 *                           {@link http://us2.php.net/sort}
1286
 *
1287
 * @return bool
1288
 */
1289
function elgg_sort_3d_array_by_value(&$array, $element, $sort_order = SORT_ASC,
1290
$sort_type = SORT_LOCALE_STRING) {
1291
1292
	$sort = array();
1293
1294
	foreach ($array as $v) {
1295
		if (isset($v[$element])) {
1296
			$sort[] = strtolower($v[$element]);
1297
		} else {
1298
			$sort[] = null;
1299
		}
1300
	};
1301
1302
	return array_multisort($sort, $sort_order, $sort_type, $array);
1303
}
1304
1305
/**
1306
 * Return the state of a php.ini setting as a bool
1307
 *
1308
 * @warning Using this on ini settings that are not boolean
1309
 * will be inaccurate!
1310
 *
1311
 * @param string $ini_get_arg The INI setting
1312
 *
1313
 * @return bool Depending on whether it's on or off
1314
 */
1315
function ini_get_bool($ini_get_arg) {
1316
	$temp = strtolower(ini_get($ini_get_arg));
1317
1318
	if ($temp == '1' || $temp == 'on' || $temp == 'true') {
1319
		return true;
1320
	}
1321
	return false;
1322
}
1323
1324
/**
1325
 * Returns a PHP INI setting in bytes.
1326
 *
1327
 * @tip Use this for arithmetic when determining if a file can be uploaded.
1328
 *
1329
 * @param string $setting The php.ini setting
1330
 *
1331
 * @return int
1332
 * @since 1.7.0
1333
 * @link http://www.php.net/manual/en/function.ini-get.php
1334
 */
1335
function elgg_get_ini_setting_in_bytes($setting) {
1336
	// retrieve INI setting
1337
	$val = ini_get($setting);
1338
1339
	// convert INI setting when shorthand notation is used
1340
	$last = strtolower($val[strlen($val) - 1]);
1341
	switch($last) {
1342
		case 'g':
1343
			$val *= 1024;
1344
			// fallthrough intentional
1345
		case 'm':
1346
			$val *= 1024;
1347
			// fallthrough intentional
1348
		case 'k':
1349
			$val *= 1024;
1350
	}
1351
1352
	// return byte value
1353
	return $val;
1354
}
1355
1356
/**
1357
 * Returns true is string is not empty, false, or null.
1358
 *
1359
 * Function to be used in array_filter which returns true if $string is not null.
1360
 *
1361
 * @param string $string The string to test
1362
 *
1363
 * @return bool
1364
 * @todo This is used once in metadata.php.  Use a lambda function instead.
1365
 */
1366
function is_not_null($string) {
1367 1
	if (($string === '') || ($string === false) || ($string === null)) {
1368
		return false;
1369
	}
1370
1371 1
	return true;
1372
}
1373
1374
/**
1375
 * Normalise the singular keys in an options array to plural keys.
1376
 *
1377
 * Used in elgg_get_entities*() functions to support shortcutting plural
1378
 * names by singular names.
1379
 *
1380
 * @param array $options   The options array. $options['keys'] = 'values';
1381
 * @param array $singulars A list of singular words to pluralize by adding 's'.
1382
 *
1383
 * @return array
1384
 * @since 1.7.0
1385
 * @access private
1386
 */
1387
function _elgg_normalize_plural_options_array($options, $singulars) {
1388
	foreach ($singulars as $singular) {
1389
		$plural = $singular . 's';
1390
1391
		if (array_key_exists($singular, $options)) {
1392
			if ($options[$singular] === ELGG_ENTITIES_ANY_VALUE) {
1393
				$options[$plural] = $options[$singular];
1394
			} else {
1395
				// Test for array refs #2641
1396
				if (!is_array($options[$singular])) {
1397
					$options[$plural] = array($options[$singular]);
1398
				} else {
1399
					$options[$plural] = $options[$singular];
1400
				}
1401
			}
1402
		}
1403
1404
		unset($options[$singular]);
1405
	}
1406
1407
	return $options;
1408
}
1409
1410
/**
1411
 * Emits a shutdown:system event upon PHP shutdown, but before database connections are dropped.
1412
 *
1413
 * @tip Register for the shutdown:system event to perform functions at the end of page loads.
1414
 *
1415
 * @warning Using this event to perform long-running functions is not very
1416
 * useful.  Servers will hold pages until processing is done before sending
1417
 * them out to the browser.
1418
 *
1419
 * @see http://www.php.net/register-shutdown-function
1420
 *
1421
 * @internal This is registered in engine/start.php
1422
 *
1423
 * @return void
1424
 * @see register_shutdown_hook()
1425
 * @access private
1426
 */
1427
function _elgg_shutdown_hook() {
1428
	global $START_MICROTIME;
1429
1430
	try {
1431
		_elgg_services()->logger->setDisplay(false);
1432
		elgg_trigger_event('shutdown', 'system');
1433
1434
		$time = (float)(microtime(true) - $START_MICROTIME);
1435
		$uri = _elgg_services()->request->server->get('REQUEST_URI', 'CLI');
1436
		// demoted to NOTICE from DEBUG so javascript is not corrupted
1437
		elgg_log("Page {$uri} generated in $time seconds", 'INFO');
1438
	} catch (Exception $e) {
1439
		$message = 'Error: ' . get_class($e) . ' thrown within the shutdown handler. ';
1440
		$message .= "Message: '{$e->getMessage()}' in file {$e->getFile()} (line {$e->getLine()})";
1441
		error_log($message);
1442
		error_log("Exception trace stack: {$e->getTraceAsString()}");
1443
	}
1444
1445
	// Prevent an APC session bug: https://bugs.php.net/bug.php?id=60657
1446
	session_write_close();
1447
}
1448
1449
/**
1450
 * Serve javascript pages.
1451
 *
1452
 * Searches for views under js/ and outputs them with special
1453
 * headers for caching control.
1454
 *
1455
 * @param array $page The page array
1456
 *
1457
 * @return bool
1458
 * @elgg_pagehandler js
1459
 * @access private
1460
 */
1461
function _elgg_js_page_handler($page) {
1462
	return _elgg_cacheable_view_page_handler($page, 'js');
1463
}
1464
1465
/**
1466
 * Serve individual views for Ajax.
1467
 *
1468
 * /ajax/view/<view_name>?<key/value params>
1469
 * /ajax/form/<action_name>?<key/value params>
1470
 *
1471
 * @param string[] $segments URL segments (not including "ajax")
1472
 * @return bool
1473
 *
1474
 * @see elgg_register_ajax_view()
1475
 * @elgg_pagehandler ajax
1476
 * @access private
1477
 */
1478
function _elgg_ajax_page_handler($segments) {
1479
	elgg_ajax_gatekeeper();
1480
1481
	if (count($segments) < 2) {
1482
		return false;
1483
	}
1484
1485
	if ($segments[0] === 'view' || $segments[0] === 'form') {
1486
		if ($segments[0] === 'view') {
1487
			// ignore 'view/'
1488
			$view = implode('/', array_slice($segments, 1));
1489
		} else {
1490
			// form views start with "forms", not "form"
1491
			$view = 'forms/' . implode('/', array_slice($segments, 1));
1492
		}
1493
1494
		$allowed_views = elgg_get_config('allowed_ajax_views');
1495
		if (!array_key_exists($view, $allowed_views)) {
1496
			header('HTTP/1.1 403 Forbidden');
1497
			exit;
1498
		}
1499
1500
		// pull out GET parameters through filter
1501
		$vars = array();
1502
		foreach (_elgg_services()->request->query->keys() as $name) {
1503
			$vars[$name] = get_input($name);
1504
		}
1505
1506
		if (isset($vars['guid'])) {
1507
			$vars['entity'] = get_entity($vars['guid']);
1508
		}
1509
1510
		if ($segments[0] === 'view') {
1511
			// Try to guess the mime-type
1512 View Code Duplication
			switch ($segments[1]) {
1513
				case "js":
1514
					header("Content-Type: text/javascript;charset=utf-8");
1515
					break;
1516
				case "css":
1517
					header("Content-Type: text/css;charset=utf-8");
1518
					break;
1519
			}
1520
1521
			echo elgg_view($view, $vars);
1522
		} else {
1523
			$action = implode('/', array_slice($segments, 1));
1524
			echo elgg_view_form($action, array(), $vars);
1525
		}
1526
		return true;
1527
	}
1528
1529
	return false;
1530
}
1531
1532
/**
1533
 * Serve CSS
1534
 *
1535
 * Serves CSS from the css views directory with headers for caching control
1536
 *
1537
 * @param array $page The page array
1538
 *
1539
 * @return bool
1540
 * @elgg_pagehandler css
1541
 * @access private
1542
 */
1543
function _elgg_css_page_handler($page) {
1544
	if (!isset($page[0])) {
1545
		// default css
1546
		$page[0] = 'elgg';
1547
	}
1548
	
1549
	return _elgg_cacheable_view_page_handler($page, 'css');
1550
}
1551
1552
/**
1553
 * Handle requests for /favicon.ico
1554
 *
1555
 * @param string[] $segments The URL segments
1556
 * @return bool
1557
 * @access private
1558
 * @since 1.10
1559
 */
1560
function _elgg_favicon_page_handler($segments) {
1561
	header("HTTP/1.1 404 Not Found", true, 404);
1562
1563
	header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+1 week")), true);
1564
	header("Pragma: public", true);
1565
	header("Cache-Control: public", true);
1566
1567
	// TODO in next 1.x send our default icon
1568
	//header('Content-Type: image/vnd.microsoft.icon');
1569
	//readfile(elgg_get_root_path() . '_graphics/favicon.ico');
1570
1571
	return true;
1572
}
1573
1574
/**
1575
 * Serves a JS or CSS view with headers for caching.
1576
 *
1577
 * /<css||js>/name/of/view.<last_cache>.<css||js>
1578
 *
1579
 * @param array  $page The page array
1580
 * @param string $type The type: js or css
1581
 *
1582
 * @return bool
1583
 * @access private
1584
 */
1585
function _elgg_cacheable_view_page_handler($page, $type) {
1586
1587
	switch ($type) {
1588
		case 'js':
1589
			$content_type = 'text/javascript';
1590
			break;
1591
1592
		case 'css':
1593
			$content_type = 'text/css';
1594
			break;
1595
1596
		default:
1597
			return false;
1598
			break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1599
	}
1600
1601
	if ($page) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $page of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1602
		// the view file names can have multiple dots
1603
		// eg: views/default/js/calendars/jquery.fullcalendar.min.php
1604
		// translates to the url /js/<ts>/calendars/jquery.fullcalendar.min.js
1605
		// and the view js/calendars/jquery.fullcalendar.min
1606
		// we ignore the last two dots for the ts and the ext.
1607
		// Additionally, the timestamp is optional.
1608
		$page = implode('/', $page);
1609
		$regex = '|(.+?)\.\w+$|';
1610
		if (!preg_match($regex, $page, $matches)) {
1611
			return false;
1612
		}
1613
		$view = "$type/{$matches[1]}";
1614
		if (!elgg_view_exists($view)) {
1615
			return false;
1616
		}
1617
		$return = elgg_view($view);
1618
1619
		header("Content-type: $content_type;charset=utf-8");
1620
1621
		// @todo should js be cached when simple cache turned off
1622
		//header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+10 days")), true);
1623
		//header("Pragma: public");
1624
		//header("Cache-Control: public");
1625
		//header("Content-Length: " . strlen($return));
1626
1627
		echo $return;
1628
		return true;
1629
	}
1630
	return false;
1631
}
1632
1633
/**
1634
 * Reverses the ordering in an ORDER BY clause.  This is achived by replacing
1635
 * asc with desc, or appending desc to the end of the clause.
1636
 *
1637
 * This is used mostly for elgg_get_entities() and other similar functions.
1638
 *
1639
 * @param string $order_by An order by clause
1640
 * @access private
1641
 * @return string
1642
 * @access private
1643
 */
1644
function _elgg_sql_reverse_order_by_clause($order_by) {
1645
	$order_by = strtolower($order_by);
1646
1647
	if (strpos($order_by, ' asc') !== false) {
1648
		$return = str_replace(' asc', ' desc', $order_by);
1649
	} elseif (strpos($order_by, ' desc') !== false) {
1650
		$return = str_replace(' desc', ' asc', $order_by);
1651
	} else {
1652
		// no order specified, so default to desc since mysql defaults to asc
1653
		$return = $order_by . ' desc';
1654
	}
1655
1656
	return $return;
1657
}
1658
1659
/**
1660
 * Enable objects with an enable() method.
1661
 *
1662
 * Used as a callback for \ElggBatch.
1663
 *
1664
 * @todo why aren't these static methods on \ElggBatch?
1665
 *
1666
 * @param object $object The object to enable
1667
 * @return bool
1668
 * @access private
1669
 */
1670
function elgg_batch_enable_callback($object) {
1671
	// our db functions return the number of rows affected...
1672
	return $object->enable() ? true : false;
1673
}
1674
1675
/**
1676
 * Disable objects with a disable() method.
1677
 *
1678
 * Used as a callback for \ElggBatch.
1679
 *
1680
 * @param object $object The object to disable
1681
 * @return bool
1682
 * @access private
1683
 */
1684
function elgg_batch_disable_callback($object) {
1685
	// our db functions return the number of rows affected...
1686
	return $object->disable() ? true : false;
1687
}
1688
1689
/**
1690
 * Delete objects with a delete() method.
1691
 *
1692
 * Used as a callback for \ElggBatch.
1693
 *
1694
 * @param object $object The object to disable
1695
 * @return bool
1696
 * @access private
1697
 */
1698
function elgg_batch_delete_callback($object) {
1699
	// our db functions return the number of rows affected...
1700
	return $object->delete() ? true : false;
1701
}
1702
1703
/**
1704
 * Checks if there are some constraints on the options array for
1705
 * potentially dangerous operations.
1706
 *
1707
 * @param array  $options Options array
1708
 * @param string $type    Options type: metadata or annotation
1709
 * @return bool
1710
 * @access private
1711
 */
1712
function _elgg_is_valid_options_for_batch_operation($options, $type) {
1713
	if (!$options || !is_array($options)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1714
		return false;
1715
	}
1716
1717
	// at least one of these is required.
1718
	$required = array(
1719
		// generic restraints
1720
		'guid', 'guids'
1721
	);
1722
1723
	switch ($type) {
1724
		case 'metadata':
1725
			$metadata_required = array(
1726
				'metadata_owner_guid', 'metadata_owner_guids',
1727
				'metadata_name', 'metadata_names',
1728
				'metadata_value', 'metadata_values'
1729
			);
1730
1731
			$required = array_merge($required, $metadata_required);
1732
			break;
1733
1734
		case 'annotations':
1735
		case 'annotation':
1736
			$annotations_required = array(
1737
				'annotation_owner_guid', 'annotation_owner_guids',
1738
				'annotation_name', 'annotation_names',
1739
				'annotation_value', 'annotation_values'
1740
			);
1741
1742
			$required = array_merge($required, $annotations_required);
1743
			break;
1744
1745
		default:
1746
			return false;
1747
	}
1748
1749
	foreach ($required as $key) {
1750
		// check that it exists and is something.
1751
		if (isset($options[$key]) && $options[$key]) {
1752
			return true;
1753
		}
1754
	}
1755
1756
	return false;
1757
}
1758
1759
/**
1760
 * Intercepts the index page when Walled Garden mode is enabled.
1761
 *
1762
 * @return bool
1763
 * @access private
1764
 */
1765
function _elgg_walled_garden_index() {
1766
1767
	elgg_load_css('elgg.walled_garden');
1768
	elgg_load_js('elgg.walled_garden');
1769
	
1770
	$content = elgg_view('core/walled_garden/login');
1771
1772
	$params = array(
1773
		'content' => $content,
1774
		'class' => 'elgg-walledgarden-double',
1775
		'id' => 'elgg-walledgarden-login',
1776
	);
1777
	$body = elgg_view_layout('walled_garden', $params);
1778
	echo elgg_view_page('', $body, 'walled_garden');
1779
1780
	return true;
1781
}
1782
1783
1784
/**
1785
 * Serve walled garden sections
1786
 *
1787
 * @param array $page Array of URL segments
1788
 * @return string
1789
 * @access private
1790
 */
1791
function _elgg_walled_garden_ajax_handler($page) {
1792
	$view = $page[0];
1793
	if (!elgg_view_exists("core/walled_garden/$view")) {
1794
		return false;
1795
	}
1796
	$params = array(
1797
		'content' => elgg_view("core/walled_garden/$view"),
1798
		'class' => 'elgg-walledgarden-single hidden',
1799
		'id' => str_replace('_', '-', "elgg-walledgarden-$view"),
1800
	);
1801
	echo elgg_view_layout('walled_garden', $params);
1802
	return true;
1803
}
1804
1805
/**
1806
 * Checks the status of the Walled Garden and forwards to a login page
1807
 * if required.
1808
 *
1809
 * If the site is in Walled Garden mode, all page except those registered as
1810
 * plugin pages by {@elgg_hook public_pages walled_garden} will redirect to
1811
 * a login page.
1812
 *
1813
 * @since 1.8.0
1814
 * @elgg_event_handler init system
1815
 * @return void
1816
 * @access private
1817
 */
1818
function _elgg_walled_garden_init() {
1819
	global $CONFIG;
1820
1821
	elgg_register_css('elgg.walled_garden', elgg_get_simplecache_url('css', 'walled_garden'));
1822
	elgg_register_js('elgg.walled_garden', elgg_get_simplecache_url('js', 'walled_garden'));
1823
1824
	elgg_register_page_handler('walled_garden', '_elgg_walled_garden_ajax_handler');
1825
1826
	// check for external page view
1827
	if (isset($CONFIG->site) && $CONFIG->site instanceof \ElggSite) {
1828
		$CONFIG->site->checkWalledGarden();
1829
	}
1830
}
1831
1832
/**
1833
 * Remove public access for walled gardens
1834
 *
1835
 * @param string $hook
1836
 * @param string $type
1837
 * @param array $accesses
1838
 * @return array
1839
 * @access private
1840
 */
1841
function _elgg_walled_garden_remove_public_access($hook, $type, $accesses) {
1842
	if (isset($accesses[ACCESS_PUBLIC])) {
1843
		unset($accesses[ACCESS_PUBLIC]);
1844
	}
1845
	return $accesses;
1846
}
1847
1848
/**
1849
 * Boots the engine
1850
 *
1851
 * 1. sets error handlers
1852
 * 2. connects to database
1853
 * 3. verifies the installation succeeded
1854
 * 4. loads application configuration
1855
 * 5. loads i18n data
1856
 * 6. loads cached autoloader state
1857
 * 7. loads site configuration
1858
 *
1859
 * @access private
1860
 */
1861
function _elgg_engine_boot() {
1862
	// Register the error handlers
1863
	set_error_handler('_elgg_php_error_handler');
1864
	set_exception_handler('_elgg_php_exception_handler');
1865
1866
	_elgg_services()->db->setupConnections();
1867
1868
	_elgg_services()->db->assertInstalled();
1869
1870
	_elgg_load_application_config();
1871
1872
	_elgg_load_autoload_cache();
1873
1874
	_elgg_load_site_config();
1875
1876
	_elgg_session_boot();
1877
1878
	_elgg_services()->systemCache->loadAll();
1879
1880
	_elgg_services()->translator->loadTranslations();
1881
}
1882
1883
/**
1884
 * Elgg's main init.
1885
 *
1886
 * Handles core actions for comments, the JS pagehandler, and the shutdown function.
1887
 *
1888
 * @elgg_event_handler init system
1889
 * @return void
1890
 * @access private
1891
 */
1892
function _elgg_init() {
1893
	global $CONFIG;
1894
1895
	elgg_register_action('comment/save');
1896
	elgg_register_action('comment/delete');
1897
1898
	elgg_register_page_handler('js', '_elgg_js_page_handler');
1899
	elgg_register_page_handler('css', '_elgg_css_page_handler');
1900
	elgg_register_page_handler('ajax', '_elgg_ajax_page_handler');
1901
	elgg_register_page_handler('favicon.ico', '_elgg_favicon_page_handler');
1902
1903
	elgg_register_page_handler('manifest.json', function() {
0 ignored issues
show
function () { $site ...()); return true; } is of type object<Closure>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1904
		$site = elgg_get_site_entity();
1905
		$resource = new \Elgg\Http\WebAppManifestResource($site);
1906
		header('Content-Type: application/json;charset=utf-8');
1907
		echo json_encode($resource->get());
1908
		return true;
1909
	});
1910
1911
	elgg_register_plugin_hook_handler('head', 'page', function($hook, $type, array $result) {
1912
		$result['links']['manifest'] = [
1913
			'rel' => 'manifest',
1914
			'href' => elgg_normalize_url('/manifest.json'),
1915
		];
1916
1917
		return $result;
1918
	});
1919
1920
	elgg_register_js('elgg.autocomplete', 'js/lib/ui.autocomplete.js');
1921
	elgg_register_js('jquery.ui.autocomplete.html', 'vendors/jquery/jquery.ui.autocomplete.html.js');
1922
1923
	elgg_define_js('jquery.ui.autocomplete.html', array(
1924
		'src' => '/vendors/jquery/jquery.ui.autocomplete.html.js',
1925
		'deps' => array('jquery.ui')
1926
	));
1927
	
1928
	elgg_register_external_view('js/elgg/UserPicker.js', true);
1929
1930
	elgg_register_js('elgg.friendspicker', 'js/lib/ui.friends_picker.js');
1931
	elgg_register_js('elgg.avatar_cropper', 'js/lib/ui.avatar_cropper.js');
1932
	elgg_register_js('jquery.imgareaselect', 'vendors/jquery/jquery.imgareaselect/scripts/jquery.imgareaselect.min.js');
1933
	elgg_register_js('elgg.ui.river', 'js/lib/ui.river.js');
1934
1935
	elgg_register_css('jquery.imgareaselect', 'vendors/jquery/jquery.imgareaselect/css/imgareaselect-deprecated.css');
1936
1937
	// Sets a blacklist of words in the current language.
1938
	// This is a comma separated list in word:blacklist.
1939
	// @todo possibly deprecate
1940
	$CONFIG->wordblacklist = array();
1941
	$list = explode(',', elgg_echo('word:blacklist'));
1942
	if ($list) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $list of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1943
		foreach ($list as $l) {
1944
			$CONFIG->wordblacklist[] = trim($l);
1945
		}
1946
	}
1947
}
1948
1949
/**
1950
 * Adds unit tests for the general API.
1951
 *
1952
 * @param string $hook   unit_test
1953
 * @param string $type   system
1954
 * @param array  $value  array of test files
1955
 * @param array  $params empty
1956
 *
1957
 * @elgg_plugin_hook unit_tests system
1958
 * @return array
1959
 * @access private
1960
 */
1961
function _elgg_api_test($hook, $type, $value, $params) {
1962
	global $CONFIG;
1963
	$value[] = $CONFIG->path . 'engine/tests/ElggTravisInstallTest.php';
1964
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreHelpersTest.php';
1965
	$value[] = $CONFIG->path . 'engine/tests/ElggCoreRegressionBugsTest.php';
1966
	$value[] = $CONFIG->path . 'engine/tests/ElggBatchTest.php';
1967
	return $value;
1968
}
1969
1970
/**#@+
1971
 * Controls access levels on \ElggEntity entities, metadata, and annotations.
1972
 *
1973
 * @warning ACCESS_DEFAULT is a place holder for the input/access view. Do not
1974
 * use it when saving an entity.
1975
 *
1976
 * @var int
1977
 */
1978
define('ACCESS_DEFAULT', -1);
1979
define('ACCESS_PRIVATE', 0);
1980
define('ACCESS_LOGGED_IN', 1);
1981
define('ACCESS_PUBLIC', 2);
1982
define('ACCESS_FRIENDS', -2);
1983
/**#@-*/
1984
1985
/**
1986
 * Constant to request the value of a parameter be ignored in elgg_get_*() functions
1987
 *
1988
 * @see elgg_get_entities()
1989
 * @var null
1990
 * @since 1.7
1991
 */
1992
define('ELGG_ENTITIES_ANY_VALUE', null);
1993
1994
/**
1995
 * Constant to request the value of a parameter be nothing in elgg_get_*() functions.
1996
 *
1997
 * @see elgg_get_entities()
1998
 * @var int 0
1999
 * @since 1.7
2000
 */
2001
define('ELGG_ENTITIES_NO_VALUE', 0);
2002
2003
/**
2004
 * Used in calls to forward() to specify the browser should be redirected to the
2005
 * referring page.
2006
 *
2007
 * @see forward
2008
 * @var int -1
2009
 */
2010
define('REFERRER', -1);
2011
2012
/**
2013
 * Alternate spelling for REFERRER.  Included because of some bad documentation
2014
 * in the original HTTP spec.
2015
 *
2016
 * @see forward()
2017
 * @link http://en.wikipedia.org/wiki/HTTP_referrer#Origin_of_the_term_referer
2018
 * @var int -1
2019
 */
2020
define('REFERER', -1);
2021
2022
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
2023
	$events->registerHandler('init', 'system', '_elgg_init');
2024
	$events->registerHandler('boot', 'system', '_elgg_engine_boot', 1);
2025
	$hooks->registerHandler('unit_test', 'system', '_elgg_api_test');
2026
2027
	$events->registerHandler('init', 'system', '_elgg_walled_garden_init', 1000);
2028
};
2029