Issues (2128)

main/inc/lib/internationalization.lib.php (7 issues)

Code
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
use Patchwork\Utf8;
6
7
/**
8
 * File: internationalization.lib.php
9
 * Internationalization library for Chamilo 1.x LMS
10
 * A library implementing internationalization related functions.
11
 * License: GNU General Public License Version 3 (Free Software Foundation)ww.
12
 *
13
 * @author Ivan Tcholakov, <[email protected]>, 2009, 2010
14
 * @author More authors, mentioned in the correpsonding fragments of this source.
15
 *
16
 * @package chamilo.library
17
 */
18
19
// Special tags for marking untranslated variables.
20
define('SPECIAL_OPENING_TAG', '[=');
21
define('SPECIAL_CLOSING_TAG', '=]');
22
23
// Predefined date formats in Chamilo provided by the language sub-system.
24
// To be used as a parameter for the function api_format_date()
25
26
define('TIME_NO_SEC_FORMAT', 0); // 15:23
27
define('DATE_FORMAT_SHORT', 1); // Aug 25, 09
28
define('DATE_FORMAT_LONG', 2); // Monday August 25, 09
29
define('DATE_FORMAT_LONG_NO_DAY', 10); // August 25, 2009
30
define('DATE_TIME_FORMAT_LONG', 3); // Monday August 25, 2009 at 03:28 PM
31
32
define('DATE_FORMAT_NUMBER', 4); // 25.08.09
33
define('DATE_TIME_FORMAT_LONG_24H', 5); // August 25, 2009 at 15:28
34
define('DATE_TIME_FORMAT_SHORT', 6); // Aug 25, 2009 at 03:28 PM
35
define('DATE_TIME_FORMAT_SHORT_TIME_FIRST', 7); // 03:28 PM, Aug 25 2009
36
define('DATE_FORMAT_NUMBER_NO_YEAR', 8); // 25.08 dd-mm
37
define('DATE_FORMAT_ONLY_DAYNAME', 9); // Monday, Sunday, etc
38
39
// Formatting person's name.
40
// Formatting a person's name using the pattern as it has been
41
// configured in the internationalization database for every language.
42
// This (default) option would be the most used.
43
define('PERSON_NAME_COMMON_CONVENTION', 0);
44
// The following options may be used in limited number of places for overriding the common convention:
45
46
// Formatting a person's name in Western order: first_name last_name
47
define('PERSON_NAME_WESTERN_ORDER', 1);
48
// Formatting a person's name in Eastern order: last_name first_name
49
define('PERSON_NAME_EASTERN_ORDER', 2);
50
// Contextual: formatting person's name in library order: last_name, first_name
51
define('PERSON_NAME_LIBRARY_ORDER', 3);
52
// Contextual: formatting a person's name assotiated with an email-address.
53
// Ivan: I am not sure how seems email servers an clients would interpret name order, so I assign the Western order.
54
define('PERSON_NAME_EMAIL_ADDRESS', PERSON_NAME_WESTERN_ORDER);
55
// Contextual: formatting a person's name for data-exporting operations.
56
// For backward compatibility this format has been set to Eastern order.
57
define('PERSON_NAME_DATA_EXPORT', PERSON_NAME_EASTERN_ORDER);
58
59
/**
60
 * Returns a translated (localized) string, called by its ID.
61
 *
62
 * @param string $variable              this is the ID (name) of the translated string to be retrieved
63
 * @param bool   $returnEmptyIfNotFound If variable is not found, then: if false: returns variable name with or without brackets; true: returns ''
64
 * @param string $language              (optional)    Language ID. If it is omitted, the current interface language is assumed.
65
 *
66
 * @return string returns the requested string in the correspondent language
67
 *
68
 * @author Roan Embrechts
69
 * @author Patrick Cool
70
 * @author Ivan Tcholakov, 2009-2010 (caching functionality, additional parameter $language, other adaptations).
71
 *
72
 * Notes:
73
 * 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').
74
 * 2. Untranslated variables might be indicated by special opening and closing tags  -  [=  =]
75
 * The special tags do not show up in these two cases:
76
 * - when the system has been switched to "production server mode";
77
 * - when a special platform setting 'hide_dltt_markup' is set to "true" (the name of this setting comes from history);
78
 * 3. Translations are created many contributors through using a special tool: Chamilo Translation Application.
79
 *
80
 * @see http://translate.chamilo.org/
81
 */
82
function get_lang($variable, $returnEmptyIfNotFound = false, $language = null)
83
{
84
    // For serving some old hacks:
85
    // By manipulating this global variable the translation may
86
    // be done in different languages too (not the elegant way).
87
    global $language_interface;
88
    // Because of possibility for manipulations of the global
89
    // variable $language_interface, we need its initial value.
90
    global $language_interface_initial_value;
91
    global $used_lang_vars, $_configuration;
92
    // add language_measure_frequency to your main/inc/conf/configuration.php in order to generate language
93
    // variables frequency measurements (you can then see them trhough main/cron/lang/langstats.php)
94
    // The $langstats object is instanciated at the end of main/inc/global.inc.php
95
    if (isset($_configuration['language_measure_frequency']) &&
96
        $_configuration['language_measure_frequency'] == 1
97
    ) {
98
        require_once api_get_path(SYS_CODE_PATH).'/cron/lang/langstats.class.php';
99
        global $langstats;
100
        $langstats->add_use($variable, '');
101
    }
102
103
    if (!isset($used_lang_vars)) {
104
        $used_lang_vars = [];
105
    }
106
107
    // Caching results from some API functions, for speed.
108
    static $initialized, $show_special_markup;
109
    if (!isset($initialized)) {
110
        $test_server_mode = api_get_setting('server_type') === 'test';
111
        $show_special_markup = api_get_setting('hide_dltt_markup') != 'true' || $test_server_mode;
112
        $initialized = true;
113
    }
114
115
    // Combining both ways for requesting specific language.
116
    if (empty($language)) {
117
        $language = $language_interface;
118
    }
119
    $lang_postfix = isset($is_interface_language) && $is_interface_language ? '' : '('.$language.')';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $is_interface_language does not exist. Did you maybe mean $language?
Loading history...
120
121
    $is_interface_language = $language == $language_interface_initial_value;
122
123
    // This is a cache for already translated language variables. By using it, we avoid repetitive translations, gaining speed.
124
    static $cache;
125
126
    // Looking up into the cache for existing translation.
127
    if (isset($cache[$language][$variable])) {
128
        // There is a previously saved translation, returning it.
129
        //return $cache[$language][$variable];
130
        $ret = $cache[$language][$variable];
131
        $used_lang_vars[$variable.$lang_postfix] = $ret;
132
133
        return $ret;
134
    }
135
136
    // There is no cached translation, we have to retrieve it:
137
    // - from a global variable (the faster way) - on production server mode;
138
    // - from a local variable after reloading the language files - on test server mode or when requested language
139
    // is different than the genuine interface language.
140
    $read_global_variables = $is_interface_language;
141
    $langvar = '';
142
143
    if ($read_global_variables) {
144
        if (isset($GLOBALS[$variable])) {
145
            $langvar = $GLOBALS[$variable];
146
        } elseif (isset($GLOBALS["lang$variable"])) {
147
            $langvar = $GLOBALS["lang$variable"];
148
        } else {
149
            if (!$returnEmptyIfNotFound) {
150
                $langvar = $show_special_markup ? SPECIAL_OPENING_TAG.$variable.SPECIAL_CLOSING_TAG : $variable;
151
            } else {
152
                return '';
153
            }
154
        }
155
    }
156
    if (empty($langvar) || !is_string($langvar) && !$returnEmptyIfNotFound) {
157
        $langvar = $show_special_markup ? SPECIAL_OPENING_TAG.$variable.SPECIAL_CLOSING_TAG : $variable;
158
    }
159
    $ret = $cache[$language][$variable] = $langvar;
160
    $used_lang_vars[$variable.$lang_postfix] = $ret;
161
162
    return $ret;
163
}
164
165
/**
166
 * Gets the current interface language.
167
 *
168
 * @param bool $purified              (optional)    When it is true, a purified (refined) language value will be returned, for example 'french' instead of 'french_unicode'
169
 * @param bool $check_sub_language    (optional) Whether we have to consider sub-languages for the determination of a common parent language
170
 * @param bool $setParentLanguageName (optional) If $check_sub_language is true and there is a parent, return the parent language rather than the sub-language
171
 *
172
 * @throws Exception
173
 *
174
 * @return string the current language of the interface
175
 */
176
function api_get_interface_language(
177
    bool $purified = false,
178
    bool $check_sub_language = false,
179
    bool $setParentLanguageName = true
180
): string {
181
    global $language_interface;
182
183
    if (empty($language_interface)) {
184
        return 'english';
185
    }
186
187
    if ($check_sub_language) {
188
        static $parent_language_name = null;
189
190
        if (!isset($parent_language_name)) {
191
            // 2. The current language is a sub language, so we grab the father's
192
            // setting according to the internalization_database/name_order_convetions.php file
193
            $language_id = api_get_language_id($language_interface);
194
            $language_info = api_get_language_info($language_id);
195
196
            if (!empty($language_id) &&
197
                !empty($language_info)
198
            ) {
199
                if (!empty($language_info['parent_id'])) {
200
                    $language_info = api_get_language_info($language_info['parent_id']);
201
                    if ($setParentLanguageName) {
202
                        $parent_language_name = $language_info['english_name'];
203
                    }
204
205
                    if (!empty($parent_language_name)) {
206
                        return $parent_language_name;
207
                    }
208
                }
209
210
                return $language_info['english_name'];
211
            }
212
213
            return 'english';
214
        } else {
215
            return $parent_language_name;
216
        }
217
    } else {
218
        // 2. Normal way
219
        $interface_language = $purified ? api_purify_language_id($language_interface) : $language_interface;
220
    }
221
222
    return $interface_language;
223
}
224
225
/**
226
 * Returns a purified language id, without possible suffixes that will disturb language identification in certain cases.
227
 *
228
 * @param string $language the input language identificator, for example 'french_unicode'
229
 * @param string the same purified or filtered language id, for example 'french'
230
 *
231
 * @return string
232
 */
233
function api_purify_language_id($language)
234
{
235
    static $purified = [];
236
    if (!isset($purified[$language])) {
237
        $purified[$language] = trim(
238
            str_replace(
239
                ['_unicode', '_latin', '_corporate', '_org', '_km'],
240
                '',
241
                strtolower($language)
242
            )
243
        );
244
    }
245
246
    return $purified[$language];
247
}
248
249
/**
250
 * Gets language isocode column from the language table, taking the given language as a query parameter.
251
 *
252
 * @param string $language     This is the name of the folder containing translations for the corresponding language (e.g arabic, english).
253
 * @param string $default_code This is the value to be returned if there was no code found corresponding to the given language.
254
 *                             If $language is omitted, interface language is assumed then.
255
 *
256
 * @return string The found isocode or null on error.
257
 *                Returned codes are according to the following standards (in order of preference):
258
 *                -  ISO 639-1 : Alpha-2 code (two-letters code - en, fr, es, ...)
259
 *                -  RFC 4646  : five-letter code based on the ISO 639 two-letter language codes
260
 *                and the ISO 3166 two-letter territory codes (pt-BR, ...)
261
 *                -  ISO 639-2 : Alpha-3 code (three-letters code - ast, fur, ...)
262
 */
263
function api_get_language_isocode($language = null, $default_code = 'en')
264
{
265
    static $iso_code = [];
266
    if (empty($language)) {
267
        $language = api_get_interface_language(false, true);
268
    }
269
    if (!isset($iso_code[$language])) {
270
        if (!class_exists('Database')) {
271
            // This might happen, in case of calling this function early during the global initialization.
272
            return $default_code; // This might happen, in case of calling this function early during the global initialization.
273
        }
274
        $sql = "SELECT isocode
275
                FROM ".Database::get_main_table(TABLE_MAIN_LANGUAGE)."
276
                WHERE dokeos_folder = '$language'";
277
        $sql_result = Database::query($sql);
278
        if (Database::num_rows($sql_result)) {
279
            $result = Database::fetch_array($sql_result);
280
            $iso_code[$language] = trim($result['isocode']);
281
        } else {
282
            $language_purified_id = api_purify_language_id($language);
283
            $iso_code[$language] = isset($iso_code[$language_purified_id]) ? $iso_code[$language_purified_id] : null;
284
        }
285
        if (empty($iso_code[$language])) {
286
            $iso_code[$language] = $default_code;
287
        }
288
    }
289
290
    return $iso_code[$language];
291
}
292
293
/**
294
 * Gets language iso code column from the language table.
295
 *
296
 * @return array An array with the current isocodes
297
 *
298
 * */
