Completed
Push — master ( be3b3f...d1dc3f )
by Ismayil
13:04
created

output.php ➔ _elgg_sane_validate_url()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 12
nc 8
nop 1
dl 0
loc 23
ccs 12
cts 12
cp 1
crap 6
rs 8.5906
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 17 and the first side effect is on line 545.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Output functions
4
 * Processing text for output such as pulling out URLs and extracting excerpts
5
 *
6
 * @package Elgg
7
 * @subpackage Core
8
 */
9
10
/**
11
 * Takes a string and turns any URLs into formatted links
12
 *
13
 * @param string $text The input string
14
 *
15
 * @return string The output string with formatted links
16
 */
17
function parse_urls($text) {
18
19
	$linkify = new \Misd\Linkify\Linkify();
20
		
21
	return $linkify->processUrls($text, ['attr' => ['rel' => 'nofollow']]);
22
}
23
24
/**
25
 * Takes a string and turns any email addresses into formatted links
26
 *
27
 * @param string $text The input string
28
 *
29
 * @return string The output string with formatted links
30
 *
31
 * @since 2.3
32
 */
33
function elgg_parse_emails($text) {
34
	$linkify = new \Misd\Linkify\Linkify();
35
		
36
	return $linkify->processEmails($text, ['attr' => ['rel' => 'nofollow']]);
37
}
38
39
/**
40
 * Create paragraphs from text with line spacing
41
 *
42
 * @param string $string The string
43
 *
44
 * @return string
45
 **/
46
function elgg_autop($string) {
47
	return _elgg_services()->autoP->process($string);
48
}
49
50
/**
51
 * Returns an excerpt.
52
 * Will return up to n chars stopping at the nearest space.
53
 * If no spaces are found (like in Japanese) will crop off at the
54
 * n char mark. Adds ... if any text was chopped.
55
 *
56
 * @param string $text      The full text to excerpt
57
 * @param int    $num_chars Return a string up to $num_chars long
58
 *
59
 * @return string
60
 * @since 1.7.2
61
 */
62
function elgg_get_excerpt($text, $num_chars = 250) {
63
	$view = 'output/excerpt';
64
	$vars = [
65
		'text' => $text,
66
		'num_chars' => $num_chars,
67
	];
68
	$viewtype = elgg_view_exists($view) ? '' : 'default';
69
70
	return _elgg_view_under_viewtype($view, $vars, $viewtype);
71
}
72
73
/**
74
 * Format bytes to a human readable format
75
 *
76
 * @param int $size      File size in bytes to format
77
 *
78
 * @param int $precision Precision to round formatting bytes to
79
 *
80
 * @return string
81
 * @since 1.9.0
82
 */
83
function elgg_format_bytes($size, $precision = 2) {
84
	if (!$size || $size < 0) {
85
		return false;
86
	}
87
88
	$base = log($size) / log(1024);
89
	$suffixes = array('B', 'kB', 'MB', 'GB', 'TB');
90
91
	return round(pow(1024, $base - floor($base)), $precision) . ' ' . $suffixes[floor($base)];
92
}
93
94
/**
95
 * Converts an associative array into a string of well-formed HTML/XML attributes
96
 * Returns a concatenated string of HTML attributes to be inserted into a tag (e.g., <tag $attrs>)
97
 *
98
 * @see elgg_format_element
99
 *
100
 * @param array $attrs Attributes
101
 *                     An array of attribute => value pairs
102
 *                     Attribute value can be a scalar value, an array of scalar values, or true
103
 *                     <code>
104
 *                     $attrs = array(
105
 *                         'class' => ['elgg-input', 'elgg-input-text'], // will be imploded with spaces
106
 *                         'style' => ['margin-left:10px;', 'color: #666;'], // will be imploded with spaces
107
 *                         'alt' => 'Alt text', // will be left as is
108
 *                         'disabled' => true, // will be converted to disabled="disabled"
109
 *                         'data-options' => json_encode(['foo' => 'bar']), // will be output as an escaped JSON string
110
 *                         'batch' => <\ElggBatch>, // will be ignored
111
 *                         'items' => [<\ElggObject>], // will be ignored
112
 *                     );
113
 *                     </code>
114
 *
115
 * @return string
116
 */
