Completed
Push — master ( f5c42a...6861f5 )
by Julito
75:53 queued 27:38
created

internationalization.lib.php ➔ get_lang()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 3
nop 1
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use Patchwork\Utf8;
5
use Chamilo\CoreBundle\Framework\Container;
6
use Symfony\Component\Intl\Intl;
7
8
/**
9
 * File: internationalization.lib.php
10
 * Internationalization library for Chamilo 1.x LMS
11
 * A library implementing internationalization related functions.
12
 * License: GNU General Public License Version 3 (Free Software Foundation)ww
13
 * @author Ivan Tcholakov, <[email protected]>, 2009, 2010
14
 * @author More authors, mentioned in the correpsonding fragments of this source.
15
 * @package chamilo.library
16
 */
17
18
/**
19
 * Constants
20
 */
21
22
// Predefined date formats in Chamilo provided by the language sub-system.
23
// To be used as a parameter for the function api_format_date()
24
25
define('TIME_NO_SEC_FORMAT', 0); // 15:23
26
define('DATE_FORMAT_SHORT', 1); // Aug 25, 09
27
define('DATE_FORMAT_LONG', 2); // Monday August 25, 09
28
define('DATE_FORMAT_LONG_NO_DAY', 10); // August 25, 2009
29
define('DATE_TIME_FORMAT_LONG', 3); // Monday August 25, 2009 at 03:28 PM
30
31
define('DATE_FORMAT_NUMBER', 4); // 25.08.09
32
define('DATE_TIME_FORMAT_LONG_24H', 5); // August 25, 2009 at 15:28
33
define('DATE_TIME_FORMAT_SHORT', 6); // Aug 25, 2009 at 03:28 PM
34
define('DATE_TIME_FORMAT_SHORT_TIME_FIRST', 7); // 03:28 PM, Aug 25 2009
35
define('DATE_FORMAT_NUMBER_NO_YEAR', 8); // 25.08 dd-mm
36
define('DATE_FORMAT_ONLY_DAYNAME', 9); // Monday, Sunday, etc
37
38
// Formatting person's name.
39
// Formatting a person's name using the pattern as it has been
40
// configured in the internationalization database for every language.
41
// This (default) option would be the most used.
42
define('PERSON_NAME_COMMON_CONVENTION', 0);
43
// The following options may be used in limited number of places for overriding the common convention:
44
45
// Formatting a person's name in Western order: first_name last_name
46
define('PERSON_NAME_WESTERN_ORDER', 1);
47
// Formatting a person's name in Eastern order: last_name first_name
48
define('PERSON_NAME_EASTERN_ORDER', 2);
49
// Contextual: formatting person's name in library order: last_name, first_name
50
define('PERSON_NAME_LIBRARY_ORDER', 3);
51
// Contextual: formatting a person's name assotiated with an email-address.
52
// Ivan: I am not sure how seems email servers an clients would interpret name order, so I assign the Western order.
53
define('PERSON_NAME_EMAIL_ADDRESS', PERSON_NAME_WESTERN_ORDER);
54
// Contextual: formatting a person's name for data-exporting operations.
55
// For backward compatibility this format has been set to Eastern order.
56
define('PERSON_NAME_DATA_EXPORT', PERSON_NAME_EASTERN_ORDER);
57
58
/**
59
 * Returns a translated (localized) string
60
 * @param string $variable				This is the identificator (name) of the translated string to be retrieved.
61
62
 * @return string						Returns the requested string in the correspondent language.
63
 * Notes:
64
 * 1. If the name of a given language variable has the prefix "lang" it may be omited, i.e. get_lang('Yes') == get_lang('Yes').
65
 * 2. Translations are created many contributors through using a special tool: Chamilo Translation Application.
66
 * @link http://translate.chamilo.org/
67
 */
68
function get_lang($variable)
69
{
70
    $defaultDomain = 'messages';
71
    $translated = Container::getTranslator()->trans(
72
        $variable,
73
        array(),
74
        $defaultDomain
75
    );
76
77
    if ($translated == $variable) {
78
        // Check the langVariable for BC
79
        $translated = Container::getTranslator()->trans(
80
            "lang$variable",
81
            array(),
82
            $defaultDomain
83
        );
84
85
        if ($translated == "lang$variable") {
86
            return $variable;
87
    }
88
    }
89
90
    return $translated;
91
}
92
93
/**
94
 * Gets the current interface language.
95
 * @param bool $purified (optional)	When it is true, a purified (refined)
96
 * language value will be returned, for example 'french' instead of 'french_unicode'.
97
 * @param bool $setParentLanguageName
98
 * @return string					The current language of the interface.
99
 */
100
function api_get_interface_language(
101
    $purified = false,
102
    $check_sub_language = false,
103
    $setParentLanguageName = true
104
) {
105
    global $language_interface;
106
107
    if (empty($language_interface)) {
108
        return 'english';
109
    }
110
111
    if ($check_sub_language) {
112
        static $parent_language_name = null;
113
114
        if (!isset($parent_language_name)) {
115
            // 2. The current language is a sub language so we grab the father's
116
            // setting according to the internalization_database/name_order_convetions.php file
117
            $language_id = api_get_language_id($language_interface);
118
            $language_info = api_get_language_info($language_id);
119
120
            if (
121
                !empty($language_id) &&
122
                !empty($language_info)
123
            ) {
124
                if (!empty($language_info['parent_id'])) {
125
                    $language_info = api_get_language_info($language_info['parent_id']);
126
                    if ($setParentLanguageName) {
127
                        $parent_language_name = $language_info['english_name'];
128
                    }
129
130
                    if (!empty($parent_language_name)) {
131
                        return $parent_language_name;
132
                    }
133
                }
134
135
                return $language_info['english_name'];
136
            }
137
138
            return 'english';
139
        } else {
140
            return $parent_language_name;
141
        }
142
    } else {
143
        // 2. Normal way
144
        $interface_language = $purified ? api_purify_language_id($language_interface) : $language_interface;
145
    }
146
147
    return $interface_language;
148
}
149
150
/**
151
 * Returns a purified language id, without possible suffixes that will disturb language identification in certain cases.
152
 * @param string $language	The input language identificator, for example 'french_unicode'.
153
 * @param string			The same purified or filtered language identificator, for example 'french'.
154
 */
155
function api_purify_language_id($language) {
156
    static $purified = array();
157
    if (!isset($purified[$language])) {
158
        $purified[$language] = trim(str_replace(array('_unicode', '_latin', '_corporate', '_org', '_km'), '', strtolower($language)));
159
    }
160
    return $purified[$language];
161
}
162
163
/**
164
 * Gets language isocode column from the language table, taking the given language as a query parameter.
165
 * @param string $language		This is the name of the folder containing translations for the corresponding language (e.g arabic, english).
166
 * @param string $default_code	This is the value to be returned if there was no code found corresponding to the given language.
167
 * If $language is omitted, interface language is assumed then.
168
 * @return string			The found isocode or null on error.
169
 * Returned codes are according to the following standards (in order of preference):
170
 * -  ISO 639-1 : Alpha-2 code (two-letters code - en, fr, es, ...)
171
 * -  RFC 4646  : five-letter code based on the ISO 639 two-letter language codes
172
 *    and the ISO 3166 two-letter territory codes (pt-BR, ...)
173
 * -  ISO 639-2 : Alpha-3 code (three-letters code - ast, fur, ...)
174
 */
175
function api_get_language_isocode($language = null, $default_code = 'en')
176
{
177
    static $iso_code = array();
178
    if (empty($language)) {
179
        $language = api_get_interface_language(false, true);
180
    }
181
    if (!isset($iso_code[$language])) {
182
        if (!class_exists('Database')) {
183
            // This might happen, in case of calling this function early during the global initialization.
184
            return $default_code; // This might happen, in case of calling this function early during the global initialization.
185
        }
186
        $sql = "SELECT isocode 
187
                FROM ".Database::get_main_table(TABLE_MAIN_LANGUAGE)." 
188
                WHERE dokeos_folder = '$language'";
189
        $sql_result = Database::query($sql);
190
        if (Database::num_rows($sql_result)) {
191
            $result = Database::fetch_array($sql_result);
192
            $iso_code[$language] = trim($result['isocode']);
193
        } else {
194
            $language_purified_id = api_purify_language_id($language);
195
            $iso_code[$language] = isset($iso_code[$language_purified_id]) ? $iso_code[$language_purified_id] : null;
196
        }
197
        if (empty($iso_code[$language])) {
198
            $iso_code[$language] = $default_code;
199
        }
200
    }
201
202
    return $iso_code[$language];
203
}
204
205
/**
206
 * Gets language iso code column from the language table
207
 *
208
 * @return array    An array with the current isocodes
209
 *
210
 * */
211 View Code Duplication
function api_get_platform_isocodes()
212
{
213
    $iso_code = array();
214
    $sql = "SELECT isocode 
215
            FROM ".Database::get_main_table(TABLE_MAIN_LANGUAGE)." 
216
            ORDER BY isocode ";
217
    $sql_result = Database::query($sql);
218
    if (Database::num_rows($sql_result)) {
219
        while ($row = Database::fetch_array($sql_result)) {
220
            $iso_code[] = trim($row['isocode']);
221
        }
222
    }
223
    return $iso_code;
224
}
225
226
/**
227
 * Gets text direction according to the given language.
228
 * @param string $language	This is the name of the
229
 * folder containing translations for the corresponding language (e.g 'arabic', 'english', ...).
230
 * ISO-codes are acceptable too ('ar', 'en', ...).
231
 * If $language is omitted, interface language is assumed then.
232
 * @return string			The correspondent to the language text direction ('ltr' or 'rtl').
233
 */
234
function api_get_text_direction($language = null)
235
{
236
    static $text_direction = array();
237
238
    if (empty($language)) {
239
    	$language = api_get_interface_language();
240
    }
241
    if (!isset($text_direction[$language])) {
242
        $text_direction[$language] = in_array(
243
            api_purify_language_id($language),
244
            array(
245
                'arabic',
246
                'ar',
247
                'dari',
248
                'prs',
249
                'hebrew',
250
                'he',
251
                'iw',
252
                'pashto',
253
                'ps',
254
                'persian',
255
                'fa',
256
                'ur',
257
                'yiddish',
258
                'yid'
259
            )
260
        ) ? 'rtl' : 'ltr';
261
    }
262
263
    return $text_direction[$language];
264
}
265
266
/**
267
 * Returns an alphabetized list of timezones in an associative array that can be used to populate a select
268
 *
269
 * @return array List of timezone identifiers
270
 *
271
 * @author Guillaume Viguier <[email protected]>
272
 */