299
function api_get_platform_isocodes()
300
{
301
    $iso_code = [];
302
    $sql = "SELECT isocode
303
            FROM ".Database::get_main_table(TABLE_MAIN_LANGUAGE)."
304
            ORDER BY isocode ";
305
    $sql_result = Database::query($sql);
306
    if (Database::num_rows($sql_result)) {
307
        while ($row = Database::fetch_array($sql_result)) {
308
            $iso_code[] = trim($row['isocode']);
309
        }
310
    }
311
312
    return $iso_code;
313
}
314
315
/**
316
 * Gets text direction according to the given language.
317
 *
318
 * @param string $language This is the name of the
319
 *                         folder containing translations for the corresponding language (e.g 'arabic', 'english', ...).
320
 *                         ISO-codes are acceptable too ('ar', 'en', ...).
321
 *                         If $language is omitted, interface language is assumed then.
322
 *
323
 * @return string the correspondent to the language text direction ('ltr' or 'rtl')
324
 */
325
function api_get_text_direction($language = null)
326
{
327
    static $text_direction = [];
328
329
    if (empty($language)) {
330
        $language = api_get_interface_language();
331
    }
332
    if (!isset($text_direction[$language])) {
333
        $text_direction[$language] = in_array(
334
            api_purify_language_id($language),
335
            [
336
                'arabic',
337
                'ar',
338
                'dari',
339
                'prs',
340
                'hebrew',
341
                'he',
342
                'iw',
343
                'pashto',
344
                'ps',
345
                'persian',
346
                'fa',
347
                'ur',
348
                'yiddish',
349
                'yid',
350
            ]
351
        ) ? 'rtl' : 'ltr';
352
    }
353
354
    return $text_direction[$language];
355
}
356
357
/**
358
 * Returns an alphabetized list of timezones in an associative array
359
 * that can be used to populate a select.
360
 *
361
 * @return array List of timezone identifiers
362
 *
363
 * @author Guillaume Viguier <[email protected]>
364
 */
365
function api_get_timezones()
366
{
367
    $timezone_identifiers = DateTimeZone::listIdentifiers();
368
    sort($timezone_identifiers);
369
    $out = [];
370
    foreach ($timezone_identifiers as $tz) {
371
        $out[$tz] = $tz;
372
    }
373
    $null_option = ['' => ''];
374
    $result = array_merge($null_option, $out);
375
376
    return $result;
377
}
378
379
/**
380
 * Returns the timezone to be converted to/from, based on user or admin preferences.
381
 *
382
 * @return string The timezone chosen
383
 */
384
function api_get_timezone()
385
{
386
    $timezone = Session::read('system_timezone');
387
    if (empty($timezone)) {
388
        // First, get the default timezone of the server
389
        $timezone = date_default_timezone_get();
390
        // Second, see if a timezone has been chosen for the platform
391
        $timezoneFromSettings = api_get_setting('timezone_value', 'timezones');
392
393
        if ($timezoneFromSettings != null) {
394
            $timezone = $timezoneFromSettings;
395
        }
396
397
        // If allowed by the administrator
398
        $allowUserTimezones = api_get_setting('use_users_timezone', 'timezones');
399
400
        if ($allowUserTimezones === 'true') {
401
            $userId = api_get_user_id();
402
            // Get the timezone based on user preference, if it exists
403
            $newExtraField = new ExtraFieldValue('user');
404
            $data = $newExtraField->get_values_by_handler_and_field_variable($userId, 'timezone');
405
            if (!empty($data) && isset($data['timezone']) && !empty($data['timezone'])) {
406
                $timezone = $data['timezone'];
407
            }
408
        }
409
        Session::write('system_timezone', $timezone);
410
    }
411
412
    return $timezone;
413
}
414
415
/**
416
 * Returns the given date as a DATETIME in UTC timezone.
417
 * This function should be used before entering any date in the DB.
418
 *
419
 * @param mixed $time                    Date to be converted (can be a string supported by date() or a timestamp)
420
 * @param bool  $returnNullIfInvalidDate If the date is not correct return null instead of the current date
421
 * @param bool  $returnObj               Returns a DateTime object
422
 *
423
 * @return string|DateTime The DATETIME in UTC to be inserted in the DB,
424
 *                         or null if the format of the argument is not supported
425
 *
426
 * @author Julio Montoya - Adding the 2nd parameter
427
 * @author Guillaume Viguier <[email protected]>
428
 */
429
function api_get_utc_datetime(
430
    $time = null,
431
    $returnNullIfInvalidDate = false,
432
    $returnObj = false
433
) {
434
    if (is_null($time) || empty($time) || $time === '0000-00-00 00:00:00') {
435
        if ($returnNullIfInvalidDate) {
436
            return null;
437
        }
438
        if ($returnObj) {
439
            return new DateTime(gmdate('Y-m-d H:i:s'), new DateTimeZone('UTC'));
440
        }
441
442
        return gmdate('Y-m-d H:i:s');
443
    }
444
445
    // If time is a timestamp, return directly in utc
446
    if (is_numeric($time)) {
447
        $time = (int) $time;
448
449
        $gmDate = gmdate('Y-m-d H:i:s', $time);
450
451
        if ($returnObj) {
452
            return new DateTime($gmDate, new DateTimeZone('UTC'));
453
        }
454
455
        return $gmDate;
456
    }
457
    try {
458
        $fromTimezone = api_get_timezone();
459
        $date = new DateTime($time, new DateTimezone($fromTimezone));
460
        $date->setTimezone(new DateTimeZone('UTC'));
461
        if ($returnObj) {
462
            return $date;
463
        } else {
464
            return $date->format('Y-m-d H:i:s');
465
        }
466
    } catch (Exception $e) {
467
        return null;
468
    }
469
}
470
471
/**
472
 * Returns a DATETIME string converted to the right timezone.
473
 *
474
 * @param mixed  $time                    The time to be converted
475
 * @param string $to_timezone             If null, the timezone will be determined based on user preference,
476
 *                                        or timezone chosen by the admin for the platform.
477
 * @param string $from_timezone           The timezone to be converted from. If null, UTC will be assumed.
478
 * @param bool   $returnNullIfInvalidDate Return null if date is not correct.
479
 * @param bool   $showTime                Show time.
480
 * @param bool   $humanForm               Return a readable date time.
481
 * @param string $format                  Specify format.
482
 *
483
 * @return string The converted time formatted as Y-m-d H:i:s
484
 *
485
 * @author Guillaume Viguier <[email protected]>
486
 */
487
function api_get_local_time(
488
    $time = null,
489
    $to_timezone = null,
490
    $from_timezone = null,
491
    $returnNullIfInvalidDate = false,
492
    $showTime = true,
493
    $humanForm = false,
494
    $format = ''
495
) {
496
    // Determining the timezone to be converted from
497
    if (is_null($from_timezone)) {
498
        $from_timezone = 'UTC';
499
    }
500
501
    // If time is a timestamp, convert it to a string
502
    if (is_null($time) || empty($time) || $time == '0000-00-00 00:00:00') {
503
        if ($returnNullIfInvalidDate) {
504
            return null;
505
        }
506
        $from_timezone = 'UTC';
507
        $time = gmdate('Y-m-d H:i:s');
508
    }
509
510
    if (is_numeric($time)) {
511
        $time = (int) $time;
512
        if ($returnNullIfInvalidDate) {
513
            if (strtotime(date('d-m-Y H:i:s', $time)) !== (int) $time) {
514
                return null;
515
            }
516
        }
517
518
        $from_timezone = 'UTC';
519
        $time = gmdate('Y-m-d H:i:s', $time);
520
    }
521
522
    if ($time instanceof DateTime) {
523
        $time = $time->format('Y-m-d H:i:s');
524
        $from_timezone = 'UTC';
525
    }
526
527
    try {
528
        // Determining the timezone to be converted to
529
        if (is_null($to_timezone)) {
530
            $to_timezone = api_get_timezone();
531
        }
532
533
        $date = new DateTime($time, new DateTimezone($from_timezone));
534
        $date->setTimezone(new DateTimeZone($to_timezone));
535
536
        if (!empty($format)) {
537
            return $date->format($format);
538
        }
539
540
        return api_get_human_date_time($date, $showTime, $humanForm);
541
    } catch (Exception $e) {
542
        return '';
543
    }
544
}
545
546
/**
547
 * Converts a string into a timestamp safely (handling timezones), using strtotime.
548
 *
549
 * @param string $time     to be converted
550
 * @param string $timezone (if null, the timezone will be determined based
551
 *                         on user preference, or timezone chosen by the admin for the platform)
552
 *
553
 * @return int Timestamp
554
 *
555
 * @author Guillaume Viguier <[email protected]>
556
 */
557
function api_strtotime($time, $timezone = null)
558
{
559
    $system_timezone = date_default_timezone_get();
560
    if (!empty($timezone)) {
561
        date_default_timezone_set($timezone);
562
    }
563
    $timestamp = strtotime($time);
564
    if (!empty($timezone)) {
565
        // only reset timezone if it was changed
566
        date_default_timezone_set($system_timezone);
567
    }
568
569
    return $timestamp;
570
}
571
572
/**
573
 * Returns formatted date/time, correspondent to a given language.
574
 * The given date should be in the timezone chosen by the administrator
575
 * and/or user. Use api_get_local_time to get it.
576
 *
577
 * @author Patrick Cool <[email protected]>, Ghent University
578
 * @author Christophe Gesche<[email protected]>
579
 *         originally inspired from from PhpMyAdmin
580
 * @author Ivan Tcholakov, 2009, code refactoring, adding support for predefined date/time formats.
581
 * @author Guillaume Viguier <[email protected]>
582
 *
583
 * @param mixed $time Timestamp or datetime string
584
 * @param string|int Date format (see date formats in the Chamilo system:
585
 * TIME_NO_SEC_FORMAT,
586
 * DATE_FORMAT_SHORT,
587
 * DATE_FORMAT_LONG,
588
 * DATE_TIME_FORMAT_LONG
589
 * @param string $language (optional) Language id
590
 *                         If it is omitted, the current interface language is assumed
591
 *
592
 * @return string returns the formatted date
593
 *
594
 * @see http://php.net/manual/en/function.strftime.php
595
 */