117
function elgg_format_attributes(array $attrs = array()) {
118 7
	if (!is_array($attrs) || empty($attrs)) {
119
		return '';
120
	}
121
122 7
	$attributes = [];
123
124 7
	foreach ($attrs as $attr => $val) {
125 7
		if (0 !== strpos($attr, 'data-') && false !== strpos($attr, '_')) {
126
			// this is probably a view $vars variable not meant for output
127 1
			continue;
128
		}
129
130 7
		$attr = strtolower($attr);
131
132 7
		if (!isset($val) || $val === false) {
133 1
			continue;
134
		}
135
136 7
		if ($val === true) {
137 2
			$val = $attr; //e.g. checked => true ==> checked="checked"
138 2
		}
139
140 7
		if (is_scalar($val)) {
141 7
			$val = [$val];
142 7
		}
143
144 7
		if (!is_array($val)) {
145 1
			continue;
146
		}
147
148
		// Check if array contains non-scalar values and bail if so
149
		$filtered_val = array_filter($val, function($e) {
150 7
			return is_scalar($e);
151 7
		});
152
153 7
		if (count($val) != count($filtered_val)) {
154 1
			continue;
155
		}
156
157 7
		$val = implode(' ', $val);
158
159 7
		$val = htmlspecialchars($val, ENT_QUOTES, 'UTF-8', false);
160 7
		$attributes[] = "$attr=\"$val\"";
161 7
	}
162
163 7
	return implode(' ', $attributes);
164
}
165
166
/**
167
 * Format an HTML element
168
 *
169
 * @param string|array $tag_name   The element tagName. e.g. "div". This will not be validated.
170
 *                                 All function arguments can be given as a single array: The array will be used
171
 *                                 as $attributes, except for the keys "#tag_name", "#text", and "#options", which
172
 *                                 will be extracted as the other arguments.
173
 *
174
 * @param array        $attributes The element attributes. This is passed to elgg_format_attributes().
175
 *
176
 * @param string       $text       The contents of the element. Assumed to be HTML unless encode_text is true.
177
 *
178
 * @param array        $options    Options array with keys:
179
 *
180
 *   encode_text   => (bool, default false) If true, $text will be HTML-escaped. Already-escaped entities
181
 *                    will not be double-escaped.
182
 *
183
 *   double_encode => (bool, default false) If true, the $text HTML escaping will be allowed to double
184
 *                    encode HTML entities: '&times;' will become '&amp;times;'
185
 *
186
 *   is_void       => (bool) If given, this determines whether the function will return just the open tag.
187
 *                    Otherwise this will be determined by the tag name according to this list:
188
 *                    http://www.w3.org/html/wg/drafts/html/master/single-page.html#void-elements
189
 *
190
 *   is_xml        => (bool, default false) If true, void elements will be formatted like "<tag />"
191
 *
192
 * @return string
193
 * @throws InvalidArgumentException
194
 * @since 1.9.0
195
 */
