Passed
Pull Request — master (#6042)
by Yannick
08:29
created

getQuarterDates()   B

Complexity

Conditions 11
Paths 20

Size

Total Lines 42
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 36
nc 20
nop 1
dl 0
loc 42
rs 7.3166
c 1
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\DataFixtures\LanguageFixtures;
6
use Chamilo\CoreBundle\Framework\Container;
7
use ChamiloSession as Session;
8
use Westsworld\TimeAgo;
9
10
/**
11
 * File: internationalization.lib.php
12
 * Internationalization library for Chamilo 1.x LMS
13
 * A library implementing internationalization related functions.
14
 * License: GNU General Public License Version 3 (Free Software Foundation)ww.
15
 *
16
 * @author Ivan Tcholakov, <[email protected]>, 2009, 2010
17
 * @author More authors, mentioned in the correpsonding fragments of this source.
18
 */
19
// Predefined date formats in Chamilo provided by the language sub-system.
20
// To be used as a parameter for the function api_format_date()
21
define('TIME_NO_SEC_FORMAT', 0); // 15:23
22
define('DATE_FORMAT_SHORT', 1); // Aug 25, 09
23
define('DATE_FORMAT_LONG', 2); // Monday August 25, 09
24
define('DATE_FORMAT_LONG_NO_DAY', 10); // August 25, 2009
25
define('DATE_TIME_FORMAT_LONG', 3); // Monday August 25, 2009 at 03:28 PM
26
27
define('DATE_FORMAT_NUMBER', 4); // 25.08.09
28
define('DATE_TIME_FORMAT_LONG_24H', 5); // August 25, 2009 at 15:28
29
define('DATE_TIME_FORMAT_SHORT', 6); // Aug 25, 2009 at 03:28 PM
30
define('DATE_TIME_FORMAT_SHORT_TIME_FIRST', 7); // 03:28 PM, Aug 25 2009
31
define('DATE_FORMAT_NUMBER_NO_YEAR', 8); // 25.08 dd-mm
32
define('DATE_FORMAT_ONLY_DAYNAME', 9); // Monday, Sunday, etc
33
34
// Formatting person's name.
35
// Formatting a person's name using the pattern as it has been
36
// configured in the internationalization database for every language.
37
// This (default) option would be the most used.
38
define('PERSON_NAME_COMMON_CONVENTION', 0);
39
// The following options may be used in limited number of places for overriding the common convention:
40
41
// Formatting a person's name in Western order: first_name last_name
42
define('PERSON_NAME_WESTERN_ORDER', 1);
43
// Formatting a person's name in Eastern order: last_name first_name
44
define('PERSON_NAME_EASTERN_ORDER', 2);
45
// Contextual: formatting person's name in library order: last_name, first_name
46
define('PERSON_NAME_LIBRARY_ORDER', 3);
47
// Contextual: formatting a person's name assotiated with an email-address.
48
// Ivan: I am not sure how seems email servers an clients would interpret name order, so I assign the Western order.
49
define('PERSON_NAME_EMAIL_ADDRESS', PERSON_NAME_WESTERN_ORDER);
50
// Contextual: formatting a person's name for data-exporting operations.
51
// For backward compatibility this format has been set to Eastern order.
52
define('PERSON_NAME_DATA_EXPORT', PERSON_NAME_EASTERN_ORDER);
53
54
/**
55
 * Returns a translated (localized) string.
56
 */
57
function get_lang(string $variable, ?string $locale = null): string
58
{
59
    $translator = Container::$translator ?: Container::$container->get('translator');
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

59
    $translator = Container::$translator ?: Container::$container->/** @scrutinizer ignore-call */ get('translator');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
60
61
    if (!$translator) {
62
        return $variable;
63
    }
64
65
    // Using symfony
66
    $defaultDomain = 'messages';
67
68
    // Check for locale fallbacks (in case no translation is available).
69
    static $fallbacks = null;
70
    $englishInQueue = (!empty($locale) && $locale === 'en_US');
71
    if ($fallbacks === null) {
72
        if (!empty($locale)) {
73
            while (!empty($parent = SubLanguageManager::getParentLocale($locale))) {
74
                $fallbacks[] = $parent;
75
                if ($parent === 'en_US') {
76
                    $englishInQueue = true;
77
                }
78
            }
79
        }
80
        // If there were no parent language, still consider en_US as global fallback
81
        if (!$englishInQueue) {
82
            $fallbacks[] = 'en_US';
83
        }
84
    }
85
    // Test a basic translation to the current language.
86
    $translation = $translator->trans(
87
        $variable,
88
        [],
89
        $defaultDomain,
90
        $locale
91
    );
92
    // If no translation was found, $translation is empty.
93
    // Check fallbacks for a valid translation.
94
    $i = 0;
95
    while (empty($translation) && !empty($fallbacks[$i])) {
96
        $fallback = $fallbacks[$i];
97
        $translation = $translator->trans(
98
            $variable,
99
            [],
100
            $defaultDomain,
101
            $fallback
102
        );
103
        $i++;
104
    }
105
106
    return $translation;
107
}
108
109
/**
110
 * Gets language iso code.
111
 */
112
function api_get_language_isocode()
113
{
114
    $request = Container::getRequest();
115
116
    if ($request) {
117
        return $request->getLocale();
118
    }
119
120
    return 'en_US';
121
}
122
123
/**
124
 * Gets language iso code column from the language table.
125
 *
126
 * @return array An array with the current isocodes
127
 *
128
 * */
129
function api_get_platform_isocodes()
130
{
131
    $list = [];
132
    $sql = "SELECT isocode
133
            FROM ".Database::get_main_table(TABLE_MAIN_LANGUAGE)."
134
            ORDER BY isocode ";
135
    $result = Database::query($sql);
136
    if (Database::num_rows($result)) {
137
        while ($row = Database::fetch_array($result)) {
138
            $list[] = trim($row['isocode']);
139
        }
140
    }
141
142
    return $list;
143
}
144
145
/**
146
 * Gets text direction according to the given language.
147
 *
148
 * @param string $iso Iso code example en_US, fr_FR, If $language is omitted, interface language is assumed then.
149
 *
150
 * @return string the correspondent to the language text direction ('ltr' or 'rtl')
151
 */
152
function api_get_text_direction($iso = '')
153
{
154
    $languages = LanguageFixtures::getLanguages();
155
    $rightDirection = array_column($languages, 'direction', 'isocode');
156
157
    if (empty($iso)) {
158
        $iso = api_get_language_isocode();
159
    }
160
161
    if (isset($rightDirection[$iso])) {
162
        return 'rtl';
163
    }
164
165
    return 'ltr';
166
}
167
168
/**
169
 * Returns an alphabetized list of timezones in an associative array
170
 * that can be used to populate a select.
171
 *
172
 * @return array List of timezone identifiers
173
 *
174
 * @author Guillaume Viguier <[email protected]>
175
 */
176
function api_get_timezones()
177
{
178
    $timezone_identifiers = DateTimeZone::listIdentifiers();
179
    sort($timezone_identifiers);
180
    $out = [];
181
    foreach ($timezone_identifiers as $tz) {
182
        $out[$tz] = $tz;
183
    }
184
    $null_option = ['' => ''];
185
186
    return array_merge($null_option, $out);
187
}
188
189
/**
190
 * Returns the timezone to be converted to/from, based on user or admin preferences.
191
 *
192
 * @return string The timezone chosen
193
 */
194
function api_get_timezone()
195
{
196
    $timezone = Session::read('system_timezone');
197
    if (empty($timezone)) {
198
        // First, get the default timezone of the server
199
        $timezone = date_default_timezone_get();
200
        // Second, see if a timezone has been chosen for the platform
201
        $timezoneFromSettings = api_get_setting('platform.timezone', false, 'timezones');
202
203
        if (null != $timezoneFromSettings) {
204
            $timezone = $timezoneFromSettings;
205
        }
206
207
        // If allowed by the administrator
208
        $allowUserTimezones = api_get_setting('profile.use_users_timezone', false, 'timezones');
209
        $userId = api_get_user_id();
210
211
        if ('true' === $allowUserTimezones && !empty($userId)) {
212
            // Get the timezone based on user preference, if it exists
213
            $newExtraField = new ExtraFieldValue('user');
214
            $data = $newExtraField->get_values_by_handler_and_field_variable($userId, 'timezone');
215
            if (!empty($data) && isset($data['timezone']) && !empty($data['timezone'])) {
216
                $timezone = $data['timezone'];
217
            }
218
        }
219
        Session::write('system_timezone', $timezone);
220
    }
221
222
    return $timezone;
223
}
224
225
/**
226
 * Returns the given date as a DATETIME in UTC timezone.
227
 * This function should be used before entering any date in the DB.
228
 *
229
 * @param mixed $time                    Date to be converted (can be a string supported by date() or a timestamp)
230
 * @param bool  $returnNullIfInvalidDate If the date is not correct return null instead of the current date
231
 * @param bool  $returnObj               Returns a DateTime object
232
 *
233
 * @return string|DateTime The DATETIME in UTC to be inserted in the DB,
234
 *                         or null if the format of the argument is not supported
235
 *
236
 * @author Julio Montoya - Adding the 2nd parameter
237
 * @author Guillaume Viguier <[email protected]>
238
 */
239
function api_get_utc_datetime($time = null, $returnNullIfInvalidDate = false, $returnObj = false)
240
{
241
    if (is_null($time) || empty($time) || '0000-00-00 00:00:00' === $time) {
242
        if ($returnNullIfInvalidDate) {
243
            return null;
244
        }
245
        if ($returnObj) {
246
            return new DateTime(gmdate('Y-m-d H:i:s'), new DateTimeZone('UTC'));
247
        }
248
249
        return gmdate('Y-m-d H:i:s');
250
    }
251
252
    // If time is a timestamp, return directly in utc
253
    if (is_numeric($time)) {
254
        $time = (int) $time;
255
        $time = gmdate('Y-m-d H:i:s', $time);
256
        if ($returnObj) {
257
            return new DateTime($time, new DateTimeZone('UTC'));
258
        }
259
260
        return $time;
261
    }
262
    try {
263
        $fromTimezone = api_get_timezone();
264
        $date = new DateTime($time, new DateTimezone($fromTimezone));
265
        $date->setTimezone(new DateTimeZone('UTC'));
266
        if ($returnObj) {
267
            return $date;
268
        } else {
269
            return $date->format('Y-m-d H:i:s');
270
        }
271
    } catch (Exception $e) {
272
        return null;
273
    }
274
}
275
276
/**
277
 * Returns a DATETIME string converted to the right timezone.
278
 *
279
 * @param mixed  $time                    The time to be converted
280
 * @param string $to_timezone             The timezone to be converted to.
281
 *                                        If null, the timezone will be determined based on user preference,
282
 *                                        or timezone chosen by the admin for the platform.
283
 * @param string $from_timezone           The timezone to be converted from. If null, UTC will be assumed.
284
 * @param bool   $returnNullIfInvalidDate
285
 * @param bool   $showTime
286
 * @param bool   $humanForm
287
 * @param string $format
288
 *
289
 * @return string The converted time formatted as Y-m-d H:i:s
290
 *
291
 * @author Guillaume Viguier <[email protected]>
292
 */
293
function api_get_local_time(
294
    $time = null,
295
    $to_timezone = null,
296
    $from_timezone = null,
297
    $returnNullIfInvalidDate = false,
298
    $showTime = true,
299
    $humanForm = false,
300
    $format = ''
301
) {
302
    // Determining the timezone to be converted from
303
    if (is_null($from_timezone)) {
304
        $from_timezone = 'UTC';
305
    }
306
307
    // If time is a timestamp, convert it to a string
308
    if (is_null($time) || empty($time) || '0000-00-00 00:00:00' == $time) {
309
        if ($returnNullIfInvalidDate) {
310
            return null;
311
        }
312
        $from_timezone = 'UTC';
313
        $time = gmdate('Y-m-d H:i:s');
314
    }
315
316
    if (is_numeric($time)) {
317
        $time = (int) $time;
318
        if ($returnNullIfInvalidDate) {
319
            if (strtotime(date('d-m-Y H:i:s', $time)) !== $time) {
320
                return null;
321
            }
322
        }
323
324
        $from_timezone = 'UTC';
325
        $time = gmdate('Y-m-d H:i:s', $time);
326
    }
327
328
    if ($time instanceof DateTime) {
329
        $time = $time->format('Y-m-d H:i:s');
330
        $from_timezone = 'UTC';
331
    }
332
333
    try {
334
        // Determining the timezone to be converted to
335
        if (is_null($to_timezone)) {
336
            $to_timezone = api_get_timezone();
337
        }
338
339
        $date = new DateTime($time, new DateTimezone($from_timezone));
340
        $date->setTimezone(new DateTimeZone($to_timezone));
341
342
        if (!empty($format)) {
343
            return $date->format($format);
344
        }
345
346
        return api_get_human_date_time($date, $showTime, $humanForm);
347
    } catch (Exception $e) {
348
        return '';
349
    }
350
}
351
352
/**
353
 * Converts a string into a timestamp safely (handling timezones), using strtotime.
354
 *
355
 * @param string $time     to be converted
356
 * @param string $timezone (if null, the timezone will be determined based
357
 *                         on user preference, or timezone chosen by the admin for the platform)
358
 *
359
 * @return int Timestamp
360
 *
361
 * @author Guillaume Viguier <[email protected]>
362
 */
363
function api_strtotime($time, $timezone = null)
364
{
365
    $system_timezone = date_default_timezone_get();
366
    if (!empty($timezone)) {
367
        date_default_timezone_set($timezone);
368
    }
369
    $timestamp = strtotime($time);
370
    if (!empty($timezone)) {
371
        // only reset timezone if it was changed
372
        date_default_timezone_set($system_timezone);
373
    }
374
375
    return $timestamp;
376
}
377
378
/**
379
 * Returns formatted date/time, correspondent to a given language.
380
 * The given date should be in the timezone chosen by the administrator
381
 * and/or user. Use api_get_local_time to get it.
382
 *
383
 * @param mixed $time Timestamp or datetime string
384
 * @param string|int Date format (see date formats in the Chamilo system:
385
 *                         TIME_NO_SEC_FORMAT,
386
 *                         DATE_FORMAT_SHORT,
387
 *                         DATE_FORMAT_LONG,
388
 *                         DATE_TIME_FORMAT_LONG
389
 * @param string $language (optional) Language id
390
 *                         If it is omitted, the current interface language is assumed
391
 *
392
 * @return string returns the formatted date
393
 *
394
 * @author Patrick Cool <[email protected]>, Ghent University
395
 * @author Christophe Gesche<[email protected]>
396
 *         originally inspired from from PhpMyAdmin
397
 * @author Ivan Tcholakov, 2009, code refactoring, adding support for predefined date/time formats.
398
 * @author Guillaume Viguier <[email protected]>
399
 *
400
 * @see    http://php.net/manual/en/function.strftime.php
401
 */
402
function api_format_date($time, $format = null, $language = null)
403
{
404
    if (empty($time)) {
405
        return '';
406
    }
407
408
    $system_timezone = date_default_timezone_get();
409
    date_default_timezone_set(api_get_timezone());
410
411
    if (is_string($time)) {
412
        $time = strtotime($time);
413
    }
414
415
    if (is_null($format)) {
416
        $format = DATE_TIME_FORMAT_LONG;
417
    }
418
    if ($time instanceof DateTime) {
419
        $time = $time->format('Y-m-d H:i:s');
420
    }
421
422
    $datetype = null;
423
    $timetype = null;
424
425
    if (is_int($format)) {
426
        switch ($format) {
427
            case DATE_FORMAT_ONLY_DAYNAME:
428
                $datetype = IntlDateFormatter::SHORT;
429
                $timetype = IntlDateFormatter::NONE;
430
431
                break;
432
            case DATE_FORMAT_NUMBER_NO_YEAR:
433
                $datetype = IntlDateFormatter::SHORT;
434
                $timetype = IntlDateFormatter::NONE;
435
436
                break;
437
            case DATE_FORMAT_NUMBER:
438
                $datetype = IntlDateFormatter::SHORT;
439
                $timetype = IntlDateFormatter::NONE;
440
441
                break;
442
            case TIME_NO_SEC_FORMAT:
443
                $datetype = IntlDateFormatter::NONE;
444
                $timetype = IntlDateFormatter::SHORT;
445
446
                break;
447
            case DATE_FORMAT_SHORT:
448
                $datetype = IntlDateFormatter::LONG;
449
                $timetype = IntlDateFormatter::NONE;
450
451
                break;
452
            case DATE_FORMAT_LONG:
453
                $datetype = IntlDateFormatter::FULL;
454
                $timetype = IntlDateFormatter::NONE;
455
456
                break;
457
            case DATE_TIME_FORMAT_LONG:
458
                $datetype = IntlDateFormatter::FULL;
459
                $timetype = IntlDateFormatter::SHORT;
460
461
                break;
462
            case DATE_FORMAT_LONG_NO_DAY:
463
                $datetype = IntlDateFormatter::FULL;
464
                $timetype = IntlDateFormatter::SHORT;
465
466
                break;
467
            case DATE_TIME_FORMAT_SHORT:
468
                $datetype = IntlDateFormatter::FULL;
469
                $timetype = IntlDateFormatter::SHORT;
470
471
                break;
472
            case DATE_TIME_FORMAT_SHORT_TIME_FIRST:
473
                $datetype = IntlDateFormatter::FULL;
474
                $timetype = IntlDateFormatter::SHORT;
475
476
                break;
477
            case DATE_TIME_FORMAT_LONG_24H:
478
                $datetype = IntlDateFormatter::FULL;
479
                $timetype = IntlDateFormatter::SHORT;
480
481
                break;
482
            default:
483
                $datetype = IntlDateFormatter::FULL;
484
                $timetype = IntlDateFormatter::SHORT;
485
        }
486
    }
487
488
    // Use ICU
489
    if (is_null($language)) {
490
        $language = api_get_language_isocode();
491
    }
492
    $date_formatter = new IntlDateFormatter($language, $datetype, $timetype, date_default_timezone_get());
493
    $formatted_date = api_to_system_encoding($date_formatter->format($time), 'UTF-8');
494
495
    date_default_timezone_set($system_timezone);
496
497
    return $formatted_date;
498
}
499
500
/**
501
 * Returns the difference between the current date (date(now)) with the parameter
502
 * $date in a string format like "2 days ago, 1 hour ago".
503
 * You can use it like this:
504
 * Display::dateToStringAgoAndLongDate($dateInUtc);.
505
 *
506
 * @param string|DateTime $date                 Result of a date function in this format -> date('Y-m-d H:i:s', time());
507
 * @param string          $timeZone
508
 * @param bool            $returnDateDifference
509
 *
510
 * @return string
511
 *
512
 * @author Julio Montoya
513
 */
514
function date_to_str_ago($date, $timeZone = 'UTC', $returnDateDifference = false)
515
{
516
    if ('0000-00-00 00:00:00' === $date) {
517
        return '';
518
    }
519
520
    $getOldTimezone = api_get_timezone();
521
    $isoCode = api_get_language_isocode();
522
    if ('pt' === $isoCode) {
523
        $isoCode = 'pt-BR';
524
    }
525
    if ('fr_FR' === $isoCode) {
526
        $isoCode = 'Fr';
527
    }
528
    $isoCode = ucfirst($isoCode);
529
    $class = "Westsworld\TimeAgo\Translations\\".$isoCode;
530
    if (class_exists($class)) {
531
        $language = new $class();
532
    } else {
533
        $language = new Westsworld\TimeAgo\Translations\En();
534
    }
535
536
    $timeAgo = new TimeAgo($language);
537
    if (!($date instanceof DateTime)) {
538
        $date = api_get_utc_datetime($date, null, true);
539
    }
540
541
    $value = $timeAgo->inWords($date);
542
    date_default_timezone_set($getOldTimezone);
543
544
    if ($returnDateDifference) {
545
        $now = new DateTime('now', $date->getTimezone());
546
        $value = $date->diff($now);
547
548
        return [
549
            'years' => $value->y,
550
            'months' => $value->m,
551
            'days' => $value->d,
552
            'hours' => $value->h,
553
            'minutes' => $value->i,
554
            'seconds' => $value->s,
555
        ];
556
    }
557
558
    return $value;
559
}
560
561
/**
562
 * Converts a date to the right timezone and localizes it in the format given as an argument.
563
 *
564
 * @param mixed The time to be converted
565
 * @param mixed Format to be used (TIME_NO_SEC_FORMAT, DATE_FORMAT_SHORT, DATE_FORMAT_LONG, DATE_TIME_FORMAT_LONG)
566
 * @param string Timezone to be converted from. If null, UTC will be assumed.
567
 *
568
 * @return string Converted and localized date
569
 *
570
 * @author Guillaume Viguier <[email protected]>
571
 */
572
function api_convert_and_format_date($time = null, $format = null, $from_timezone = null)
573
{
574
    // First, convert the datetime to the right timezone
575
    $time = api_get_local_time($time, null, $from_timezone, true);
576
577
    // Second, localize the date
578
    return api_format_date($time, $format);
579
}
580
581
/**
582
 * Returns an array of translated week days in short names.
583
 *
584
 * @param string $language (optional) Language id. If it is omitted, the current interface language is assumed.
585
 *
586
 * @return string Returns an array of week days (short names).
587
 *                Example: api_get_week_days_short('english') means array('Sun', 'Mon', ... 'Sat').
588
 *                Note: For all languges returned days are in the English order.
589
 */
590
function api_get_week_days_short($language = null)
591
{
592
    $days = &_api_get_day_month_names($language);
593
594
    return $days['days_short'];
595
}
596
597
/**
598
 * Returns an array of translated week days.
599
 *
600
 * @param string $language (optional) Language id. If it is omitted,
601
 *                         the current interface language is assumed.
602
 *
603
 * @return string Returns an array of week days.
604
 *                Example: api_get_week_days_long('english') means array('Sunday, 'Monday', ... 'Saturday').
605
 *                Note: For all languges returned days are in the English order.
606
 */
607
function api_get_week_days_long($language = null)
608
{
609
    $days = &_api_get_day_month_names($language);
610
611
    return $days['days_long'];
612
}
613
614
/**
615
 * Returns an array of translated months in short names.
616
 *
617
 * @param string $language (optional)    Language id.
618
 *                         If it is omitted, the current interface language is assumed.
619
 *
620
 * @return string Returns an array of months (short names).
621
 *                Example: api_get_months_short('english') means array('Jan', 'Feb', ... 'Dec').
622
 */
623
function api_get_months_short($language = null)
624
{
625
    $months = &_api_get_day_month_names($language);
626
627
    return $months['months_short'];
628
}
629
630
/**
631
 * Returns an array of translated months.
632
 *
633
 * @param string $language (optional)    Language id.
634
 *                         If it is omitted, the current interface language is assumed.
635
 *
636
 * @return string Returns an array of months.
637
 *                Example: api_get_months_long('english') means array('January, 'February' ... 'December').
638
 */
639
function api_get_months_long($language = null)
640
{
641
    $months = &_api_get_day_month_names($language);
642
643
    return $months['months_long'];
644
}
645
646
/**
647
 * Name order conventions.
648
 */
649
650
/**
651
 * Builds a person (full) name depending on the convention for a given language.
652
 *
653
 * @param string     $first_name the first name of the person
654
 * @param string     $last_name  the last name of the person
655
 * @param string     $title      the title of the person
656
 * @param int|string $format     (optional) The person name format.
657
 *                               It may be a pattern-string (for example '%t %l, %f' or '%T %F %L', ...) or
658
 *                               some of the constants
659
 *                               PERSON_NAME_COMMON_CONVENTION (default),
660
 *                               PERSON_NAME_WESTERN_ORDER,
661
 *                               PERSON_NAME_EASTERN_ORDER,
662
 *                               PERSON_NAME_LIBRARY_ORDER.
663
 * @param string     $language   (optional)
664
 *                               The language id. If it is omitted, the current interface language is assumed.
665
 *                               This parameter has meaning with the format PERSON_NAME_COMMON_CONVENTION only.
666
 * @param string     $username
667
 *
668
 * @return string The result is sort of full name of the person.
669
 *                Sample results:
670
 *                Peter Ustinoff or Dr. Peter Ustinoff     - the Western order
671
 *                Ustinoff Peter or Dr. Ustinoff Peter     - the Eastern order
672
 *                Ustinoff, Peter or - Dr. Ustinoff, Peter - the library order
673
 *                Note: See the file main/inc/lib/internationalization_database/name_order_conventions.php
674
 *                where you can check the convention for your language.
675
 *
676
 * @author Carlos Vargas <[email protected]> - initial implementation.
677
 * @author Ivan Tcholakov
678
 */
679
function api_get_person_name(
680
    $first_name,
681
    $last_name,
682
    $title = null,
683
    $format = null,
684
    $language = null,
685
    $username = null
686
) {
687
    static $valid = [];
688
    if (empty($format)) {
689
        $format = PERSON_NAME_COMMON_CONVENTION;
690
    }
691
    // We check if the language is supported, otherwise we check the
692
    // interface language of the parent language of sublanguage
693
    if (empty($language)) {
694
        // Do not set $setParentLanguageName because this function is called before
695
        // the main language is loaded in global.inc.php
696
        $language = api_get_language_isocode();
697
    }
698
    if (!isset($valid[$format][$language])) {
699
        if (is_int($format)) {
700
            switch ($format) {
701
                case PERSON_NAME_COMMON_CONVENTION:
702
                    $valid[$format][$language] = _api_get_person_name_convention($language, 'format');
703
                    break;
704
                case PERSON_NAME_WESTERN_ORDER:
705
                    $valid[$format][$language] = '%t %f %l';
706
                    break;
707
                case PERSON_NAME_EASTERN_ORDER:
708
                    $valid[$format][$language] = '%t %l %f';
709
                    break;
710
                case PERSON_NAME_LIBRARY_ORDER:
711
                    $valid[$format][$language] = '%t %l, %f';
712
                    break;
713
                default:
714
                    $valid[$format][$language] = '%t %f %l';
715
                    break;
716
            }
717
        } else {
718
            $valid[$format][$language] = _api_validate_person_name_format($format);
719
        }
720
    }
721
722
    $format = $valid[$format][$language];
723
724
    $keywords = [
725
        '%firstname',
726
        '%f',
727
        '%F',
728
        '%lastname',
729
        '%l',
730
        '%L',
731
        '%title',
732
        '%t',
733
        '%T',
734
        '%username',
735
        '%u',
736
        '%U',
737
    ];
738
739
    $values = [
740
        $first_name,
741
        $first_name,
742
        api_strtoupper($first_name),
743
        $last_name,
744
        $last_name,
745
        api_strtoupper($last_name),
746
        $title,
747
        $title,
748
        api_strtoupper($title),
749
        $username,
750
        $username,
751
        api_strtoupper($username),
752
    ];
753
    $person_name = str_replace($keywords, $values, $format);
754
755
    return _api_clean_person_name($person_name);
756
}
757
758
/**
759
 * Checks whether a given format represents person name in Western order (for which first name is first).
760
 *
761
 * @param int|string $format   (optional)    The person name format.
762
 *                             It may be a pattern-string (for example '%t. %l, %f') or some of the constants
763
 *                             PERSON_NAME_COMMON_CONVENTION (default),
764
 *                             PERSON_NAME_WESTERN_ORDER,
765
 *                             PERSON_NAME_EASTERN_ORDER,
766
 *                             PERSON_NAME_LIBRARY_ORDER.
767
 * @param string     $language (optional) The language id. If it is omitted,
768
 *                             the current interface language is assumed. This parameter has meaning with the
769
 *                             format PERSON_NAME_COMMON_CONVENTION only.
770
 *
771
 * @return bool The result TRUE means that the order is first_name last_name,
772
 *              FALSE means last_name first_name.
773
 *              Note: You may use this function for determining the order of the fields or
774
 *              columns "First name" and "Last name" in forms, tables and reports.
775
 *
776
 * @author Ivan Tcholakov
777
 */
778
function api_is_western_name_order($format = null, $language = null)
779
{
780
    static $order = [];
781
    if (empty($format)) {
782
        $format = PERSON_NAME_COMMON_CONVENTION;
783
    }
784
785
    if (empty($language)) {
786
        $language = api_get_language_isocode();
787
    }
788
    if (!isset($order[$format][$language])) {
789
        $test_name = api_get_person_name('%f', '%l', '%t', $format, $language);
790
        $order[$format][$language] = stripos($test_name, '%f') <= stripos($test_name, '%l');
791
    }
792
793
    return $order[$format][$language];
794
}
795
796
/**
797
 * Returns a directive for sorting person names depending on a given language
798
 * and based on the options in the internationalization "database".
799
 *
800
 * @param string $language (optional) The input language.
801
 *                         If it is omitted, the current interface language is assumed.
802
 *
803
 * @return bool Returns boolean value. TRUE means ORDER BY first_name, last_name
804
 *              FALSE means ORDER BY last_name, first_name.
805
 *              Note: You may use this function:
806
 *              2. for constructing the ORDER clause of SQL queries, related to first_name and last_name;
807
 *              3. for adjusting php-implemented sorting in tables and reports.
808
 *
809
 * @author Ivan Tcholakov
810
 */
811
function api_sort_by_first_name($language = null)
812
{
813
    static $sort_by_first_name = [];
814
815
    if (empty($language)) {
816
        $language = api_get_language_isocode();
817
    }
818
    if (!isset($sort_by_first_name[$language])) {
819
        $sort_by_first_name[$language] = _api_get_person_name_convention($language, 'sort_by');
820
    }
821
822
    return $sort_by_first_name[$language];
823
}
824
825
/**
826
 * Multibyte string conversion functions.
827
 */
828
829
/**
830
 * Converts character encoding of a given string.
831
 *
832
 * @param string $string        the string being converted
833
 * @param string $to_encoding   the encoding that $string is being converted to
834
 * @param string $from_encoding (optional)    The encoding that $string is being converted from.
835
 *                              If it is omitted, the platform character set is assumed.
836
 *
837
 * @return string Returns the converted string.
838
 *                This function is aimed at replacing the function mb_convert_encoding() for human-language strings.
839
 *
840
 * @see http://php.net/manual/en/function.mb-convert-encoding
841
 */
842
function api_convert_encoding($string, $to_encoding, $from_encoding = 'UTF-8')
843
{
844
    if (strtoupper($to_encoding) === strtoupper($from_encoding)) {
845
        return $string;
846
    }
847
848
    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...
849
}
850
851
/**
852
 * Converts a given string into UTF-8 encoded string.
853
 *
854
 * @param string $string        the string being converted
855
 * @param string $from_encoding (optional) The encoding that $string is being converted from.
856
 *                              If it is omitted, the platform character set is assumed.
857
 *
858
 * @return string Returns the converted string.
859
 *                This function is aimed at replacing the function utf8_encode() for human-language strings.
860
 *
861
 * @see http://php.net/manual/en/function.utf8-encode
862
 */
863
function api_utf8_encode($string, $from_encoding = 'UTF-8')
864
{
865
    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...
866
}
867
868
/**
869
 * Converts a given string from UTF-8 encoding to a specified encoding.
870
 *
871
 * @param string $string     the string being converted
872
 * @param string $toEncoding (optional)    The encoding that $string is being converted to.
873
 *                           If it is omitted, the platform character set is assumed.
874
 *
875
 * @return string Returns the converted string.
876
 *                This function is aimed at replacing the function utf8_decode() for human-language strings.
877
 *
878
 * @see http://php.net/manual/en/function.utf8-decode
879
 */
880
function api_utf8_decode($string, $toEncoding = null)
881
{
882
    if (null === $toEncoding) {
883
        return $string;
884
    }
885
886
    return mb_convert_encoding($string, $toEncoding, 'UTF-8');
0 ignored issues
show
Bug Best Practice introduced by
The expression return mb_convert_encodi..., $toEncoding, 'UTF-8') also could return the type array which is incompatible with the documented return type string.
Loading history...
887
}
888
889
/**
890
 * Converts a given string into the system encoding (or platform character set).
891
 * When $from encoding is omitted on UTF-8 platforms then language dependent encoding
892
 * is guessed/assumed. On non-UTF-8 platforms omitted $from encoding is assumed as UTF-8.
893
 * When the parameter $check_utf8_validity is true the function checks string's
894
 * UTF-8 validity and decides whether to try to convert it or not.
895
 * This function is useful for problem detection or making workarounds.
896
 *
897
 * @param string $string              the string being converted
898
 * @param string $from_encoding       (optional) The encoding that $string is being converted from.
899
 *                                    It is guessed when it is omitted.
900
 * @param bool   $check_utf8_validity (optional)    A flag for UTF-8 validity check as condition for making conversion
901
 *
902
 * @return string returns the converted string
903
 */