596
function api_format_date($time, $format = null, $language = null)
597
{
598
    if (empty($time)) {
599
        return '';
600
    }
601
602
    $system_timezone = date_default_timezone_get();
603
    date_default_timezone_set(api_get_timezone());
604
605
    if (is_string($time)) {
606
        $time = strtotime($time);
607
    }
608
609
    if (is_null($format)) {
610
        $format = DATE_TIME_FORMAT_LONG;
611
    }
612
613
    $datetype = null;
614
    $timetype = null;
615
616
    if (is_int($format)) {
617
        switch ($format) {
618
            case DATE_FORMAT_ONLY_DAYNAME:
619
                $date_format = get_lang('dateFormatOnlyDayName', '', $language);
620
                if (INTL_INSTALLED) {
621
                    $datetype = IntlDateFormatter::SHORT;
622
                    $timetype = IntlDateFormatter::NONE;
623
                }
624
                break;
625
            case DATE_FORMAT_NUMBER_NO_YEAR:
626
                $date_format = get_lang('dateFormatShortNumberNoYear', '', $language);
627
                if (INTL_INSTALLED) {
628
                    $datetype = IntlDateFormatter::SHORT;
629
                    $timetype = IntlDateFormatter::NONE;
630
                }
631
                break;
632
            case DATE_FORMAT_NUMBER:
633
                $date_format = get_lang('dateFormatShortNumber', '', $language);
634
                if (INTL_INSTALLED) {
635
                    $datetype = IntlDateFormatter::SHORT;
636
                    $timetype = IntlDateFormatter::NONE;
637
                }
638
                break;
639
            case TIME_NO_SEC_FORMAT:
640
                $date_format = get_lang('timeNoSecFormat', '', $language);
641
                if (INTL_INSTALLED) {
642
                    $datetype = IntlDateFormatter::NONE;
643
                    $timetype = IntlDateFormatter::SHORT;
644
                }
645
                break;
646
            case DATE_FORMAT_SHORT:
647
                $date_format = get_lang('dateFormatShort', '', $language);
648
                if (INTL_INSTALLED) {
649
                    $datetype = IntlDateFormatter::LONG;
650
                    $timetype = IntlDateFormatter::NONE;
651
                }
652
                break;
653
            case DATE_FORMAT_LONG:
654
                $date_format = get_lang('dateFormatLong', '', $language);
655
                if (INTL_INSTALLED) {
656
                    $datetype = IntlDateFormatter::FULL;
657
                    $timetype = IntlDateFormatter::NONE;
658
                }
659
                break;
660
            case DATE_TIME_FORMAT_LONG:
661
                $date_format = get_lang('dateTimeFormatLong', '', $language);
662
                if (INTL_INSTALLED) {
663
                    $datetype = IntlDateFormatter::FULL;
664
                    $timetype = IntlDateFormatter::SHORT;
665
                }
666
                break;
667
            case DATE_FORMAT_LONG_NO_DAY:
668
                $date_format = get_lang('dateFormatLongNoDay', '', $language);
669
                if (INTL_INSTALLED) {
670
                    $datetype = IntlDateFormatter::FULL;
671
                    $timetype = IntlDateFormatter::SHORT;
672
                }
673
                break;
674
            case DATE_TIME_FORMAT_SHORT:
675
                $date_format = get_lang('dateTimeFormatShort', '', $language);
676
                if (INTL_INSTALLED) {
677
                    $datetype = IntlDateFormatter::FULL;
678
                    $timetype = IntlDateFormatter::SHORT;
679
                }
680
                break;
681
            case DATE_TIME_FORMAT_SHORT_TIME_FIRST:
682
                $date_format = get_lang('dateTimeFormatShortTimeFirst', '', $language);
683
                if (INTL_INSTALLED) {
684
                    $datetype = IntlDateFormatter::FULL;
685
                    $timetype = IntlDateFormatter::SHORT;
686
                }
687
                break;
688
            case DATE_TIME_FORMAT_LONG_24H:
689
                $date_format = get_lang('dateTimeFormatLong24H', '', $language);
690
                if (INTL_INSTALLED) {
691
                    $datetype = IntlDateFormatter::FULL;
692
                    $timetype = IntlDateFormatter::SHORT;
693
                }
694
                break;
695
            default:
696
                $date_format = get_lang('dateTimeFormatLong', '', $language);
697
                if (INTL_INSTALLED) {
698
                    $datetype = IntlDateFormatter::FULL;
699
                    $timetype = IntlDateFormatter::SHORT;
700
                }
701
        }
702
    } else {
703
        $date_format = $format;
704
    }
705
706
    if (0) {
707
        //if using PHP 5.3 format dates like: $dateFormatShortNumber, can't be used
708
        //
709
        // Use ICU
710
        if (is_null($language)) {
711
            $language = api_get_language_isocode();
712
        }
713
        $date_formatter = new IntlDateFormatter($language, $datetype, $timetype, date_default_timezone_get());
714
        //$date_formatter->setPattern($date_format);
715
        $formatted_date = api_to_system_encoding($date_formatter->format($time), 'UTF-8');
716
    } else {
717
        // We replace %a %A %b %B masks of date format with translated strings
718
        $translated = &_api_get_day_month_names($language);
719
        $date_format = str_replace(
720
            ['%A', '%a', '%B', '%b'],
721
            [
722
                $translated['days_long'][(int) strftime('%w', $time)],
723
                $translated['days_short'][(int) strftime('%w', $time)],
724
                $translated['months_long'][(int) strftime('%m', $time) - 1],
725
                $translated['months_short'][(int) strftime('%m', $time) - 1],
726
            ],
727
            $date_format
728
        );
729
        $formatted_date = api_to_system_encoding(strftime($date_format, $time), 'UTF-8');
730
    }
731
    date_default_timezone_set($system_timezone);
732
733
    return $formatted_date;
734
}
735
736
/**
737
 * Returns the difference between the current date (date(now)) with the parameter
738
 * $date in a string format like "2 days ago, 1 hour ago".
739
 * You can use it like this:
740
 * Display::dateToStringAgoAndLongDate($dateInUtc);.
741
 *
742
 * @param string $date                 Result of a date function in this format -> date('Y-m-d H:i:s', time());
743
 * @param string $timeZone
744
 * @param bool   $returnDateDifference
745
 *
746
 * @return string
747
 *
748
 * @author Julio Montoya
749
 */
750
function date_to_str_ago($date, $timeZone = 'UTC', $returnDateDifference = false)
751
{
752
    if ($date === '0000-00-00 00:00:00') {
753
        return '';
754
    }
755
756
    $getOldTimezone = api_get_timezone();
757
    $isoCode = api_get_language_isocode();
758
    if ($isoCode == 'pt') {
759
        $isoCode = 'pt-BR';
760
    }
761
    $checkFile = api_get_path(SYS_PATH).'vendor/jimmiw/php-time-ago/translations/'.$isoCode.'.php';
762
    if (!file_exists($checkFile)) {
763
        $isoCode = 'en';
764
    }
765
    $timeAgo = new TimeAgo($timeZone, $isoCode);
766
    $value = $timeAgo->inWords($date);
767
768
    date_default_timezone_set($getOldTimezone);
769
770
    if ($returnDateDifference) {
771
        $value = $timeAgo->dateDifference($date);
772
    }
773
774
    return $value;
775
}
776
777
/**
778
 * Converts a date to the right timezone and localizes it in the format given as an argument.
779
 *
780
 * @param mixed The time to be converted
781
 * @param mixed Format to be used (TIME_NO_SEC_FORMAT, DATE_FORMAT_SHORT, DATE_FORMAT_LONG, DATE_TIME_FORMAT_LONG)
782
 * @param string Timezone to be converted from. If null, UTC will be assumed.
783
 *
784
 * @return string Converted and localized date
785
 *
786
 * @author Guillaume Viguier <[email protected]>
787
 */
788
function api_convert_and_format_date($time = null, $format = null, $from_timezone = null)
789
{
790
    // First, convert the datetime to the right timezone
791
    $time = api_get_local_time($time, null, $from_timezone, true);
792
    // Second, localize the date
793
    return api_format_date($time, $format);
794
}
795
796
/**
797
 * Returns an array of translated week days in short names.
798
 *
799
 * @param string $language (optional) Language id. If it is omitted, the current interface language is assumed.
800
 *
801
 * @return string Returns an array of week days (short names).
802
 *                Example: api_get_week_days_short('english') means array('Sun', 'Mon', ... 'Sat').
803
 *                Note: For all languges returned days are in the English order.
804
 */
805
function api_get_week_days_short($language = null)
806
{
807
    $days = &_api_get_day_month_names($language);
808
809
    return $days['days_short'];
810
}
811
812
/**
813
 * Returns an array of translated week days.
814
 *
815
 * @param string $language (optional) Language id. If it is omitted,
816
 *                         the current interface language is assumed.
817
 *
818
 * @return string Returns an array of week days.
819
 *                Example: api_get_week_days_long('english') means array('Sunday, 'Monday', ... 'Saturday').
820
 *                Note: For all languges returned days are in the English order.
821
 */
822
function api_get_week_days_long($language = null)
823
{
824
    $days = &_api_get_day_month_names($language);
825
826
    return $days['days_long'];
827
}
828
829
/**
830
 * Returns an array of translated months in short names.
831
 *
832
 * @param string $language (optional)    Language id.
833
 *                         If it is omitted, the current interface language is assumed.
834
 *
835
 * @return string Returns an array of months (short names).
836
 *                Example: api_get_months_short('english') means array('Jan', 'Feb', ... 'Dec').
837
 */
838
function api_get_months_short($language = null)
839
{
840
    $months = &_api_get_day_month_names($language);
841
842
    return $months['months_short'];
843
}
844
845
/**
846
 * Returns an array of translated months.
847
 *
848
 * @param string $language (optional)    Language id.
849
 *                         If it is omitted, the current interface language is assumed.
850
 *
851
 * @return string Returns an array of months.
852
 *                Example: api_get_months_long('english') means array('January, 'February' ... 'December').
853
 */
854
function api_get_months_long($language = null)
855
{
856
    $months = &_api_get_day_month_names($language);
857
858
    return $months['months_long'];
859
}
860
861
/**
862
 * Name order conventions.
863
 */
864
865
/**
866
 * Builds a person (full) name depending on the convention for a given language.
867
 *
868
 * @param string     $first_name the first name of the person
869
 * @param string     $last_name  the last name of the person
870
 * @param string     $title      the title of the person
871
 * @param int|string $format     (optional) The person name format.
872
 *                               It may be a pattern-string (for example '%t %l, %f' or '%T %F %L', ...) or
873
 *                               some of the constants
874
 *                               PERSON_NAME_COMMON_CONVENTION (default),
875
 *                               PERSON_NAME_WESTERN_ORDER,
876
 *                               PERSON_NAME_EASTERN_ORDER,
877
 *                               PERSON_NAME_LIBRARY_ORDER.
878
 * @param string     $language   (optional)
879
 *                               The language id. If it is omitted, the current interface language is assumed.
880
 *                               This parameter has meaning with the format PERSON_NAME_COMMON_CONVENTION only.
881
 * @param string     $username
882
 *
883
 * @return string The result is sort of full name of the person.
884
 *                Sample results:
885
 *                Peter Ustinoff or Dr. Peter Ustinoff     - the Western order
886
 *                Ustinoff Peter or Dr. Ustinoff Peter     - the Eastern order
887
 *                Ustinoff, Peter or - Dr. Ustinoff, Peter - the library order
888
 *                Note: See the file main/inc/lib/internationalization_database/name_order_conventions.php
889
 *                where you can check the convention for your language.
890
 *
891
 * @author Carlos Vargas <[email protected]> - initial implementation.
892
 * @author Ivan Tcholakov
893
 */
894
function api_get_person_name(
895
    $first_name,
896
    $last_name,
897
    $title = null,
898
    $format = null,
899
    $language = null,
900
    $username = null
901
) {
902
    static $valid = [];
903
    if (empty($format)) {
904
        $format = PERSON_NAME_COMMON_CONVENTION;
905
    }
906
    // We check if the language is supported, otherwise we check the
907
    // interface language of the parent language of sublanguage
908
    if (empty($language)) {
909
        // Do not set $setParentLanguageName because this function is called before
910
        // the main language is loaded in global.inc.php
911
        $language = api_get_interface_language(false, true, false);
912
    }
913
914
    if (!isset($valid[$format][$language])) {
915
        if (is_int($format)) {
916
            switch ($format) {
917
                case PERSON_NAME_COMMON_CONVENTION:
918
                    $valid[$format][$language] = _api_get_person_name_convention($language, 'format');
919
                    $usernameOrderFromDatabase = api_get_setting('user_name_order');
920
                    if (isset($usernameOrderFromDatabase) && !empty($usernameOrderFromDatabase)) {
921
                        $valid[$format][$language] = $usernameOrderFromDatabase;
922
                    }
923
                    break;
924
                case PERSON_NAME_WESTERN_ORDER:
925
                    $valid[$format][$language] = '%t %f %l';
926
                    break;
927
                case PERSON_NAME_EASTERN_ORDER:
928
                    $valid[$format][$language] = '%t %l %f';
929
                    break;
930
                case PERSON_NAME_LIBRARY_ORDER:
931
                    $valid[$format][$language] = '%t %l, %f';
932
                    break;
933
                default:
934
                    $valid[$format][$language] = '%t %f %l';
935
                    break;
936
            }
937
        } else {
938
            $valid[$format][$language] = _api_validate_person_name_format($format);
939
        }
940
    }
941
942
    $format = $valid[$format][$language];
943
944
    $keywords = [
945
        '%firstname',
946
        '%f',
947
        '%F',
948
        '%lastname',
949
        '%l',
950
        '%L',
951
        '%title',
952
        '%t',
953
        '%T',
954
        '%username',
955
        '%u',
956
        '%U',
957
    ];
958
959
    $values = [
960
        $first_name,
961
        $first_name,
962
        api_strtoupper($first_name),
963
        $last_name,
964
        $last_name,
965
        api_strtoupper($last_name),
966
        $title,
967
        $title,
968
        api_strtoupper($title),
969
        $username,
970
        $username,
971
        api_strtoupper($username),
972
    ];
973
    $person_name = str_replace($keywords, $values, $format);
974
975
    return _api_clean_person_name($person_name);
976
}
977
978
/**
979
 * Checks whether a given format represents person name in Western order (for which first name is first).
980
 *
981
 * @param int|string $format   (optional)    The person name format.
982
 *                             It may be a pattern-string (for example '%t. %l, %f') or some of the constants
983
 *                             PERSON_NAME_COMMON_CONVENTION (default),
984
 *                             PERSON_NAME_WESTERN_ORDER,
985
 *                             PERSON_NAME_EASTERN_ORDER,
986
 *                             PERSON_NAME_LIBRARY_ORDER.
987
 * @param string     $language (optional) The language id. If it is omitted,
988
 *                             the current interface language is assumed. This parameter has meaning with the
989
 *                             format PERSON_NAME_COMMON_CONVENTION only.
990
 *
991
 * @return bool The result TRUE means that the order is first_name last_name,
992
 *              FALSE means last_name first_name.
993
 *              Note: You may use this function for determining the order of the fields or
994
 *              columns "First name" and "Last name" in forms, tables and reports.
995
 *
996
 * @author Ivan Tcholakov
997
 */