196
function elgg_format_element($tag_name, array $attributes = array(), $text = '', array $options = array()) {
197 9
	if (is_array($tag_name)) {
198 6
		$args = $tag_name;
199
200 6
		if ($attributes !== [] || $text !== '' || $options !== []) {
201
			throw new \InvalidArgumentException('If $tag_name is an array, the other arguments must not be set');
202
		}
203
204 6
		if (isset($args['#tag_name'])) {
205 5
			$tag_name = $args['#tag_name'];
206 5
		}
207 6
		if (isset($args['#text'])) {
208 4
			$text = $args['#text'];
209 4
		}
210 6
		if (isset($args['#options'])) {
211 5
			$options = $args['#options'];
212 5
		}
213
214 6
		unset($args['#tag_name'], $args['#text'], $args['#options']);
215 6
		$attributes = $args;
216 6
	}
217
218 9
	if (!is_string($tag_name) || $tag_name === '') {
219 1
		throw new \InvalidArgumentException('$tag_name is required');
220
	}
221
222 8
	if (isset($options['is_void'])) {
223 1
		$is_void = $options['is_void'];
224 1
	} else {
225
		// from http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
226 7
		$is_void = in_array(strtolower($tag_name), array(
227 7
			'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem',
228 7
			'meta', 'param', 'source', 'track', 'wbr'
229 7
		));
230
	}
231
232 8
	if (!empty($options['encode_text'])) {
233 2
		$double_encode = empty($options['double_encode']) ? false : true;
234 2
		$text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8', $double_encode);
235 2
	}
236
237 8
	if ($attributes) {
238 4
		$attrs = elgg_format_attributes($attributes);
239 4
		if ($attrs !== '') {
240 4
			$attrs = " $attrs";
241 4
		}
242 4
	} else {
243 5
		$attrs = '';
244
	}
245
246 8
	if ($is_void) {
247 2
		return empty($options['is_xml']) ? "<{$tag_name}{$attrs}>" : "<{$tag_name}{$attrs} />";
248
	} else {
249 6
		return "<{$tag_name}{$attrs}>$text</$tag_name>";
250
	}
251
}
252
253
/**
254
 * Converts shorthand urls to absolute urls.
255
 *
256
 * No change is made if the URL: is absolute, protocol-relative, starts with a protocol/fragment/query.
257
 *
258
 * @example
259
 * elgg_normalize_url('');                   // 'http://my.site.com/'
260
 * elgg_normalize_url('dashboard');          // 'http://my.site.com/dashboard'
261
 * elgg_normalize_url('http://google.com/'); // no change
262
 * elgg_normalize_url('//google.com/');      // no change
263
 *
264
 * @param string $url The URL to normalize
265
 *
266
 * @return string The absolute url
267
 */
268
function elgg_normalize_url($url) {
269 223
	$url = str_replace(' ', '%20', $url);
270
271 223
	if (_elgg_sane_validate_url($url)) {
272 58
		return $url;
273
	}
274
275 217
	if (preg_match("#^([a-z]+)\\:#", $url, $m)) {
276
		// we don't let http/https: URLs fail filter_var(), but anything else starting with a protocol
277
		// is OK
278 1
		if ($m[1] !== 'http' && $m[1] !== 'https') {
279 1
			return $url;
280
		}
281
	}
282
283 216
	if (preg_match("#^(\\#|\\?|//)#", $url)) {
284
		// starts with '//' (protocol-relative link), query, or fragment
285 2
		return $url;
286
	}
287
288 214
	if (preg_match("#^[^/]*\\.php(\\?.*)?$#", $url)) {
289
		// root PHP scripts: 'install.php', 'install.php?step=step'. We don't want to confuse these
290
		// for domain names.
291 3
		return elgg_get_site_url() . $url;
292
	}
293
294 212
	if (preg_match("#^[^/?]*\\.#", $url)) {
295
		// URLs starting with domain: 'example.com', 'example.com/subpage'
296 2
		return "http://$url";
297
	}
298
299
	// 'page/handler', 'mod/plugin/file.php'
300
	// trim off any leading / because the site URL is stored
301
	// with a trailing /
302 210
	return elgg_get_site_url() . ltrim($url, '/');
303
}
304
305
/**
306
 * When given a title, returns a version suitable for inclusion in a URL
307
 *
308
 * @param string $title The title
309
 *
310
 * @return string The optimized title
311
 * @since 1.7.2
312
 */