273
function api_get_timezones()
274
{
275
    $timezone_identifiers = DateTimeZone::listIdentifiers();
276
    sort($timezone_identifiers);
277
    $out = array();
278
    foreach ($timezone_identifiers as $tz) {
279
        $out[$tz] = $tz;
280
    }
281
    $null_option = array('' => '');
282
    $result = array_merge($null_option, $out);
283
284
    return $result;
285
}
286
287
/**
288
 * Returns the timezone to be converted to/from, based on user or admin preferences
289
 *
290
 * @return string The timezone chosen
291
 */
292
function api_get_timezone()
293
{
294
    // First, get the default timezone of the server
295
    $to_timezone = date_default_timezone_get();
296
    // Second, see if a timezone has been chosen for the platform
297
    $timezone_value = api_get_setting('timezone_value', 'timezones');
298
299
    if ($timezone_value != null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $timezone_value of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
300
        $to_timezone = $timezone_value;
301
    }
302
    // If allowed by the administrator
303
    $use_users_timezone = api_get_setting('use_users_timezone', 'timezones');
304
305
    if ($use_users_timezone === 'true') {
306
        $userId = api_get_user_id();
307
        // Get the timezone based on user preference, if it exists
308
        $timezone_user = UserManager::get_extra_user_data_by_field($userId, 'timezone');
309
        if (isset($timezone_user['timezone']) && $timezone_user['timezone'] != null) {
310
            $to_timezone = $timezone_user['timezone'];
311
        }
312
    }
313
314
    return $to_timezone;
315
}
316
317
/**
318
 * Returns the given date as a DATETIME in UTC timezone.
319
 * This function should be used before entering any date in the DB.
320
 *
321
 * @param mixed $time The date to be converted (can be a string supported by date() or a timestamp)
322
 * @param bool $return_null_if_invalid_date if the date is not correct return null instead of the current date
323
 * @param bool $returnObj
324
 *
325
 * @return string The DATETIME in UTC to be inserted in the DB,
326
 * or null if the format of the argument is not supported
327
 *
328
 * @author Julio Montoya - Adding the 2nd parameter
329
 * @author Guillaume Viguier <[email protected]>
330
 */
331
function api_get_utc_datetime($time = null, $return_null_if_invalid_date = false, $returnObj = false)
332
{
333
    $from_timezone = api_get_timezone();
334
    $to_timezone = 'UTC';
335
    if (is_null($time) || empty($time) || $time === '0000-00-00 00:00:00') {
336
        if ($return_null_if_invalid_date) {
337
            return null;
338
        }
339
        if ($returnObj) {
340
            return $date = new DateTime(gmdate('Y-m-d H:i:s'));
341
        }
342
343
        return gmdate('Y-m-d H:i:s');
344
    }
345
346
    // If time is a timestamp, return directly in utc
347
    if (is_numeric($time)) {
348
        $time = intval($time);
349
350
        return gmdate('Y-m-d H:i:s', $time);
351
    }
352
    try {
353
        $date = new DateTime($time, new DateTimezone($from_timezone));
354
        $date->setTimezone(new DateTimeZone($to_timezone));
355
        if ($returnObj) {
356
            return $date;
357
        } else {
358
            return $date->format('Y-m-d H:i:s');
359
        }
360
    } catch (Exception $e) {
361
        return null;
362
    }
363
}
364
365
/**
366
 * Returns a DATETIME string converted to the right timezone
367
 * @param mixed The time to be converted
368
 * @param string The timezone to be converted to.
369
 * If null, the timezone will be determined based on user preference,
370
 * or timezone chosen by the admin for the platform.
371
 * @param string The timezone to be converted from. If null, UTC will be assumed.
372
 * @return string The converted time formatted as Y-m-d H:i:s
373
 *
374
 * @author Guillaume Viguier <[email protected]>
375
 */
376
function api_get_local_time(
377
    $time = null,
378
    $to_timezone = null,
379
    $from_timezone = null,
380
    $return_null_if_invalid_date = false,
381
    $showTime = true,
382
    $humanForm = false
383
) {
384
    // Determining the timezone to be converted from
385
    if (is_null($from_timezone)) {
386
        $from_timezone = 'UTC';
387
    }
388
    // Determining the timezone to be converted to
389
    if (is_null($to_timezone)) {
390
        $to_timezone = api_get_timezone();
391
    }
392
393
    // If time is a timestamp, convert it to a string
394
    if (is_null($time) || empty($time) || $time == '0000-00-00 00:00:00') {
395
        if ($return_null_if_invalid_date) {
396
            return null;
397
        }
398
        $from_timezone = 'UTC';
399
        $time = gmdate('Y-m-d H:i:s');
400
    }
401
    if (is_numeric($time)) {
402
        $time = intval($time);
403
        $from_timezone = 'UTC';
404
        $time = gmdate('Y-m-d H:i:s', $time);
405
    }
406
    if ($time instanceof DateTime) {
407
        $time = $time->format('Y-m-d H:i:s');
408
        $from_timezone = 'UTC';
409
    }
410
411
    try {
412
        $date = new DateTime($time, new DateTimezone($from_timezone));
413
        $date->setTimezone(new DateTimeZone($to_timezone));
414
415
        return api_get_human_date_time($date, $showTime, $humanForm);
416
    } catch (Exception $e) {
417
        return '';
418
    }
419
}
420
421
/**
422
 * Converts a string into a timestamp safely (handling timezones), using strtotime
423
 *
424
 * @param string $time to be converted
425
 * @param string $timezone (if null, the timezone will be determined based
426
 * on user preference, or timezone chosen by the admin for the platform)
427
 * @return int Timestamp
428
 *
429
 * @author Guillaume Viguier <[email protected]>
430
 */
431
function api_strtotime($time, $timezone = null)
432
{
433
    $system_timezone = date_default_timezone_get();
434
    if (!empty($timezone)) {
435
        date_default_timezone_set($timezone);
436
    }
437
    $timestamp = strtotime($time);
438
    date_default_timezone_set($system_timezone);
439
440
    return $timestamp;
441
}
442
443
/**
444
 * Returns formatted date/time, correspondent to a given language.
445
 * The given date should be in the timezone chosen by the administrator
446
 * and/or user. Use api_get_local_time to get it.
447
 *
448
 * @author Patrick Cool <[email protected]>, Ghent University
449
 * @author Christophe Gesche<[email protected]>
450
 *         originally inspired from from PhpMyAdmin
451
 * @author Ivan Tcholakov, 2009, code refactoring, adding support for predefined date/time formats.
452
 * @author Guillaume Viguier <[email protected]>
453
 *
454
 * @param mixed Timestamp or datetime string
455
 * @param mixed Date format (string or int; see date formats in the Chamilo system: TIME_NO_SEC_FORMAT, DATE_FORMAT_SHORT, DATE_FORMAT_LONG, DATE_TIME_FORMAT_LONG)
456
 * @param string $language (optional)		Language identificator. If it is omited, the current interface language is assumed.
457
 * @return string							Returns the formatted date.
458
 *
459
 * @link http://php.net/manual/en/function.strftime.php
460
 */
461
function api_format_date($time, $format = null, $language = null)
462
{
463
    $system_timezone = date_default_timezone_get();
464
    date_default_timezone_set(api_get_timezone());
465
466
    if (is_string($time)) {
467
        $time = strtotime($time);
468
    }
469
470
    if (is_null($format)) {
471
        $format = DATE_TIME_FORMAT_LONG;
472
    }
473
474
    $datetype = null;
475
    $timetype = null;
476
477
    if (is_int($format)) {
478
        switch ($format) {
479 View Code Duplication
            case DATE_FORMAT_ONLY_DAYNAME:
480
                $date_format = get_lang('dateFormatOnlyDayName', '', $language);
481
                if (INTL_INSTALLED) {
482
                    $datetype = IntlDateFormatter::SHORT;
483
                    $timetype = IntlDateFormatter::NONE;
484
                }
485
                break;
486 View Code Duplication
            case DATE_FORMAT_NUMBER_NO_YEAR:
487
                $date_format = get_lang('dateFormatShortNumberNoYear', '', $language);
488
                if (INTL_INSTALLED) {
489
                    $datetype = IntlDateFormatter::SHORT;
490
                    $timetype = IntlDateFormatter::NONE;
491
                }
492
                break;
493 View Code Duplication
            case DATE_FORMAT_NUMBER:
494
                $date_format = get_lang('dateFormatShortNumber', '', $language);
495
                if (INTL_INSTALLED) {
496
                    $datetype = IntlDateFormatter::SHORT;
497
                    $timetype = IntlDateFormatter::NONE;
498
                }
499
                break;
500 View Code Duplication
            case TIME_NO_SEC_FORMAT:
501
                $date_format = get_lang('timeNoSecFormat', '', $language);
502
                if (INTL_INSTALLED) {
503
                    $datetype = IntlDateFormatter::NONE;
504
                    $timetype = IntlDateFormatter::SHORT;
505
                }
506
                break;
507 View Code Duplication
            case DATE_FORMAT_SHORT:
508
                $date_format = get_lang('dateFormatShort', '', $language);
509
                if (INTL_INSTALLED) {
510
                    $datetype = IntlDateFormatter::LONG;
511
                    $timetype = IntlDateFormatter::NONE;
512
                }
513
                break;
514 View Code Duplication
            case DATE_FORMAT_LONG:
515
                $date_format = get_lang('dateFormatLong', '', $language);
516
                if (INTL_INSTALLED) {
517
                    $datetype = IntlDateFormatter::FULL;
518
                    $timetype = IntlDateFormatter::NONE;
519
                }
520
                break;
521 View Code Duplication
            case DATE_TIME_FORMAT_LONG:
522
                $date_format = get_lang('dateTimeFormatLong', '', $language);
523
                if (INTL_INSTALLED) {
524
                    $datetype = IntlDateFormatter::FULL;
525
                    $timetype = IntlDateFormatter::SHORT;
526
                }
527
                break;
528 View Code Duplication
            case DATE_FORMAT_LONG_NO_DAY:
529
                $date_format = get_lang('dateFormatLongNoDay', '', $language);
530
                if (INTL_INSTALLED) {
531
                    $datetype = IntlDateFormatter::FULL;
532
                    $timetype = IntlDateFormatter::SHORT;
533
                }
534
                break;
535 View Code Duplication
            case DATE_TIME_FORMAT_SHORT:
536
                $date_format = get_lang('dateTimeFormatShort', '', $language);
537
                if (INTL_INSTALLED) {
538
                    $datetype = IntlDateFormatter::FULL;
539
                    $timetype = IntlDateFormatter::SHORT;
540
                }
541
                break;
542 View Code Duplication
            case DATE_TIME_FORMAT_SHORT_TIME_FIRST:
543
                $date_format = get_lang('dateTimeFormatShortTimeFirst', '', $language);
544
                if (INTL_INSTALLED) {
545
                    $datetype = IntlDateFormatter::FULL;
546
                    $timetype = IntlDateFormatter::SHORT;
547
                }
548
                break;
549 View Code Duplication
            case DATE_TIME_FORMAT_LONG_24H:
550
                $date_format = get_lang('dateTimeFormatLong24H', '', $language);
551
                if (INTL_INSTALLED) {
552
                    $datetype = IntlDateFormatter::FULL;
553
                    $timetype = IntlDateFormatter::SHORT;
554
                }
555
                break;
556
            default:
557
                $date_format = get_lang('dateTimeFormatLong', '', $language);
558
                if (INTL_INSTALLED) {
559
                    $datetype = IntlDateFormatter::FULL;
560
                    $timetype = IntlDateFormatter::SHORT;
561
                }
562
        }
563
    } else {
564
        $date_format = $format;
565
    }
566
567
    if (0) {
568
        //if using PHP 5.3 format dates like: $dateFormatShortNumber, can't be used
569
        //
570
        // Use ICU
571
        if (is_null($language)) {
572
            $language = api_get_language_isocode();
573
        }
574
        $date_formatter = new IntlDateFormatter($language, $datetype, $timetype, date_default_timezone_get());
575
        //$date_formatter->setPattern($date_format);
576
        $formatted_date = api_to_system_encoding($date_formatter->format($time), 'UTF-8');
0 ignored issues
show
Security Bug introduced by
It seems like $date_formatter->format($time) targeting Symfony\Component\Intl\D...DateFormatter::format() can also be of type false; however, api_to_system_encoding() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
577
578
    } else {
579
        // We replace %a %A %b %B masks of date format with translated strings
580
        $translated = &_api_get_day_month_names($language);
581
        $date_format = str_replace(
582
            array('%A', '%a', '%B', '%b'),
583
            array(
584
                $translated['days_long'][(int)strftime('%w', $time)],
585
                $translated['days_short'][(int)strftime('%w', $time)],
586
                $translated['months_long'][(int)strftime('%m', $time) - 1],
587
                $translated['months_short'][(int)strftime('%m', $time) - 1],
588
            ),
589
            $date_format
590
        );
591
        $formatted_date = api_to_system_encoding(strftime($date_format, $time), 'UTF-8');
592
    }
593
    date_default_timezone_set($system_timezone);
594
595
    return $formatted_date;
596
}
597
598
/**
599
 * Returns the difference between the current date (date(now)) with the parameter
600
 * $date in a string format like "2 days ago, 1 hour ago".
601
 * You can use it like this:
602
 * Display::dateToStringAgoAndLongDate($dateInUtc);
603
 *
604
 * @param string $date Result of a date function in this format -> date('Y-m-d H:i:s', time());
605
 * @param string $timeZone
606
 * @return string
607
 *
608
 * @author Julio Montoya
609
 */