998
function api_is_western_name_order($format = null, $language = null)
999
{
1000
    static $order = [];
1001
    if (empty($format)) {
1002
        $format = PERSON_NAME_COMMON_CONVENTION;
1003
    }
1004
1005
    if (empty($language)) {
1006
        $language = api_get_interface_language(false, true);
1007
    }
1008
    if (!isset($order[$format][$language])) {
1009
        $test_name = api_get_person_name('%f', '%l', '%t', $format, $language);
1010
        $order[$format][$language] = stripos($test_name, '%f') <= stripos($test_name, '%l');
1011
    }
1012
1013
    return $order[$format][$language];
1014
}
1015
1016
/**
1017
 * Returns a directive for sorting person names depending on a given language
1018
 * and based on the options in the internationalization "database".
1019
 *
1020
 * @param string $language (optional) The input language.
1021
 *                         If it is omitted, the current interface language is assumed.
1022
 *
1023
 * @return bool Returns boolean value. TRUE means ORDER BY first_name, last_name
1024
 *              FALSE means ORDER BY last_name, first_name.
1025
 *              Note: You may use this function:
1026
 *              2. for constructing the ORDER clause of SQL queries, related to first_name and last_name;
1027
 *              3. for adjusting php-implemented sorting in tables and reports.
1028
 *
1029
 * @author Ivan Tcholakov
1030
 */
1031
function api_sort_by_first_name($language = null)
1032
{
1033
    static $sort_by_first_name = [];
1034
1035
    if (empty($language)) {
1036
        $language = api_get_interface_language(false, true);
1037
    }
1038
    if (!isset($sort_by_first_name[$language])) {
1039
        $sort_by_first_name[$language] = _api_get_person_name_convention($language, 'sort_by');
1040
    }
1041
1042
    return $sort_by_first_name[$language];
1043
}
1044
1045
/**
1046
 * Return an array with the quarter dates.
1047
 * If no DateTime is not sent, use the current date.
1048
 *
1049
 * @param string|null $date (optional) The date or null.
1050
 *
1051
 * @return array E.G.: ['quarter_start' => '2022-10-11',
1052
 *               'quarter_end' => '2022-12-31',
1053
 *               'quarter_title' => 'Q4 2022']
1054
 */
1055
function getQuarterDates(string $date = null): array
1056
{
1057
    if (empty($date)) {
1058
        $date = api_get_utc_datetime();
1059
    }
1060
1061
    if (strlen($date > 10)) {
1062
        $date = substr($date, 0, 10);
1063
    }
1064
1065
    $month = substr($date, 5, 2);
1066
    $year = substr($date, 0, 4);
1067
1068
    switch ($month) {
1069
        case $month >= 1 && $month <= 3:
1070
            $start = "$year-01-01";
1071
            $end = "$year-03-31";
1072
            $quarter = 1;
1073
            break;
1074
        case $month >= 4 && $month <= 6:
1075
            $start = "$year-04-01";
1076
            $end = "$year-06-30";
1077
            $quarter = 2;
1078
            break;
1079
        case $month >= 7 && $month <= 9:
1080
            $start = "$year-07-01";
1081
            $end = "$year-09-30";
1082
            $quarter = 3;
1083
            break;
1084
        case $month >= 10 && $month <= 12:
1085
            $start = "$year-10-01";
1086
            $end = "$year-12-31";
1087
            $quarter = 4;
1088
            break;
1089
        default:
1090
            // Should never happen
1091
            $start = "$year-01-01";
1092
            $end = "$year-03-31";
1093
            $quarter = 1;
1094
            break;
1095
    }
1096
1097
    return [
1098
        'quarter_start' => $start,
1099
        'quarter_end' => $end,
1100
        'quarter_title' => sprintf(get_lang('QuarterXQY'), $quarter, $year),
1101
    ];
1102
}
1103
1104
/**
1105
 * Multibyte string conversion functions.
1106
 */
1107
1108
/**
1109
 * Converts character encoding of a given string.
1110
 *
1111
 * @param string $string        the string being converted
1112
 * @param string $to_encoding   the encoding that $string is being converted to
1113
 * @param string $from_encoding (optional)    The encoding that $string is being converted from.
1114
 *                              If it is omitted, the platform character set is assumed.
1115
 *
1116
 * @return string Returns the converted string.
1117
 *                This function is aimed at replacing the function mb_convert_encoding() for human-language strings.
1118
 *
1119
 * @see http://php.net/manual/en/function.mb-convert-encoding
1120
 */
1121
function api_convert_encoding($string, $to_encoding, $from_encoding = 'UTF-8')
1122
{
1123
    if (strtoupper($to_encoding) === strtoupper($from_encoding)) {
1124
        return $string;
1125
    }
1126
1127
    return mb_convert_encoding($string, $to_encoding, $from_encoding);
0 ignored issues
show
Bug Best Practice introduced by
The expression return mb_convert_encodi...coding, $from_encoding) also could return the type array which is incompatible with the documented return type string.
Loading history...
1128
}
1129
1130
/**
1131
 * Converts a given string into UTF-8 encoded string.
1132
 *
1133
 * @param string $string        the string being converted
1134
 * @param string $from_encoding (optional) The encoding that $string is being converted from.
1135
 *                              If it is omitted, the platform character set is assumed.
1136
 *
1137
 * @return string Returns the converted string.
1138
 *                This function is aimed at replacing the function utf8_encode() for human-language strings.
1139
 *
1140
 * @see http://php.net/manual/en/function.utf8-encode
1141
 */
1142
function api_utf8_encode($string, $from_encoding = 'UTF-8')
1143
{
1144
    return mb_convert_encoding($string, 'UTF-8', $from_encoding);
0 ignored issues
show
Bug Best Practice introduced by
The expression return mb_convert_encodi...UTF-8', $from_encoding) also could return the type array which is incompatible with the documented return type string.
Loading history...
1145
}
1146
1147
/**
1148
 * Converts a given string from UTF-8 encoding to a specified encoding.
1149
 *
1150
 * @param string $string      the string being converted
1151
 * @param string $to_encoding (optional)    The encoding that $string is being converted to.
1152
 *                            If it is omitted, the platform character set is assumed.
1153
 *
1154
 * @return string Returns the converted string.
1155
 *                This function is aimed at replacing the function utf8_decode() for human-language strings.
1156
 *
1157
 * @see http://php.net/manual/en/function.utf8-decode
1158
 */
1159
function api_utf8_decode($string, $to_encoding = 'UTF-8')
1160
{
1161
    return mb_convert_encoding($string, $to_encoding, 'UTF-8');
0 ignored issues
show
Bug Best Practice introduced by
The expression return mb_convert_encodi... $to_encoding, 'UTF-8') also could return the type array which is incompatible with the documented return type string.
Loading history...
1162
}
1163
1164
/**
1165
 * Converts a given string into the system encoding (or platform character set).
1166
 * When $from encoding is omitted on UTF-8 platforms then language dependent encoding
1167
 * is guessed/assumed. On non-UTF-8 platforms omitted $from encoding is assumed as UTF-8.
1168
 * When the parameter $check_utf8_validity is true the function checks string's
1169
 * UTF-8 validity and decides whether to try to convert it or not.
1170
 * This function is useful for problem detection or making workarounds.
1171
 *
1172
 * @param string $string              the string being converted
1173
 * @param string $from_encoding       (optional) The encoding that $string is being converted from.
1174
 *                                    It is guessed when it is omitted.
1175
 * @param bool   $check_utf8_validity (optional)    A flag for UTF-8 validity check as condition for making conversion
1176
 *
1177
 * @return string returns the converted string
1178
 */
1179
function api_to_system_encoding($string, $from_encoding = 'UTF-8', $check_utf8_validity = false)
1180
{
1181
    $system_encoding = api_get_system_encoding();
1182
1183
    return api_convert_encoding($string, $system_encoding, $from_encoding);
1184
}
1185
1186
/**
1187
 * Converts all applicable characters to HTML entities.
1188
 *
1189
 * @param string $string      the input string
1190
 * @param int    $quote_style (optional)    The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES
1191
 * @param string $encoding    (optional)    The encoding (of the input string) used in conversion.
1192
 *                            If it is omitted, the platform character set is assumed.
1193
 *
1194
 * @return string Returns the converted string.
1195
 *                This function is aimed at replacing the function htmlentities() for human-language strings.
1196
 *
1197
 * @see http://php.net/manual/en/function.htmlentities
1198
 */
1199
function api_htmlentities($string, $quote_style = ENT_COMPAT, $encoding = 'UTF-8')
1200
{
1201
    switch ($quote_style) {
1202
        case ENT_COMPAT:
1203
            $string = str_replace(['&', '"', '<', '>'], ['&amp;', '&quot;', '&lt;', '&gt;'], $string);
1204
            break;
1205
        case ENT_QUOTES:
1206
            $string = str_replace(['&', '\'', '"', '<', '>'], ['&amp;', '&#039;', '&quot;', '&lt;', '&gt;'], $string);
1207
            break;
1208
    }
1209
1210
    return mb_convert_encoding($string, 'HTML-ENTITIES', 'UTF-8');
0 ignored issues
show
Bug Best Practice introduced by
The expression return mb_convert_encodi...TML-ENTITIES', 'UTF-8') also could return the type array which is incompatible with the documented return type string.
Loading history...
1211
}
1212
1213
/**
1214
 * Converts HTML entities into normal characters.
1215
 *
1216
 * @param string $string      the input string
1217
 * @param int    $quote_style (optional)    The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES
1218
 * @param string $encoding    (optional)    The encoding (of the result) used in conversion.
1219
 *                            If it is omitted, the platform character set is assumed.
1220
 *
1221
 * @return string Returns the converted string.
1222
 *                This function is aimed at replacing the function html_entity_decode() for human-language strings.
1223
 *
1224
 * @see http://php.net/html_entity_decode
1225
 */
1226
function api_html_entity_decode($string, $quote_style = ENT_COMPAT, $encoding = 'UTF-8')
1227
{
1228
    if (empty($encoding)) {
1229
        $encoding = _api_mb_internal_encoding();
1230
    }
1231
    if (api_is_encoding_supported($encoding)) {
1232
        if (!api_is_utf8($encoding)) {
1233
            $string = api_utf8_encode($string, $encoding);
1234
        }
1235
        $string = html_entity_decode($string, $quote_style, 'UTF-8');
1236
        if (!api_is_utf8($encoding)) {
1237
            return api_utf8_decode($string, $encoding);
1238
        }
1239
1240
        return $string;
1241
    }
1242
1243
    return $string; // Here the function gives up.
1244
}
1245
1246
/**
1247
 * This function encodes (conditionally) a given string to UTF-8 if XmlHttp-request has been detected.
1248
 *
1249
 * @param string $string        the string being converted
1250
 * @param string $from_encoding (optional)    The encoding that $string is being converted from.
1251
 *                              If it is omitted, the platform character set is assumed.
1252
 *
1253
 * @return string returns the converted string
1254
 */