904
function api_to_system_encoding($string, $from_encoding = null, $check_utf8_validity = false)
905
{
906
    $system_encoding = api_get_system_encoding();
907
908
    return api_convert_encoding($string, $system_encoding, $from_encoding);
909
}
910
911
/**
912
 * Converts all applicable characters to HTML entities.
913
 *
914
 * @param string $string      the input string
915
 * @param int    $quote_style (optional)    The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES
916
 *
917
 * @return string Returns the converted string.
918
 *                This function is aimed at replacing the function htmlentities() for human-language strings.
919
 *
920
 * @see http://php.net/manual/en/function.htmlentities
921
 */
922
function api_htmlentities($string, $quote_style = ENT_COMPAT)
923
{
924
    switch ($quote_style) {
925
        case ENT_COMPAT:
926
            $string = str_replace(['&', '"', '<', '>'], ['&amp;', '&quot;', '&lt;', '&gt;'], $string);
927
            break;
928
        case ENT_QUOTES:
929
            $string = str_replace(['&', '\'', '"', '<', '>'], ['&amp;', '&#039;', '&quot;', '&lt;', '&gt;'], $string);
930
            break;
931
    }
932
933
    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...
934
}
935
936
/**
937
 * Converts HTML entities into normal characters.
938
 *
939
 * @param string $string      the input string
940
 * @param int    $quote_style (optional)    The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES
941
 * @param string $encoding    (optional)    The encoding (of the result) used in conversion.
942
 *                            If it is omitted, the platform character set is assumed.
943
 *
944
 * @return string Returns the converted string.
945
 *                This function is aimed at replacing the function html_entity_decode() for human-language strings.
946
 *
947
 * @see http://php.net/html_entity_decode
948
 */