610
function date_to_str_ago($date, $timeZone = 'UTC')
611
{
612
    if ($date === '0000-00-00 00:00:00') {
613
        return '';
614
    }
615
616
    $getOldTimezone = api_get_timezone();
617
    $timeAgo = new TimeAgo($timeZone, api_get_language_isocode());
618
    $value = $timeAgo->inWords($date);
619
620
    date_default_timezone_set($getOldTimezone);
621
622
    return $value;
623
}
624
625
/**
626
 * Converts a date to the right timezone and localizes it in the format given as an argument
627
 * @param mixed The time to be converted
628
 * @param mixed Format to be used (TIME_NO_SEC_FORMAT, DATE_FORMAT_SHORT, DATE_FORMAT_LONG, DATE_TIME_FORMAT_LONG)
629
 * @param string Timezone to be converted from. If null, UTC will be assumed.
630
 * @return string Converted and localized date
631
 *
632
 * @author Guillaume Viguier <[email protected]>
633
 */
634
function api_convert_and_format_date($time = null, $format = null, $from_timezone = null) {
635
    // First, convert the datetime to the right timezone
636
    $time = api_get_local_time($time, null, $from_timezone);
637
    // Second, localize the date
638
    return api_format_date($time, $format);
639
}
640
641
/**
642
 * Returns an array of translated week days in short names.
643
 * @param string $language (optional)	Language id. If it is omitted, the current interface language is assumed.
644
 * @return string						Returns an array of week days (short names).
645
 * Example: api_get_week_days_short('english') means array('Sun', 'Mon', ... 'Sat').
646
 * Note: For all languges returned days are in the English order.
647
 */
648
function api_get_week_days_short($language = null) {
649
    $days = &_api_get_day_month_names($language);
650
    return $days['days_short'];
651
}
652
653
/**
654
 * Returns an array of translated week days.
655
 * @param string $language (optional)	Language id. If it is omitted, the current interface language is assumed.
656
 * @return string						Returns an array of week days.
657
 * Example: api_get_week_days_long('english') means array('Sunday, 'Monday', ... 'Saturday').
658
 * Note: For all languges returned days are in the English order.
659
 */
660
function api_get_week_days_long($language = null) {
661
    $days = &_api_get_day_month_names($language);
662
    return $days['days_long'];
663
}
664
665
/**
666
 * Returns an array of translated months in short names.
667
 * @param string $language (optional)	Language id. If it is omitted, the current interface language is assumed.
668
 * @return string						Returns an array of months (short names).
669
 * Example: api_get_months_short('english') means array('Jan', 'Feb', ... 'Dec').
670
 */
671
function api_get_months_short($language = null) {
672
    $months = &_api_get_day_month_names($language);
673
    return $months['months_short'];
674
}
675
676
/**
677
 * Returns an array of translated months.
678
 * @param string $language (optional)	Language id. If it is omitted, the current interface language is assumed.
679
 * @return string						Returns an array of months.
680
 * Example: api_get_months_long('english') means array('January, 'February' ... 'December').
681
 */
682
function api_get_months_long($language = null) {
683
    $months = &_api_get_day_month_names($language);
684
    return $months['months_long'];
685
}
686
687
/**
688
 * Name order conventions
689
 */
690
691
/**
692
 * Builds a person (full) name depending on the convention for a given language.
693
 * @param string $first_name			The first name of the person.
694
 * @param string $last_name				The last name of the person.
695
 * @param string $title					The title of the person.
696
 * @param int|string $format (optional)	The person name format. It may be a pattern-string (for example '%t %l, %f' or '%T %F %L', ...) or some of the constants PERSON_NAME_COMMON_CONVENTION (default), PERSON_NAME_WESTERN_ORDER, PERSON_NAME_EASTERN_ORDER, PERSON_NAME_LIBRARY_ORDER.
697
 * @param string $language (optional)	The language id. If it is omitted, the current interface language is assumed. This parameter has meaning with the format PERSON_NAME_COMMON_CONVENTION only.
698
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
699
 * @return bool							The result is sort of full name of the person.
700
 * Sample results:
701
 * Peter Ustinoff or Dr. Peter Ustinoff     - the Western order
702
 * Ustinoff Peter or Dr. Ustinoff Peter     - the Eastern order
703
 * Ustinoff, Peter or - Dr. Ustinoff, Peter - the library order
704
 * Note: See the file chamilo/main/inc/lib/internationalization_database/name_order_conventions.php where you can revise the convention for your language.
705
 * @author Carlos Vargas <[email protected]> - initial implementation.
706
 * @author Ivan Tcholakov
707
 */
708
function api_get_person_name(
709
    $first_name,
710
    $last_name,
711
    $title = null,
712
    $format = null,
713
    $language = null,
714
    $encoding = null,
715
    $username = null
716
) {
717
    static $valid = array();
718
    if (empty($format)) {
719
        $format = PERSON_NAME_COMMON_CONVENTION;
720
    }
721
    //We check if the language is supported, otherwise we check the interface language of the parent language of sublanguage
722
723
    if (empty($language)) {
724
        // Do not set $setParentLanguageName because this function is called before
725
        // the main language is loaded in global.inc.php
726
        $language = api_get_interface_language(false, true, false);
727
    }
728
729
    if (!isset($valid[$format][$language])) {
730
        if (is_int($format)) {
731
            switch ($format) {
732
                case PERSON_NAME_COMMON_CONVENTION:
733
                    $valid[$format][$language] = _api_get_person_name_convention($language, 'format');
734
                    $usernameOrderFromDatabase = api_get_setting('user_name_order');
735
                    if (isset($usernameOrderFromDatabase) && !empty($usernameOrderFromDatabase)) {
736
                        $valid[$format][$language] = $usernameOrderFromDatabase;
737
                    }
738
                    break;
739
                case PERSON_NAME_WESTERN_ORDER:
740
                    $valid[$format][$language] = '%t %f %l';
741
                    break;
742
                case PERSON_NAME_EASTERN_ORDER:
743
                    $valid[$format][$language] = '%t %l %f';
744
                    break;
745
                case PERSON_NAME_LIBRARY_ORDER:
746
                    $valid[$format][$language] = '%t %l, %f';
747
                    break;
748
                default:
749
                    $valid[$format][$language] = '%t %f %l';
750
                    break;
751
            }
752
        } else {
753
            $valid[$format][$language] = _api_validate_person_name_format($format);
754
        }
755
    }
756
757
    $format = $valid[$format][$language];
758
759
    $keywords = array(
760
        '%firstname',
761
        '%f',
762
        '%F',
763
        '%lastname',
764
        '%l',
765
        '%L',
766
        '%title',
767
        '%t',
768
        '%T',
769
        '%username',
770
        '%u',
771
        '%U',
772
    );
773
774
    $values = array(
775
        $first_name,
776
        $first_name,
777
        api_strtoupper($first_name),
778
        $last_name,
779
        $last_name,
780
        api_strtoupper($last_name),
781
        $title,
782
        $title,
783
        api_strtoupper($title),
784
        $username,
785
        $username,
786
        api_strtoupper($username),
787
    );
788
    $person_name = str_replace($keywords, $values, $format);
789
790
    return _api_clean_person_name($person_name);
791
}
792
793
/**
794
 * Checks whether a given format represents person name in Western order (for which first name is first).
795
 * @param int|string $format (optional)	The person name format. It may be a pattern-string (for example '%t. %l, %f') or some of the constants PERSON_NAME_COMMON_CONVENTION (default), PERSON_NAME_WESTERN_ORDER, PERSON_NAME_EASTERN_ORDER, PERSON_NAME_LIBRARY_ORDER.
796
 * @param string $language (optional)	The language id. If it is omitted, the current interface language is assumed. This parameter has meaning with the format PERSON_NAME_COMMON_CONVENTION only.
797
 * @return bool							The result TRUE means that the order is first_name last_name, FALSE means last_name first_name.
798
 * Note: You may use this function for determing the order of the fields or columns "First name" and "Last name" in forms, tables and reports.
799
 * @author Ivan Tcholakov
800
 */