1255
function api_xml_http_response_encode($string, $from_encoding = 'UTF8')
1256
{
1257
    if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
1258
        if (empty($from_encoding)) {
1259
            $from_encoding = _api_mb_internal_encoding();
1260
        }
1261
        if (!api_is_utf8($from_encoding)) {
1262
            return api_utf8_encode($string, $from_encoding);
1263
        }
1264
    }
1265
1266
    return $string;
1267
}
1268
1269
/**
1270
 * Transliterates a string with arbitrary encoding into a plain ASCII string.
1271
 *
1272
 * Example:
1273
 * echo api_transliterate(api_html_entity_decode(
1274
 *    '&#1060;&#1105;&#1076;&#1086;&#1088; '.
1275
 *    '&#1052;&#1080;&#1093;&#1072;&#1081;&#1083;&#1086;&#1074;&#1080;&#1095; '.
1276
 *    '&#1044;&#1086;&#1089;&#1090;&#1086;&#1077;&#1074;&#1082;&#1080;&#1081;',
1277
 *    ENT_QUOTES, 'UTF-8'), 'X', 'UTF-8');
1278
 * The output should be: Fyodor Mihaylovich Dostoevkiy
1279
 *
1280
 * @param string $string        the input string
1281
 * @param string $unknown       (optional) Replacement character for unknown characters and illegal UTF-8 sequences
1282
 * @param string $from_encoding (optional) The encoding of the input string.
1283
 *                              If it is omitted, the platform character set is assumed.
1284
 *
1285
 * @return string plain ASCII output
1286
 */
1287
function api_transliterate($string, $unknown = '?', $from_encoding = null)
1288
{
1289
    return URLify::transliterate($string);
1290
}
1291
1292
/**
1293
 * Takes the first character in a string and returns its Unicode codepoint.
1294
 *
1295
 * @param string $character the input string
1296
 * @param string $encoding  (optional) The encoding of the input string.
1297
 *                          If it is omitted, the platform character set will be used by default.
1298
 *
1299
 * @return int Returns: the codepoint of the first character; or 0xFFFD (unknown character) when the input string is empty.
1300
 *             This is a multibyte aware version of the function ord().
1301
 *
1302
 * @see http://php.net/manual/en/function.ord.php
1303
 * Note the difference with the original funtion ord(): ord('') returns 0, api_ord('') returns 0xFFFD (unknown character).
1304
 */
1305
function api_ord($character, $encoding = 'UTF-8')
1306
{
1307
    return Utf8::ord(api_utf8_encode($character, $encoding));
1308
}
1309
1310
/**
1311
 * This function returns a string or an array with all occurrences of search
1312
 * in subject (ignoring case) replaced with the given replace value.
1313
 *
1314
 * @param mixed  $search   string or array of strings to be found
1315
 * @param mixed  $replace  string or array of strings used for replacement
1316
 * @param mixed  $subject  string or array of strings being searched
1317
 * @param int    $count    (optional) The number of matched and replaced needles
1318
 *                         will be returned in count, which is passed by reference
1319
 * @param string $encoding (optional) The used internally by this function character encoding.
1320
 *                         If it is omitted, the platform character set will be used by default.
1321
 *
1322
 * @return mixed String or array as a result.
1323
 *               Notes:
1324
 *               If $subject is an array, then the search and replace is performed with
1325
 *               every entry of subject, the return value is an array.
1326
 *               If $search and $replace are arrays, then the function takes a value from
1327
 *               each array and uses it to do search and replace on subject.
1328
 *               If $replace has fewer values than search, then an empty string is used for the rest of replacement values.
1329
 *               If $search is an array and $replace is a string, then this replacement string is used for every value of search.
1330
 *               This function is aimed at replacing the function str_ireplace() for human-language strings.
1331
 *
1332
 * @see http://php.net/manual/en/function.str-ireplace
1333
 *
1334
 * @author Henri Sivonen, mailto:[email protected]
1335
 *
1336
 * @see http://hsivonen.iki.fi/php-utf8/
1337
 * Adaptation for Chamilo 1.8.7, 2010
1338
 * Initial implementation Dokeos LMS, August 2009
1339
 *
1340
 * @author Ivan Tcholakov
1341
 */
1342
function api_str_ireplace($search, $replace, $subject, &$count = null, $encoding = null)
1343
{
1344
    return Utf8::str_ireplace($search, $replace, $subject, $count);
1345
}
1346
1347
/**
1348
 * Converts a string to an array.
1349
 *
1350
 * @param string $string       the input string
1351
 * @param int    $split_length maximum character-length of the chunk, one character by default
1352
 * @param string $encoding     (optional) The used internally by this function
1353
 *                             character encoding. If it is omitted, the platform character set will be used by default.
1354
 *
1355
 * @return array The result array of chunks with the spcified length.
1356
 *               Notes:
1357
 *               If the optional split_length parameter is specified, the returned array will be broken down into chunks
1358
 *               with each being split_length in length, otherwise each chunk will be one character in length.
1359
 *               FALSE is returned if split_length is less than 1.
1360
 *               If the split_length length exceeds the length of string, the entire string is returned as the first (and only) array element.
1361
 *               This function is aimed at replacing the function str_split() for human-language strings.
1362
 *
1363
 * @see http://php.net/str_split
1364
 */
1365
function api_str_split($string, $split_length = 1, $encoding = null)
1366
{
1367
    return Utf8::str_split($string, $split_length);
1368
}
1369
1370
/**
1371
 * Finds position of first occurrence of a string within another, case insensitive.
1372
 *
1373
 * @param string $haystack the string from which to get the position of the first occurrence
1374
 * @param string $needle   the string to be found
1375
 * @param int    $offset   The position in $haystack to start searching from.
1376
 *                         If it is omitted, searching starts from the beginning.
1377
 * @param string $encoding (optional) The used internally by this function
1378
 *                         character encoding. If it is omitted, the platform character set will be used by default.
1379
 *
1380
 * @return mixed Returns the numeric position of the first occurrence of
1381
 *               $needle in the $haystack, or FALSE if $needle is not found.
1382
 *               Note: The first character's position is 0, the second character position is 1, and so on.
1383
 *               This function is aimed at replacing the functions stripos() and mb_stripos() for human-language strings.
1384
 *
1385
 * @see http://php.net/manual/en/function.stripos
1386
 * @see http://php.net/manual/en/function.mb-stripos
1387
 */
1388
function api_stripos($haystack, $needle, $offset = 0, $encoding = null)
1389
{
1390
    return Utf8::stripos($haystack, $needle, $offset);
1391
}
1392
1393
/**
1394
 * Finds first occurrence of a string within another, case insensitive.
1395
 *
1396
 * @param string $haystack      the string from which to get the first occurrence
1397
 * @param mixed  $needle        the string to be found
1398
 * @param bool   $before_needle (optional) Determines which portion of $haystack
1399
 *                              this function returns. The default value is FALSE.
1400
 * @param string $encoding      (optional) The used internally by this function
1401
 *                              character encoding. If it is omitted, the platform character set will be used by default.
1402
 *
1403
 * @return mixed Returns the portion of $haystack, or FALSE if $needle is not found.
1404
 *               Notes:
1405
 *               If $needle is not a string, it is converted to an integer and applied as the
1406
 *               ordinal value (codepoint if the encoding is UTF-8) of a character.
1407
 *               If $before_needle is set to TRUE, the function returns all of $haystack
1408
 *               from the beginning to the first occurrence of $needle.
1409
 *               If $before_needle is set to FALSE, the function returns all of $haystack f
1410
 *               rom the first occurrence of $needle to the end.
1411
 *               This function is aimed at replacing the functions stristr() and mb_stristr() for human-language strings.
1412
 *
1413
 * @see http://php.net/manual/en/function.stristr
1414
 * @see http://php.net/manual/en/function.mb-stristr
1415
 */
1416
function api_stristr($haystack, $needle, $before_needle = false, $encoding = null)
1417
{
1418
    return Utf8::stristr($haystack, $needle, $before_needle);
1419
}
1420
1421
/**
1422
 * Returns length of the input string.
1423
 *
1424
 * @param string $string   the string which length is to be calculated
1425
 * @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.
1426
 *
1427
 * @return int Returns the number of characters within the string. A multi-byte character is counted as 1.
1428
 *             This function is aimed at replacing the functions strlen() and mb_strlen() for human-language strings.
1429
 *
1430
 * @see http://php.net/manual/en/function.strlen
1431
 * @see http://php.net/manual/en/function.mb-strlen
1432
 * Note: When you use strlen() to test for an empty string, you needn't change it to api_strlen().
1433
 * For example, in lines like the following:
1434
 * if (strlen($string) > 0)
1435
 * if (strlen($string) != 0)
1436
 * there is no need the original function strlen() to be changed, it works correctly and faster for these cases.
1437
 */
1438
function api_strlen($string, $encoding = null)
1439
{
1440
    return Utf8::strlen($string);
1441
}
1442
1443
/**
1444
 * Finds position of first occurrence of a string within another.
1445
 *
1446
 * @param string $haystack the string from which to get the position of the first occurrence
1447
 * @param string $needle   the string to be found
1448
 * @param int    $offset   (optional) The position in $haystack to start searching from. If it is omitted, searching starts from the beginning.
1449
 * @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.
1450
 *
1451
 * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
1452
 *               Note: The first character's position is 0, the second character position is 1, and so on.
1453
 *               This function is aimed at replacing the functions strpos() and mb_strpos() for human-language strings.
1454
 *
1455
 * @see http://php.net/manual/en/function.strpos
1456
 * @see http://php.net/manual/en/function.mb-strpos
1457
 */
1458
function api_strpos($haystack, $needle, $offset = 0, $encoding = null)
1459
{
1460
    return Utf8::strpos($haystack, $needle, $offset);
1461
}
1462
1463
/**
1464
 * Finds the last occurrence of a character in a string.
1465
 *
1466
 * @param string $haystack      the string from which to get the last occurrence
1467
 * @param mixed  $needle        the string which first character is to be found
1468
 * @param bool   $before_needle (optional) Determines which portion of $haystack this function returns. The default value is FALSE.
1469
 * @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.
1470
 *
1471
 * @return mixed Returns the portion of $haystack, or FALSE if the first character from $needle is not found.
1472
 *               Notes:
1473
 *               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.
1474
 *               If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence.
1475
 *               If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence to the end.
1476
 *               This function is aimed at replacing the functions strrchr() and mb_strrchr() for human-language strings.
1477
 *
1478
 * @see http://php.net/manual/en/function.strrchr
1479
 * @see http://php.net/manual/en/function.mb-strrchr
1480
 */
1481
function api_strrchr($haystack, $needle, $before_needle = false, $encoding = null)
1482
{
1483
    return Utf8::strrchr($haystack, $needle);
1484
}
1485
1486
/**
1487
 * Reverses a string.
1488
 *
1489
 * @param string $string   the string to be reversed
1490
 * @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.
1491
 *
1492
 * @return string Returns the reversed string.
1493
 *                This function is aimed at replacing the function strrev() for human-language strings.
1494
 *
1495
 * @see http://php.net/manual/en/function.strrev
1496
 */
1497
function api_strrev($string, $encoding = null)
1498
{
1499
    return Utf8::strrev($string);
1500
}
1501
1502
/**
1503
 * Finds the position of last occurrence (case insensitive) of a string in a string.
1504
 *
1505
 * @param string $haystack the string from which to get the position of the last occurrence
1506
 * @param string $needle   the string to be found
1507
 * @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.
1508
 * @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.
1509
 *
1510
 * @return mixed Returns the numeric position of the first occurrence (case insensitive) of $needle in the $haystack, or FALSE if $needle is not found.
1511
 *               Note: The first character's position is 0, the second character position is 1, and so on.
1512
 *               This function is aimed at replacing the functions strripos() and mb_strripos() for human-language strings.
1513
 *
1514
 * @see http://php.net/manual/en/function.strripos
1515
 * @see http://php.net/manual/en/function.mb-strripos
1516
 */