949
function api_html_entity_decode($string, $quoteStyle = ENT_COMPAT, $encoding = 'UTF-8')
950
{
951
    return html_entity_decode($string, $quoteStyle, $encoding);
952
953
    /*if (empty($encoding)) {
954
        $encoding = _api_mb_internal_encoding();
955
    }
956
    if (api_is_encoding_supported($encoding)) {
957
        if (!api_is_utf8($encoding)) {
958
            $string = api_utf8_encode($string, $encoding);
959
        }
960
        $string = html_entity_decode($string, $quote_style, 'UTF-8');
961
        if (!api_is_utf8($encoding)) {
962
            return api_utf8_decode($string, $encoding);
963
        }
964
965
        return $string;
966
    }
967
968
    return $string; // Here the function gives up.*/
969
}
970
971
/**
972
 * This function encodes (conditionally) a given string to UTF-8 if XmlHttp-request has been detected.
973
 *
974
 * @param string $string        the string being converted
975
 * @param string $from_encoding (optional)    The encoding that $string is being converted from.
976
 *                              If it is omitted, the platform character set is assumed.
977
 *
978
 * @return string returns the converted string
979
 */
980
function api_xml_http_response_encode($string, $from_encoding = 'UTF8')
981
{
982
    if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && 'xmlhttprequest' == strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])) {
983
        if (empty($from_encoding)) {
984
            $from_encoding = _api_mb_internal_encoding();
985
        }
986
        /*if (!api_is_utf8($from_encoding)) {
987
            return api_utf8_encode($string, $from_encoding);
988
        }*/
989
    }