313
function elgg_get_friendly_title($title) {
314
315
	// return a URL friendly title to short circuit normal title formatting
316
	$params = array('title' => $title);
317
	$result = elgg_trigger_plugin_hook('format', 'friendly:title', $params, null);
318
	if ($result) {
319
		return $result;
320
	}
321
322
	// titles are often stored HTML encoded
323
	$title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
324
	
325
	$title = \Elgg\Translit::urlize($title);
326
327
	return $title;
328
}
329
330
/**
331
 * Formats a UNIX timestamp in a friendly way (eg "less than a minute ago")
332
 *
333
 * @see elgg_view_friendly_time()
334
 *
335
 * @param int $time         A UNIX epoch timestamp
336
 * @param int $current_time Current UNIX epoch timestamp (optional)
337
 *
338
 * @return string The friendly time string
339
 * @since 1.7.2
340
 */
341
function elgg_get_friendly_time($time, $current_time = null) {
342
	
343 7
	if (!$current_time) {
344
		$current_time = time();
345
	}
346
347
	// return a time string to short circuit normal time formatting
348 7
	$params = array('time' => $time, 'current_time' => $current_time);
349 7
	$result = elgg_trigger_plugin_hook('format', 'friendly:time', $params, null);
350 7
	if ($result) {
351
		return $result;
352
	}
353
354 7
	$diff = abs((int)$current_time - (int)$time);
355
356 7
	$minute = 60;
357 7
	$hour = $minute * 60;
358 7
	$day = $hour * 24;
359
360 7
	if ($diff < $minute) {
361 1
		return elgg_echo("friendlytime:justnow");
362
	}
363
	
364 6
	if ($diff < $hour) {
365 3
		$granularity = ':minutes';
366 3
		$diff = round($diff / $minute);
367 6
	} else if ($diff < $day) {
368 1
		$granularity = ':hours';
369 1
		$diff = round($diff / $hour);
370 1
	} else {
371 2
		$granularity = ':days';
372 2
		$diff = round($diff / $day);
373
	}
374
375 6
	if ($diff == 0) {
376
		$diff = 1;
377
	}
378
	
379 6
	$future = ((int)$current_time - (int)$time < 0) ? ':future' : '';
380 6
	$singular = ($diff == 1) ? ':singular' : '';
381
382 6
	return elgg_echo("friendlytime{$future}{$granularity}{$singular}", array($diff));
383
}
384
385
/**
386
 * Returns a human-readable message for PHP's upload error codes
387
 *
388
 * @param int $error_code The code as stored in $_FILES['name']['error']
389
 * @return string
390
 */
391
function elgg_get_friendly_upload_error($error_code) {
392
	switch ($error_code) {
393
		case UPLOAD_ERR_OK:
394
			return '';
395
			
396
		case UPLOAD_ERR_INI_SIZE:
397
			$key = 'ini_size';
398
			break;
399
		
400
		case UPLOAD_ERR_FORM_SIZE:
401
			$key = 'form_size';
402
			break;
403
404
		case UPLOAD_ERR_PARTIAL:
405
			$key = 'partial';
406
			break;
407
408
		case UPLOAD_ERR_NO_FILE:
409
			$key = 'no_file';
410
			break;
411
412
		case UPLOAD_ERR_NO_TMP_DIR:
413
			$key = 'no_tmp_dir';
414
			break;
415
416
		case UPLOAD_ERR_CANT_WRITE:
417
			$key = 'cant_write';
418
			break;
419
420
		case UPLOAD_ERR_EXTENSION:
421
			$key = 'extension';
422
			break;
423
		
424
		default:
425
			$key = 'unknown';
426
			break;
427
	}
428
429
	return elgg_echo("upload:error:$key");
430
}
431
432
433
/**
434
 * Strip tags and offer plugins the chance.
435
 * Plugins register for output:strip_tags plugin hook.
436
 * Original string included in $params['original_string']
437
 *
438
 * @param string $string         Formatted string
439
 * @param string $allowable_tags Optional parameter to specify tags which should not be stripped
440
 *
441
 * @return string String run through strip_tags() and any plugin hooks.
442
 */