801
function api_is_western_name_order($format = null, $language = null)
802
{
803
    static $order = array();
804
    if (empty($format)) {
805
        $format = PERSON_NAME_COMMON_CONVENTION;
806
    }
807
808
    if (empty($language)) {
809
        $language = api_get_interface_language(false, true);
810
    }
811
    if (!isset($order[$format][$language])) {
812
        $test_name = api_get_person_name('%f', '%l', '%t', $format, $language);
813
        $order[$format][$language] = stripos($test_name, '%f') <= stripos($test_name, '%l');
814
    }
815
    return $order[$format][$language];
816
}
817
818
/**
819
 * Returns a directive for sorting person names depending on a given language and based on the options in the internationalization "database".
820
 * @param string $language (optional)	The input language. If it is omitted, the current interface language is assumed.
821
 * @return bool							Returns boolean value. TRUE means ORDER BY first_name, last_name; FALSE means ORDER BY last_name, first_name.
822
 * Note: You may use this function:
823
 * 2. for constructing the ORDER clause of SQL queries, related to first_name and last_name;
824
 * 3. for adjusting php-implemented sorting in tables and reports.
825
 * @author Ivan Tcholakov
826
 */
827
function api_sort_by_first_name($language = null) {
828
    $userNameSortBy = api_get_setting('user_name_sort_by');
829
    if (!empty($userNameSortBy) && in_array($userNameSortBy, array('firstname', 'lastname'))) {
830
        return $userNameSortBy == 'firstname' ? true : false;
831
    }
832
833
    static $sort_by_first_name = array();
834
835
    if (empty($language)) {
836
        $language = api_get_interface_language(false, true);
837
    }
838
    if (!isset($sort_by_first_name[$language])) {
839
        $sort_by_first_name[$language] = _api_get_person_name_convention($language, 'sort_by');
840
    }
841
842
    return $sort_by_first_name[$language];
843
}
844
845
/**
846
 * Multibyte string conversion functions
847
 */
848
849
/**
850
 * Converts character encoding of a given string.
851
 * @param string $string					The string being converted.
852
 * @param string $to_encoding				The encoding that $string is being converted to.
853
 * @param string $from_encoding (optional)	The encoding that $string is being converted from.
854
 * If it is omitted, the platform character set is assumed.
855
 * @return string							Returns the converted string.
856
 * This function is aimed at replacing the function mb_convert_encoding() for human-language strings.
857
 * @link http://php.net/manual/en/function.mb-convert-encoding
858
 */
859
function api_convert_encoding($string, $to_encoding, $from_encoding = 'UTF-8')
860
{
861
    return mb_convert_encoding($string, $to_encoding, $from_encoding);
862
}
863
864
/**
865
 * Converts a given string into UTF-8 encoded string.
866
 * @param string $string					The string being converted.
867
 * @param string $from_encoding (optional)	The encoding that $string is being converted from.
868
 * If it is omitted, the platform character set is assumed.
869
 * @return string							Returns the converted string.
870
 * This function is aimed at replacing the function utf8_encode() for human-language strings.
871
 * @link http://php.net/manual/en/function.utf8-encode
872
 */
873
function api_utf8_encode($string, $from_encoding = 'UTF-8')
874
{
875
    return mb_convert_encoding($string, 'UTF-8', $from_encoding);
876
}
877
878
/**
879
 * Converts a given string from UTF-8 encoding to a specified encoding.
880
 * @param string $string					The string being converted.
881
 * @param string $to_encoding (optional)	The encoding that $string is being converted to.
882
 * If it is omitted, the platform character set is assumed.
883
 * @return string							Returns the converted string.
884
 * This function is aimed at replacing the function utf8_decode() for human-language strings.
885
 * @link http://php.net/manual/en/function.utf8-decode
886
 */
887
function api_utf8_decode($string, $to_encoding = null)
888
{
889
    return mb_convert_encoding($string, $to_encoding, 'UTF-8');
890
}
891
892
/**
893
 * Converts a given string into the system ecoding (or platform character set).
894
 * When $from encoding is omited on UTF-8 platforms then language dependent encoding
895
 * is guessed/assumed. On non-UTF-8 platforms omited $from encoding is assumed as UTF-8.
896
 * When the parameter $check_utf8_validity is true the function checks string's
897
 * UTF-8 validity and decides whether to try to convert it or not.
898
 * This function is useful for problem detection or making workarounds.
899
 * @param string $string						The string being converted.
900
 * @param string $from_encoding (optional)		The encoding that $string is being converted from.
901
 * It is guessed when it is omitted.
902
 * @param bool $check_utf8_validity (optional)	A flag for UTF-8 validity check as condition for making conversion.
903
 * @return string								Returns the converted string.
904
 */
905
function api_to_system_encoding($string, $from_encoding = null, $check_utf8_validity = false)
906
{
907
    $system_encoding = api_get_system_encoding();
908
    return api_convert_encoding($string, $system_encoding, $from_encoding);
909
}
910
911
/**
912
 * Converts all applicable characters to HTML entities.
913
 * @param string $string				The input string.
914
 * @param int $quote_style (optional)	The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES.
915
 * @param string $encoding (optional)	The encoding (of the input string) used in conversion.
916
 * If it is omitted, the platform character set is assumed.
917
 * @return string						Returns the converted string.
918
 * This function is aimed at replacing the function htmlentities() for human-language strings.
919
 * @link http://php.net/manual/en/function.htmlentities
920
 */
921
function api_htmlentities($string, $quote_style = ENT_COMPAT, $encoding = 'UTF-8')
922
{
923
    switch ($quote_style) {
924 View Code Duplication
        case ENT_COMPAT:
925
            $string = str_replace(array('&', '"', '<', '>'), array('&amp;', '&quot;', '&lt;', '&gt;'), $string);
926
            break;
927 View Code Duplication
        case ENT_QUOTES:
928
            $string = str_replace(array('&', '\'', '"', '<', '>'), array('&amp;', '&#039;', '&quot;', '&lt;', '&gt;'), $string);
929
            break;
930
    }
931
932
    return mb_convert_encoding($string, 'HTML-ENTITIES', 'UTF-8');
933
}
934
935
/**
936
 * Converts HTML entities into normal characters.
937
 * @param string $string				The input string.
938
 * @param int $quote_style (optional)	The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES.
939
 * @param string $encoding (optional)	The encoding (of the result) used in conversion.
940
 * If it is omitted, the platform character set is assumed.
941
 * @return string						Returns the converted string.
942
 * This function is aimed at replacing the function html_entity_decode() for human-language strings.
943
 * @link http://php.net/html_entity_decode
944
 */
945
function api_html_entity_decode($string, $quote_style = ENT_COMPAT, $encoding = 'UTF-8') {
946
    if (empty($encoding)) {
947
        $encoding = _api_mb_internal_encoding();
948
    }
949
    if (api_is_encoding_supported($encoding)) {
950
        if (!api_is_utf8($encoding)) {
951
            $string = api_utf8_encode($string, $encoding);
952
        }
953
        $string = html_entity_decode($string, $quote_style, 'UTF-8');
954
        if (!api_is_utf8($encoding)) {
955
            return api_utf8_decode($string, $encoding);
956
        }
957
        return $string;
958
    }
959
    return $string; // Here the function gives up.
960
}
961
962
/**
963
 * This function encodes (conditionally) a given string to UTF-8 if XmlHttp-request has been detected.
964
 * @param string $string					The string being converted.
965
 * @param string $from_encoding (optional)	The encoding that $string is being converted from.
966
 * If it is omitted, the platform character set is assumed.
967
 * @return string							Returns the converted string.
968
 */
969
function api_xml_http_response_encode($string, $from_encoding = 'UTF8')
970
{
971
    if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
972
        if (empty($from_encoding)) {
973
            $from_encoding = _api_mb_internal_encoding();
974
        }
975
        if (!api_is_utf8($from_encoding)) {
976
            return api_utf8_encode($string, $from_encoding);
977
        }
978
    }
979
    return $string;
980
}
981
982
/**
983
 * Transliterates a string with arbitrary encoding into a plain ASCII string.
984
 *
985
 * Example:
986
 * echo api_transliterate(api_html_entity_decode(
987
 * 	'&#1060;&#1105;&#1076;&#1086;&#1088; '.
988
 * 	'&#1052;&#1080;&#1093;&#1072;&#1081;&#1083;&#1086;&#1074;&#1080;&#1095; '.
989
 * 	'&#1044;&#1086;&#1089;&#1090;&#1086;&#1077;&#1074;&#1082;&#1080;&#1081;',
990
 * 	ENT_QUOTES, 'UTF-8'), 'X', 'UTF-8');
991
 * The output should be: Fyodor Mihaylovich Dostoevkiy
992
 *
993
 * @param string $string					The input string.
994
 * @param string $unknown (optional)		Replacement character for unknown characters and illegal UTF-8 sequences.
995
 * @param string $from_encoding (optional)	The encoding of the input string.
996
 * If it is omitted, the platform character set is assumed.
997
 * @return string							Plain ASCII output.
998
 *
999
 */
1000
function api_transliterate($string, $unknown = '?', $from_encoding = null)
1001
{
1002
    return URLify::transliterate($string);
1003
}
1004
1005
/**
1006
 * Takes the first character in a string and returns its Unicode codepoint.
1007
 * @param string $character				The input string.
1008
 * @param string $encoding (optional)	The encoding of the input string. If it is omitted, the platform character set will be used by default.
1009
 * @return int							Returns: the codepoint of the first character; or 0xFFFD (unknown character) when the input string is empty.
1010
 * This is a multibyte aware version of the function ord().
1011
 * @link http://php.net/manual/en/function.ord.php
1012
 * Note the difference with the original funtion ord(): ord('') returns 0, api_ord('') returns 0xFFFD (unknown character).
1013
 */