990
991
    return $string;
992
}
993
994
/**
995
 * Transliterates a string with arbitrary encoding into a plain ASCII string.
996
 *
997
 * Example:
998
 * echo api_transliterate(api_html_entity_decode(
999
 *    '&#1060;&#1105;&#1076;&#1086;&#1088; '.
1000
 *    '&#1052;&#1080;&#1093;&#1072;&#1081;&#1083;&#1086;&#1074;&#1080;&#1095; '.
1001
 *    '&#1044;&#1086;&#1089;&#1090;&#1086;&#1077;&#1074;&#1082;&#1080;&#1081;',
1002
 *    ENT_QUOTES, 'UTF-8'), 'X', 'UTF-8');
1003
 * The output should be: Fyodor Mihaylovich Dostoevkiy
1004
 *
1005
 * @param string $string        the input string
1006
 * @param string $unknown       (optional) Replacement character for unknown characters and illegal UTF-8 sequences
1007
 * @param string $from_encoding (optional) The encoding of the input string.
1008
 *                              If it is omitted, the platform character set is assumed.
1009
 *
1010
 * @return string plain ASCII output
1011
 */
1012
function api_transliterate($string, $unknown = '?', $from_encoding = null)
1013
{
1014
    return URLify::transliterate($string);
1015
}
1016
1017
/**
1018
 * Takes the first character in a string and returns its Unicode codepoint.
1019
 *
1020
 * @param string $character the input string
1021
 * @param string $encoding  (optional) The encoding of the input string.
1022
 *                          If it is omitted, the platform character set will be used by default.
1023
 *
1024
 * @return int Returns: the codepoint of the first character; or 0xFFFD (unknown character) when the input string is
1025
 *             empty. This is a multibyte aware version of the function ord().
1026
 *
1027
 * @see http://php.net/manual/en/function.ord.php
1028
 * Note the difference with the original funtion ord(): ord('') returns 0, api_ord('') returns 0xFFFD (unknown
1029
 * character).
1030
 */
1031
function api_ord($character, $encoding = 'UTF-8')
1032
{
1033
    return ord(api_utf8_encode($character, $encoding));
1034
}
1035
1036
/**
1037
 * This function returns a string or an array with all occurrences of search
1038
 * in subject (ignoring case) replaced with the given replace value.
1039
 *
1040
 * @param mixed  $search   string or array of strings to be found
1041
 * @param mixed  $replace  string or array of strings used for replacement
1042
 * @param mixed  $subject  string or array of strings being searched
1043
 * @param int    $count    (optional) The number of matched and replaced needles
1044
 *                         will be returned in count, which is passed by reference
1045
 * @param string $encoding (optional) The used internally by this function character encoding.
1046
 *                         If it is omitted, the platform character set will be used by default.
1047
 *
1048
 * @return mixed String or array as a result.
1049
 *               Notes:
1050
 *               If $subject is an array, then the search and replace is performed with
1051
 *               every entry of subject, the return value is an array.
1052
 *               If $search and $replace are arrays, then the function takes a value from
1053
 *               each array and uses it to do search and replace on subject.
1054
 *               If $replace has fewer values than search, then an empty string is used for the rest of replacement
1055
 *               values. If $search is an array and $replace is a string, then this replacement string is used for
1056
 *               every value of search. This function is aimed at replacing the function str_ireplace() for
1057
 *               human-language strings.
1058
 *
1059
 * @see    http://php.net/manual/en/function.str-ireplace
1060
 *
1061
 * @author Henri Sivonen, mailto:[email protected]
1062
 *
1063
 * @see    http://hsivonen.iki.fi/php-utf8/
1064
 * Adaptation for Chamilo 1.8.7, 2010
1065
 * Initial implementation Dokeos LMS, August 2009
1066
 *
1067
 * @author Ivan Tcholakov
1068
 */
1069
function api_str_ireplace($search, $replace, $subject, &$count = null, $encoding = null)
1070
{
1071
    return str_ireplace($search, $replace, $subject, $count);
1072
}
1073
1074
/**
1075
 * Converts a string to an array.
1076
 *
1077
 * @param string $string       the input string
1078
 * @param int    $split_length maximum character-length of the chunk, one character by default
1079
 * @param string $encoding     (optional) The used internally by this function
1080
 *                             character encoding. If it is omitted, the platform character set will be used by
1081
 *                             default.
1082
 *
1083
 * @return array The result array of chunks with the spcified length.
1084
 *               Notes:
1085
 *               If the optional split_length parameter is specified, the returned array will be broken down into
1086
 *               chunks
1087
 *               with each being split_length in length, otherwise each chunk will be one character in length.
1088
 *               FALSE is returned if split_length is less than 1.
1089
 *               If the split_length length exceeds the length of string, the entire string is returned as the first
1090
 *               (and only) array element. This function is aimed at replacing the function str_split() for
1091
 *               human-language strings.
1092
 *
1093
 * @see http://php.net/str_split
1094
 */