443
function elgg_strip_tags($string, $allowable_tags = null) {
444 2
	$params['original_string'] = $string;
445 2
	$params['allowable_tags'] = $allowable_tags;
446
447 2
	$string = strip_tags($string, $allowable_tags);
448 2
	$string = elgg_trigger_plugin_hook('format', 'strip_tags', $params, $string);
449
450 2
	return $string;
451
}
452
453
/**
454
 * Decode HTML markup into a raw text string
455
 *
456
 * This applies html_entity_decode() to a string while re-entitising HTML
457
 * special char entities to prevent them from being decoded back to their
458
 * unsafe original forms.
459
 *
460
 * This relies on html_entity_decode() not translating entities when
461
 * doing so leaves behind another entity, e.g. &amp;gt; if decoded would
462
 * create &gt; which is another entity itself. This seems to escape the
463
 * usual behaviour where any two paired entities creating a HTML tag are
464
 * usually decoded, i.e. a lone &gt; is not decoded, but &lt;foo&gt; would
465
 * be decoded to <foo> since it creates a full tag.
466
 *
467
 * Note: html_entity_decode() is poorly explained in the manual - which is really
468
 * bad given its potential for misuse on user input already escaped elsewhere.
469
 * Stackoverflow is littered with advice to use this function in the precise
470
 * way that would lead to user input being capable of injecting arbitrary HTML.
471
 *
472
 * @param string $string Encoded HTML
473
 *
474
 * @return string
475
 *
476
 * @author Pádraic Brady
477
 * @copyright Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com)
478
 * @license Released under dual-license GPL2/MIT by explicit permission of Pádraic Brady
479
 */
480
function elgg_html_decode($string) {
481
	$string = str_replace(
482
		array('&gt;', '&lt;', '&amp;', '&quot;', '&#039;'),
483
		array('&amp;gt;', '&amp;lt;', '&amp;amp;', '&amp;quot;', '&amp;#039;'),
484
		$string
485
	);
486
	$string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
487
	$string = str_replace(
488
		array('&amp;gt;', '&amp;lt;', '&amp;amp;', '&amp;quot;', '&amp;#039;'),
489
		array('&gt;', '&lt;', '&amp;', '&quot;', '&#039;'),
490
		$string
491
	);
492
	return $string;
493
}
494
495
/**
496
 * Prepares query string for output to prevent CSRF attacks.
497
 *
498
 * @param string $string
499
 * @return string
500
 *
501
 * @access private
502
 */
503
function _elgg_get_display_query($string) {
504
	//encode <,>,&, quotes and characters above 127
505
	if (function_exists('mb_convert_encoding')) {
506
		$display_query = mb_convert_encoding($string, 'HTML-ENTITIES', 'UTF-8');
507
	} else {
508
		// if no mbstring extension, we just strip characters
509
		$display_query = preg_replace("/[^\x01-\x7F]/", "", $string);
510
	}
511
	return htmlspecialchars($display_query, ENT_QUOTES, 'UTF-8', false);
512
}
513
514
/**
515
 * Use a "fixed" filter_var() with FILTER_VALIDATE_URL that handles multi-byte chars.
516
 *
517
 * @param string $url URL to validate
518
 * @return string|false
519
 * @access private
520
 */
521
function _elgg_sane_validate_url($url) {
522
	// based on http://php.net/manual/en/function.filter-var.php#104160
523 223
	$res = filter_var($url, FILTER_VALIDATE_URL);
524 223
	if ($res) {
525 58
		return $res;
526
	}
527
528
	// Check if it has unicode chars.
529 217
	$l = elgg_strlen($url);
530 217
	if (strlen($url) == $l) {
531 217
		return $res;
532
	}
533
534
	// Replace wide chars by “X”.
535 1
	$s = '';
536 1
	for ($i = 0; $i < $l; ++$i) {
537 1
		$ch = elgg_substr($url, $i, 1);
538 1
		$s .= (strlen($ch) > 1) ? 'X' : $ch;
539 1
	}
540
541
	// Re-check now.
542 1
	return filter_var($s, FILTER_VALIDATE_URL) ? $url : false;
543
}
544
545
return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
0 ignored issues
show
Unused Code introduced by
The parameter $events is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $hooks is not used and could be removed.

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

Loading history...
546
547
};
548