Completed
Push — 1.10.x ( 8b7a4d...379b29 )
by Angel Fernando Quiroz
46:09 queued 14:30
created

main/inc/lib/text.lib.php (5 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

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

1
<?php
2
/* For licensing terms, see /license.txt */
3
/**
4
 * This is the text library for Chamilo.
5
 * It is loaded during the global initialization,
6
 * so the functions below are available everywhere.
7
 *
8
 * @package chamilo.library
9
 */
10
11
define('EXERCISE_NUMBER_OF_DECIMALS', 2);
12
13
/**
14
 * This function strips all html-tags found in the input string and outputs a pure text.
15
 * Mostly, the function is to be used before language or encoding detection of the input string.
16
 * @param  string $string    The input string with html-tags to be converted to plain text.
17
 * @return string            The returned plain text as a result.
18
 */
19
function api_html_to_text($string)
20
{
21
    // These purifications have been found experimentally, for nice looking output.
22
    $string = preg_replace('/<br[^>]*>/i', "\n", $string);
23
    $string = preg_replace('/<\/?(div|p|h[1-6]|table|ol|ul|blockquote)[^>]*>/i', "\n", $string);
24
    $string = preg_replace('/<\/(tr|li)[^>]*>/i', "\n", $string);
25
    $string = preg_replace('/<\/(td|th)[^>]*>/i', "\t", $string);
26
27
    $string = strip_tags($string);
28
29
    // Line endings unification and cleaning.
30
    $string = str_replace(array("\r\n", "\n\r", "\r"), "\n", $string);
31
    $string = preg_replace('/\s*\n/', "\n", $string);
32
    $string = preg_replace('/\n+/', "\n", $string);
33
34
    return trim($string);
35
}
36
37
/**
38
 * Detects encoding of html-formatted text.
39
 * @param  string $string                The input html-formatted text.
40
 * @return string                        Returns the detected encoding.
41
 */
42
function api_detect_encoding_html($string) {
43
    if (@preg_match('/<head.*(<meta[^>]*content=[^>]*>).*<\/head>/si', $string, $matches)) {
44
        if (@preg_match('/<meta[^>]*charset=(.*)["\';][^>]*>/si', $matches[1], $matches)) {
45
            return api_refine_encoding_id(trim($matches[1]));
46
        }
47
    }
48
    return api_detect_encoding(api_html_to_text($string));
49
}
50
51
/**
52
 * Converts the text of a html-document to a given encoding, the meta-tag is changed accordingly.
53
 * @param string $string                The input full-html document.
54
 * @param string                        The new encoding value to be set.
55
 */
56
function api_set_encoding_html(&$string, $encoding) {
57
    $old_encoding = api_detect_encoding_html($string);
58
    if (@preg_match('/(.*<head.*)(<meta[^>]*content=[^>]*>)(.*<\/head>.*)/si', $string, $matches)) {
59
        $meta = $matches[2];
60
        if (@preg_match("/(<meta[^>]*charset=)(.*)([\"';][^>]*>)/si", $meta, $matches1)) {
61
            $meta = $matches1[1] . $encoding . $matches1[3];
62
            $string = $matches[1] . $meta . $matches[3];
63
        } else {
64
            $string = $matches[1] . '<meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'"/>' . $matches[3];
65
        }
66
    } else {
67
        $count = 1;
68
        $string = str_ireplace('</head>', '<meta http-equiv="Content-Type" content="text/html; charset='.$encoding.'"/></head>', $string, $count);
69
    }
70
    $string = api_convert_encoding($string, $encoding, $old_encoding);
0 ignored issues
show
It seems like $old_encoding defined by api_detect_encoding_html($string) on line 57 can also be of type array; however, api_convert_encoding() 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...
71
}
72
73
/**
74
 * Returns the title of a html document.
75
 * @param string $string                The contents of the input document.
76
 * @param string $input_encoding        The encoding of the input document. If the value is not set, it is detected.
77
 * @param string $$output_encoding      The encoding of the retrieved title. If the value is not set, the system encoding is assumend.
78
 * @return string                       The retrieved title, html-entities and extra-whitespace between the words are cleaned.
79
 */
80
function api_get_title_html(&$string, $output_encoding = null, $input_encoding = null) {
81
    if (@preg_match('/<head.+<title[^>]*>(.*)<\/title>/msi', $string, $matches)) {
82
        if (empty($output_encoding)) {
83
            $output_encoding = api_get_system_encoding();
84
        }
85
        if (empty($input_encoding)) {
86
            $input_encoding = api_detect_encoding_html($string);
87
        }
88
        return trim(@preg_replace('/\s+/', ' ', api_html_entity_decode(api_convert_encoding($matches[1], $output_encoding, $input_encoding), ENT_QUOTES, $output_encoding)));
89
    }
90
    return '';
91
}
92
93
94
/* XML processing functions */
95
96
// A regular expression for accessing declared encoding within xml-formatted text.
97
// Published by Steve Minutillo,
98
// http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss/
99
define('_PCRE_XML_ENCODING', '/<\?xml.*encoding=[\'"](.*?)[\'"].*\?>/m');
100
101
/**
102
 * Detects encoding of xml-formatted text.
103
 * @param string $string                The input xml-formatted text.
104
 * @param string $default_encoding      This is the default encoding to be returned if there is no way the xml-text's encoding to be detected. If it not spesified, the system encoding is assumed then.
105
 * @return string                       Returns the detected encoding.
106
 * @todo The second parameter is to be eliminated. See api_detect_encoding_html().
107
 */
108
function api_detect_encoding_xml($string, $default_encoding = null) {
109
    if (preg_match(_PCRE_XML_ENCODING, $string, $matches)) {
110
        return api_refine_encoding_id($matches[1]);
111
    }
112
    if (api_is_valid_utf8($string)) {
113
        return 'UTF-8';
114
    }
115
    if (empty($default_encoding)) {
116
        $default_encoding = _api_mb_internal_encoding();
117
    }
118
    return api_refine_encoding_id($default_encoding);
119
}
120
121
122
/**
123
 * Converts character encoding of a xml-formatted text. If inside the text the encoding is declared, it is modified accordingly.
124
 * @param string $string                    The text being converted.
125
 * @param string $to_encoding               The encoding that text is being converted to.
126
 * @param string $from_encoding (optional)  The encoding that text is being converted from. If it is omited, it is tried to be detected then.
127
 * @return string                           Returns the converted xml-text.
128
 */
129
function api_convert_encoding_xml($string, $to_encoding, $from_encoding = null) {
130
    return _api_convert_encoding_xml($string, $to_encoding, $from_encoding);
131
}
132
133
/**
134
 * Converts character encoding of a xml-formatted text into UTF-8. If inside the text the encoding is declared, it is set to UTF-8.
135
 * @param string $string                    The text being converted.
136
 * @param string $from_encoding (optional)  The encoding that text is being converted from. If it is omited, it is tried to be detected then.
137
 * @return string                           Returns the converted xml-text.
138
 */
139
function api_utf8_encode_xml($string, $from_encoding = null) {
140
    return _api_convert_encoding_xml($string, 'UTF-8', $from_encoding);
141
}
142
143
/**
144
 * Converts character encoding of a xml-formatted text from UTF-8 into a specified encoding. If inside the text the encoding is declared, it is modified accordingly.
145
 * @param string $string                    The text being converted.
146
 * @param string $to_encoding (optional)    The encoding that text is being converted to. If it is omited, the platform character set is assumed.
147
 * @return string                           Returns the converted xml-text.
148
 */
149
function api_utf8_decode_xml($string, $to_encoding = 'UTF-8') {
150
    return _api_convert_encoding_xml($string, $to_encoding, 'UTF-8');
151
}
152
153
/**
154
 * Converts character encoding of a xml-formatted text. If inside the text the encoding is declared, it is modified accordingly.
155
 * @param string $string                    The text being converted.
156
 * @param string $to_encoding               The encoding that text is being converted to.
157
 * @param string $from_encoding (optional)  The encoding that text is being converted from. If the value is empty, it is tried to be detected then.
158
 * @return string                           Returns the converted xml-text.
159
 */
160
function _api_convert_encoding_xml(&$string, $to_encoding, $from_encoding) {
161
    if (empty($from_encoding)) {
162
        $from_encoding = api_detect_encoding_xml($string);
163
    }
164
    $to_encoding = api_refine_encoding_id($to_encoding);
165
    if (!preg_match('/<\?xml.*\?>/m', $string, $matches)) {
166
        return api_convert_encoding('<?xml version="1.0" encoding="'.$to_encoding.'"?>'."\n".$string, $to_encoding, $from_encoding);
0 ignored issues
show
It seems like $to_encoding defined by api_refine_encoding_id($to_encoding) on line 164 can also be of type array; however, api_convert_encoding() 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...
It seems like $from_encoding defined by api_detect_encoding_xml($string) on line 162 can also be of type array; however, api_convert_encoding() 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...
167
    }
168
    if (!preg_match(_PCRE_XML_ENCODING, $string)) {
169
        if (strpos($matches[0], 'standalone') !== false) {
170
            // The encoding option should precede the standalone option, othewise DOMDocument fails to load the document.
171
            $replace = str_replace('standalone', ' encoding="'.$to_encoding.'" standalone' , $matches[0]);
172
        } else {
173
            $replace = str_replace('?>', ' encoding="'.$to_encoding.'"?>' , $matches[0]);
174
        }
175
        return api_convert_encoding(str_replace($matches[0], $replace, $string), $to_encoding, $from_encoding);
0 ignored issues
show
It seems like $to_encoding defined by api_refine_encoding_id($to_encoding) on line 164 can also be of type array; however, api_convert_encoding() 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...
It seems like $from_encoding defined by api_detect_encoding_xml($string) on line 162 can also be of type array; however, api_convert_encoding() 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...
176
    }
177
    global $_api_encoding;
178
    $_api_encoding = api_refine_encoding_id($to_encoding);
179
    return api_convert_encoding(preg_replace_callback(_PCRE_XML_ENCODING, '_api_convert_encoding_xml_callback', $string), $to_encoding, $from_encoding);
180
}
181
182
/**
183
 * A callback for serving the function _api_convert_encoding_xml().
184
 * @param array $matches    Input array of matches corresponding to the xml-declaration.
185
 * @return string           Returns the xml-declaration with modified encoding.
186
 */
187
function _api_convert_encoding_xml_callback($matches) {
188
    global $_api_encoding;
189
    return str_replace($matches[1], $_api_encoding, $matches[0]);
190
}
191
192
/* Functions for supporting ASCIIMathML mathematical formulas and ASCIIsvg maathematical graphics */
193
194
/**
195
 * Dectects ASCIIMathML formula presence within a given html text.
196
 * @param string $html      The input html text.
197
 * @return bool             Returns TRUE when there is a formula found or FALSE otherwise.
198
 */
199 View Code Duplication
function api_contains_asciimathml($html) {
200
    if (!preg_match_all('/<span[^>]*class\s*=\s*[\'"](.*?)[\'"][^>]*>/mi', $html, $matches)) {
201
        return false;
202
    }
203
    foreach ($matches[1] as $string) {
204
        $string = ' '.str_replace(',', ' ', $string).' ';
205
        if (preg_match('/\sAM\s/m', $string)) {
206
            return true;
207
        }
208
    }
209
    return false;
210
}
211
212
/**
213
 * Dectects ASCIIsvg graphics presence within a given html text.
214
 * @param string $html      The input html text.
215
 * @return bool             Returns TRUE when there is a graph found or FALSE otherwise.
216
 */
217 View Code Duplication
function api_contains_asciisvg($html) {
218
    if (!preg_match_all('/<embed([^>]*?)>/mi', $html, $matches)) {
219
        return false;
220
    }
221
    foreach ($matches[1] as $string) {
222
        $string = ' '.str_replace(',', ' ', $string).' ';
223
        if (preg_match('/sscr\s*=\s*[\'"](.*?)[\'"]/m', $string)) {
224
            return true;
225
        }
226
    }
227
    return false;
228
}
229
230
/* Miscellaneous text processing functions */
231
232
/**
233
 * Convers a string from camel case into underscore.
234
 * Works correctly with ASCII strings only, implementation for human-language strings is not necessary.
235
 * @param string $string                            The input string (ASCII)
236
 * @return string                                   The converted result string
237
 */
238
function api_camel_case_to_underscore($string) {
239
    return strtolower(preg_replace('/([a-z])([A-Z])/', "$1_$2", $string));
240
}
241
242
/**
243
 * Converts a string with underscores into camel case.
244
 * Works correctly with ASCII strings only, implementation for human-language strings is not necessary.
245
 * @param string $string                            The input string (ASCII)
246
 * @param bool $capitalise_first_char (optional)    If true (default), the function capitalises the first char in the result string.
247
 * @return string                                   The converted result string
248
 */
249
function api_underscore_to_camel_case($string, $capitalise_first_char = true) {
250
    if ($capitalise_first_char) {
251
        $string = ucfirst($string);
252
    }
253
    return preg_replace_callback('/_([a-z])/', '_api_camelize', $string);
254
}
255
256
// A function for internal use, only for this library.
257
function _api_camelize($match) {
258
    return strtoupper($match[1]);
259
}
260
261
/**
262
 * Truncates a string.
263
 *
264
 * @author Brouckaert Olivier
265
 * @param  string $text                  The text to truncate.
266
 * @param  integer $length               The approximate desired length. The length of the suffix below is to be added to have the total length of the result string.
267
 * @param  string $suffix                A suffix to be added as a replacement.
268
 * @param string $encoding (optional)    The encoding to be used. If it is omitted, the platform character set will be used by default.
269
 * @param  boolean $middle               If this parameter is true, truncation is done in the middle of the string.
270
 * @return string                        Truncated string, decorated with the given suffix (replacement).
271
 */
272
function api_trunc_str($text, $length = 30, $suffix = '...', $middle = false, $encoding = null) {
273
    if (empty($encoding)) {
274
        $encoding = api_get_system_encoding();
275
    }
276
    $text_length = api_strlen($text, $encoding);
277
    if ($text_length <= $length) {
278
        return $text;
279
    }
280
    if ($middle) {
281
        return rtrim(api_substr($text, 0, round($length / 2), $encoding)).$suffix.ltrim(api_substr($text, - round($length / 2), $text_length, $encoding));
282
    }
283
    return rtrim(api_substr($text, 0, $length, $encoding)).$suffix;
284
}
285
286
/**
287
 * Handling simple and double apostrofe in order that strings be stored properly in database
288
 *
289
 * @author Denes Nagy
290
 * @param  string variable - the variable to be revised
291
 */
292
function domesticate($input) {
293
    $input = stripslashes($input);
294
    $input = str_replace("'", "''", $input);
295
    $input = str_replace('"', "''", $input);
296
    return ($input);
297
}
298
299
/**
300
 * function make_clickable($string)
301
 *
302
 * @desc   Completes url contained in the text with "<a href ...".
303
 *         However the function simply returns the submitted text without any
304
 *         transformation if it already contains some "<a href:" or "<img src=".
305
 * @param string $text text to be converted
306
 * @return text after conversion
307
 * @author Rewritten by Nathan Codding - Feb 6, 2001.
308
 *         completed by Hugues Peeters - July 22, 2002
309
 *
310
 * Actually this function is taken from the PHP BB 1.4 script
311
 * - Goes through the given string, and replaces xxxx://yyyy with an HTML <a> tag linking
312
 *     to that URL
313
 * - Goes through the given string, and replaces www.xxxx.yyyy[zzzz] with an HTML <a> tag linking
314
 *     to http://www.xxxx.yyyy[/zzzz]
315
 * - Goes through the given string, and replaces xxxx@yyyy with an HTML mailto: tag linking
316
 *        to that email address
317
 * - Only matches these 2 patterns either after a space, or at the beginning of a line
318
 *
319
 * Notes: the email one might get annoying - it's easy to make it more restrictive, though.. maybe
320
 * have it require something like [email protected] or such. We'll see.
321
 */
322
323
/**
324
 * Callback to convert URI match to HTML A element.
325
 *
326
 * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
327
 * make_clickable()}.
328
 *
329
 * @since Wordpress 2.3.2
330
 * @access private
331
 *
332
 * @param array $matches Single Regex Match.
333
 * @return string HTML A element with URI address.
334
 */
335
function _make_url_clickable_cb($matches) {
336
    $url = $matches[2];
337
338
    if ( ')' == $matches[3] && strpos( $url, '(' ) ) {
339
        // If the trailing character is a closing parethesis, and the URL has an opening parenthesis in it, add the closing parenthesis to the URL.
340
        // Then we can let the parenthesis balancer do its thing below.
341
        $url .= $matches[3];
342
        $suffix = '';
343
    } else {
344
        $suffix = $matches[3];
345
    }
346
347
    // Include parentheses in the URL only if paired
348
    while ( substr_count( $url, '(' ) < substr_count( $url, ')' ) ) {
349
        $suffix = strrchr( $url, ')' ) . $suffix;
350
        $url = substr( $url, 0, strrpos( $url, ')' ) );
351
    }
352
353
    $url = esc_url($url);
354
    if ( empty($url) )
355
        return $matches[0];
356
357
    return $matches[1] . "<a href=\"$url\" rel=\"nofollow\">$url</a>" . $suffix;
358
}
359
360
/**
361
 * Checks and cleans a URL.
362
 *
363
 * A number of characters are removed from the URL. If the URL is for displaying
364
 * (the default behaviour) ampersands are also replaced. The 'clean_url' filter
365
 * is applied to the returned cleaned URL.
366
 *
367
 * @since wordpress 2.8.0
368
 * @uses wp_kses_bad_protocol() To only permit protocols in the URL set
369
 *		via $protocols or the common ones set in the function.
370
 *
371
 * @param string $url The URL to be cleaned.
372
 * @param array $protocols Optional. An array of acceptable protocols.
373
 *		Defaults to 'http', 'https', 'ftp', 'ftps', 'mailto', 'news', 'irc', 'gopher', 'nntp', 'feed', 'telnet', 'mms', 'rtsp', 'svn' if not set.
374
 * @param string $_context Private. Use esc_url_raw() for database usage.
375
 * @return string The cleaned $url after the 'clean_url' filter is applied.
376
 */
377
function esc_url( $url, $protocols = null, $_context = 'display' ) {
378
    //$original_url = $url;
379
380
    if ( '' == $url )
381
        return $url;
382
    $url = preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i', '', $url);
383
    $strip = array('%0d', '%0a', '%0D', '%0A');
384
    $url = _deep_replace($strip, $url);
385
    $url = str_replace(';//', '://', $url);
386
    /* If the URL doesn't appear to contain a scheme, we
387
     * presume it needs http:// appended (unless a relative
388
     * link starting with /, # or ? or a php file).
389
     */
390
    if ( strpos($url, ':') === false && ! in_array( $url[0], array( '/', '#', '?' ) ) &&
391
        ! preg_match('/^[a-z0-9-]+?\.php/i', $url) )
392
        $url = 'http://' . $url;
393
394
    return Security::remove_XSS($url);
395
396
    /*// Replace ampersands and single quotes only when displaying.
397
    if ( 'display' == $_context ) {
398
        $url = wp_kses_normalize_entities( $url );
399
        $url = str_replace( '&amp;', '&#038;', $url );
400
        $url = str_replace( "'", '&#039;', $url );
401
    }
402
403
    if ( '/' === $url[0] ) {
404
        $good_protocol_url = $url;
405
    } else {
406
        if ( ! is_array( $protocols ) )
407
            $protocols = wp_allowed_protocols();
408
        $good_protocol_url = wp_kses_bad_protocol( $url, $protocols );
409
        if ( strtolower( $good_protocol_url ) != strtolower( $url ) )
410
            return '';
411
    }
412
413
    /**
414
     * Filter a string cleaned and escaped for output as a URL.
415
     *
416
     * @since 2.3.0
417
     *
418
     * @param string $good_protocol_url The cleaned URL to be returned.
419
     * @param string $original_url      The URL prior to cleaning.
420
     * @param string $_context          If 'display', replace ampersands and single quotes only.
421
     */
422
    //return apply_filters( 'clean_url', $good_protocol_url, $original_url, $_context );98
423
}
424
425
426
/**
427
 * Perform a deep string replace operation to ensure the values in $search are no longer present
428
 *
429
 * Repeats the replacement operation until it no longer replaces anything so as to remove "nested" values
430
 * e.g. $subject = '%0%0%0DDD', $search ='%0D', $result ='' rather than the '%0%0DD' that
431
 * str_replace would return
432
 *
433
 * @since wordpress  2.8.1
434
 * @access private
435
 *
436
 * @param string|array $search The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles.
437
 * @param string $subject The string being searched and replaced on, otherwise known as the haystack.
438
 * @return string The string with the replaced svalues.
439
 */
440
function _deep_replace( $search, $subject ) {
441
    $subject = (string) $subject;
442
443
    $count = 1;
444
    while ( $count ) {
445
        $subject = str_replace( $search, '', $subject, $count );
446
    }
447
448
    return $subject;
449
}
450
451
452
/**
453
 * Callback to convert URL match to HTML A element.
454
 *
455
 * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
456
 * make_clickable()}.
457
 *
458
 * @since wordpress  2.3.2
459
 * @access private
460
 *
461
 * @param array $matches Single Regex Match.
462
 * @return string HTML A element with URL address.
463
 */
464
function _make_web_ftp_clickable_cb($matches) {
465
    $ret = '';
466
    $dest = $matches[2];
467
    $dest = 'http://' . $dest;
468
    $dest = esc_url($dest);
469
    if ( empty($dest) )
470
        return $matches[0];
471
472
    // removed trailing [.,;:)] from URL
473
    if ( in_array( substr($dest, -1), array('.', ',', ';', ':', ')') ) === true ) {
474
        $ret = substr($dest, -1);
475
        $dest = substr($dest, 0, strlen($dest)-1);
476
    }
477
    return $matches[1] . "<a href=\"$dest\" rel=\"nofollow\">$dest</a>$ret";
478
}
479
480
/**
481
 * Callback to convert email address match to HTML A element.
482
 *
483
 * This function was backported from 2.5.0 to 2.3.2. Regex callback for {@link
484
 * make_clickable()}.
485
 *
486
 * @since wordpress  2.3.2
487
 * @access private
488
 *
489
 * @param array $matches Single Regex Match.
490
 * @return string HTML A element with email address.
491
 */
492
function _make_email_clickable_cb($matches) {
493
    $email = $matches[2] . '@' . $matches[3];
494
    return $matches[1] . "<a href=\"mailto:$email\">$email</a>";
495
}
496
497
/**
498
 * Convert plaintext URI to HTML links.
499
 *
500
 * Converts URI, www and ftp, and email addresses. Finishes by fixing links
501
 * within links.
502
 *
503
 * @since wordpress  0.71
504
 *
505
 * @param string $text Content to convert URIs.
506
 * @return string Content with converted URIs.
507
 */
508
function make_clickable( $text ) {
509
    $r = '';
510
    $textarr = preg_split( '/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // split out HTML tags
511
    $nested_code_pre = 0; // Keep track of how many levels link is nested inside <pre> or <code>
512
    foreach ( $textarr as $piece ) {
513
514
        if ( preg_match( '|^<code[\s>]|i', $piece ) || preg_match( '|^<pre[\s>]|i', $piece ) )
515
            $nested_code_pre++;
516
        elseif ( ( '</code>' === strtolower( $piece ) || '</pre>' === strtolower( $piece ) ) && $nested_code_pre )
517
            $nested_code_pre--;
518
519
        if ( $nested_code_pre || empty( $piece ) || ( $piece[0] === '<' && ! preg_match( '|^<\s*[\w]{1,20}+://|', $piece ) ) ) {
520
            $r .= $piece;
521
            continue;
522
        }
523
524
        // Long strings might contain expensive edge cases ...
525
        if ( 10000 < strlen( $piece ) ) {
526
            // ... break it up
527
            foreach ( _split_str_by_whitespace( $piece, 2100 ) as $chunk ) { // 2100: Extra room for scheme and leading and trailing paretheses
528
                if ( 2101 < strlen( $chunk ) ) {
529
                    $r .= $chunk; // Too big, no whitespace: bail.
530
                } else {
531
                    $r .= make_clickable( $chunk );
532
                }
533
            }
534
        } else {
535
            $ret = " $piece "; // Pad with whitespace to simplify the regexes
536
537
            $url_clickable = '~
538
				([\\s(<.,;:!?])                                        # 1: Leading whitespace, or punctuation
539
				(                                                      # 2: URL
540
					[\\w]{1,20}+://                                # Scheme and hier-part prefix
541
					(?=\S{1,2000}\s)                               # Limit to URLs less than about 2000 characters long
542
					[\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]*+         # Non-punctuation URL character
543
					(?:                                            # Unroll the Loop: Only allow puctuation URL character if followed by a non-punctuation URL character
544
						[\'.,;:!?)]                            # Punctuation URL character
545
						[\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]++ # Non-punctuation URL character
546
					)*
547
				)
548
				(\)?)                                                  # 3: Trailing closing parenthesis (for parethesis balancing post processing)
549
			~xS'; // The regex is a non-anchored pattern and does not have a single fixed starting character.
550
            // Tell PCRE to spend more time optimizing since, when used on a page load, it will probably be used several times.
551
552
            $ret = preg_replace_callback( $url_clickable, '_make_url_clickable_cb', $ret );
553
554
            $ret = preg_replace_callback( '#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret );
555
            $ret = preg_replace_callback( '#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret );
556
557
            $ret = substr( $ret, 1, -1 ); // Remove our whitespace padding.
558
            $r .= $ret;
559
        }
560
    }
561
562
    // Cleanup of accidental links within links
563
    $r = preg_replace( '#(<a([ \r\n\t]+[^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i', "$1$3</a>", $r );
564
    return $r;
565
}
566
567
/**
568
 * Breaks a string into chunks by splitting at whitespace characters.
569
 * The length of each returned chunk is as close to the specified length goal as possible,
570
 * with the caveat that each chunk includes its trailing delimiter.
571
 * Chunks longer than the goal are guaranteed to not have any inner whitespace.
572
 *
573
 * Joining the returned chunks with empty delimiters reconstructs the input string losslessly.
574
 *
575
 * Input string must have no null characters (or eventual transformations on output chunks must not care about null characters)
576
 *
577
 * <code>
578
 * _split_str_by_whitespace( "1234 67890 1234 67890a cd 1234   890 123456789 1234567890a    45678   1 3 5 7 90 ", 10 ) ==
579
 * array (
580
 *   0 => '1234 67890 ',  // 11 characters: Perfect split
581
 *   1 => '1234 ',        //  5 characters: '1234 67890a' was too long
582
 *   2 => '67890a cd ',   // 10 characters: '67890a cd 1234' was too long
583
 *   3 => '1234   890 ',  // 11 characters: Perfect split
584
 *   4 => '123456789 ',   // 10 characters: '123456789 1234567890a' was too long
585
 *   5 => '1234567890a ', // 12 characters: Too long, but no inner whitespace on which to split
586
 *   6 => '   45678   ',  // 11 characters: Perfect split
587
 *   7 => '1 3 5 7 9',    //  9 characters: End of $string
588
 * );
589
 * </code>
590
 *
591
 * @since wordpress  3.4.0
592
 * @access private
593
 *
594
 * @param string $string The string to split.
595
 * @param int $goal The desired chunk length.
596
 * @return array Numeric array of chunks.
597
 */
598
function _split_str_by_whitespace( $string, $goal ) {
599
    $chunks = array();
600
601
    $string_nullspace = strtr( $string, "\r\n\t\v\f ", "\000\000\000\000\000\000" );
602
603
    while ( $goal < strlen( $string_nullspace ) ) {
604
        $pos = strrpos( substr( $string_nullspace, 0, $goal + 1 ), "\000" );
605
606
        if ( false === $pos ) {
607
            $pos = strpos( $string_nullspace, "\000", $goal + 1 );
608
            if ( false === $pos ) {
609
                break;
610
            }
611
        }
612
613
        $chunks[] = substr( $string, 0, $pos + 1 );
614
        $string = substr( $string, $pos + 1 );
615
        $string_nullspace = substr( $string_nullspace, $pos + 1 );
616
    }
617
618
    if ( $string ) {
619
        $chunks[] = $string;
620
    }
621
622
    return $chunks;
623
}
624
625
/**
626
 * This functions cuts a paragraph
627
 * i.e cut('Merry Xmas from Lima',13) = "Merry Xmas fr..."
628
 * @param string    The text to "cut"
629
 * @param int       Count of chars
630
 * @param bool      Whether to embed in a <span title="...">...</span>
631
 * @return string
632
 * */
633
function cut($text, $maxchar, $embed = false) {
634
    if (api_strlen($text) > $maxchar) {
635
        if ($embed) {
636
            return '<p title="'.$text.'">'.api_substr($text, 0, $maxchar).'...</p>';
637
        }
638
        return api_substr($text, 0, $maxchar).' ...';
639
    }
640
    return $text;
641
}
642
643
/**
644
 * Show a number as only integers if no decimals, but will show 2 decimals if exist.
645
 *
646
 * @param mixed     Number to convert
647
 * @param int       Decimal points 0=never, 1=if needed, 2=always
648
 * @return mixed    An integer or a float depends on the parameter
649
 */
650
function float_format($number, $flag = 1) {
651
    if (is_numeric($number)) {
652
        if (!$number) {
653
            $result = ($flag == 2 ? '0.'.str_repeat('0', EXERCISE_NUMBER_OF_DECIMALS) : '0');
654
        } else {
655
656
            if (floor($number) == $number) {
657
                $result = number_format($number, ($flag == 2 ? EXERCISE_NUMBER_OF_DECIMALS : 0));
658
            } else {
659
                $result = number_format(round($number, 2), ($flag == 0 ? 0 : EXERCISE_NUMBER_OF_DECIMALS));
660
            }
661
        }
662
        return $result;
663
    }
664
}
665
666
// TODO: To be checked for correct timezone management.
667
/**
668
 * Function to obtain last week timestamps
669
 * @return array    Times for every day inside week
670
 */
671
function get_last_week() {
672
    $week = date('W');
673
    $year = date('Y');
674
675
    $lastweek = $week - 1;
676
    if ($lastweek == 0) {
677
        $week = 52;
678
        $year--;
679
    }
680
681
    $lastweek = sprintf("%02d", $lastweek);
682
    $arrdays = array();
683
    for ($i = 1; $i <= 7; $i++) {
684
        $arrdays[] = strtotime("$year"."W$lastweek"."$i");
685
    }
686
    return $arrdays;
687
}
688
689
/**
690
 * Gets the week from a day
691
 * @param   string   Date in UTC (2010-01-01 12:12:12)
692
 * @return  int      Returns an integer with the week number of the year
693
 */
694
function get_week_from_day($date) {
695
    if (!empty($date)) {
696
       $time = api_strtotime($date,'UTC');
697
       return date('W', $time);
698
    } else {
699
        return date('W');
700
    }
701
}
702
703
/**
704
 * This function splits the string into words and then joins them back together again one by one.
705
 * Example: "Test example of a long string"
706
 * 			substrwords(5) = Test ... *
707
 * @param string
708
 * @param int the max number of character
709
 * @param string how the string will be end
710
 * @return a reduce string
711
 */
712
713
function substrwords($text,$maxchar,$end='...')
714
{
715
	if(strlen($text)>$maxchar)
716
	{
717
		$words=explode(" ",$text);
718
		$output = '';
719
		$i=0;
720
		while(1)
721
		{
722
			$length = (strlen($output)+strlen($words[$i]));
723
			if($length>$maxchar)
724
			{
725
				break;
726
			}
727
			else
728
			{
729
				$output = $output." ".$words[$i];
730
				$i++;
731
			};
732
		};
733
	}
734
	else
735
	{
736
		$output = $text;
737
		return $output;
738
	}
739
	return $output.$end;
740
}
741
742
function implode_with_key($glue, $array) {
743
    if (!empty($array)) {
744
        $string = '';
745
        foreach($array as $key => $value) {
746
            if (empty($value)) {
747
                $value = 'null';
748
            }
749
            $string .= $key." : ".$value." $glue ";
750
        }
751
        return $string;
752
    }
753
    return '';
754
}
755
756
757
/**
758
 * Transform the file size in a human readable format.
759
 *
760
 * @param  int  $file_size    Size of the file in bytes
761
 * @return string A human readable representation of the file size
762
 */
763
function format_file_size($file_size)
764
{
765
    $file_size = intval($file_size);
766
    if ($file_size >= 1073741824) {
767
        $file_size = round($file_size / 1073741824 * 100) / 100 . 'G';
768
    } elseif($file_size >= 1048576) {
769
        $file_size = round($file_size / 1048576 * 100) / 100 . 'M';
770
    } elseif($file_size >= 1024) {
771
        $file_size = round($file_size / 1024 * 100) / 100 . 'k';
772
    } else {
773
        $file_size = $file_size . 'B';
774
    }
775
    return $file_size;
776
}
777
778
function return_datetime_from_array($array)
779
{
780
    $year	 = '0000';
781
    $month = $day = $hours = $minutes = $seconds = '00';
782
    if (isset($array['Y']) && (isset($array['F']) || isset($array['M']))  && isset($array['d']) && isset($array['H']) && isset($array['i'])) {
783
        $year = $array['Y'];
784
        $month = isset($array['F'])?$array['F']:$array['M'];
785
        if (intval($month) < 10 ) $month = '0'.$month;
786
        $day = $array['d'];
787
        if (intval($day) < 10 ) $day = '0'.$day;
788
        $hours = $array['H'];
789
        if (intval($hours) < 10 ) $hours = '0'.$hours;
790
        $minutes = $array['i'];
791
        if (intval($minutes) < 10 ) $minutes = '0'.$minutes;
792
    }
793
    if (checkdate($month,$day,$year)) {
794
        $datetime = $year.'-'.$month.'-'.$day.' '.$hours.':'.$minutes.':'.$seconds;
795
    }
796
    return $datetime;
797
}
798
799
/**
800
 * Converts an string CLEANYO[admin][amann,acostea]
801
 * into an array:
802
 *
803
 * array(
804
 *  CLEANYO
805
 *  admin
806
 *  amann,acostea
807
 * )
808
 *
809
 * @param $array
810
 * @return array
811
 */
812
function bracketsToArray($array)
813
{
814
    return preg_split('/[\[\]]+/', $array, -1, PREG_SPLIT_NO_EMPTY);
815
}
816
817
/**
818
 * @param string $string
819
 * @param bool $capitalizeFirstCharacter
820
 * @return mixed
821
 */
822
function underScoreToCamelCase($string, $capitalizeFirstCharacter = true)
823
{
824
    $str = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
825
826
    if (!$capitalizeFirstCharacter) {
827
        $str[0] = strtolower($str[0]);
828
    }
829
830
    return $str;
831
}
832