1095
function api_str_split($string, $split_length = 1, $encoding = null)
1096
{
1097
    return str_split($string, $split_length);
0 ignored issues
show
Bug Best Practice introduced by
The expression return str_split($string, $split_length) also could return the type true which is incompatible with the documented return type array.
Loading history...
1098
}
1099
1100
/**
1101
 * Finds position of first occurrence of a string within another, case insensitive.
1102
 *
1103
 * @param string $haystack the string from which to get the position of the first occurrence
1104
 * @param string $needle   the string to be found
1105
 * @param int    $offset   The position in $haystack to start searching from.
1106
 *                         If it is omitted, searching starts from the beginning.
1107
 * @param string $encoding (optional) The used internally by this function
1108
 *                         character encoding. If it is omitted, the platform character set will be used by default.
1109
 *
1110
 * @return mixed Returns the numeric position of the first occurrence of
1111
 *               $needle in the $haystack, or FALSE if $needle is not found.
1112
 *               Note: The first character's position is 0, the second character position is 1, and so on.
1113
 *               This function is aimed at replacing the functions stripos() and mb_stripos() for human-language
1114
 *               strings.
1115
 *
1116
 * @see http://php.net/manual/en/function.stripos
1117
 * @see http://php.net/manual/en/function.mb-stripos
1118
 */
1119
function api_stripos($haystack, $needle, $offset = 0, $encoding = null)
1120
{
1121
    return stripos($haystack, $needle, $offset);
1122
}
1123
1124
/**
1125
 * Finds first occurrence of a string within another, case insensitive.
1126
 *
1127
 * @param string $haystack      the string from which to get the first occurrence
1128
 * @param mixed  $needle        the string to be found
1129
 * @param bool   $before_needle (optional) Determines which portion of $haystack
1130
 *                              this function returns. The default value is FALSE.
1131
 * @param string $encoding      (optional) The used internally by this function
1132
 *                              character encoding. If it is omitted, the platform character set will be used by
1133
 *                              default.
1134
 *
1135
 * @return mixed Returns the portion of $haystack, or FALSE if $needle is not found.
1136
 *               Notes:
1137
 *               If $needle is not a string, it is converted to an integer and applied as the
1138
 *               ordinal value (codepoint if the encoding is UTF-8) of a character.
1139
 *               If $before_needle is set to TRUE, the function returns all of $haystack
1140
 *               from the beginning to the first occurrence of $needle.
1141
 *               If $before_needle is set to FALSE, the function returns all of $haystack f
1142
 *               rom the first occurrence of $needle to the end.
1143
 *               This function is aimed at replacing the functions stristr() and mb_stristr() for human-language
1144
 *               strings.
1145
 *
1146
 * @see http://php.net/manual/en/function.stristr
1147
 * @see http://php.net/manual/en/function.mb-stristr
1148
 */
1149
function api_stristr($haystack, $needle, $before_needle = false, $encoding = null)
1150
{
1151
    return stristr($haystack, $needle, $before_needle);
1152
}
1153
1154
/**
1155
 * Returns length of the input string.
1156
 *
1157
 * @param string $string   the string which length is to be calculated
1158
 * @param string $encoding (optional) The used internally by this function character encoding. If it is omitted, the
1159
 *                         platform character set will be used by default.
1160
 *
1161
 * @return int Returns the number of characters within the string. A multi-byte character is counted as 1.
1162
 *             This function is aimed at replacing the functions strlen() and mb_strlen() for human-language strings.
1163
 *
1164
 * @see http://php.net/manual/en/function.strlen
1165
 * @see http://php.net/manual/en/function.mb-strlen
1166
 * Note: When you use strlen() to test for an empty string, you needn't change it to api_strlen().
1167
 * For example, in lines like the following:
1168
 * if (strlen($string) > 0)
1169
 * if (strlen($string) != 0)
1170
 * there is no need the original function strlen() to be changed, it works correctly and faster for these cases.
1171
 */
1172
function api_strlen($string, $encoding = null)
1173
{
1174
    return strlen($string);
1175
}
1176
1177
/**
1178
 * Finds position of first occurrence of a string within another.
1179
 *
1180
 * @param string $haystack the string from which to get the position of the first occurrence
1181
 * @param string $needle   the string to be found
1182
 * @param int    $offset   (optional) The position in $haystack to start searching from. If it is omitted, searching
1183
 *                         starts from the beginning.
1184
 * @param string $encoding (optional)    The used internally by this function character encoding. If it is omitted, the
1185
 *                         platform character set will be used by default.
1186
 *
1187
 * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle
1188
 *               is not found. Note: The first character's position is 0, the second character position is 1, and so
1189
 *               on. This function is aimed at replacing the functions strpos() and mb_strpos() for human-language
1190
 *               strings.
1191
 *
1192
 * @see http://php.net/manual/en/function.strpos
1193
 * @see http://php.net/manual/en/function.mb-strpos
1194
 */
1195
function api_strpos($haystack, $needle, $offset = 0, $encoding = null)
1196
{
1197
    return strpos($haystack, $needle, $offset);
1198
}
1199
1200
/**
1201
 * Finds the last occurrence of a character in a string.
1202
 *
1203
 * @param string $haystack      the string from which to get the last occurrence
1204
 * @param mixed  $needle        the string which first character is to be found
1205
 * @param bool   $before_needle (optional) Determines which portion of $haystack this function returns. The default
1206
 *                              value is FALSE.
1207
 * @param string $encoding      (optional) The used internally by this function character encoding. If it is omitted,
1208
 *                              the platform character set will be used by default.
1209
 *
1210
 * @return mixed Returns the portion of $haystack, or FALSE if the first character from $needle is not found.
1211
 *               Notes:
1212
 *               If $needle is not a string, it is converted to an integer and applied as the ordinal value (codepoint
1213
 *               if the encoding is UTF-8) of a character. If $before_needle is set to TRUE, the function returns all
1214
 *               of $haystack from the beginning to the first occurrence. If $before_needle is set to FALSE, the
1215
 *               function returns all of $haystack from the first occurrence to the end. This function is aimed at
1216
 *               replacing the functions strrchr() and mb_strrchr() for human-language strings.
1217
 *
1218
 * @see http://php.net/manual/en/function.strrchr
1219
 * @see http://php.net/manual/en/function.mb-strrchr
1220
 */
1221
function api_strrchr($haystack, $needle, $before_needle = false, $encoding = null)
1222
{
1223
    return strrchr($haystack, $needle);
1224
}
1225
1226
/**
1227
 * Finds the position of last occurrence (case insensitive) of a string in a string.
1228
 *
1229
 * @param string $haystack the string from which to get the position of the last occurrence
1230
 * @param string $needle   the string to be found
1231
 * @param int    $offset   (optional) $offset may be specified to begin searching an arbitrary position. Negative
1232
 *                         values will stop searching at an arbitrary point prior to the end of the string.
1233
 * @param string $encoding (optional) The used internally by this function character encoding. If it is omitted, the
1234
 *                         platform character set will be used by default.
1235
 *
1236
 * @return mixed Returns the numeric position of the first occurrence (case insensitive) of $needle in the $haystack,
1237
 *               or FALSE if $needle is not found. Note: The first character's position is 0, the second character
1238
 *               position is 1, and so on. This function is aimed at replacing the functions strripos() and
1239
 *               mb_strripos() for human-language strings.
1240
 *
1241
 * @see http://php.net/manual/en/function.strripos
1242
 * @see http://php.net/manual/en/function.mb-strripos
1243
 */
1244
function api_strripos($haystack, $needle, $offset = 0, $encoding = null)
1245
{
1246
    return strripos($haystack, $needle, $offset);
1247
}
1248
1249
/**
1250
 * Finds the position of last occurrence of a string in a string.
1251
 *
1252
 * @param string $haystack the string from which to get the position of the last occurrence
1253
 * @param string $needle   the string to be found
1254
 * @param int    $offset   (optional) $offset may be specified to begin searching an arbitrary position. Negative
1255
 *                         values will stop searching at an arbitrary point prior to the end of the string.
1256
 * @param string $encoding (optional) The used internally by this function character encoding. If it is omitted, the
1257
 *                         platform character set will be used by default.
1258
 *
1259
 * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle
1260
 *               is not found. Note: The first character's position is 0, the second character position is 1, and so
1261
 *               on. This function is aimed at replacing the functions strrpos() and mb_strrpos() for human-language
1262
 *               strings.
1263
 *
1264
 * @see http://php.net/manual/en/function.strrpos
1265
 * @see http://php.net/manual/en/function.mb-strrpos
1266
 */
1267
function api_strrpos($haystack, $needle, $offset = 0, $encoding = null)
1268
{
1269
    return strrpos($haystack, $needle, $offset);
1270
}
1271
1272
/**
1273
 * Finds first occurrence of a string within another.
1274
 *
1275
 * @param string $haystack      the string from which to get the first occurrence
1276
 * @param mixed  $needle        the string to be found
1277
 * @param bool   $before_needle (optional) Determines which portion of $haystack this function returns. The default
1278
 *                              value is FALSE.
1279
 * @param string $encoding      (optional) The used internally by this function character encoding. If it is omitted,
1280
 *                              the platform character set will be used by default.
1281
 *
1282
 * @return mixed Returns the portion of $haystack, or FALSE if $needle is not found.
1283
 *               Notes:
1284
 *               If $needle is not a string, it is converted to an integer and applied as the ordinal value (codepoint
1285
 *               if the encoding is UTF-8) of a character. If $before_needle is set to TRUE, the function returns all
1286
 *               of $haystack from the beginning to the first occurrence of $needle. If $before_needle is set to FALSE,
1287
 *               the function returns all of $haystack from the first occurrence of $needle to the end. This function
1288
 *               is aimed at replacing the functions strstr() and mb_strstr() for human-language strings.
1289
 *
1290
 * @see http://php.net/manual/en/function.strstr
1291
 * @see http://php.net/manual/en/function.mb-strstr
1292
 */