1014
function api_ord($character, $encoding = null) {
1015
    return Utf8::ord(api_utf8_encode($character, $encoding));
1016
}
1017
1018
/**
1019
 * This function returns a string or an array with all occurrences of search in subject (ignoring case) replaced with the given replace value.
1020
 * @param mixed $search					String or array of strings to be found.
1021
 * @param mixed $replace				String or array of strings used for replacement.
1022
 * @param mixed $subject				String or array of strings being searched.
1023
 * @param int $count (optional)			The number of matched and replaced needles will be returned in count, which is passed by reference.
1024
 * @param string $encoding (optional)	The used internally by this function character encoding.
1025
 * If it is omitted, the platform character set will be used by default.
1026
 * @return mixed						String or array as a result.
1027
 * Notes:
1028
 * If $subject is an array, then the search and replace is performed with every entry of subject, the return value is an array.
1029
 * If $search and $replace are arrays, then the function takes a value from each array and uses it to do search and replace on subject.
1030
 * If $replace has fewer values than search, then an empty string is used for the rest of replacement values.
1031
 * If $search is an array and $replace is a string, then this replacement string is used for every value of search.
1032
 * This function is aimed at replacing the function str_ireplace() for human-language strings.
1033
 * @link http://php.net/manual/en/function.str-ireplace
1034
 * @author Henri Sivonen, mailto:[email protected]
1035
 * @link http://hsivonen.iki.fi/php-utf8/
1036
 * Adaptation for Chamilo 1.8.7, 2010
1037
 * Initial implementation Dokeos LMS, August 2009
1038
 * @author Ivan Tcholakov
1039
 */
1040
function api_str_ireplace($search, $replace, $subject, & $count = null, $encoding = null)
1041
{
1042
    return Utf8::str_ireplace($search, $replace, $subject, $count);
1043
}
1044
1045
/**
1046
 * Converts a string to an array.
1047
 * @param string $string				The input string.
1048
 * @param int $split_length				Maximum character-length of the chunk, one character by default.
1049
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1050
 * @return array						The result array of chunks with the spcified length.
1051
 * Notes:
1052
 * If the optional split_length parameter is specified, the returned array will be broken down into chunks
1053
 * with each being split_length in length, otherwise each chunk will be one character in length.
1054
 * FALSE is returned if split_length is less than 1.
1055
 * If the split_length length exceeds the length of string, the entire string is returned as the first (and only) array element.
1056
 * This function is aimed at replacing the function str_split() for human-language strings.
1057
 * @link http://php.net/str_split
1058
 */
1059
function api_str_split($string, $split_length = 1, $encoding = null)
1060
{
1061
    return Utf8::str_split($string, $split_length);
1062
}
1063
1064
/**
1065
 * Finds position of first occurrence of a string within another, case insensitive.
1066
 * @param string $haystack				The string from which to get the position of the first occurrence.
1067
 * @param string $needle				The string to be found.
1068
 * @param int $offset					The position in $haystack to start searching from. If it is omitted, searching starts from the beginning.
1069
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1070
 * @return mixed						Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
1071
 * Note: The first character's position is 0, the second character position is 1, and so on.
1072
 * This function is aimed at replacing the functions stripos() and mb_stripos() for human-language strings.
1073
 * @link http://php.net/manual/en/function.stripos
1074
 * @link http://php.net/manual/en/function.mb-stripos
1075
 */
1076
function api_stripos($haystack, $needle, $offset = 0, $encoding = null)
1077
{
1078
    return Utf8::stripos($haystack, $needle, $offset);
1079
}
1080
1081
/**
1082
 * Finds first occurrence of a string within another, case insensitive.
1083
 * @param string $haystack					The string from which to get the first occurrence.
1084
 * @param mixed $needle						The string to be found.
1085
 * @param bool $before_needle (optional)	Determines which portion of $haystack this function returns. The default value is FALSE.
1086
 * @param string $encoding (optional)		The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1087
 * @return mixed							Returns the portion of $haystack, or FALSE if $needle is not found.
1088
 * Notes:
1089
 * If $needle is not a string, it is converted to an integer and applied as the ordinal value (codepoint if the encoding is UTF-8) of a character.
1090
 * If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence of $needle.
1091
 * If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence of $needle to the end.
1092
 * This function is aimed at replacing the functions stristr() and mb_stristr() for human-language strings.
1093
 * @link http://php.net/manual/en/function.stristr
1094
 * @link http://php.net/manual/en/function.mb-stristr
1095
 */
1096
function api_stristr($haystack, $needle, $before_needle = false, $encoding = null)
1097
{
1098
    return Utf8::stristr($haystack, $needle, $before_needle);
1099
}
1100
1101
/**
1102
 * Returns length of the input string.
1103
 * @param string $string				The string which length is to be calculated.
1104
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1105
 * @return int							Returns the number of characters within the string. A multi-byte character is counted as 1.
1106
 * This function is aimed at replacing the functions strlen() and mb_strlen() for human-language strings.
1107
 * @link http://php.net/manual/en/function.strlen
1108
 * @link http://php.net/manual/en/function.mb-strlen
1109
 * Note: When you use strlen() to test for an empty string, you needn't change it to api_strlen().
1110
 * For example, in lines like the following:
1111
 * if (strlen($string) > 0)
1112
 * if (strlen($string) != 0)
1113
 * there is no need the original function strlen() to be changed, it works correctly and faster for these cases.
1114
 */
1115
function api_strlen($string, $encoding = null)
1116
{
1117
    return Utf8::strlen($string);
1118
}
1119
1120
/**
1121
 * Finds position of first occurrence of a string within another.
1122
 * @param string $haystack				The string from which to get the position of the first occurrence.
1123
 * @param string $needle				The string to be found.
1124
 * @param int $offset (optional)		The position in $haystack to start searching from. If it is omitted, searching starts from the beginning.
1125
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1126
 * @return mixed						Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
1127
 * Note: The first character's position is 0, the second character position is 1, and so on.
1128
 * This function is aimed at replacing the functions strpos() and mb_strpos() for human-language strings.
1129
 * @link http://php.net/manual/en/function.strpos
1130
 * @link http://php.net/manual/en/function.mb-strpos
1131
 */
1132
function api_strpos($haystack, $needle, $offset = 0, $encoding = null)
1133
{
1134
    return Utf8::strpos($haystack, $needle, $offset);
1135
}
1136
1137
/**
1138
 * Finds the last occurrence of a character in a string.
1139
 * @param string $haystack					The string from which to get the last occurrence.
1140
 * @param mixed $needle						The string which first character is to be found.
1141
 * @param bool $before_needle (optional)	Determines which portion of $haystack this function returns. The default value is FALSE.
1142
 * @param string $encoding (optional)		The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1143
 * @return mixed							Returns the portion of $haystack, or FALSE if the first character from $needle is not found.
1144
 * Notes:
1145
 * If $needle is not a string, it is converted to an integer and applied as the ordinal value (codepoint if the encoding is UTF-8) of a character.
1146
 * If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence.
1147
 * If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence to the end.
1148
 * This function is aimed at replacing the functions strrchr() and mb_strrchr() for human-language strings.
1149
 * @link http://php.net/manual/en/function.strrchr
1150
 * @link http://php.net/manual/en/function.mb-strrchr
1151
 */
1152
function api_strrchr($haystack, $needle, $before_needle = false, $encoding = null)
1153
{
1154
    return Utf8::strrchr($haystack, $needle);
1155
}
1156
1157
/**
1158
 * Reverses a string.
1159
 * @param string $string				The string to be reversed.
1160
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1161
 * @return string						Returns the reversed string.
1162
 * This function is aimed at replacing the function strrev() for human-language strings.
1163
 * @link http://php.net/manual/en/function.strrev
1164
 */
1165
function api_strrev($string, $encoding = null)
1166
{
1167
    return Utf8::strrev($string);
1168
}
1169
1170
/**
1171
 * Finds the position of last occurrence (case insensitive) of a string in a string.
1172
 * @param string $haystack				The string from which to get the position of the last occurrence.
1173
 * @param string $needle				The string to be found.
1174
 * @param int $offset (optional)		$offset may be specified to begin searching an arbitrary position. Negative values will stop searching at an arbitrary point prior to the end of the string.
1175
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1176
 * @return mixed						Returns the numeric position of the first occurrence (case insensitive) of $needle in the $haystack, or FALSE if $needle is not found.
1177
 * Note: The first character's position is 0, the second character position is 1, and so on.
1178
 * This function is aimed at replacing the functions strripos() and mb_strripos() for human-language strings.
1179
 * @link http://php.net/manual/en/function.strripos
1180
 * @link http://php.net/manual/en/function.mb-strripos
1181
 */
1182
function api_strripos($haystack, $needle, $offset = 0, $encoding = null)
1183
{
1184
    return Utf8::strripos($haystack, $needle, $offset);
1185
}
1186
1187
/**
1188
 * Finds the position of last occurrence of a string in a string.
1189
 * @param string $haystack				The string from which to get the position of the last occurrence.
1190
 * @param string $needle				The string to be found.
1191
 * @param int $offset (optional)		$offset may be specified to begin searching an arbitrary position. Negative values will stop searching at an arbitrary point prior to the end of the string.
1192
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1193
 * @return mixed						Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
1194
 * Note: The first character's position is 0, the second character position is 1, and so on.
1195
 * This function is aimed at replacing the functions strrpos() and mb_strrpos() for human-language strings.
1196
 * @link http://php.net/manual/en/function.strrpos
1197
 * @link http://php.net/manual/en/function.mb-strrpos
1198
 */
1199
function api_strrpos($haystack, $needle, $offset = 0, $encoding = null)
1200
{
1201
    return Utf8::strrpos($haystack, $needle, $offset);
1202
}
1203
1204
/**
1205
 * Finds first occurrence of a string within another.
1206
 * @param string $haystack					The string from which to get the first occurrence.
1207
 * @param mixed $needle						The string to be found.
1208
 * @param bool $before_needle (optional)	Determines which portion of $haystack this function returns. The default value is FALSE.
1209
 * @param string $encoding (optional)		The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1210
 * @return mixed							Returns the portion of $haystack, or FALSE if $needle is not found.
1211
 * Notes:
1212
 * If $needle is not a string, it is converted to an integer and applied as the ordinal value (codepoint if the encoding is UTF-8) of a character.
1213
 * If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence of $needle.
1214
 * If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence of $needle to the end.
1215
 * This function is aimed at replacing the functions strstr() and mb_strstr() for human-language strings.
1216
 * @link http://php.net/manual/en/function.strstr
1217
 * @link http://php.net/manual/en/function.mb-strstr
1218
 */
1219
function api_strstr($haystack, $needle, $before_needle = false, $encoding = null)
1220
{
1221
    return Utf8::strstr($haystack, $needle, $before_needle);
1222
}
1223
1224
/**
1225
 * Makes a string lowercase.
1226
 * @param string $string				The string being lowercased.
1227
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1228
 * @return string						Returns the string with all alphabetic characters converted to lowercase.
1229
 * This function is aimed at replacing the functions strtolower() and mb_strtolower() for human-language strings.
1230
 * @link http://php.net/manual/en/function.strtolower
1231
 * @link http://php.net/manual/en/function.mb-strtolower
1232
 */
