internationalization.lib.php 94 KB

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