1293
function api_strstr($haystack, $needle, $before_needle = false, $encoding = null)
1294
{
1295
    return strstr($haystack, $needle, $before_needle);
1296
}
1297
1298
/**
1299
 * Makes a string lowercase.
1300
 *
1301
 * @param string $string   the string being lowercased
1302
 * @param string $encoding (optional)    The used internally by this function character encoding. If it is omitted, the
1303
 *                         platform character set will be used by default.
1304
 *
1305
 * @return string Returns the string with all alphabetic characters converted to lowercase.
1306
 *                This function is aimed at replacing the functions strtolower() and mb_strtolower() for human-language
1307
 *                strings.
1308
 *
1309
 * @see http://php.net/manual/en/function.strtolower
1310
 * @see http://php.net/manual/en/function.mb-strtolower
1311
 */
1312
function api_strtolower($string, $encoding = null)
1313
{
1314
    return strtolower($string);
1315
}
1316
1317
/**
1318
 * Makes a string uppercase.
1319
 *
1320
 * @param string $string   the string being uppercased
1321
 * @param string $encoding (optional)    The used internally by this function character encoding. If it is omitted, the
1322
 *                         platform character set will be used by default.
1323
 *
1324
 * @return string Returns the string with all alphabetic characters converted to uppercase.
1325
 *                This function is aimed at replacing the functions strtoupper() and mb_strtoupper() for human-language
1326
 *                strings.
1327
 *
1328
 * @see http://php.net/manual/en/function.strtoupper
1329
 * @see http://php.net/manual/en/function.mb-strtoupper
1330
 */
1331
function api_strtoupper(?string $string, $encoding = null)
1332
{
1333
    return strtoupper((string) $string);
1334
}
1335
1336
/**
1337
 * // Gets part of a string.
1338
 *
1339
 * @param string $string   the input string
1340
 * @param int    $start    the first position from which the extracted part begins
1341
 * @param int    $length   the length in character of the extracted part
1342
 * @param string $encoding (optional) The used internally by this function
1343
 *                         character encoding. If it is omitted, the platform character set will be used by default.
1344
 *
1345
 * @return string Returns the part of the string specified by the start and length parameters.
1346
 *                Note: First character's position is 0. Second character position is 1, and so on.
1347
 *                This function is aimed at replacing the functions substr() and mb_substr() for human-language strings.
1348
 *
1349
 * @see http://php.net/manual/en/function.substr
1350
 * @see http://php.net/manual/en/function.mb-substr
1351
 */
1352
function api_substr($string, $start, $length = null, $encoding = null)
1353
{
1354
    if (is_null($length)) {
1355
        $length = api_strlen($string, $encoding);
1356
    }
1357
1358
    return substr($string, $start, $length);
1359
}
1360
1361
/**
1362
 * Counts the number of substring occurrences.
1363
 *
1364
 * @param string $haystack the string being checked
1365
 * @param string $needle   the string being found
1366
 * @param string $encoding (optional) The used internally by this function character encoding.
1367
 *                         If it is omitted, the platform character set will be used by default.
1368
 *
1369
 * @return int the number of times the needle substring occurs in the haystack string
1370
 *
1371
 * @see http://php.net/manual/en/function.mb-substr-count.php
1372
 */
1373
function api_substr_count($haystack, $needle, $encoding = null)
1374
{
1375
    return substr_count($haystack, $needle);
1376
}
1377
1378
/**
1379
 * Replaces text within a portion of a string.
1380
 *
1381
 * @param string $string      the input string
1382
 * @param string $replacement the replacement string
1383
 * @param int    $start       The position from which replacing will begin.
1384
 *                            Notes:
1385
 *                            If $start is positive, the replacing will begin at the $start'th offset into the string.
1386
 *                            If $start is negative, the replacing will begin at the $start'th character from the end
1387
 *                            of the string.
1388
 * @param int    $length      (optional) The position where replacing will end.
1389
 *                            Notes:
1390
 *                            If given and is positive, it represents the length of the portion of the string which is
1391
 *                            to be replaced. If it is negative, it represents the number of characters from the end of
1392
 *                            string at which to stop replacing. If it is not given, then it will default to
1393
 *                            api_strlen($string); i.e. end the replacing at the end of string. If $length is zero,
1394
 *                            then this function will have the effect of inserting replacement into the string at the
1395
 *                            given start offset.
1396
 * @param string $encoding    (optional)    The used internally by this function character encoding.
1397
 *                            If it is omitted, the platform character set will be used by default.
1398
 *
1399
 * @return string The result string is returned.
1400
 *                This function is aimed at replacing the function substr_replace() for human-language strings.
1401
 *
1402
 * @see http://php.net/manual/function.substr-replace
1403
 */
1404
function api_substr_replace($string, $replacement, $start, $length = null, $encoding = null)
1405
{
1406
    if (is_null($length)) {
1407
        $length = api_strlen($string);
1408
    }
1409
1410
    return substr_replace($string, $replacement, $start, $length);
0 ignored issues
show
Bug Best Practice introduced by
The expression return substr_replace($s...ement, $start, $length) also could return the type array which is incompatible with the documented return type string.
Loading history...
1411
}
1412
1413
/**
1414
 * Makes a string's first character uppercase.
1415
 *
1416
 * @param string $string   the input string
1417
 * @param string $encoding (optional)    The used internally by this function character encoding.
1418
 *                         If it is omitted, the platform character set will be used by default.
1419
 *
1420
 * @return string Returns a string with the first character capitalized, if that character is alphabetic.
1421
 *                This function is aimed at replacing the function ucfirst() for human-language strings.
1422
 *
1423
 * @see http://php.net/manual/en/function.ucfirst
1424
 */
1425
function api_ucfirst($string, $encoding = null)
1426
{
1427
    return ucfirst($string);
1428
}
1429
1430
/**
1431
 * Uppercases the first character of each word in a string.
1432
 *
1433
 * @param string $string   the input string
1434
 * @param string $encoding (optional) The used internally by this function character encoding.
1435
 *                         If it is omitted, the platform character set will be used by default.
1436
 *
1437
 * @return string Returns the modified string.
1438
 *                This function is aimed at replacing the function ucwords() for human-language strings.
1439
 *
1440
 * @see http://php.net/manual/en/function.ucwords
1441
 */
1442
function api_ucwords($string, $encoding = null)
1443
{
1444
    return ucwords($string);
1445
}
1446
1447
/**
1448
 * Performs a regular expression match, UTF-8 aware when it is applicable.
1449
 *
1450
 * @param string $pattern  the pattern to search for, as a string
1451
 * @param string $subject  the input string
1452
 * @param array  &$matches (optional) If matches is provided,
1453
 *                         then it is filled with the results of search (as an array).
1454
 *                         $matches[0] will contain the text that matched the full pattern, $matches[1] will have the
1455
 *                         text that matched the first captured parenthesized subpattern, and so on.
1456
 * @param int    $flags    (optional) Could be PREG_OFFSET_CAPTURE. If this flag is passed, for every occurring match
1457
 *                         the appendant string offset will also be returned. Note that this changes the return value
1458
 *                         in an array where every element is an array consisting of the matched string at index 0 and
1459
 *                         its string offset into subject at index 1.
1460
 * @param int    $offset   (optional)        Normally, the search starts from the beginning of the subject string. The
1461
 *                         optional parameter offset can be used to specify the alternate place from which to start
1462
 *                         the search.
1463
 * @param string $encoding (optional)    The used internally by this function character encoding. If it is omitted,
1464
 *                         the platform character set will be used by default.
1465
 *
1466
 * @return int|bool returns the number of times pattern matches or FALSE if an error occurred
1467
 *
1468
 * @see http://php.net/preg_match
1469
 */
1470
function api_preg_match(
1471
    $pattern,
1472
    $subject,
1473
    &$matches = null,
1474
    $flags = 0,
1475
    $offset = 0,
1476
    $encoding = null
1477
) {
1478
    return preg_match($pattern.'u', $subject, $matches, $flags, $offset);
1479
}
1480
1481
/**
1482
 * Performs a global regular expression match, UTF-8 aware when it is applicable.
1483
 *
1484
 * @param string $pattern  the pattern to search for, as a string
1485
 * @param string $subject  the input string
1486
 * @param array  &$matches (optional)    Array of all matches in multi-dimensional array ordered according to $flags
1487
 * @param int    $flags    (optional)            Can be a combination of the following flags (note that it doesn't
1488
 *                         make sense to use PREG_PATTERN_ORDER together with PREG_SET_ORDER): PREG_PATTERN_ORDER -
1489
 *                         orders results so that $matches[0] is an array of full pattern matches, $matches[1] is an
1490
 *                         array of strings matched by the first parenthesized subpattern, and so on; PREG_SET_ORDER -
1491
 *                         orders results so that $matches[0] is an array of first set of matches, $matches[1] is an
1492
 *                         array of second set of matches, and so on; PREG_OFFSET_CAPTURE - If this flag is passed,
1493
 *                         for every occurring match the appendant string offset will also be returned. Note that this
1494
 *                         changes the value of matches in an array where every element is an array consisting of the
1495
 *                         matched string at offset 0 and its string offset into subject at offset 1. If no order flag
1496
 *                         is given, PREG_PATTERN_ORDER is assumed.
1497
 * @param int    $offset   (optional)        Normally, the search starts from the beginning of the subject string. The
1498
 *                         optional parameter offset can be used to specify the alternate place from which to start
1499
 *                         the search.
1500
 * @param string $encoding (optional)    The used internally by this function character encoding. If it is omitted,
1501
 *                         the platform character set will be used by default.
1502
 *
1503
 * @return int|bool returns the number of full pattern matches (which might be zero), or FALSE if an error occurred
1504
 *
1505
 * @see http://php.net/preg_match_all
1506
 */