1233
function api_strtolower($string, $encoding = null)
1234
{
1235
    return Utf8::strtolower($string);
1236
}
1237
1238
/**
1239
 * Makes a string uppercase.
1240
 * @param string $string				The string being uppercased.
1241
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1242
 * @return string						Returns the string with all alphabetic characters converted to uppercase.
1243
 * This function is aimed at replacing the functions strtoupper() and mb_strtoupper() for human-language strings.
1244
 * @link http://php.net/manual/en/function.strtoupper
1245
 * @link http://php.net/manual/en/function.mb-strtoupper
1246
 */
1247
function api_strtoupper($string, $encoding = null)
1248
{
1249
    return Utf8::strtoupper($string);
1250
}
1251
1252
/**
1253
// Gets part of a string.
1254
 * @param string $string				The input string.
1255
 * @param int $start					The first position from which the extracted part begins.
1256
 * @param int $length					The length in character of the extracted part.
1257
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1258
 * @return string						Returns the part of the string specified by the start and length parameters.
1259
 * Note: First character's position is 0. Second character position is 1, and so on.
1260
 * This function is aimed at replacing the functions substr() and mb_substr() for human-language strings.
1261
 * @link http://php.net/manual/en/function.substr
1262
 * @link http://php.net/manual/en/function.mb-substr
1263
 */
1264 View Code Duplication
function api_substr($string, $start, $length = null, $encoding = null)
1265
{
1266
    if (is_null($length)) {
1267
        $length = api_strlen($string, $encoding);
1268
    }
1269
    return Utf8::substr($string, $start, $length);
1270
}
1271
1272
/**
1273
 * Counts the number of substring occurrences.
1274
 * @param string $haystack				The string being checked.
1275
 * @param string $needle				The string being found.
1276
 * @param string $encoding (optional)	The used internally by this function character encoding.
1277
 * If it is omitted, the platform character set will be used by default.
1278
 * @return int							The number of times the needle substring occurs in the haystack string.
1279
 * @link http://php.net/manual/en/function.mb-substr-count.php
1280
 */
1281
function api_substr_count($haystack, $needle, $encoding = null)
1282
{
1283
    return Utf8::substr_count($haystack, $needle);
1284
}
1285
1286
/**
1287
 * Replaces text within a portion of a string.
1288
 * @param string $string				The input string.
1289
 * @param string $replacement			The replacement string.
1290
 * @param int $start					The position from which replacing will begin.
1291
 * Notes:
1292
 * If $start is positive, the replacing will begin at the $start'th offset into the string.
1293
 * If $start is negative, the replacing will begin at the $start'th character from the end of the string.
1294
 * @param int $length (optional)		The position where replacing will end.
1295
 * Notes:
1296
 * If given and is positive, it represents the length of the portion of the string which is to be replaced.
1297
 * If it is negative, it represents the number of characters from the end of string at which to stop replacing.
1298
 * If it is not given, then it will default to api_strlen($string); i.e. end the replacing at the end of string.
1299
 * If $length is zero, then this function will have the effect of inserting replacement into the string at the given start offset.
1300
 * @param string $encoding (optional)	The used internally by this function character encoding.
1301
 * If it is omitted, the platform character set will be used by default.
1302
 * @return string						The result string is returned.
1303
 * This function is aimed at replacing the function substr_replace() for human-language strings.
1304
 * @link http://php.net/manual/function.substr-replace
1305
 */
1306 View Code Duplication
function api_substr_replace($string, $replacement, $start, $length = null, $encoding = null)
1307
{
1308
    if (is_null($length)) {
1309
        $length = api_strlen($string);
1310
    }
1311
1312
    return UTf8::substr_replace($string, $replacement, $start, $length);
1313
}
1314
1315
/**
1316
 * Makes a string's first character uppercase.
1317
 * @param string $string				The input string.
1318
 * @param string $encoding (optional)	The used internally by this function character encoding.
1319
 * If it is omitted, the platform character set will be used by default.
1320
 * @return string						Returns a string with the first character capitalized, if that character is alphabetic.
1321
 * This function is aimed at replacing the function ucfirst() for human-language strings.
1322
 * @link http://php.net/manual/en/function.ucfirst
1323
 */
1324
function api_ucfirst($string, $encoding = null)
1325
{
1326
    return Utf8::ucfirst($string);
1327
}
1328
1329
/**
1330
 * Uppercases the first character of each word in a string.
1331
 * @param string $string				The input string.
1332
 * @param string $encoding (optional)	The used internally by this function character encoding.
1333
 * If it is omitted, the platform character set will be used by default.
1334
 * @return string						Returns the modified string.
1335
 * This function is aimed at replacing the function ucwords() for human-language strings.
1336
 * @link http://php.net/manual/en/function.ucwords
1337
 */
1338
function api_ucwords($string, $encoding = null)
1339
{
1340
    return Utf8::ucwords($string);
1341
}
1342
1343
/**
1344
 * Performs a regular expression match, UTF-8 aware when it is applicable.
1345
 * @param string $pattern				The pattern to search for, as a string.
1346
 * @param string $subject				The input string.
1347
 * @param array &$matches (optional)	If matches is provided, then it is filled with the results of search (as an array).
1348
 * 										$matches[0] will contain the text that matched the full pattern, $matches[1] will have the text that matched the first captured parenthesized subpattern, and so on.
1349
 * @param int $flags (optional)			Could be PREG_OFFSET_CAPTURE. If this flag is passed, for every occurring match the appendant string offset will also be returned.
1350
 * 										Note that this changes the return value in an array where every element is an array consisting of the matched string at index 0 and its string offset into subject at index 1.
1351
 * @param int $offset (optional)		Normally, the search starts from the beginning of the subject string. The optional parameter offset can be used to specify the alternate place from which to start the search.
1352
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1353
 * @return int|boolean					Returns the number of times pattern matches or FALSE if an error occurred.
1354
 * @link http://php.net/preg_match
1355
 */
1356
function api_preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0, $encoding = null) {
1357
    if (empty($encoding)) {
1358
        $encoding = _api_mb_internal_encoding();
1359
    }
1360
    return preg_match(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $matches, $flags, $offset);
1361
}
1362
1363
/**
1364
 * Performs a global regular expression match, UTF-8 aware when it is applicable.
1365
 * @param string $pattern				The pattern to search for, as a string.
1366
 * @param string $subject				The input string.
1367
 * @param array &$matches (optional)	Array of all matches in multi-dimensional array ordered according to $flags.
1368
 * @param int $flags (optional)			Can be a combination of the following flags (note that it doesn't make sense to use PREG_PATTERN_ORDER together with PREG_SET_ORDER):
1369
 * PREG_PATTERN_ORDER - orders results so that $matches[0] is an array of full pattern matches, $matches[1] is an array of strings matched by the first parenthesized subpattern, and so on;
1370
 * PREG_SET_ORDER - orders results so that $matches[0] is an array of first set of matches, $matches[1] is an array of second set of matches, and so on;
1371
 * PREG_OFFSET_CAPTURE - If this flag is passed, for every occurring match the appendant string offset will also be returned. Note that this changes the value of matches
1372
 * in an array where every element is an array consisting of the matched string at offset 0 and its string offset into subject at offset 1.
1373
 * If no order flag is given, PREG_PATTERN_ORDER is assumed.
1374
 * @param int $offset (optional)		Normally, the search starts from the beginning of the subject string. The optional parameter offset can be used to specify the alternate place from which to start the search.
1375
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1376
 * @return int|boolean					Returns the number of full pattern matches (which might be zero), or FALSE if an error occurred.
1377
 * @link http://php.net/preg_match_all
1378
 */
1379
function api_preg_match_all($pattern, $subject, &$matches, $flags = PREG_PATTERN_ORDER, $offset = 0, $encoding = null) {
1380
    if (empty($encoding)) {
1381
        $encoding = _api_mb_internal_encoding();
1382
    }
1383
    if (is_null($flags)) {
1384
        $flags = PREG_PATTERN_ORDER;
1385
    }
1386
    return preg_match_all(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $matches, $flags, $offset);
1387
}
1388
1389
/**
1390
 * Performs a regular expression search and replace, UTF-8 aware when it is applicable.
1391
 * @param string|array $pattern			The pattern to search for. It can be either a string or an array with strings.
1392
 * @param string|array $replacement		The string or an array with strings to replace.
1393
 * @param string|array $subject			The string or an array with strings to search and replace.
1394
 * @param int $limit					The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit).
1395
 * @param int &$count					If specified, this variable will be filled with the number of replacements done.
1396
 * @param string $encoding (optional)	The used internally by this function character encoding.
1397
 * If it is omitted, the platform character set will be used by default.
1398
 * @return array|string|null			returns an array if the subject parameter is an array, or a string otherwise.
1399
 * If matches are found, the new subject will be returned, otherwise subject will be returned unchanged or NULL if an error occurred.
1400
 * @link http://php.net/preg_replace
1401
 */
1402
function api_preg_replace($pattern, $replacement, $subject, $limit = -1, &$count = 0, $encoding = null) {
1403
    if (empty($encoding)) {
1404
        $encoding = _api_mb_internal_encoding();
1405
    }
1406
    $is_utf8 = api_is_utf8($encoding);
1407
    if (is_array($pattern)) {
1408
        foreach ($pattern as &$p) {
1409
            $p = $is_utf8 ? $p.'u' : $p;
1410
        }
1411
    } else {
1412
        $pattern = $is_utf8 ? $pattern.'u' : $pattern;
1413
    }
1414
    return preg_replace($pattern, $replacement, $subject, $limit, $count);
1415
}
1416
1417
/**
1418
 * Splits a string by a regular expression, UTF-8 aware when it is applicable.
1419
 * @param string $pattern				The pattern to search for, as a string.
1420
 * @param string $subject				The input string.
1421
 * @param int $limit (optional)			If specified, then only substrings up to $limit are returned with the rest of the string being placed in the last substring. A limit of -1, 0 or null means "no limit" and, as is standard across PHP.
1422
 * @param int $flags (optional)			$flags can be any combination of the following flags (combined with bitwise | operator):
1423
 * PREG_SPLIT_NO_EMPTY - if this flag is set, only non-empty pieces will be returned;
1424
 * PREG_SPLIT_DELIM_CAPTURE - if this flag is set, parenthesized expression in the delimiter pattern will be captured and returned as well;
1425
 * PREG_SPLIT_OFFSET_CAPTURE - If this flag is set, for every occurring match the appendant string offset will also be returned.
1426
 * Note that this changes the return value in an array where every element is an array consisting of the matched string at offset 0 and its string offset into subject at offset 1.
1427
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1428
 * @return array						Returns an array containing substrings of $subject split along boundaries matched by $pattern.
1429
 * @link http://php.net/preg_split
1430
 */