1517
function api_strripos($haystack, $needle, $offset = 0, $encoding = null)
1518
{
1519
    return Utf8::strripos($haystack, $needle, $offset);
1520
}
1521
1522
/**
1523
 * Finds the position of last occurrence of a string in a string.
1524
 *
1525
 * @param string $haystack the string from which to get the position of the last occurrence
1526
 * @param string $needle   the string to be found
1527
 * @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.
1528
 * @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.
1529
 *
1530
 * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
1531
 *               Note: The first character's position is 0, the second character position is 1, and so on.
1532
 *               This function is aimed at replacing the functions strrpos() and mb_strrpos() for human-language strings.
1533
 *
1534
 * @see http://php.net/manual/en/function.strrpos
1535
 * @see http://php.net/manual/en/function.mb-strrpos
1536
 */
1537
function api_strrpos($haystack, $needle, $offset = 0, $encoding = null)
1538
{
1539
    return Utf8::strrpos($haystack, $needle, $offset);
1540
}
1541
1542
/**
1543
 * Finds first occurrence of a string within another.
1544
 *
1545
 * @param string $haystack      the string from which to get the first occurrence
1546
 * @param mixed  $needle        the string to be found
1547
 * @param bool   $before_needle (optional) Determines which portion of $haystack this function returns. The default value is FALSE.
1548
 * @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.
1549
 *
1550
 * @return mixed Returns the portion of $haystack, or FALSE if $needle is not found.
1551
 *               Notes:
1552
 *               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.
1553
 *               If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence of $needle.
1554
 *               If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence of $needle to the end.
1555
 *               This function is aimed at replacing the functions strstr() and mb_strstr() for human-language strings.
1556
 *
1557
 * @see http://php.net/manual/en/function.strstr
1558
 * @see http://php.net/manual/en/function.mb-strstr
1559
 */
1560
function api_strstr($haystack, $needle, $before_needle = false, $encoding = null)
1561
{
1562
    return Utf8::strstr($haystack, $needle, $before_needle);
1563
}
1564
1565
/**
1566
 * Makes a string lowercase.
1567
 *
1568
 * @param string $string   the string being lowercased
1569
 * @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.
1570
 *
1571
 * @return string Returns the string with all alphabetic characters converted to lowercase.
1572
 *                This function is aimed at replacing the functions strtolower() and mb_strtolower() for human-language strings.
1573
 *
1574
 * @see http://php.net/manual/en/function.strtolower
1575
 * @see http://php.net/manual/en/function.mb-strtolower
1576
 */
1577
function api_strtolower($string, $encoding = null)
1578
{
1579
    return Utf8::strtolower($string);
1580
}
1581
1582
/**
1583
 * Makes a string uppercase.
1584
 *
1585
 * @param string $string   the string being uppercased
1586
 * @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.
1587
 *
1588
 * @return string Returns the string with all alphabetic characters converted to uppercase.
1589
 *                This function is aimed at replacing the functions strtoupper() and mb_strtoupper() for human-language strings.
1590
 *
1591
 * @see http://php.net/manual/en/function.strtoupper
1592
 * @see http://php.net/manual/en/function.mb-strtoupper
1593
 */
1594
function api_strtoupper($string, $encoding = null)
1595
{
1596
    return Utf8::strtoupper($string);
1597
}
1598
1599
/**
1600
 * // Gets part of a string.
1601
 *
1602
 * @param string $string   the input string
1603
 * @param int    $start    the first position from which the extracted part begins
1604
 * @param int    $length   the length in character of the extracted part
1605
 * @param string $encoding (optional) The used internally by this function
1606
 *                         character encoding. If it is omitted, the platform character set will be used by default.
1607
 *
1608
 * @return string Returns the part of the string specified by the start and length parameters.
1609
 *                Note: First character's position is 0. Second character position is 1, and so on.
1610
 *                This function is aimed at replacing the functions substr() and mb_substr() for human-language strings.
1611
 *
1612
 * @see http://php.net/manual/en/function.substr
1613
 * @see http://php.net/manual/en/function.mb-substr
1614
 */
1615
function api_substr($string, $start, $length = null, $encoding = null)
1616
{
1617
    if (is_null($length)) {
1618
        $length = api_strlen($string, $encoding);
1619
    }
1620
1621
    return Utf8::substr($string, $start, $length);
1622
}
1623
1624
/**
1625
 * Counts the number of substring occurrences.
1626
 *
1627
 * @param string $haystack the string being checked
1628
 * @param string $needle   the string being found
1629
 * @param string $encoding (optional) The used internally by this function character encoding.
1630
 *                         If it is omitted, the platform character set will be used by default.
1631
 *
1632
 * @return int the number of times the needle substring occurs in the haystack string
1633
 *
1634
 * @see http://php.net/manual/en/function.mb-substr-count.php
1635
 */
1636
function api_substr_count($haystack, $needle, $encoding = null)
1637
{
1638
    return Utf8::substr_count($haystack, $needle);
1639
}
1640
1641
/**
1642
 * Replaces text within a portion of a string.
1643
 *
1644
 * @param string $string      the input string
1645
 * @param string $replacement the replacement string
1646
 * @param int    $start       The position from which replacing will begin.
1647
 *                            Notes:
1648
 *                            If $start is positive, the replacing will begin at the $start'th offset into the string.
1649
 *                            If $start is negative, the replacing will begin at the $start'th character from the end of the string.
1650
 * @param int    $length      (optional) The position where replacing will end.
1651
 *                            Notes:
1652
 *                            If given and is positive, it represents the length of the portion of the string which is to be replaced.
1653
 *                            If it is negative, it represents the number of characters from the end of string at which to stop replacing.
1654
 *                            If it is not given, then it will default to api_strlen($string); i.e. end the replacing at the end of string.
1655
 *                            If $length is zero, then this function will have the effect of inserting replacement into the string at the given start offset.
1656
 * @param string $encoding    (optional)    The used internally by this function character encoding.
1657
 *                            If it is omitted, the platform character set will be used by default.
1658
 *
1659
 * @return string The result string is returned.
1660
 *                This function is aimed at replacing the function substr_replace() for human-language strings.
1661
 *
1662
 * @see http://php.net/manual/function.substr-replace
1663
 */
1664
function api_substr_replace($string, $replacement, $start, $length = null, $encoding = null)
1665
{
1666
    if (is_null($length)) {
1667
        $length = api_strlen($string);
1668
    }
1669
1670
    return UTf8::substr_replace($string, $replacement, $start, $length);
1671
}
1672
1673
/**
1674
 * Makes a string's first character uppercase.
1675
 *
1676
 * @param string $string   the input string
1677
 * @param string $encoding (optional)    The used internally by this function character encoding.
1678
 *                         If it is omitted, the platform character set will be used by default.
1679
 *
1680
 * @return string Returns a string with the first character capitalized, if that character is alphabetic.
1681
 *                This function is aimed at replacing the function ucfirst() for human-language strings.
1682
 *
1683
 * @see http://php.net/manual/en/function.ucfirst
1684
 */
1685
function api_ucfirst($string, $encoding = null)
1686
{
1687
    return Utf8::ucfirst($string);
1688
}
1689
1690
/**
1691
 * Uppercases the first character of each word in a string.
1692
 *
1693
 * @param string $string   the input string
1694
 * @param string $encoding (optional) The used internally by this function character encoding.
1695
 *                         If it is omitted, the platform character set will be used by default.
1696
 *
1697
 * @return string Returns the modified string.
1698
 *                This function is aimed at replacing the function ucwords() for human-language strings.
1699
 *
1700
 * @see http://php.net/manual/en/function.ucwords
1701
 */
1702
function api_ucwords($string, $encoding = null)
1703
{
1704
    return Utf8::ucwords($string);
1705
}
1706
1707
/**
1708
 * Performs a regular expression match, UTF-8 aware when it is applicable.
1709
 *
1710
 * @param string $pattern  the pattern to search for, as a string
1711
 * @param string $subject  the input string
1712
 * @param array  &$matches (optional) If matches is provided,
1713
 *                         then it is filled with the results of search (as an array).
1714
 *                         $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.
1715
 * @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.
1716
 *                         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.
1717
 * @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.
1718
 * @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.
1719
 *
1720
 * @return int|bool returns the number of times pattern matches or FALSE if an error occurred
1721
 *
1722
 * @see http://php.net/preg_match
1723
 */
1724
function api_preg_match(
1725
    $pattern,
1726
    $subject,
1727
    &$matches = null,
1728
    $flags = 0,
1729
    $offset = 0,
1730
    $encoding = null
1731
) {
1732
    if (empty($encoding)) {
1733
        $encoding = _api_mb_internal_encoding();
1734
    }
1735
1736
    return preg_match(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $matches, $flags, $offset);
1737
}
1738
1739
/**
1740
 * Performs a global regular expression match, UTF-8 aware when it is applicable.
1741
 *
1742
 * @param string $pattern  the pattern to search for, as a string
1743
 * @param string $subject  the input string
1744
 * @param array  &$matches (optional)    Array of all matches in multi-dimensional array ordered according to $flags
1745
 * @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):
1746
 *                         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;
1747
 *                         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;
1748
 *                         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
1749
 *                         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.
1750
 *                         If no order flag is given, PREG_PATTERN_ORDER is assumed.
1751
 * @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.
1752
 * @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.
1753
 *
1754
 * @return int|bool returns the number of full pattern matches (which might be zero), or FALSE if an error occurred
1755
 *
1756
 * @see http://php.net/preg_match_all
1757
 */
1758
function api_preg_match_all($pattern, $subject, &$matches, $flags = PREG_PATTERN_ORDER, $offset = 0, $encoding = null)
1759
{
1760
    if (empty($encoding)) {
1761
        $encoding = _api_mb_internal_encoding();
1762
    }
1763
    if (is_null($flags)) {
1764
        $flags = PREG_PATTERN_ORDER;
1765
    }
1766
1767
    return preg_match_all(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $matches, $flags, $offset);
1768
}
1769
1770
/**
1771
 * Performs a regular expression search and replace, UTF-8 aware when it is applicable.
1772
 *
1773
 * @param string|array $pattern     The pattern to search for. It can be either a string or an array with strings.
1774
 * @param string|array $replacement the string or an array with strings to replace
1775
 * @param string|array $subject     the string or an array with strings to search and replace
1776
 * @param int          $limit       The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit).
1777
 * @param int          &$count      If specified, this variable will be filled with the number of replacements done
1778
 * @param string       $encoding    (optional)	The used internally by this function character encoding.
1779
 *                                  If it is omitted, the platform character set will be used by default.
1780
 *
1781
 * @return array|string|null returns an array if the subject parameter is an array, or a string otherwise.
1782
 *                           If matches are found, the new subject will be returned, otherwise subject will be returned unchanged or NULL if an error occurred.
1783
 *
1784
 * @see http://php.net/preg_replace
1785
 */
1786
function api_preg_replace($pattern, $replacement, $subject, $limit = -1, &$count = 0, $encoding = null)
1787
{
1788
    if (empty($encoding)) {
1789
        $encoding = _api_mb_internal_encoding();
1790
    }
1791
    $is_utf8 = api_is_utf8($encoding);
1792
    if (is_array($pattern)) {
1793
        foreach ($pattern as &$p) {
1794
            $p = $is_utf8 ? $p.'u' : $p;
1795
        }
1796
    } else {
1797
        $pattern = $is_utf8 ? $pattern.'u' : $pattern;
1798
    }
1799
1800
    return preg_replace($pattern, $replacement, $subject, $limit, $count);
1801
}
1802
1803
/**
1804
 * Splits a string by a regular expression, UTF-8 aware when it is applicable.
1805
 *
1806
 * @param string $pattern  the pattern to search for, as a string
1807
 * @param string $subject  the input string
1808
 * @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.
1809
 * @param int    $flags    (optional)			$flags can be any combination of the following flags (combined with bitwise | operator):
1810
 *                         PREG_SPLIT_NO_EMPTY - if this flag is set, only non-empty pieces will be returned;
1811
 *                         PREG_SPLIT_DELIM_CAPTURE - if this flag is set, parenthesized expression in the delimiter pattern will be captured and returned as well;
1812
 *                         PREG_SPLIT_OFFSET_CAPTURE - If this flag is set, for every occurring match the appendant string offset will also be returned.
1813
 *                         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.
1814
 * @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.
1815
 *
1816
 * @return array returns an array containing substrings of $subject split along boundaries matched by $pattern
1817
 *
1818
 * @see http://php.net/preg_split
1819
 */