1507
function api_preg_match_all($pattern, $subject, &$matches, $flags = PREG_PATTERN_ORDER, $offset = 0, $encoding = null)
1508
{
1509
    if (empty($encoding)) {
1510
        $encoding = _api_mb_internal_encoding();
1511
    }
1512
    if (is_null($flags)) {
1513
        $flags = PREG_PATTERN_ORDER;
1514
    }
1515
1516
    return preg_match_all($pattern.'u', $subject, $matches, $flags, $offset);
1517
1518
    //return preg_match_all(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $matches, $flags, $offset);
1519
}
1520
1521
/**
1522
 * Performs a regular expression search and replace, UTF-8 aware when it is applicable.
1523
 *
1524
 * @param string|array $pattern     The pattern to search for. It can be either a string or an array with strings.
1525
 * @param string|array $replacement the string or an array with strings to replace
1526
 * @param string|array $subject     the string or an array with strings to search and replace
1527
 * @param int          $limit       The maximum possible replacements for each pattern in each subject string.
1528
 *                                  Defaults to -1 (no limit).
1529
 * @param int          &$count      If specified, this variable will be filled with the number of replacements done
1530
 * @param string       $encoding    (optional)    The used internally by this function character encoding.
1531
 *                                  If it is omitted, the platform character set will be used by default.
1532
 *
1533
 * @return array|string|null returns an array if the subject parameter is an array, or a string otherwise.
1534
 *                           If matches are found, the new subject will be returned, otherwise subject will be returned
1535
 *                           unchanged or NULL if an error occurred.
1536
 *
1537
 * @see http://php.net/preg_replace
1538
 */
1539
function api_preg_replace($pattern, $replacement, $subject, $limit = -1, $count = 0, $encoding = null)
1540
{
1541
    /*if (empty($encoding)) {
1542
        $encoding = _api_mb_internal_encoding();
1543
    }
1544
    $is_utf8 = api_is_utf8($encoding);
1545
    if (is_array($pattern)) {
1546
        foreach ($pattern as &$p) {
1547
            $p = $is_utf8 ? $p.'u' : $p;
1548
        }
1549
    } else {
1550
        $pattern = $is_utf8 ? $pattern.'u' : $pattern;
1551
    }*/
1552
    $pattern = $pattern.'u';
0 ignored issues
show
Bug introduced by
Are you sure $pattern of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1552
    $pattern = /** @scrutinizer ignore-type */ $pattern.'u';
Loading history...
1553
1554
    return preg_replace($pattern, $replacement, $subject, $limit, $count);
1555
}
1556
1557
/**
1558
 * Splits a string by a regular expression, UTF-8 aware when it is applicable.
1559
 *
1560
 * @param string $pattern  the pattern to search for, as a string
1561
 * @param string $subject  the input string
1562
 * @param int    $limit    (optional)            If specified, then only substrings up to $limit are returned with the
1563
 *                         rest of the string being placed in the last substring. A limit of -1, 0 or null means "no
1564
 *                         limit" and, as is standard across PHP.
1565
 * @param int    $flags    (optional)            $flags can be any combination of the following flags (combined with
1566
 *                         bitwise | operator): PREG_SPLIT_NO_EMPTY - if this flag is set, only non-empty pieces will
1567
 *                         be returned; PREG_SPLIT_DELIM_CAPTURE - if this flag is set, parenthesized expression in the
1568
 *                         delimiter pattern will be captured and returned as well; PREG_SPLIT_OFFSET_CAPTURE - If this
1569
 *                         flag is set, for every occurring match the appendant string offset will also be returned.
1570
 *                         Note that this changes the return value in an array where every element is an array
1571
 *                         consisting of the matched string at offset 0 and its string offset into subject at offset 1.
1572
 * @param string $encoding (optional)    The used internally by this function character encoding. If it is omitted, the
1573
 *                         platform character set will be used by default.
1574
 *
1575
 * @return array returns an array containing substrings of $subject split along boundaries matched by $pattern
1576
 *
1577
 * @see http://php.net/preg_split
1578
 */
1579
function api_preg_split($pattern, $subject, $limit = -1, $flags = 0)
1580
{
1581
    return preg_split($pattern.'u', $subject, $limit, $flags);
1582
}
1583
1584
/**
1585
 * String comparison.
1586
 */
1587
1588
/**
1589
 * Performs string comparison, case insensitive, language sensitive, with extended multibyte support.
1590
 *
1591
 * @param string $string1  the first string
1592
 * @param string $string2  the second string
1593
 * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface
1594
 *                         language is assumed then.
1595
 * @param string $encoding (optional) The used internally by this function character encoding. If it is omitted, the
1596
 *                         platform character set will be used by default.
1597
 *
1598
 * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the
1599
 *             strings are equal. This function is aimed at replacing the function strcasecmp() for human-language
1600
 *             strings.
1601
 *
1602
 * @see http://php.net/manual/en/function.strcasecmp
1603
 */
1604
function api_strcasecmp($string1, $string2, $language = null, $encoding = null)
1605
{
1606
    return api_strcmp(api_strtolower($string1, $encoding), api_strtolower($string2, $encoding), $language, $encoding);
1607
}
1608
1609
/**
1610
 * Performs string comparison, case sensitive, language sensitive, with extended multibyte support.
1611
 *
1612
 * @param string $string1  the first string
1613
 * @param string $string2  the second string
1614
 * @param string $language (optional)    The language in which comparison is to be made. If language is omitted,
1615
 *                         interface language is assumed then.
1616
 * @param string $encoding (optional)    The used internally by this function character encoding.
1617
 *                         If it is omitted, the platform character set will be used by default.
1618
 *
1619
 * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the
1620
 *             strings are equal. This function is aimed at replacing the function strcmp() for human-language strings.
1621
 *
1622
 * @see http://php.net/manual/en/function.strcmp.php
1623
 * @see http://php.net/manual/en/collator.compare.php
1624
 */
1625
function api_strcmp($string1, $string2, $language = null, $encoding = null)
1626
{
1627
    return strcmp($string1, $string2);
1628
}
1629
1630
/**
1631
 * Performs string comparison in so called "natural order", case sensitive, language sensitive, with extended multibyte
1632
 * support.
1633
 *
1634
 * @param string $string1  the first string
1635
 * @param string $string2  the second string
1636
 * @param string $language (optional)    The language in which comparison is to be made. If language is omitted,
1637
 *                         interface language is assumed then.
1638
 * @param string $encoding (optional)    The used internally by this function character encoding.
1639
 *                         If it is omitted, the platform character set will be used by default.
1640
 *
1641
 * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the
1642
 *             strings are equal. This function is aimed at replacing the function strnatcmp() for human-language
1643
 *             strings.
1644
 *
1645
 * @see http://php.net/manual/en/function.strnatcmp.php
1646
 * @see http://php.net/manual/en/collator.compare.php
1647
 */
1648
function api_strnatcmp($string1, $string2, $language = null, $encoding = null)
1649
{
1650
    return strnatcmp($string1, $string2);
1651
}
1652
1653
/**
1654
 * Sorting arrays.
1655
 */
1656
1657
/**
1658
 * Sorts an array using natural order algorithm.
1659
 *
1660
 * @param array  $array    the input array
1661
 * @param string $language (optional)    The language in which comparison is to be made. If language is omitted,
1662
 *                         interface language is assumed then.
1663
 * @param string $encoding (optional)    The used internally by this function character encoding.
1664
 *                         If it is omitted, the platform character set will be used by default.
1665
 *
1666
 * @return bool Returns TRUE on success, FALSE on error.
1667
 *              This function is aimed at replacing the function natsort() for sorting human-language strings.
1668
 *
1669
 * @see http://php.net/manual/en/function.natsort.php
1670
 */
1671
function api_natsort(&$array, $language = null, $encoding = null)
1672
{
1673
    return natsort($array);
1674
}
1675
1676
/**
1677
 * Sorts an array using natural order algorithm in reverse order.
1678
 *
1679
 * @param array  $array    the input array
1680
 * @param string $language (optional)    The language in which comparison is to be made. If language is omitted,
1681
 *                         interface language is assumed then.
1682
 * @param string $encoding (optional)    The used internally by this function character encoding.
1683
 *                         If it is omitted, the platform character set will be used by default.
1684
 *
1685
 * @return bool returns TRUE on success, FALSE on error
1686
 */
1687
function api_natrsort(&$array, $language = null, $encoding = null)
1688
{
1689
    return uasort($array, '_api_strnatrcmp');
1690
}
1691
1692
/**
1693
 * Encoding management functions.
1694
 */
1695
1696
/**
1697
 * This function unifies the encoding identificators, so they could be compared.
1698
 *
1699
 * @param string|array $encoding the specified encoding
1700
 *
1701
 * @return string returns the encoding identificator modified in suitable for comparison way
1702
 */
1703
function api_refine_encoding_id($encoding)
1704
{
1705
    if (is_array($encoding)) {
1706
        return array_map('api_refine_encoding_id', $encoding);
1707
    }
1708
1709
    return strtoupper(str_replace('_', '-', $encoding));
1710
}
1711
1712
/**
1713
 * This function returns the encoding, currently used by the system.
1714
 *
1715
 * @return string The system's encoding.
1716
 *                Note: The value of api_get_setting('platform_charset') is tried to be returned first,
1717
 *                on the second place the global variable $charset is tried to be returned. If for some
1718
 *                reason both attempts fail, then the libraly's internal value will be returned.
1719
 */
1720
function api_get_system_encoding()
1721
{
1722
    return 'UTF-8';
1723
}
1724
1725
/**
1726
 * Detects encoding of plain text.
1727
 *
1728
 * @param string $string the input text
1729
 *
1730
 * @return string returns the detected encoding
1731
 */
1732
function api_detect_encoding($string)
1733
{
1734
    // Testing against valid UTF-8 first.
1735
    if (api_is_valid_utf8($string)) {
1736
        return 'UTF-8';
1737
    }
1738
1739
    return mb_detect_encoding($string);
1740
}
1741
1742
/**
1743
 * String validation functions concerning certain encodings.
1744
 */
1745
1746
/**
1747
 * Checks a string for UTF-8 validity.
1748
 *
1749
 * @param string $string
1750
 *
1751
 * @return string
1752
 */
1753
function api_is_valid_utf8($string)
1754
{
1755
    return mb_check_encoding($string, 'UTF-8');
1756
}
1757
1758
/**
1759
 * Checks whether a string contains 7-bit ASCII characters only.
1760
 *
1761
 * @param string $string the string to be tested/validated
1762
 *
1763
 * @return bool returns TRUE when the tested string contains 7-bit
1764
 *              ASCII characters only, FALSE othewise
1765
 */
1766
function api_is_valid_ascii(&$string)
1767
{
1768
    return 'ASCII' == mb_detect_encoding($string, 'ASCII', true) ? true : false;
1769
}
1770
1771
/**
1772
 * Return true a date is valid.
1773
 *
1774
 * @param string $date   example: 2014-06-30 13:05:05
1775
 * @param string $format example: "Y-m-d H:i:s"
1776
 *
1777
 * @return bool
1778
 */