1431
function api_preg_split($pattern, $subject, $limit = -1, $flags = 0, $encoding = null) {
1432
    if (empty($encoding)) {
1433
        $encoding = _api_mb_internal_encoding();
1434
    }
1435
    return preg_split(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $limit, $flags);
1436
}
1437
1438
/**
1439
 * String comparison
1440
 */
1441
1442
/**
1443
 * Performs string comparison, case insensitive, language sensitive, with extended multibyte support.
1444
 * @param string $string1				The first string.
1445
 * @param string $string2				The second string.
1446
 * @param string $language (optional)	The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1447
 * @param string $encoding (optional)	The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
1448
 * @return int							Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
1449
 * This function is aimed at replacing the function strcasecmp() for human-language strings.
1450
 * @link http://php.net/manual/en/function.strcasecmp
1451
 */
1452
function api_strcasecmp($string1, $string2, $language = null, $encoding = null) {
1453
    return api_strcmp(api_strtolower($string1, $encoding), api_strtolower($string2, $encoding), $language, $encoding);
1454
}
1455
1456
/**
1457
 * Performs string comparison, case sensitive, language sensitive, with extended multibyte support.
1458
 * @param string $string1				The first string.
1459
 * @param string $string2				The second string.
1460
 * @param string $language (optional)	The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1461
 * @param string $encoding (optional)	The used internally by this function character encoding.
1462
 * If it is omitted, the platform character set will be used by default.
1463
 * @return int							Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
1464
 * This function is aimed at replacing the function strcmp() for human-language strings.
1465
 * @link http://php.net/manual/en/function.strcmp.php
1466
 * @link http://php.net/manual/en/collator.compare.php
1467
 */
1468
function api_strcmp($string1, $string2, $language = null, $encoding = null)
1469
{
1470
    return strcmp($string1, $string2);
1471
}
1472
1473
/**
1474
 * Performs string comparison in so called "natural order", case sensitive, language sensitive, with extended multibyte support.
1475
 * @param string $string1				The first string.
1476
 * @param string $string2				The second string.
1477
 * @param string $language (optional)	The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1478
 * @param string $encoding (optional)	The used internally by this function character encoding.
1479
 * If it is omitted, the platform character set will be used by default.
1480
 * @return int							Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
1481
 * This function is aimed at replacing the function strnatcmp() for human-language strings.
1482
 * @link http://php.net/manual/en/function.strnatcmp.php
1483
 * @link http://php.net/manual/en/collator.compare.php
1484
 */
1485
function api_strnatcmp($string1, $string2, $language = null, $encoding = null)
1486
{
1487
    return strnatcmp($string1, $string2);
1488
}
1489
1490
/**
1491
 * Sorting arrays
1492
 */
1493
1494
/**
1495
 * Sorts an array using natural order algorithm.
1496
 * @param array $array					The input array.
1497
 * @param string $language (optional)	The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1498
 * @param string $encoding (optional)	The used internally by this function character encoding.
1499
 * If it is omitted, the platform character set will be used by default.
1500
 * @return bool							Returns TRUE on success, FALSE on error.
1501
 * This function is aimed at replacing the function natsort() for sorting human-language strings.
1502
 * @link http://php.net/manual/en/function.natsort.php
1503
 */
1504
function api_natsort(&$array, $language = null, $encoding = null)
1505
{
1506
    return natsort($array);
1507
}
1508
1509
/**
1510
 * Sorts an array using natural order algorithm in reverse order.
1511
 * @param array $array					The input array.
1512
 * @param string $language (optional)	The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1513
 * @param string $encoding (optional)	The used internally by this function character encoding.
1514
 * If it is omitted, the platform character set will be used by default.
1515
 * @return bool							Returns TRUE on success, FALSE on error.
1516
 */
1517
function api_natrsort(&$array, $language = null, $encoding = null)
1518
{
1519
    return uasort($array, '_api_strnatrcmp');
1520
}
1521
1522
/**
1523
 * Encoding management functions
1524
 */
1525
1526
/**
1527
 * This function unifies the encoding identificators, so they could be compared.
1528
 * @param string|array $encoding	The specified encoding.
1529
 * @return string					Returns the encoding identificator modified in suitable for comparison way.
1530
 */
1531
function api_refine_encoding_id($encoding) {
1532
    if (is_array($encoding)){
1533
        return array_map('api_refine_encoding_id', $encoding);
1534
    }
1535
    return strtoupper(str_replace('_', '-', $encoding));
1536
}
1537
1538
/**
1539
 * This function checks whether two $encoding are equal (same, equvalent).
1540
 * @param string|array $encoding1		The first encoding
1541
 * @param string|array $encoding2		The second encoding
1542
 * @param bool $strict					When this parameter is TRUE the comparison ignores aliases of encodings.
1543
 * When the parameter is FALSE, aliases are taken into account.
1544
 * @return bool							Returns TRUE if the encodings are equal, FALSE otherwise.
1545
 */
1546
function api_equal_encodings($encoding1, $encoding2, $strict = false) {
1547
    static $equal_encodings = array();
1548
    if (is_array($encoding1)) {
1549
        foreach ($encoding1 as $encoding) {
1550
            if (api_equal_encodings($encoding, $encoding2, $strict)) {
1551
                return true;
1552
            }
1553
        }
1554
        return false;
1555
    }
1556
    elseif (is_array($encoding2)) {
1557
        foreach ($encoding2 as $encoding) {
1558
            if (api_equal_encodings($encoding1, $encoding, $strict)) {
1559
                return true;
1560
            }
1561
        }
1562
        return false;
1563
    }
1564
    if (!isset($equal_encodings[$encoding1][$encoding2][$strict])) {
1565
        $encoding_1 = api_refine_encoding_id($encoding1);
1566
        $encoding_2 = api_refine_encoding_id($encoding2);
1567
        if ($encoding_1 == $encoding_2) {
1568
            $result = true;
1569
        } else {
1570
            if ($strict) {
1571
                $result = false;
1572
            } else {
1573
                $alias1 = _api_get_character_map_name($encoding_1);
0 ignored issues
show
Bug introduced by
It seems like $encoding_1 defined by api_refine_encoding_id($encoding1) on line 1565 can also be of type array; however, _api_get_character_map_name() 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...
1574
                $alias2 = _api_get_character_map_name($encoding_2);
0 ignored issues
show
Bug introduced by
It seems like $encoding_2 defined by api_refine_encoding_id($encoding2) on line 1566 can also be of type array; however, _api_get_character_map_name() 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...
1575
                $result = !empty($alias1) && !empty($alias2) && $alias1 == $alias2;
1576
            }
1577
        }
1578
        $equal_encodings[$encoding1][$encoding2][$strict] = $result;
1579
    }
1580
    return $equal_encodings[$encoding1][$encoding2][$strict];
1581
}
1582
1583
/**
1584
 * This function checks whether a given encoding is UTF-8.
1585
 * @param string $encoding		The tested encoding.
1586
 * @return bool					Returns TRUE if the given encoding id means UTF-8, otherwise returns false.
1587
 */
1588
function api_is_utf8($encoding)
1589
{
1590
    static $result = array();
1591
    if (!isset($result[$encoding])) {
1592
        $result[$encoding] = api_equal_encodings($encoding, 'UTF-8');
1593
    }
1594
    return $result[$encoding];
1595
}
1596
1597
/**
1598
 * This function returns the encoding, currently used by the system.
1599
 * @return string	The system's encoding.
1600
 * Note: The value of api_get_setting('platform_charset') is tried to be returned first,
1601
 * on the second place the global variable $charset is tried to be returned. If for some
1602
 * reason both attempts fail, then the libraly's internal value will be returned.
1603
 */
1604
function api_get_system_encoding()
1605
{
1606
    return 'UTF-8';
1607
}
1608
1609
/**
1610
 * Checks whether a specified encoding is supported by this API.
1611
 * @param string $encoding	The specified encoding.
1612
 * @return bool				Returns TRUE when the specified encoding is supported, FALSE othewise.
1613
 */
1614
function api_is_encoding_supported($encoding) {
1615
    static $supported = array();
1616
    if (!isset($supported[$encoding])) {
1617
        $supported[$encoding] = _api_mb_supports($encoding) || _api_iconv_supports($encoding) || _api_convert_encoding_supports($encoding);
1618
    }
1619
    return $supported[$encoding];
1620
}
1621
1622
/**
1623
 * Detects encoding of plain text.
1624
 * @param string $string				The input text.
1625
 * @param string $language (optional)	The language of the input text, provided if it is known.
1626
 * @return string						Returns the detected encoding.
1627
 */
1628
function api_detect_encoding($string, $language = null) {
1629
    // Testing against valid UTF-8 first.
1630
    if (api_is_valid_utf8($string)) {
1631
        return 'UTF-8';
1632
    }
1633
1634
    return mb_detect_encoding($string);
1635
}
1636
1637
/**
1638
 * String validation functions concerning certain encodings
1639
 */
1640
1641
/**
1642
 * Checks a string for UTF-8 validity.
1643
 *
1644
 */
1645
function api_is_valid_utf8(&$string)
1646
{
1647
    return Utf8::isUtf8($string);
1648
}
1649
1650
/**
1651
 * Checks whether a string contains 7-bit ASCII characters only.
1652
 * @param string $string	The string to be tested/validated.
1653
 * @return bool				Returns TRUE when the tested string contains 7-bit ASCII characters only, FALSE othewise.
1654
 */
1655
function api_is_valid_ascii(&$string)
1656
{
1657
    return mb_detect_encoding($string, 'ASCII', true) == 'ASCII' ? true : false;
1658
}
1659
1660
/**
1661
 * Return true a date is valid
1662
1663
 * @param string $date example: 2014-06-30 13:05:05
1664
 * @param string $format example: "Y-m-d H:i:s"
1665
 *
1666
 * @return bool
1667
 */
1668
function api_is_valid_date($date, $format = 'Y-m-d H:i:s')
1669
{
1670
    $d = DateTime::createFromFormat($format, $date);
1671
    return $d && $d->format($format) == $date;
1672
}
1673
1674
/**
1675
 * Returns the variable translated
1676
 * @param string $variable the string to translate
1677
 * @param string $pluginName the Plugin name
1678
 * @return string the variable translated
1679
 */
1680
function get_plugin_lang($variable, $pluginName) {
1681
    $plugin = $pluginName::create();
1682
    return $plugin->get_lang($variable);
1683
}
1684
1685
/**
1686
 * Returns an array of translated week days and months, short and normal names.
1687
 * @param string $language (optional)	Language id. If it is omitted, the current interface language is assumed.
1688
 * @return array						Returns a multidimensional array with translated week days and months.
1689
 */