1820
function api_preg_split($pattern, $subject, $limit = -1, $flags = 0, $encoding = null)
1821
{
1822
    if (empty($encoding)) {
1823
        $encoding = _api_mb_internal_encoding();
1824
    }
1825
1826
    return preg_split(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $limit, $flags);
1827
}
1828
1829
/**
1830
 * String comparison.
1831
 */
1832
1833
/**
1834
 * Performs string comparison, case insensitive, language sensitive, with extended multibyte support.
1835
 *
1836
 * @param string $string1  the first string
1837
 * @param string $string2  the second string
1838
 * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1839
 * @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.
1840
 *
1841
 * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
1842
 *             This function is aimed at replacing the function strcasecmp() for human-language strings.
1843
 *
1844
 * @see http://php.net/manual/en/function.strcasecmp
1845
 */
1846
function api_strcasecmp($string1, $string2, $language = null, $encoding = null)
1847
{
1848
    return api_strcmp(api_strtolower($string1, $encoding), api_strtolower($string2, $encoding), $language, $encoding);
1849
}
1850
1851
/**
1852
 * Performs string comparison, case sensitive, language sensitive, with extended multibyte support.
1853
 *
1854
 * @param string $string1  the first string
1855
 * @param string $string2  the second string
1856
 * @param string $language (optional)	The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1857
 * @param string $encoding (optional)	The used internally by this function character encoding.
1858
 *                         If it is omitted, the platform character set will be used by default.
1859
 *
1860
 * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
1861
 *             This function is aimed at replacing the function strcmp() for human-language strings.
1862
 *
1863
 * @see http://php.net/manual/en/function.strcmp.php
1864
 * @see http://php.net/manual/en/collator.compare.php
1865
 */
1866
function api_strcmp($string1, $string2, $language = null, $encoding = null)
1867
{
1868
    return strcmp($string1, $string2);
1869
}
1870
1871
/**
1872
 * Performs string comparison in so called "natural order", case sensitive, language sensitive, with extended multibyte support.
1873
 *
1874
 * @param string $string1  the first string
1875
 * @param string $string2  the second string
1876
 * @param string $language (optional)	The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1877
 * @param string $encoding (optional)	The used internally by this function character encoding.
1878
 *                         If it is omitted, the platform character set will be used by default.
1879
 *
1880
 * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
1881
 *             This function is aimed at replacing the function strnatcmp() for human-language strings.
1882
 *
1883
 * @see http://php.net/manual/en/function.strnatcmp.php
1884
 * @see http://php.net/manual/en/collator.compare.php
1885
 */
1886
function api_strnatcmp($string1, $string2, $language = null, $encoding = null)
1887
{
1888
    return strnatcmp($string1, $string2);
1889
}
1890
1891
/**
1892
 * Sorting arrays.
1893
 */
1894
1895
/**
1896
 * Sorts an array using natural order algorithm.
1897
 *
1898
 * @param array  $array    the input array
1899
 * @param string $language (optional)	The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1900
 * @param string $encoding (optional)	The used internally by this function character encoding.
1901
 *                         If it is omitted, the platform character set will be used by default.
1902
 *
1903
 * @return bool Returns TRUE on success, FALSE on error.
1904
 *              This function is aimed at replacing the function natsort() for sorting human-language strings.
1905
 *
1906
 * @see http://php.net/manual/en/function.natsort.php
1907
 */
1908
function api_natsort(&$array, $language = null, $encoding = null)
1909
{
1910
    return natsort($array);
1911
}
1912
1913
/**
1914
 * Sorts an array using natural order algorithm in reverse order.
1915
 *
1916
 * @param array  $array    the input array
1917
 * @param string $language (optional)	The language in which comparison is to be made. If language is omitted, interface language is assumed then.
1918
 * @param string $encoding (optional)	The used internally by this function character encoding.
1919
 *                         If it is omitted, the platform character set will be used by default.
1920
 *
1921
 * @return bool returns TRUE on success, FALSE on error
1922
 */
1923
function api_natrsort(&$array, $language = null, $encoding = null)
1924
{
1925
    return uasort($array, '_api_strnatrcmp');
1926
}
1927
1928
/**
1929
 * Encoding management functions.
1930
 */
1931
1932
/**
1933
 * This function unifies the encoding identificators, so they could be compared.
1934
 *
1935
 * @param string|array $encoding the specified encoding
1936
 *
1937
 * @return string returns the encoding identificator modified in suitable for comparison way
1938
 */
1939
function api_refine_encoding_id($encoding)
1940
{
1941
    if (is_array($encoding)) {
1942
        return array_map('api_refine_encoding_id', $encoding);
1943
    }
1944
1945
    return strtoupper(str_replace('_', '-', $encoding));
1946
}
1947
1948
/**
1949
 * This function checks whether two $encoding are equal (same, equvalent).
1950
 *
1951
 * @param string|array $encoding1 The first encoding
1952
 * @param string|array $encoding2 The second encoding
1953
 * @param bool         $strict    When this parameter is TRUE the comparison ignores aliases of encodings.
1954
 *                                When the parameter is FALSE, aliases are taken into account.
1955
 *
1956
 * @return bool returns TRUE if the encodings are equal, FALSE otherwise
1957
 */
1958
function api_equal_encodings($encoding1, $encoding2, $strict = false)
1959
{
1960
    static $equal_encodings = [];
1961
    if (is_array($encoding1)) {
1962
        foreach ($encoding1 as $encoding) {
1963
            if (api_equal_encodings($encoding, $encoding2, $strict)) {
1964
                return true;
1965
            }
1966
        }
1967
1968
        return false;
1969
    } elseif (is_array($encoding2)) {
1970
        foreach ($encoding2 as $encoding) {
1971
            if (api_equal_encodings($encoding1, $encoding, $strict)) {
1972
                return true;
1973
            }
1974
        }
1975
1976
        return false;
1977
    }
1978
    if (!isset($equal_encodings[$encoding1][$encoding2][$strict])) {
1979
        $encoding_1 = api_refine_encoding_id($encoding1);
1980
        $encoding_2 = api_refine_encoding_id($encoding2);
1981
        if ($encoding_1 == $encoding_2) {
1982
            $result = true;
1983
        } else {
1984
            if ($strict) {
1985
                $result = false;
1986
            } else {
1987
                $alias1 = _api_get_character_map_name($encoding_1);
1988
                $alias2 = _api_get_character_map_name($encoding_2);
1989
                $result = !empty($alias1) && !empty($alias2) && $alias1 == $alias2;
1990
            }
1991
        }
1992
        $equal_encodings[$encoding1][$encoding2][$strict] = $result;
1993
    }
1994
1995
    return $equal_encodings[$encoding1][$encoding2][$strict];
1996
}
1997
1998
/**
1999
 * This function checks whether a given encoding is UTF-8.
2000
 *
2001
 * @param string $encoding the tested encoding
2002
 *
2003
 * @return bool returns TRUE if the given encoding id means UTF-8, otherwise returns false
2004
 */
2005
function api_is_utf8($encoding)
2006
{
2007
    static $result = [];
2008
    if (!isset($result[$encoding])) {
2009
        $result[$encoding] = api_equal_encodings($encoding, 'UTF-8');
2010
    }
2011
2012
    return $result[$encoding];
2013
}
2014
2015
/**
2016
 * This function returns the encoding, currently used by the system.
2017
 *
2018
 * @return string The system's encoding.
2019
 *                Note: The value of api_get_setting('platform_charset') is tried to be returned first,
2020
 *                on the second place the global variable $charset is tried to be returned. If for some
2021
 *                reason both attempts fail, then the libraly's internal value will be returned.
2022
 */
2023
function api_get_system_encoding()
2024
{
2025
    return 'UTF-8';
2026
}
2027
2028
/**
2029
 * Checks whether a specified encoding is supported by this API.
2030
 *
2031
 * @param string $encoding the specified encoding
2032
 *
2033
 * @return bool returns TRUE when the specified encoding is supported, FALSE othewise
2034
 */
2035
function api_is_encoding_supported($encoding)
2036
{
2037
    static $supported = [];
2038
    if (!isset($supported[$encoding])) {
2039
        $supported[$encoding] = _api_mb_supports($encoding) || _api_iconv_supports($encoding) || _api_convert_encoding_supports($encoding);
2040
    }
2041
2042
    return $supported[$encoding];
2043
}
2044
2045
/**
2046
 * Detects encoding of plain text.
2047
 *
2048
 * @param string $string   the input text
2049
 * @param string $language (optional) The language of the input text, provided if it is known
2050
 *
2051
 * @return string returns the detected encoding
2052
 */
2053
function api_detect_encoding($string, $language = null)
2054
{
2055
    // Testing against valid UTF-8 first.
2056
    if (api_is_valid_utf8($string)) {
2057
        return 'UTF-8';
2058
    }
2059
2060
    return mb_detect_encoding($string);
2061
}
2062
2063
/**
2064
 * String validation functions concerning certain encodings.
2065
 */
2066
2067
/**
2068
 * Checks a string for UTF-8 validity.
2069
 *
2070
 * @param string $string
2071
 *
2072
 * @return string
2073
 */
2074
function api_is_valid_utf8($string)
2075
{
2076
    return Utf8::isUtf8($string);
2077
}
2078
2079
/**
2080
 * Checks whether a string contains 7-bit ASCII characters only.
2081
 *
2082
 * @param string $string the string to be tested/validated
2083
 *
2084
 * @return bool returns TRUE when the tested string contains 7-bit
2085
 *              ASCII characters only, FALSE othewise
2086
 */
2087
function api_is_valid_ascii(&$string)
2088
{
2089
    return mb_detect_encoding($string, 'ASCII', true) == 'ASCII' ? true : false;
2090
}
2091
2092
/**
2093
 * Return true a date is valid.
2094
 *
2095
 * @param string $date   example: 2014-06-30 13:05:05
2096
 * @param string $format example: "Y-m-d H:i:s"
2097
 *
2098
 * @return bool
2099
 */
2100
function api_is_valid_date($date, $format = 'Y-m-d H:i:s')
2101
{
2102
    $d = DateTime::createFromFormat($format, $date);
2103
2104
    return $d && $d->format($format) == $date;
2105
}
2106
2107
/**
2108
 * Returns the variable translated.
2109
 *
2110
 * @param string $variable   the string to translate
2111
 * @param string $pluginName the Plugin name
2112
 *
2113
 * @return string the variable translated
2114
 */
2115
function get_plugin_lang($variable, $pluginName)
2116
{
2117
    $plugin = $pluginName::create();
2118
2119
    return $plugin->get_lang($variable);
2120
}
2121
2122
/**
2123
 * Returns an array of translated week days and months, short and normal names.
2124
 *
2125
 * @param string $language (optional Language id. If it is omitted,
2126
 *                         the current interface language is assumed.
2127
 *
2128
 * @return array returns a multidimensional array with translated week days and months
2129
 */
2130
function &_api_get_day_month_names($language = null)
2131
{
2132
    static $date_parts = [];
2133
    if (empty($language)) {
2134
        $language = api_get_interface_language();
2135
    }
2136
    if (!isset($date_parts[$language])) {
2137
        $week_day = [
2138
            'Sunday',
2139
            'Monday',
2140
            'Tuesday',
2141
            'Wednesday',
2142
            'Thursday',
2143
            'Friday',
2144
            'Saturday',
2145
        ];
2146
        $month = [
2147
            'January',
2148
            'February',
2149
            'March',
2150
            'April',
2151
            'May',
2152
            'June',
2153
            'July',
2154
            'August',
2155
            'September',
2156
            'October',
2157
            'November',
2158
            'December',
2159
        ];
2160
        for ($i = 0; $i < 7; $i++) {
2161
            $date_parts[$language]['days_short'][] = get_lang(
2162
                $week_day[$i].'Short',
2163
                '',
2164
                $language
2165
            );
2166
            $date_parts[$language]['days_long'][] = get_lang(
2167
                $week_day[$i].'Long',
2168
                '',
2169
                $language
2170
            );
2171
        }
2172
        for ($i = 0; $i < 12; $i++) {
2173
            $date_parts[$language]['months_short'][] = get_lang(
2174
                $month[$i].'Short',
2175
                '',
2176
                $language
2177
            );
2178
            $date_parts[$language]['months_long'][] = get_lang(
2179
                $month[$i].'Long',
2180
                '',
2181
                $language
2182
            );
2183
        }
2184
    }
2185
2186
    return $date_parts[$language];
2187
}
2188
2189
/**
2190
 * Returns returns person name convention for a given language.
2191
 *
2192
 * @param string $language the input language
2193
 * @param string $type     The type of the requested convention.
2194
 *                         It may be 'format' for name order convention or 'sort_by' for name sorting convention.
2195
 *
2196
 * @return mixed Depending of the requested type,
2197
 *               the returned result may be string or boolean; null is returned on error;
2198
 */