1779
function api_is_valid_date($date, $format = 'Y-m-d H:i:s')
1780
{
1781
    $d = DateTime::createFromFormat($format, $date);
1782
1783
    return $d && $d->format($format) == $date;
1784
}
1785
1786
/**
1787
 * Returns the variable translated.
1788
 *
1789
 * @param string $variable   the string to translate
1790
 * @param string $pluginName the Plugin name
1791
 *
1792
 * @return string the variable translated
1793
 */
1794
function get_plugin_lang($variable, $pluginName)
1795
{
1796
    $plugin = $pluginName::create();
1797
1798
    return $plugin->get_lang($variable);
1799
}
1800
1801
/**
1802
 * Returns an array of translated week days and months, short and normal names.
1803
 *
1804
 * @param string $language (optional Language id. If it is omitted,
1805
 *                         the current interface language is assumed.
1806
 *
1807
 * @return array returns a multidimensional array with translated week days and months
1808
 */
1809
function &_api_get_day_month_names($language = null)
1810
{
1811
    static $date_parts = [];
1812
    if (empty($language)) {
1813
        $language = api_get_language_isocode();
1814
    }
1815
    if (!isset($date_parts[$language])) {
1816
        $week_day = [
1817
            'Sunday',
1818
            'Monday',
1819
            'Tuesday',
1820
            'Wednesday',
1821
            'Thursday',
1822
            'Friday',
1823
            'Saturday',
1824
        ];
1825
        $month = [
1826
            'January',
1827
            'February',
1828
            'March',
1829
            'April',
1830
            'May',
1831
            'June',
1832
            'July',
1833
            'August',
1834
            'September',
1835
            'October',
1836
            'November',
1837
            'December',
1838
        ];
1839
        for ($i = 0; $i < 7; $i++) {
1840
            $date_parts[$language]['days_short'][] = get_lang(
1841
                $week_day[$i].'Short',
1842
                '',
1843
                $language
1844
            );
1845
            $date_parts[$language]['days_long'][] = get_lang(
1846
                $week_day[$i].'Long',
1847
                '',
1848
                $language
1849
            );
1850
        }
1851
        for ($i = 0; $i < 12; $i++) {
1852
            $date_parts[$language]['months_short'][] = get_lang(
1853
                $month[$i].'Short',
1854
                '',
1855
                $language
1856
            );
1857
            $date_parts[$language]['months_long'][] = get_lang(
1858
                $month[$i].'Long',
1859
                '',
1860
                $language
1861
            );
1862
        }
1863
    }
1864
1865
    return $date_parts[$language];
1866
}
1867
1868
/**
1869
 * Returns returns person name convention for a given language.
1870
 *
1871
 * @param string $iso
1872
 * @param string $type The type of the requested convention.
1873
 *                     It may be 'format' for name order convention or 'sort_by' for name sorting convention.
1874
 *
1875
 * @return mixed Depending of the requested type,
1876
 *               the returned result may be string or boolean; null is returned on error;
1877
 */
1878
function _api_get_person_name_convention($iso, $type)
1879
{
1880
    $conventions = LanguageFixtures::getLanguages();
1881
1882
    // Overwrite classic conventions
1883
    //$customConventions = api_get_configuration_value('name_order_conventions');
1884
1885
    $search1 = ['FIRST_NAME', 'LAST_NAME', 'TITLE'];
1886
    $replacement1 = ['%F', '%L', '%T'];
1887
    $search2 = ['first_name', 'last_name', 'title'];
1888
    $replacement2 = ['%f', '%l', '%t'];
1889
    $conventionsFormatted = [];
1890
    foreach ($conventions as $language) {
1891
        $iso = $language['isocode'];
1892
        $conventionsFormatted[$iso]['format'] = $language['format'];
1893
        $conventionsFormatted[$iso]['sort_by'] = $language['sort_by'];
1894
1895
        $conventionsFormatted[$iso]['format'] = str_replace(
1896
            $search1,
1897
            $replacement1,
1898
            $conventionsFormatted[$iso]['format']
1899
        );
1900
        $conventionsFormatted[$iso]['format'] = _api_validate_person_name_format(
1901
            _api_clean_person_name(
1902
                str_replace(
1903
                    '%',
1904
                    ' %',
1905
                    str_ireplace(
1906
                        $search2,
1907
                        $replacement2,
1908
                        $conventionsFormatted[$iso]['format']
1909
                    )
1910
                )
1911
            )
1912
        );
1913
1914
        $conventionsFormatted[$iso]['sort_by'] = 'last_name' !== strtolower(
1915
            $conventionsFormatted[$iso]['sort_by']
1916
        ) ? true : false;
1917
    }
1918
1919
    switch ($type) {
1920
        case 'format':
1921
            return is_string(
1922
                $conventionsFormatted[$iso]['format']
1923
            ) ? $conventionsFormatted[$iso]['format'] : '%t %f %l';
1924
        case 'sort_by':
1925
            return is_bool($conventionsFormatted[$iso]['sort_by']) ? $conventionsFormatted[$iso]['sort_by'] : true;
1926
    }
1927
1928
    return null;
1929
}
1930
1931
/**
1932
 * Replaces non-valid formats for person names with the default (English) format.
1933
 *
1934
 * @param string $format the input format to be verified
1935
 *
1936
 * @return bool returns the same format if is is valid, otherwise returns a valid English format
1937
 */
1938
function _api_validate_person_name_format($format)
1939
{
1940
    if (empty($format) || false === stripos($format, '%f') || false === stripos($format, '%l')) {
1941
        return '%t %f %l';
1942
    }
1943
1944
    return $format;
1945
}
1946
1947
/**
1948
 * Removes leading, trailing and duplicate whitespace and/or commas in a full person name.
1949
 * Cleaning is needed for the cases when not all parts of the name are available
1950
 * or when the name is constructed using a "dirty" pattern.
1951
 *
1952
 * @param string $person_name the input person name
1953
 *
1954
 * @return string returns cleaned person name
1955
 */
1956
function _api_clean_person_name($person_name)
1957
{
1958
    return preg_replace(['/\s+/', '/, ,/', '/,+/', '/^[ ,]/', '/[ ,]$/'], [' ', ', ', ',', '', ''], $person_name);
1959
}
1960
1961
/**
1962
 * A reverse function from php-core function strnatcmp(),
1963
 * performs string comparison in reverse natural (alpha-numerical) order.
1964
 *
1965
 * @param string $string1 the first string
1966
 * @param string $string2 the second string
1967
 *
1968
 * @return int returns 0 if $string1 = $string2; >0 if $string1 < $string2; <0 if $string1 > $string2
1969
 */
1970
function _api_strnatrcmp($string1, $string2)
1971
{
1972
    return strnatcmp($string2, $string1);
1973
}
1974
1975
/**
1976
 * Sets/Gets internal character encoding of the common string functions within the PHP mbstring extension.
1977
 *
1978
 * @param string $encoding (optional)    When this parameter is given, the function sets the internal encoding
1979
 *
1980
 * @return string When $encoding parameter is not given, the function returns the internal encoding.
1981
 *                Note: This function is used in the global initialization script for setting the
1982
 *                internal encoding to the platform's character set.
1983
 *
1984
 * @see http://php.net/manual/en/function.mb-internal-encoding
1985
 */
1986
function _api_mb_internal_encoding($encoding = 'UTF-8')
1987
{
1988
    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...
1989
}
1990
1991
/**
1992
 * Given a date object, return a human or ISO format, with or without h:m:s.
1993
 *
1994
 * @param object $date      The Date object
1995
 * @param bool   $showTime  Whether to show the time and date (true) or only the date (false)
1996
 * @param bool   $humanForm Whether to show day-month-year (true) or year-month-day (false)
1997
 *
1998
 * @return string Formatted date
1999
 */
2000
function api_get_human_date_time($date, $showTime = true, $humanForm = false)
2001
{
2002
    if ($showTime) {
2003
        if ($humanForm) {
2004
            return $date->format('j M Y H:i:s');
2005
        } else {
2006
            return $date->format('Y-m-d H:i:s');
2007
        }
2008
    } else {
2009
        if ($humanForm) {
2010
            return $date->format('j M Y');
2011
        } else {
2012
            return $date->format('Y-m-d');
2013
        }
2014
    }
2015
}
2016
2017
/**
2018
 * Return an array with the start and end dates of a quarter (as in 3 months period).
2019
 * If no DateTime is not sent, use the current date.
2020
 *
2021
 * @param string|null $date (optional) The date or null.
2022
 *
2023
 * @return array E.G.: ['quarter_start' => '2022-10-11',
2024
 *               'quarter_end' => '2022-12-31',
2025
 *               'quarter_title' => 'Q4 2022']
2026
 */
2027
function getQuarterDates(string $date = null): array
2028
{
2029
    if (empty($date)) {
2030
        $date = api_get_utc_datetime();
2031
    }
2032
    if (strlen($date > 10)) {
2033
        $date = substr($date, 0, 10);
2034
    }
2035
    $month = substr($date, 5, 2);
2036
    $year = substr($date, 0, 4);
2037
    switch ($month) {
2038
        case $month >= 1 && $month <= 3:
2039
            $start = "$year-01-01";
2040
            $end = "$year-03-31";
2041
            $quarter = 1;
2042
            break;
2043
        case $month >= 4 && $month <= 6:
2044
            $start = "$year-04-01";
2045
            $end = "$year-06-30";
2046
            $quarter = 2;
2047
            break;
2048
        case $month >= 7 && $month <= 9:
2049
            $start = "$year-07-01";
2050
            $end = "$year-09-30";
2051
            $quarter = 3;
2052
            break;
2053
        case $month >= 10 && $month <= 12:
2054
            $start = "$year-10-01";
2055
            $end = "$year-12-31";
2056
            $quarter = 4;
2057
            break;
2058
        default:
2059
            // Should never happen
2060
            $start = "$year-01-01";
2061
            $end = "$year-03-31";
2062
            $quarter = 1;
2063
            break;
2064
    }
2065
    return [
2066
        'quarter_start' => $start,
2067
        'quarter_end' => $end,
2068
        'quarter_title' => sprintf(get_lang('Q%s %s'), $quarter, $year),
2069
    ];
2070
}
2071