1690
function &_api_get_day_month_names($language = null) {
1691
    static $date_parts = array();
1692
    if (empty($language)) {
1693
        $language = api_get_interface_language();
1694
    }
1695
    if (!isset($date_parts[$language])) {
1696
        $week_day = array(
1697
            'Sunday',
1698
            'Monday',
1699
            'Tuesday',
1700
            'Wednesday',
1701
            'Thursday',
1702
            'Friday',
1703
            'Saturday',
1704
        );
1705
        $month = array(
1706
            'January',
1707
            'February',
1708
            'March',
1709
            'April',
1710
            'May',
1711
            'June',
1712
            'July',
1713
            'August',
1714
            'September',
1715
            'October',
1716
            'November',
1717
            'December',
1718
        );
1719 View Code Duplication
        for ($i = 0; $i < 7; $i++) {
1720
            $date_parts[$language]['days_short'][] = get_lang($week_day[$i].'Short', '', $language);
1721
            $date_parts[$language]['days_long'][] = get_lang($week_day[$i].'Long', '', $language);
1722
        }
1723 View Code Duplication
        for ($i = 0; $i < 12; $i++) {
1724
            $date_parts[$language]['months_short'][] = get_lang($month[$i].'Short', '', $language);
1725
            $date_parts[$language]['months_long'][] = get_lang($month[$i].'Long', '', $language);
1726
        }
1727
    }
1728
    return $date_parts[$language];
1729
}
1730
1731
/**
1732
 * Returns returns person name convention for a given language.
1733
 * @param string $language	The input language.
1734
 * @param string $type		The type of the requested convention.
1735
 * It may be 'format' for name order convention or 'sort_by' for name sorting convention.
1736
 * @return mixed Depending of the requested type,
1737
 * the returned result may be string or boolean; null is returned on error;
1738
 */
1739
function _api_get_person_name_convention($language, $type)
1740
{
1741
    static $conventions;
1742
    $language = api_purify_language_id($language);
1743
    if (!isset($conventions)) {
1744
        $file = dirname(__FILE__).'/internationalization_database/name_order_conventions.php';
1745
        if (file_exists($file)) {
1746
            $conventions = include ($file);
1747
        } else {
1748
            $conventions = array(
1749
                'english' => array(
1750
                    'format' => 'title first_name last_name',
1751
                    'sort_by' => 'first_name'
1752
                )
1753
            );
1754
        }
1755
        // Overwrite classic conventions
1756
        $customConventions = api_get_configuration_value('name_order_conventions');
1757
1758
        if (!empty($customConventions)) {
1759
            foreach ($customConventions as $key => $data) {
1760
                $conventions[$key] = $data;
1761
            }
1762
        }
1763
1764
        $search1 = array('FIRST_NAME', 'LAST_NAME', 'TITLE');
1765
        $replacement1 = array('%F', '%L', '%T');
1766
        $search2 = array('first_name', 'last_name', 'title');
1767
        $replacement2 = array('%f', '%l', '%t');
1768
        foreach (array_keys($conventions) as $key) {
1769
            $conventions[$key]['format'] = str_replace($search1, $replacement1, $conventions[$key]['format']);
1770
            $conventions[$key]['format'] = _api_validate_person_name_format(_api_clean_person_name(str_replace('%', ' %', str_ireplace($search2, $replacement2, $conventions[$key]['format']))));
1771
            $conventions[$key]['sort_by'] = strtolower($conventions[$key]['sort_by']) != 'last_name' ? true : false;
1772
        }
1773
    }
1774
    switch ($type) {
1775
        case 'format':
1776
            return is_string($conventions[$language]['format']) ? $conventions[$language]['format'] : '%t %f %l';
1777
        case 'sort_by':
1778
            return is_bool($conventions[$language]['sort_by']) ? $conventions[$language]['sort_by'] : true;
1779
    }
1780
    return null;
1781
}
1782
1783
/**
1784
 * Replaces non-valid formats for person names with the default (English) format.
1785
 * @param string $format	The input format to be verified.
1786
 * @return bool				Returns the same format if is is valid, otherwise returns a valid English format.
1787
 */
1788
function _api_validate_person_name_format($format) {
1789
    if (empty($format) || stripos($format, '%f') === false || stripos($format, '%l') === false) {
1790
        return '%t %f %l';
1791
    }
1792
    return $format;
1793
}
1794
1795
/**
1796
 * Removes leading, trailing and duplicate whitespace and/or commas in a full person name.
1797
 * Cleaning is needed for the cases when not all parts of the name are available or when the name is constructed using a "dirty" pattern.
1798
 * @param string $person_name	The input person name.
1799
 * @return string				Returns cleaned person name.
1800
 */
1801
function _api_clean_person_name($person_name) {
1802
    return preg_replace(array('/\s+/', '/, ,/', '/,+/', '/^[ ,]/', '/[ ,]$/'), array(' ', ', ', ',', '', ''), $person_name);
1803
}
1804
1805
/**
1806
 * Appendix to "Multibyte string conversion functions"
1807
 */
1808
1809
/**
1810
 * This is a php-implementation of a function that is similar to mb_convert_encoding() from mbstring extension.
1811
 * The function converts a given string from one to another character encoding.
1812
 * @param string $string					The string being converted.
1813
 * @param string $to_encoding				The encoding that $string is being converted to.
1814
 * @param string $from_encoding				The encoding that $string is being converted from.
1815
 * @return string							Returns the converted string.
1816
 */
1817
function _api_convert_encoding(&$string, $to_encoding, $from_encoding)
1818
{
1819
    return mb_convert_encoding($string, $to_encoding, $from_encoding);
1820
}
1821
1822
/**
1823
 * This function determines the name of corresponding to a given encoding conversion table.
1824
 * It is able to deal with some aliases of the encoding.
1825
 * @param string $encoding		The given encoding identificator, for example 'WINDOWS-1252'.
1826
 * @return string				Returns the name of the corresponding conversion table, for the same example - 'CP1252'.
1827
 */
1828
function _api_get_character_map_name($encoding)
1829
{
1830
    static $character_map_selector;
1831
    if (!isset($character_map_selector)) {
1832
        $file = dirname(__FILE__).'/internationalization_database/conversion/character_map_selector.php';
1833
        if (file_exists($file)) {
1834
            $character_map_selector = include ($file);
1835
        } else {
1836
            $character_map_selector = array();
1837
        }
1838
    }
1839
    return isset($character_map_selector[$encoding]) ? $character_map_selector[$encoding] : '';
1840
}
1841
1842
/**
1843
 * Appendix to "String comparison"
1844
 */
1845
1846
/**
1847
 * A reverse function from php-core function strnatcmp(), performs string comparison in reverse natural (alpha-numerical) order.
1848
 * @param string $string1		The first string.
1849
 * @param string $string2		The second string.
1850
 * @return int					Returns 0 if $string1 = $string2; >0 if $string1 < $string2; <0 if $string1 > $string2.
1851
 */
1852
function _api_strnatrcmp($string1, $string2)
1853
{
1854
    return strnatcmp($string2, $string1);
1855
}
1856
1857
/**
1858
 * Sets/Gets internal character encoding of the common string functions within the PHP mbstring extension.
1859
 * @param string $encoding (optional)	When this parameter is given, the function sets the internal encoding.
1860
 * @return string						When $encoding parameter is not given, the function returns the internal encoding.
1861
 * Note: This function is used in the global initialization script for setting the internal encoding to the platform's character set.
1862
 * @link http://php.net/manual/en/function.mb-internal-encoding
1863
 */
1864
function _api_mb_internal_encoding($encoding = 'UTF-8')
1865
{
1866
    return mb_internal_encoding($encoding);
1867
}
1868
1869
/**
1870
 * Checks whether the specified encoding is supported by the PHP mbstring extension.
1871
 * @param string $encoding	The specified encoding.
1872
 * @return bool				Returns TRUE when the specified encoding is supported, FALSE othewise.
1873
 */
1874
function _api_mb_supports($encoding) {
1875
    static $supported = array();
1876
    if (!isset($supported[$encoding])) {
1877
        if (MBSTRING_INSTALLED) {
1878
            $supported[$encoding] = api_equal_encodings($encoding, mb_list_encodings(), true);
1879
        } else {
1880
            $supported[$encoding] = false;
1881
        }
1882
    }
1883
    return $supported[$encoding];
1884
}
1885
1886
/**
1887
 * Checks whether the specified encoding is supported by the PHP iconv extension.
1888
 * @param string $encoding	The specified encoding.
1889
 * @return bool				Returns TRUE when the specified encoding is supported, FALSE othewise.
1890
 */
1891
function _api_iconv_supports($encoding)
1892
{
1893
    static $supported = array();
1894
    if (!isset($supported[$encoding])) {
1895
        if (ICONV_INSTALLED) {
1896
            $enc = api_refine_encoding_id($encoding);
1897
            if ($enc != 'HTML-ENTITIES') {
1898
                $test_string = '';
1899
                for ($i = 32; $i < 128; $i++) {
1900
                    $test_string .= chr($i);
1901
                }
1902
                $supported[$encoding] = (@iconv_strlen($test_string, $enc)) ? true : false;
1903
            } else {
1904
                $supported[$encoding] = false;
1905
            }
1906
        } else {
1907
            $supported[$encoding] = false;
1908
        }
1909
    }
1910
    return $supported[$encoding];
1911
}
1912
1913
// This function checks whether the function _api_convert_encoding() (the php-
1914
// implementation) is able to convert from/to a given encoding.
1915
function _api_convert_encoding_supports($encoding)
1916
{
1917
    static $supports = array();
1918
    if (!isset($supports[$encoding])) {
1919
        $supports[$encoding] = _api_get_character_map_name(api_refine_encoding_id($encoding)) != '';
0 ignored issues
show
Bug introduced by
It seems like api_refine_encoding_id($encoding) targeting api_refine_encoding_id() can also be of type array; however, _api_get_character_map_name() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1920
    }
1921
    return $supports[$encoding];
1922
}
1923
1924
/**
1925
 * Given a date object, return a human or ISO format, with or without h:m:s
1926
 * @param object $date The Date object
1927
 * @param bool $showTime Whether to show the time and date (true) or only the date (false)
1928
 * @param bool $humanForm Whether to show day-month-year (true) or year-month-day (false)
1929
 * @return string Formatted date
1930
 */
1931
function api_get_human_date_time($date, $showTime = true, $humanForm = false)
1932
{
1933
    if ($showTime) {
1934
        if ($humanForm) {
1935
           return $date->format('j M Y H:i:s');
1936
        } else {
1937
           return $date->format('Y-m-d H:i:s');
1938
        }
1939
    } else {
1940
        if ($humanForm) {
1941
           return $date->format('j M Y');
1942
        } else {
1943
           return $date->format('Y-m-d');
1944
        }
1945
    }
1946
}
1947