2199
function _api_get_person_name_convention($language, $type)
2200
{
2201
    static $conventions;
2202
    $language = api_purify_language_id($language);
2203
    if (!isset($conventions)) {
2204
        $file = __DIR__.'/internationalization_database/name_order_conventions.php';
2205
        if (file_exists($file)) {
2206
            $conventions = include $file;
2207
        } else {
2208
            $conventions = [
2209
                'english' => [
2210
                    'format' => 'title first_name last_name',
2211
                    'sort_by' => 'first_name',
2212
                ],
2213
            ];
2214
        }
2215
        // Overwrite classic conventions
2216
        $customConventions = api_get_configuration_value('name_order_conventions');
2217
2218
        if (!empty($customConventions)) {
2219
            foreach ($customConventions as $key => $data) {
2220
                $conventions[$key] = $data;
2221
            }
2222
        }
2223
2224
        $search1 = ['FIRST_NAME', 'LAST_NAME', 'TITLE'];
2225
        $replacement1 = ['%F', '%L', '%T'];
2226
        $search2 = ['first_name', 'last_name', 'title'];
2227
        $replacement2 = ['%f', '%l', '%t'];
2228
        foreach (array_keys($conventions) as $key) {
2229
            $conventions[$key]['format'] = str_replace($search1, $replacement1, $conventions[$key]['format']);
2230
            $conventions[$key]['format'] = _api_validate_person_name_format(
2231
                _api_clean_person_name(
2232
                    str_replace(
2233
                        '%',
2234
                        ' %',
2235
                        str_ireplace(
2236
                            $search2,
2237
                            $replacement2,
2238
                            $conventions[$key]['format']
2239
                        )
2240
                    )
2241
                )
2242
            );
2243
            $conventions[$key]['sort_by'] = strtolower($conventions[$key]['sort_by']) != 'last_name' ? true : false;
2244
        }
2245
    }
2246
    switch ($type) {
2247
        case 'format':
2248
            return is_string($conventions[$language]['format']) ? $conventions[$language]['format'] : '%t %f %l';
2249
        case 'sort_by':
2250
            return is_bool($conventions[$language]['sort_by']) ? $conventions[$language]['sort_by'] : true;
2251
    }
2252
2253
    return null;
2254
}
2255
2256
/**
2257
 * Replaces non-valid formats for person names with the default (English) format.
2258
 *
2259
 * @param string $format the input format to be verified
2260
 *
2261
 * @return bool returns the same format if is is valid, otherwise returns a valid English format
2262
 */
2263
function _api_validate_person_name_format($format)
2264
{
2265
    if (empty($format) || stripos($format, '%f') === false || stripos($format, '%l') === false) {
2266
        return '%t %f %l';
2267
    }
2268
2269
    return $format;
2270
}
2271
2272
/**
2273
 * Removes leading, trailing and duplicate whitespace and/or commas in a full person name.
2274
 * Cleaning is needed for the cases when not all parts of the name are available
2275
 * or when the name is constructed using a "dirty" pattern.
2276
 *
2277
 * @param string $person_name the input person name
2278
 *
2279
 * @return string returns cleaned person name
2280
 */
2281
function _api_clean_person_name($person_name)
2282
{
2283
    return preg_replace(['/\s+/', '/, ,/', '/,+/', '/^[ ,]/', '/[ ,]$/'], [' ', ', ', ',', '', ''], $person_name);
2284
}
2285
2286
/**
2287
 * Appendix to "Multibyte string conversion functions".
2288
 */
2289
2290
/**
2291
 * This is a php-implementation of a function that is similar to mb_convert_encoding() from mbstring extension.
2292
 * The function converts a given string from one to another character encoding.
2293
 *
2294
 * @param string $string        the string being converted
2295
 * @param string $to_encoding   the encoding that $string is being converted to
2296
 * @param string $from_encoding the encoding that $string is being converted from
2297
 *
2298
 * @return string returns the converted string
2299
 */
2300
function _api_convert_encoding(&$string, $to_encoding, $from_encoding)
2301
{
2302
    return mb_convert_encoding($string, $to_encoding, $from_encoding);
0 ignored issues
show
Bug Best Practice introduced by
The expression return mb_convert_encodi...coding, $from_encoding) also could return the type array which is incompatible with the documented return type string.
Loading history...
2303
}
2304
2305
/**
2306
 * This function determines the name of corresponding to a given encoding conversion table.
2307
 * It is able to deal with some aliases of the encoding.
2308
 *
2309
 * @param string $encoding the given encoding identificator, for example 'WINDOWS-1252'
2310
 *
2311
 * @return string returns the name of the corresponding conversion table, for the same example - 'CP1252'
2312
 */
2313
function _api_get_character_map_name($encoding)
2314
{
2315
    static $character_map_selector;
2316
    if (!isset($character_map_selector)) {
2317
        $file = __DIR__.'/internationalization_database/conversion/character_map_selector.php';
2318
        if (file_exists($file)) {
2319
            $character_map_selector = include $file;
2320
        } else {
2321
            $character_map_selector = [];
2322
        }
2323
    }
2324
2325
    return isset($character_map_selector[$encoding]) ? $character_map_selector[$encoding] : '';
2326
}
2327
2328
/**
2329
 * Appendix to "String comparison".
2330
 */
2331
2332
/**
2333
 * A reverse function from php-core function strnatcmp(),
2334
 * performs string comparison in reverse natural (alpha-numerical) order.
2335
 *
2336
 * @param string $string1 the first string
2337
 * @param string $string2 the second string
2338
 *
2339
 * @return int returns 0 if $string1 = $string2; >0 if $string1 < $string2; <0 if $string1 > $string2
2340
 */
2341
function _api_strnatrcmp($string1, $string2)
2342
{
2343
    return strnatcmp($string2, $string1);
2344
}
2345
2346
/**
2347
 * Sets/Gets internal character encoding of the common string functions within the PHP mbstring extension.
2348
 *
2349
 * @param string $encoding (optional)    When this parameter is given, the function sets the internal encoding
2350
 *
2351
 * @return string When $encoding parameter is not given, the function returns the internal encoding.
2352
 *                Note: This function is used in the global initialization script for setting the
2353
 *                internal encoding to the platform's character set.
2354
 *
2355
 * @see http://php.net/manual/en/function.mb-internal-encoding
2356
 */
2357
function _api_mb_internal_encoding($encoding = 'UTF-8')
2358
{
2359
    return mb_internal_encoding($encoding);
0 ignored issues
show
Bug Best Practice introduced by
The expression return mb_internal_encoding($encoding) also could return the type true which is incompatible with the documented return type string.
Loading history...
2360
}
2361
2362
/**
2363
 * Checks whether the specified encoding is supported by the PHP mbstring extension.
2364
 *
2365
 * @param string $encoding the specified encoding
2366
 *
2367
 * @return bool returns TRUE when the specified encoding is supported, FALSE othewise
2368
 */
2369
function _api_mb_supports($encoding)
2370
{
2371
    static $supported = [];
2372
    if (!isset($supported[$encoding])) {
2373
        if (MBSTRING_INSTALLED) {
2374
            $supported[$encoding] = api_equal_encodings($encoding, mb_list_encodings(), true);
2375
        } else {
2376
            $supported[$encoding] = false;
2377
        }
2378
    }
2379
2380
    return $supported[$encoding];
2381
}
2382
2383
/**
2384
 * Checks whether the specified encoding is supported by the PHP iconv extension.
2385
 *
2386
 * @param string $encoding the specified encoding
2387
 *
2388
 * @return bool returns TRUE when the specified encoding is supported, FALSE othewise
2389
 */
2390
function _api_iconv_supports($encoding)
2391
{
2392
    static $supported = [];
2393
    if (!isset($supported[$encoding])) {
2394
        if (ICONV_INSTALLED) {
2395
            $enc = api_refine_encoding_id($encoding);
2396
            if ($enc != 'HTML-ENTITIES') {
2397
                $test_string = '';
2398
                for ($i = 32; $i < 128; $i++) {
2399
                    $test_string .= chr($i);
2400
                }
2401
                $supported[$encoding] = (@iconv_strlen($test_string, $enc)) ? true : false;
2402
            } else {
2403
                $supported[$encoding] = false;
2404
            }
2405
        } else {
2406
            $supported[$encoding] = false;
2407
        }
2408
    }
2409
2410
    return $supported[$encoding];
2411
}
2412
2413
// This function checks whether the function _api_convert_encoding() (the php-
2414
// implementation) is able to convert from/to a given encoding.
2415
function _api_convert_encoding_supports($encoding)
2416
{
2417
    static $supports = [];
2418
    if (!isset($supports[$encoding])) {
2419
        $supports[$encoding] = _api_get_character_map_name(api_refine_encoding_id($encoding)) != '';
2420
    }
2421
2422
    return $supports[$encoding];
2423
}
2424
2425
/**
2426
 * Given a date object, return a human or ISO format, with or without h:m:s.
2427
 *
2428
 * @param object $date      The Date object
2429
 * @param bool   $showTime  Whether to show the time and date (true) or only the date (false)
2430
 * @param bool   $humanForm Whether to show day-month-year (true) or year-month-day (false)
2431
 *
2432
 * @return string Formatted date
2433
 */
2434
function api_get_human_date_time($date, $showTime = true, $humanForm = false)
2435
{
2436
    if ($showTime) {
2437
        if ($humanForm) {
2438
            return $date->format('j M Y H:i:s');
2439
        } else {
2440
            return $date->format('Y-m-d H:i:s');
2441
        }
2442
    } else {
2443
        if ($humanForm) {
2444
            return $date->format('j M Y');
2445
        } else {
2446
            return $date->format('Y-m-d');
2447
        }
2448
    }
2449
}
2450
2451
/**
2452
 * @param string $language
2453
 *
2454
 * @return array
2455
 */
2456
function api_get_language_files_to_load($language)
2457
{
2458
    $parent_path = SubLanguageManager::get_parent_language_path($language);
2459
    $langPath = api_get_path(SYS_LANG_PATH);
2460
2461
    $languagesToLoad = [
2462
        $langPath.'english/trad4all.inc.php', // include English always
2463
    ];
2464
2465
    if (!empty($parent_path)) { // if the sub-language feature is on
2466
        // prepare string for current language and its parent
2467
        $lang_file = $langPath.$language.'/trad4all.inc.php';
2468
        $parent_lang_file = $langPath.$parent_path.'/trad4all.inc.php';
2469
        // load the parent language file first
2470
        if (file_exists($parent_lang_file)) {
2471
            $languagesToLoad[] = $parent_lang_file;
2472
        }
2473
        // overwrite the parent language translations if there is a child
2474
        if (file_exists($lang_file)) {
2475
            $languagesToLoad[] = $lang_file;
2476
        }
2477
    } else {
2478
        // if the sub-languages feature is not on, then just load the
2479
        // set language interface
2480
        // prepare string for current language
2481
        $langFile = $langPath.$language.'/trad4all.inc.php';
2482
2483
        if (file_exists($langFile)) {
2484
            $languagesToLoad[] = $langFile;
2485
        }
2486
2487
        // Check if language/custom.php exists
2488
        $customLanguage = $langPath.$language.'/custom.php';
2489
2490
        if (file_exists($customLanguage)) {
2491
            $languagesToLoad[] = $customLanguage;
2492
        }
2493
    }
2494
2495
    return $languagesToLoad;
2496
}
2497