internationalization.lib.php 178 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * File: internationalization.lib.php
  5. * Internationalization library for Chamilo 1.8.7 LMS
  6. * A library implementing internationalization related functions.
  7. * License: GNU General Public License Version 3 (Free Software Foundation)
  8. * @author Ivan Tcholakov, <ivantcholakov@gmail.com>, 2009, 2010
  9. * @author More authors, mentioned in the correpsonding fragments of this source.
  10. * @package chamilo.library
  11. */
  12. /**
  13. * Constants
  14. */
  15. // Special tags for marking untranslated variables.
  16. define('SPECIAL_OPENING_TAG', '[=');
  17. define('SPECIAL_CLOSING_TAG', '=]');
  18. // Predefined date formats in Chamilo provided by the language sub-system.
  19. // To be used as a parameter for the function api_format_date()
  20. define('TIME_NO_SEC_FORMAT', 0); // 15:23
  21. define('DATE_FORMAT_SHORT', 1); // Aug 25, 09
  22. define('DATE_FORMAT_LONG', 2); // Monday August 25, 09
  23. define('DATE_FORMAT_LONG_NO_DAY', 10); // August 25, 2009
  24. define('DATE_TIME_FORMAT_LONG', 3); // Monday August 25, 2009 at 03:28 PM
  25. define('DATE_FORMAT_NUMBER', 4); // 25.08.09
  26. define('DATE_TIME_FORMAT_LONG_24H', 5); // August 25, 2009 at 15:28
  27. define('DATE_TIME_FORMAT_SHORT', 6); // Aug 25, 2009 at 03:28 PM
  28. define('DATE_TIME_FORMAT_SHORT_TIME_FIRST', 7); // 03:28 PM, Aug 25 2009
  29. define('DATE_FORMAT_NUMBER_NO_YEAR', 8); // 25.08 dd-mm
  30. define('DATE_FORMAT_ONLY_DAYNAME', 9); // Monday, Sunday, etc
  31. // Formatting person's name.
  32. define('PERSON_NAME_COMMON_CONVENTION', 0); // Formatting a person's name using the pattern as it has been
  33. // configured in the internationalization database for every language.
  34. // This (default) option would be the most used.
  35. // The followind options may be used in limited number of places for overriding the common convention:
  36. define('PERSON_NAME_WESTERN_ORDER', 1); // Formatting a person's name in Western order: first_name last_name
  37. define('PERSON_NAME_EASTERN_ORDER', 2); // Formatting a person's name in Eastern order: last_name first_name
  38. define('PERSON_NAME_LIBRARY_ORDER', 3); // Contextual: formatting person's name in library order: last_name, first_name
  39. define('PERSON_NAME_EMAIL_ADDRESS', PERSON_NAME_WESTERN_ORDER); // Contextual: formatting a person's name assotiated with an email-address. Ivan: I am not sure how seems email servers an clients would interpret name order, so I assign the Western order.
  40. define('PERSON_NAME_DATA_EXPORT', PERSON_NAME_EASTERN_ORDER); // Contextual: formatting a person's name for data-exporting operarions. For backward compatibility this format has been set to Eastern order.
  41. // The following constants are used for tunning language detection functionality.
  42. // We reduce the text for language detection to the given number of characters
  43. // for increasing speed and to decrease memory consumption.
  44. define ('LANGUAGE_DETECT_MAX_LENGTH', 2000);
  45. // Maximum allowed difference in so called delta-points for aborting certain language detection.
  46. // The value 80000 is good enough for speed and detection accuracy.
  47. // If you set the value of $max_delta too low, no language will be recognized.
  48. // $max_delta = 400 * 350 = 140000 is the best detection with lowest speed.
  49. define ('LANGUAGE_DETECT_MAX_DELTA', 140000);
  50. /**
  51. * Initialization
  52. */
  53. /**
  54. * Initialization of some internal default valies in the internationalization library.
  55. * @return void
  56. * Note: This function should be called only once in the global initialization script.
  57. */
  58. function api_initialize_internationalization()
  59. {
  60. if (MBSTRING_INSTALLED) {
  61. @ini_set('mbstring.func_overload', 0);
  62. @ini_set('mbstring.encoding_translation', 0);
  63. @ini_set('mbstring.http_input', 'pass');
  64. @ini_set('mbstring.http_output', 'pass');
  65. @ini_set('mbstring.language', 'neutral');
  66. }
  67. api_set_internationalization_default_encoding('UTF-8');
  68. }
  69. /**
  70. * Sets the internal default encoding for the multi-byte string functions.
  71. * @param string $encoding The specified default encoding.
  72. * @return string Returns the old value of the default encoding.
  73. */
  74. function api_set_internationalization_default_encoding($encoding)
  75. {
  76. $encoding = api_refine_encoding_id($encoding);
  77. $result = _api_mb_internal_encoding();
  78. _api_mb_internal_encoding($encoding);
  79. _api_mb_regex_encoding($encoding);
  80. _api_iconv_set_encoding('iconv_internal_encoding', $encoding);
  81. return $result;
  82. }
  83. /**
  84. * Language support
  85. */
  86. // These variables are for internal purposes only, they serve the function api_is_translated().
  87. $_api_is_translated = false;
  88. $_api_is_translated_call = false;
  89. /**
  90. * Returns a translated (localized) string, called by its identificator.
  91. * @param string $variable This is the identificator (name) of the translated string to be retrieved.
  92. * @param string $reserved This parameter has been reserved for future use.
  93. * @param string $language (optional) Language indentificator. If it is omited, the current interface language is assumed.
  94. * @return string Returns the requested string in the correspondent language.
  95. *
  96. * @author Roan Embrechts
  97. * @author Patrick Cool
  98. * @author Ivan Tcholakov, 2009-2010 (caching functionality, additional parameter $language, other adaptations).
  99. *
  100. * Notes:
  101. * 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').
  102. * 2. Untranslated variables might be indicated by special opening and closing tags - [= =]
  103. * The special tags do not show up in these two cases:
  104. * - when the system has been switched to "production server mode";
  105. * - when a special platform setting 'hide_dltt_markup' is set to "true" (the name of this setting comes from history);
  106. * 3. Translations are created many contributors through using a special tool: Chamilo Translation Application.
  107. * @link http://translate.chamilo.org/
  108. */
  109. function get_lang($variable, $reserved = null, $language = null)
  110. {
  111. global $app;
  112. $translated = $app['translator']->trans($variable);
  113. if ($translated == $variable) {
  114. // Check the langVariable for BC
  115. $translated = $app['translator']->trans("lang$variable");
  116. if ($translated == "lang$variable") {
  117. return $variable;
  118. }
  119. }
  120. return $translated;
  121. global $app;
  122. $language_interface = isset($app['language_interface']) ? $app['language_interface'] : api_get_language_interface();
  123. global
  124. // For serving some old hacks:
  125. // By manipulating this global variable the translation may be done in different languages too (not the elegant way).
  126. //$language_interface,
  127. // Because of possibility for manipulations of the global variable $language_interface, we need its initial value.
  128. $language_interface_initial_value,
  129. // For serving the function is_translated()
  130. $_api_is_translated, $_api_is_translated_call, $used_lang_vars, $_configuration;
  131. // add language_measure_frequency to your main/inc/conf/configuration.php in order to generate language
  132. // variables frequency measurements (you can then see them trhough main/cron/lang/langstats.php)
  133. // The $langstats object is instanciated at the end of main/inc/global.inc.php
  134. if (isset($_configuration['language_measure_frequency']) && $_configuration['language_measure_frequency'] == 1) {
  135. require_once api_get_path(SYS_CODE_PATH).'/cron/lang/langstats.class.php';
  136. global $langstats;
  137. $langstats->add_use($variable, '');
  138. }
  139. if (!isset($used_lang_vars)) {
  140. $used_lang_vars = array();
  141. }
  142. // Caching results from some API functions, for speed.
  143. static $initialized, $encoding, $is_utf8_encoding, $test_server_mode, $show_special_markup;
  144. if (!isset($initialized)) {
  145. $encoding = api_get_system_encoding();
  146. $is_utf8_encoding = api_is_utf8($encoding);
  147. $test_server_mode = $app['debug'] == true;
  148. $show_special_markup = api_get_setting('hide_dltt_markup') != 'true' || $test_server_mode;
  149. $initialized = true;
  150. }
  151. // Combining both ways for requesting specific language.
  152. if (empty($language)) {
  153. $language = $language_interface;
  154. }
  155. $lang_postfix = isset($is_interface_language) && $is_interface_language ? '' : '('.$language.')';
  156. $is_interface_language = $language == $language_interface_initial_value;
  157. // This is a cache for already translated language variables. By using it, we avoid repetitive translations, gaining speed.
  158. static $cache;
  159. // Looking up into the cache for existing translation.
  160. /*if (isset($cache[$language][$variable]) && !$_api_is_translated_call) {
  161. // There is a previously saved translation, returning it.
  162. //return $cache[$language][$variable];
  163. $ret = $cache[$language][$variable];
  164. $used_lang_vars[$variable.$lang_postfix] = $ret;
  165. return $ret;
  166. }*/
  167. $_api_is_translated = false;
  168. // There is no cached translation, we have to retrieve it:
  169. // - from a global variable (the faster way) - on production server mode;
  170. // - from a local variable after reloading the language files - on test server mode or when requested language is different than the genuine interface language.
  171. $read_global_variables = $is_interface_language && !$test_server_mode && !$_api_is_translated_call;
  172. // Translation mode for production servers.
  173. if ($test_server_mode == true) {
  174. $read_global_variables = true;
  175. if ($read_global_variables) {
  176. if (isset($GLOBALS[$variable])) {
  177. $langvar = $GLOBALS[$variable];
  178. $_api_is_translated = true;
  179. } elseif (isset($GLOBALS["lang$variable"])) {
  180. $langvar = $GLOBALS["lang$variable"];
  181. $_api_is_translated = true;
  182. } else {
  183. $langvar = $show_special_markup ? SPECIAL_OPENING_TAG.$variable.SPECIAL_CLOSING_TAG : $variable;
  184. }
  185. } else {
  186. if (isset($$variable)) {
  187. $langvar = $$variable;
  188. $_api_is_translated = true;
  189. } elseif (isset(${"lang$variable"})) {
  190. $langvar = ${"lang$variable"};
  191. $_api_is_translated = true;
  192. } else {
  193. $langvar = $show_special_markup ? SPECIAL_OPENING_TAG.$variable.SPECIAL_CLOSING_TAG : $variable;
  194. }
  195. }
  196. if (empty($langvar) || !is_string($langvar)) {
  197. $_api_is_translated = false;
  198. $langvar = $show_special_markup ? SPECIAL_OPENING_TAG.$variable.SPECIAL_CLOSING_TAG : $variable;
  199. }
  200. //return $cache[$language][$variable] = $is_utf8_encoding ? $langvar : api_utf8_decode($langvar, $encoding);
  201. $ret = $cache[$language][$variable] = $is_utf8_encoding ? $langvar : api_utf8_decode($langvar, $encoding);
  202. $used_lang_vars[$variable.$lang_postfix] = $ret;
  203. return $ret;
  204. }
  205. /*
  206. // Translation mode for test/development servers.
  207. if (!is_string($variable)) {
  208. //return $cache[$language][$variable] = SPECIAL_OPENING_TAG.'get_lang(?)'.SPECIAL_CLOSING_TAG;
  209. $ret = $cache[$language][$variable] = SPECIAL_OPENING_TAG.'get_lang(?)'.SPECIAL_CLOSING_TAG;
  210. $used_lang_vars[$variable.$lang_postfix] = $ret;
  211. return $ret;
  212. }*/
  213. if (isset($$variable)) {
  214. $langvar = $$variable;
  215. $_api_is_translated = true;
  216. } elseif (isset(${"lang$variable"})) {
  217. $langvar = ${"lang$variable"};
  218. $_api_is_translated = true;
  219. } else {
  220. $langvar = $show_special_markup ? SPECIAL_OPENING_TAG.$variable.SPECIAL_CLOSING_TAG : $variable;
  221. }
  222. if (empty($langvar) || !is_string($langvar)) {
  223. $_api_is_translated = false;
  224. $langvar = $show_special_markup ? SPECIAL_OPENING_TAG.$variable.SPECIAL_CLOSING_TAG : $variable;
  225. }
  226. $ret = $cache[$language][$variable] = $is_utf8_encoding ? $langvar : api_utf8_decode($langvar, $encoding);
  227. $used_lang_vars[$variable.$lang_postfix] = $ret;
  228. return $ret;
  229. }
  230. /**
  231. * Checks whether exists a translated (localized) string.
  232. * @param string $variable This is the identificator (name) of the translated string to be checked.
  233. * @param string $language (optional) Language indentificator. If it is omited, the current interface language is assumed.
  234. * @return bool Returns TRUE if translation exists, FALSE otherwise.
  235. * @author Ivan Tcholakov, 2010.
  236. */
  237. function api_is_translated($variable, $language = null)
  238. {
  239. global $_api_is_translated, $_api_is_translated_call;
  240. $_api_is_translated_call = true;
  241. get_lang($variable, $language);
  242. $_api_is_translated_call = false;
  243. return $_api_is_translated;
  244. }
  245. /**
  246. * Gets the current interface language.
  247. * @param bool $purified (optional) When it is true, a purified (refined) language value will be returned, for example 'french' instead of 'french_unicode'.
  248. * @return string The current language of the interface.
  249. */
  250. function api_get_interface_language($purified = false, $check_sub_language = false)
  251. {
  252. global $language_interface;
  253. if (empty($language_interface)) {
  254. return 'english';
  255. }
  256. //1. Checking if current language is supported
  257. $language_is_supported = api_is_language_supported($language_interface);
  258. if ($check_sub_language && !$language_is_supported) {
  259. static $parent_language_name = null;
  260. if (!isset($parent_language_name)) {
  261. //2. The current language is a sub language so we grab the father's setting according to the internalization_database/name_order_convetions.php file
  262. $language_id = api_get_language_id($language_interface);
  263. $language_info = api_get_language_info($language_id);
  264. if (!empty($language_id) && !empty($language_info)) {
  265. $language_info = api_get_language_info($language_info['parent_id']);
  266. $parent_language_name = $language_info['english_name'];
  267. if (!empty($parent_language_name)) {
  268. return $parent_language_name;
  269. }
  270. }
  271. return 'english';
  272. } else {
  273. return $parent_language_name;
  274. }
  275. } else {
  276. //2. Normal way
  277. $interface_language = $purified ? api_purify_language_id($language_interface) : $language_interface;
  278. }
  279. return $interface_language;
  280. }
  281. /**
  282. * Checks whether a given language identificator represents supported by *this library* language.
  283. * @param string $language The language identificator to be checked ('english', 'french', 'spanish', ...).
  284. * @return bool $language TRUE if the language is supported, FALSE otherwise.
  285. */
  286. function api_is_language_supported($language)
  287. {
  288. static $supported = array();
  289. if (!isset($supported[$language])) {
  290. $supported[$language] = in_array(api_purify_language_id($language), array_keys(_api_non_utf8_encodings()));
  291. }
  292. return $supported[$language];
  293. }
  294. /**
  295. * Validates the input language identificator in order always to return a language that is enabled in the system.
  296. * This function is to be used for data import when provided language identificators should be validated.
  297. * @param string $language The language identificator to be validated.
  298. * @return string Returns the input language identificator. If the input language is not enabled, platform language is returned then.
  299. */
  300. function api_get_valid_language($language)
  301. {
  302. static $enabled_languages;
  303. if (!isset($enabled_languages)) {
  304. $enabled_languages_info = api_get_languages();
  305. $enabled_languages = $enabled_languages_info['folder'];
  306. }
  307. $language = str_replace('_km', '_KM', strtolower(trim($language)));
  308. if (empty($language) || !in_array($language, $enabled_languages) || !api_is_language_supported($language)) {
  309. $language = api_get_setting('platformLanguage');
  310. }
  311. return $language;
  312. }
  313. /**
  314. * Returns a purified language id, without possible suffixes that will disturb language identification in certain cases.
  315. * @param string $language The input language identificator, for example 'french_unicode'.
  316. * @param string The same purified or filtered language identificator, for example 'french'.
  317. */
  318. function api_purify_language_id($language)
  319. {
  320. static $purified = array();
  321. if (!isset($purified[$language])) {
  322. $purified[$language] = trim(
  323. str_replace(array('_unicode', '_latin', '_corporate', '_org', '_km'), '', strtolower($language))
  324. );
  325. }
  326. return $purified[$language];
  327. }
  328. /**
  329. * Gets language isocode column from the language table, taking the given language as a query parameter.
  330. * @param string $language This is the name of the folder containing translations for the corresponding language (e.g arabic, english).
  331. * @param string $default_code This is the value to be returned if there was no code found corresponding to the given language.
  332. * If $language is omitted, interface language is assumed then.
  333. * @return string The found isocode or null on error.
  334. * Returned codes are according to the following standards (in order of preference):
  335. * - ISO 639-1 : Alpha-2 code (two-letters code - en, fr, es, ...)
  336. * - RFC 4646 : five-letter code based on the ISO 639 two-letter language codes
  337. * and the ISO 3166 two-letter territory codes (pt-BR, ...)
  338. * - ISO 639-2 : Alpha-3 code (three-letters code - ast, fur, ...)
  339. */
  340. function api_get_language_isocode($language = null, $default_code = 'en')
  341. {
  342. static $iso_code = array();
  343. if (empty($language)) {
  344. $language = api_get_interface_language(false, true);
  345. }
  346. // Try session
  347. /*if (empty($iso_code)) {
  348. $iso_code = Session::read('_setting.api_get_language_isocode');
  349. }*/
  350. if (!isset($iso_code[$language])) {
  351. $sql = "SELECT isocode FROM ".Database::get_main_table(TABLE_MAIN_LANGUAGE)." WHERE dokeos_folder = '$language'";
  352. $sql_result = Database::query($sql);
  353. if (Database::num_rows($sql_result)) {
  354. $result = Database::fetch_array($sql_result);
  355. $iso_code[$language] = trim($result['isocode']);
  356. } else {
  357. $language_purified_id = api_purify_language_id($language);
  358. $iso_code[$language] = isset($iso_code[$language_purified_id]) ? $iso_code[$language_purified_id] : null;
  359. }
  360. if (empty($iso_code[$language])) {
  361. $iso_code[$language] = $default_code;
  362. }
  363. //Session::write('_setting.api_get_language_isocode', $iso_code);
  364. }
  365. return $iso_code[$language];
  366. }
  367. /**
  368. * Gets language isocode column from the language table
  369. *
  370. * @return array An array with the current isocodes
  371. *
  372. * */
  373. function api_get_platform_isocodes()
  374. {
  375. $iso_code = array();
  376. $sql_result = Database::query(
  377. "SELECT isocode FROM ".Database::get_main_table(TABLE_MAIN_LANGUAGE)." ORDER BY isocode "
  378. );
  379. if (Database::num_rows($sql_result)) {
  380. while ($row = Database::fetch_array($sql_result)) {
  381. $iso_code[] = trim($row['isocode']);
  382. }
  383. }
  384. return $iso_code;
  385. }
  386. /**
  387. * Gets text direction according to the given language.
  388. * @param string $language This is the name of the folder containing translations for the corresponding language (e.g 'arabic', 'english', ...).
  389. * ISO-codes are acceptable too ('ar', 'en', ...). If $language is omitted, interface language is assumed then.
  390. * @return string The correspondent to the language text direction ('ltr' or 'rtl').
  391. */
  392. function api_get_text_direction($language = null)
  393. {
  394. static $text_direction = array();
  395. /*
  396. * Not necessary to validate the language because the list if rtl/ltr is harcoded
  397. *
  398. /*
  399. $language_is_supported = api_is_language_supported($language);
  400. if (!$language_is_supported || empty($language)) {
  401. $language = api_get_interface_language(false, true);
  402. }*/
  403. if (empty($language)) {
  404. $language = api_get_interface_language();
  405. }
  406. if (!isset($text_direction[$language])) {
  407. $text_direction[$language] = in_array(
  408. api_purify_language_id($language),
  409. array(
  410. 'arabic',
  411. 'ar',
  412. 'dari',
  413. 'prs',
  414. 'hebrew',
  415. 'he',
  416. 'iw',
  417. 'pashto',
  418. 'ps',
  419. 'persian',
  420. 'fa',
  421. 'ur',
  422. 'yiddish',
  423. 'yid'
  424. )
  425. ) ? 'rtl' : 'ltr';
  426. }
  427. return $text_direction[$language];
  428. }
  429. /**
  430. * This function checks whether a given language can use Latin 1 encoding.
  431. * In the past (Chamilo 1.8.6.2), the function was used in the installation script only once.
  432. * It is not clear whether this function would be use useful for something else in the future.
  433. * @param string $language The checked language.
  434. * @return bool TRUE if the given language can use Latin 1 encoding (ISO-8859-15, ISO-8859-1, WINDOWS-1252, ...), FALSE otherwise.
  435. */
  436. function api_is_latin1_compatible($language)
  437. {
  438. static $latin1_languages;
  439. if (!isset($latin1_languages)) {
  440. $latin1_languages = _api_get_latin1_compatible_languages();
  441. }
  442. $language = api_purify_language_id($language);
  443. return in_array($language, $latin1_languages);
  444. }
  445. /**
  446. * Language recognition
  447. * Based on the publication:
  448. * W. B. Cavnar and J. M. Trenkle. N-gram-based text categorization.
  449. * Proceedings of SDAIR-94, 3rd Annual Symposium on Document Analysis
  450. * and Information Retrieval, 1994.
  451. * @link http://citeseer.ist.psu.edu/cache/papers/cs/810/http:zSzzSzwww.info.unicaen.frzSz~giguetzSzclassifzSzcavnar_trenkle_ngram.pdf/n-gram-based-text.pdf
  452. */
  453. function api_detect_language(&$string, $encoding = null)
  454. {
  455. if (empty($encoding)) {
  456. $encoding = _api_mb_internal_encoding();
  457. }
  458. if (empty($string)) {
  459. return false;
  460. }
  461. $result_array = & _api_compare_n_grams(
  462. _api_generate_n_grams(api_substr($string, 0, LANGUAGE_DETECT_MAX_LENGTH, $encoding), $encoding),
  463. $encoding
  464. );
  465. if (empty($result_array)) {
  466. return false;
  467. }
  468. list($key, $delta_points) = each($result_array);
  469. return strstr($key, ':', true);
  470. }
  471. /**
  472. * Date and time conversions and formats
  473. */
  474. /**
  475. * Returns an alphabetized list of timezones in an associative array that can be used to populate a select
  476. *
  477. * @return array List of timezone identifiers
  478. *
  479. * @author Guillaume Viguier <guillaume.viguier@beeznest.com>
  480. */
  481. function api_get_timezones()
  482. {
  483. $timezone_identifiers = DateTimeZone::listIdentifiers();
  484. sort($timezone_identifiers);
  485. $out = array();
  486. foreach ($timezone_identifiers as $tz) {
  487. $out[$tz] = $tz;
  488. }
  489. $null_option = array('' => '');
  490. $result = array_merge($null_option, $out);
  491. return $result;
  492. }
  493. /**
  494. * Returns the timezone to be converted to/from, based on user or admin preferences
  495. *
  496. * @return string The timezone chosen
  497. */
  498. function _api_get_timezone()
  499. {
  500. $userId = api_get_user_id();
  501. // First, get the default timezone of the server
  502. $to_timezone = date_default_timezone_get();
  503. // Second, see if a timezone has been chosen for the platform
  504. $timezone_value = api_get_setting('timezone_value', 'timezones');
  505. if ($timezone_value != null) {
  506. $to_timezone = $timezone_value;
  507. }
  508. // If allowed by the administrator
  509. $use_users_timezone = api_get_setting('use_users_timezone', 'timezones');
  510. if ($use_users_timezone == 'true' && !empty($userId) && !api_is_anonymous()) {
  511. $userInfo = api_get_user_info();
  512. $extraFields = $userInfo['extra_fields'];
  513. // Get the timezone based on user preference, if it exists
  514. // $timezone_user = UserManager::get_extra_user_data_by_field($userId, 'timezone');
  515. if (isset($extraFields['extra_timezone']) && $extraFields['extra_timezone'] != null) {
  516. $to_timezone = $extraFields['extra_timezone'];
  517. }
  518. }
  519. return $to_timezone;
  520. }
  521. /**
  522. * Returns the given date as a DATETIME in UTC timezone. This function should be used before entering any date in the DB.
  523. *
  524. * @param mixed The date to be converted (can be a string supported by date() or a timestamp)
  525. * @param bool if the date is not correct return null instead of the current date
  526. * @return string The DATETIME in UTC to be inserted in the DB, or null if the format of the argument is not supported
  527. *
  528. * @author Julio Montoya - Adding the 2nd parameter
  529. * @author Guillaume Viguier <guillaume.viguier@beeznest.com>
  530. */
  531. function api_get_utc_datetime($time = null, $return_null_if_invalid_date = false, $returnObj = false)
  532. {
  533. $from_timezone = _api_get_timezone();
  534. $to_timezone = 'UTC';
  535. if (is_null($time) || empty($time) || $time == '0000-00-00 00:00:00') {
  536. if ($return_null_if_invalid_date) {
  537. return null;
  538. }
  539. if ($returnObj) {
  540. return $date = new DateTime(gmdate('Y-m-d H:i:s'));
  541. }
  542. return gmdate('Y-m-d H:i:s');
  543. }
  544. // If time is a timestamp, return directly in utc
  545. if (is_numeric($time)) {
  546. $time = intval($time);
  547. return gmdate('Y-m-d H:i:s', $time);
  548. }
  549. try {
  550. $date = new DateTime($time, new DateTimezone($from_timezone));
  551. $date->setTimezone(new DateTimeZone($to_timezone));
  552. if ($returnObj) {
  553. return $date;
  554. } else {
  555. return $date->format('Y-m-d H:i:s');
  556. }
  557. } catch (Exception $e) {
  558. return null;
  559. }
  560. }
  561. /**
  562. * Returns a DATETIME string converted to the right timezone
  563. * @param mixed The time to be converted
  564. * @param string The timezone to be converted to. If null, the timezone will be determined based on user preference, or timezone chosen by the admin for the platform.
  565. * @param string The timezone to be converted from. If null, UTC will be assumed.
  566. * @return string The converted time formatted as Y-m-d H:i:s
  567. *
  568. * @author Guillaume Viguier <guillaume.viguier@beeznest.com>
  569. */
  570. function api_get_local_time(
  571. $time = null,
  572. $to_timezone = null,
  573. $from_timezone = null,
  574. $return_null_if_invalid_date = false
  575. ) {
  576. // Determining the timezone to be converted from
  577. if (is_null($from_timezone)) {
  578. $from_timezone = 'UTC';
  579. }
  580. // Determining the timezone to be converted to
  581. if (is_null($to_timezone)) {
  582. $to_timezone = _api_get_timezone();
  583. }
  584. // If time is a timestamp, convert it to a string
  585. if (is_null($time) || empty($time) || $time == '0000-00-00 00:00:00') {
  586. if ($return_null_if_invalid_date) {
  587. return null;
  588. }
  589. $from_timezone = 'UTC';
  590. $time = gmdate('Y-m-d H:i:s');
  591. }
  592. if (is_numeric($time)) {
  593. $time = intval($time);
  594. $from_timezone = 'UTC';
  595. $time = gmdate('Y-m-d H:i:s', $time);
  596. }
  597. try {
  598. $date = new DateTime($time, new DateTimezone($from_timezone));
  599. $date->setTimezone(new DateTimeZone($to_timezone));
  600. return $date->format('Y-m-d H:i:s');
  601. } catch (Exception $e) {
  602. return null;
  603. }
  604. }
  605. /**
  606. * Converts a string into a timestamp safely (handling timezones), using strtotime
  607. *
  608. * @param string String to be converted
  609. * @param string Timezone (if null, the timezone will be determined based on user preference, or timezone chosen by the admin for the platform)
  610. * @return int Timestamp
  611. *
  612. * @author Guillaume Viguier <guillaume.viguier@beeznest.com>
  613. */
  614. function api_strtotime($time, $timezone = null)
  615. {
  616. $system_timezone = date_default_timezone_get();
  617. if (!empty($timezone)) {
  618. date_default_timezone_set($timezone);
  619. }
  620. $timestamp = strtotime($time);
  621. date_default_timezone_set($system_timezone);
  622. return $timestamp;
  623. }
  624. /**
  625. * Returns formated date/time, correspondent to a given language.
  626. * The given date should be in the timezone chosen by the administrator and/or user. Use api_get_local_time to get it.
  627. *
  628. * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  629. * @author Christophe Gesche<gesche@ipm.ucl.ac.be>
  630. * originally inspired from from PhpMyAdmin
  631. * @author Ivan Tcholakov, 2009, code refactoring, adding support for predefined date/time formats.
  632. * @author Guillaume Viguier <guillaume.viguier@beeznest.com>
  633. *
  634. * @param mixed Timestamp or datetime string
  635. * @param mixed Date format (string or int; see date formats in the Chamilo system: TIME_NO_SEC_FORMAT, DATE_FORMAT_SHORT, DATE_FORMAT_LONG, DATE_TIME_FORMAT_LONG)
  636. * @param string $language (optional) Language indentificator. If it is omited, the current interface language is assumed.
  637. * @return string Returns the formatted date.
  638. *
  639. * @link http://php.net/manual/en/function.strftime.php
  640. */
  641. function api_format_date($time, $format = null, $language = null)
  642. {
  643. $system_timezone = date_default_timezone_get();
  644. date_default_timezone_set(_api_get_timezone());
  645. if (is_string($time)) {
  646. $time = strtotime($time);
  647. }
  648. if (is_null($format)) {
  649. $format = DATE_TIME_FORMAT_LONG;
  650. }
  651. $datetype = null;
  652. $timetype = null;
  653. if (is_int($format)) {
  654. switch ($format) {
  655. case DATE_FORMAT_ONLY_DAYNAME:
  656. $date_format = get_lang('dateFormatOnlyDayName', '', $language);
  657. if (IS_PHP_53 && INTL_INSTALLED) {
  658. $datetype = IntlDateFormatter::SHORT;
  659. $timetype = IntlDateFormatter::NONE;
  660. }
  661. break;
  662. case DATE_FORMAT_NUMBER_NO_YEAR:
  663. $date_format = get_lang('dateFormatShortNumberNoYear', '', $language);
  664. if (IS_PHP_53 && INTL_INSTALLED) {
  665. $datetype = IntlDateFormatter::SHORT;
  666. $timetype = IntlDateFormatter::NONE;
  667. }
  668. break;
  669. case DATE_FORMAT_NUMBER:
  670. $date_format = get_lang('dateFormatShortNumber', '', $language);
  671. if (IS_PHP_53 && INTL_INSTALLED) {
  672. $datetype = IntlDateFormatter::SHORT;
  673. $timetype = IntlDateFormatter::NONE;
  674. }
  675. break;
  676. case TIME_NO_SEC_FORMAT:
  677. $date_format = get_lang('timeNoSecFormat', '', $language);
  678. if (IS_PHP_53 && INTL_INSTALLED) {
  679. $datetype = IntlDateFormatter::NONE;
  680. $timetype = IntlDateFormatter::SHORT;
  681. }
  682. break;
  683. case DATE_FORMAT_SHORT:
  684. $date_format = get_lang('dateFormatShort', '', $language);
  685. if (IS_PHP_53 && INTL_INSTALLED) {
  686. $datetype = IntlDateFormatter::LONG;
  687. $timetype = IntlDateFormatter::NONE;
  688. }
  689. break;
  690. case DATE_FORMAT_LONG:
  691. $date_format = get_lang('dateFormatLong', '', $language);
  692. if (IS_PHP_53 && INTL_INSTALLED) {
  693. $datetype = IntlDateFormatter::FULL;
  694. $timetype = IntlDateFormatter::NONE;
  695. }
  696. break;
  697. case DATE_TIME_FORMAT_LONG:
  698. $date_format = get_lang('dateTimeFormatLong', '', $language);
  699. if (IS_PHP_53 && INTL_INSTALLED) {
  700. $datetype = IntlDateFormatter::FULL;
  701. $timetype = IntlDateFormatter::SHORT;
  702. }
  703. break;
  704. case DATE_FORMAT_LONG_NO_DAY:
  705. $date_format = get_lang('dateFormatLongNoDay', '', $language);
  706. if (IS_PHP_53 && INTL_INSTALLED) {
  707. $datetype = IntlDateFormatter::FULL;
  708. $timetype = IntlDateFormatter::SHORT;
  709. }
  710. break;
  711. case DATE_TIME_FORMAT_SHORT:
  712. $date_format = get_lang('dateTimeFormatShort', '', $language);
  713. if (IS_PHP_53 && INTL_INSTALLED) {
  714. $datetype = IntlDateFormatter::FULL;
  715. $timetype = IntlDateFormatter::SHORT;
  716. }
  717. break;
  718. case DATE_TIME_FORMAT_SHORT_TIME_FIRST:
  719. $date_format = get_lang('dateTimeFormatShortTimeFirst', '', $language);
  720. if (IS_PHP_53 && INTL_INSTALLED) {
  721. $datetype = IntlDateFormatter::FULL;
  722. $timetype = IntlDateFormatter::SHORT;
  723. }
  724. break;
  725. case DATE_TIME_FORMAT_LONG_24H:
  726. $date_format = get_lang('dateTimeFormatLong24H', '', $language);
  727. if (IS_PHP_53 && INTL_INSTALLED) {
  728. $datetype = IntlDateFormatter::FULL;
  729. $timetype = IntlDateFormatter::SHORT;
  730. }
  731. break;
  732. default:
  733. $date_format = get_lang('dateTimeFormatLong', '', $language);
  734. if (IS_PHP_53 && INTL_INSTALLED) {
  735. $datetype = IntlDateFormatter::FULL;
  736. $timetype = IntlDateFormatter::SHORT;
  737. }
  738. }
  739. } else {
  740. $date_format = $format;
  741. }
  742. //if (IS_PHP_53 && INTL_INSTALLED && $datetype !== null && $timetype !== null) {
  743. if (0) {
  744. //if using PHP 5.3 format dates like: $dateFormatShortNumber, can't be used
  745. //
  746. // Use ICU
  747. if (is_null($language)) {
  748. $language = api_get_language_isocode();
  749. }
  750. /*$date_formatter = datefmt_create($language, $datetype, $timetype, date_default_timezone_get());
  751. $formatted_date = api_to_system_encoding(datefmt_format($date_formatter, $time), 'UTF-8');*/
  752. $date_formatter = new IntlDateFormatter($language, $datetype, $timetype, date_default_timezone_get());
  753. //$date_formatter->setPattern($date_format);
  754. $formatted_date = api_to_system_encoding($date_formatter->format($time), 'UTF-8');
  755. } else {
  756. // We replace %a %A %b %B masks of date format with translated strings
  757. $translated = & _api_get_day_month_names($language);
  758. $date_format = str_replace(
  759. array('%A', '%a', '%B', '%b'),
  760. array(
  761. $translated['days_long'][(int)strftime('%w', $time)],
  762. $translated['days_short'][(int)strftime('%w', $time)],
  763. $translated['months_long'][(int)strftime('%m', $time) - 1],
  764. $translated['months_short'][(int)strftime('%m', $time) - 1]
  765. ),
  766. $date_format
  767. );
  768. $formatted_date = api_to_system_encoding(strftime($date_format, $time), 'UTF-8');
  769. }
  770. date_default_timezone_set($system_timezone);
  771. return $formatted_date;
  772. }
  773. /**
  774. * Returns the difference between the current date (date(now)) with the parameter $date in a string format like "2 days, 1 hour"
  775. * Example: $date = '2008-03-07 15:44:08';
  776. * date_to_str($date) it will return 3 days, 20 hours
  777. * The given date should be in the timezone chosen by the user or administrator. Use api_get_local_time() to get it...
  778. *
  779. * @param string The string has to be the result of a date function in this format -> date('Y-m-d H:i:s', time());
  780. * @return string The difference between the current date and the parameter in a literal way "3 days, 2 hour" *
  781. * @author Julio Montoya
  782. */
  783. function date_to_str_ago($date)
  784. {
  785. static $initialized = false;
  786. static $today, $yesterday;
  787. static $min_decade, $min_year, $min_month, $min_week, $min_day, $min_hour, $min_minute;
  788. static $min_decades, $min_years, $min_months, $min_weeks, $min_days, $min_hours, $min_minutes;
  789. static $sec_time_time, $sec_time_sing, $sec_time_plu;
  790. $system_timezone = date_default_timezone_get();
  791. date_default_timezone_set(_api_get_timezone());
  792. if (!$initialized) {
  793. $today = get_lang('Today');
  794. $yesterday = get_lang('Yesterday');
  795. $min_decade = get_lang('MinDecade');
  796. $min_year = get_lang('MinYear');
  797. $min_month = get_lang('MinMonth');
  798. $min_week = get_lang('MinWeek');
  799. $min_day = get_lang('MinDay');
  800. $min_hour = get_lang('MinHour');
  801. $min_minute = get_lang('MinMinute');
  802. $min_decades = get_lang('MinDecades');
  803. $min_years = get_lang('MinYears');
  804. $min_months = get_lang('MinMonths');
  805. $min_weeks = get_lang('MinWeeks');
  806. $min_days = get_lang('MinDays');
  807. $min_hours = get_lang('MinHours');
  808. $min_minutes = get_lang('MinMinutes');
  809. // original 1
  810. //$sec_time=array('century'=>3.1556926*pow(10,9),'decade'=>315569260,'year'=>31556926,'month'=>2629743.83,'week'=>604800,'day'=>86400,'hour'=>3600,'minute'=>60,'second'=>1);
  811. //$sec_time=array(get_lang('MinDecade')=>315569260,get_lang('MinYear')=>31556926,get_lang('MinMonth')=>2629743.83,get_lang('MinWeek')=>604800,get_lang('MinDay')=>86400,get_lang('MinHour')=>3600,get_lang('MinMinute')=>60);
  812. $sec_time_time = array(315569260, 31556926, 2629743.83, 604800, 86400, 3600, 60);
  813. $sec_time_sing = array($min_decade, $min_year, $min_month, $min_week, $min_day, $min_hour, $min_minute);
  814. $sec_time_plu = array($min_decades, $min_years, $min_months, $min_weeks, $min_days, $min_hours, $min_minutes);
  815. $initialized = true;
  816. }
  817. $dst_date = is_string($date) ? strtotime($date) : $date;
  818. // For avoiding calling date() several times
  819. $date_array = date('s/i/G/j/n/Y', $dst_date);
  820. $date_split = explode('/', $date_array);
  821. $dst_s = $date_split[0];
  822. $dst_m = $date_split[1];
  823. $dst_h = $date_split[2];
  824. $dst_day = $date_split[3];
  825. $dst_mth = $date_split[4];
  826. $dst_yr = $date_split[5];
  827. $dst_date = mktime($dst_h, $dst_m, $dst_s, $dst_mth, $dst_day, $dst_yr);
  828. $time = $offset = time() - $dst_date; // Seconds between current days and today.
  829. // Here start the functions sec_to_str()
  830. $act_day = date('d');
  831. $act_mth = date('n');
  832. $act_yr = date('Y');
  833. if ($dst_day == $act_day && $dst_mth == $act_mth && $dst_yr == $act_yr) {
  834. return $today;
  835. }
  836. if ($dst_day == $act_day - 1 && $dst_mth == $act_mth && $dst_yr == $act_yr) {
  837. return $yesterday;
  838. }
  839. $str_result = array();
  840. $time_result = array();
  841. $key_result = array();
  842. $str = '';
  843. $i = 0;
  844. for ($i = 0; $i < count($sec_time_time); $i++) {
  845. $seconds = $sec_time_time[$i];
  846. if ($seconds > $time) {
  847. continue;
  848. }
  849. $current_value = intval($time / $seconds);
  850. if ($current_value != 1) {
  851. $date_str = $sec_time_plu[$i];
  852. } else {
  853. $date_str = $sec_time_sing[$i];
  854. }
  855. $key_result[] = $sec_time_sing[$i];
  856. $str_result[] = $current_value.' '.$date_str;
  857. $time_result[] = $current_value;
  858. $str .= $current_value.$date_str;
  859. $time %= $seconds;
  860. }
  861. if ($key_result[0] == $min_day && $key_result[1] == $min_minute) {
  862. $key_result[1] = ' 0 '.$min_hours;
  863. $str_result[0] = $time_result[0].' '.$key_result[0];
  864. $str_result[1] = $key_result[1];
  865. }
  866. if ($key_result[0] == $min_year && ($key_result[1] == $min_day || $key_result[1] == $min_week)) {
  867. $key_result[1] = ' 0 '.$min_months;
  868. $str_result[0] = $time_result[0].' '.$key_result[0];
  869. $str_result[1] = $key_result[1];
  870. }
  871. if (!empty($str_result[1])) {
  872. $str = $str_result[0].', '.$str_result[1];
  873. } else {
  874. $str = $str_result[0];
  875. }
  876. date_default_timezone_set($system_timezone);
  877. return $str;
  878. }
  879. /**
  880. * Converts a date to the right timezone and localizes it in the format given as an argument
  881. * @param mixed The time to be converted
  882. * @param mixed Format to be used (TIME_NO_SEC_FORMAT, DATE_FORMAT_SHORT, DATE_FORMAT_LONG, DATE_TIME_FORMAT_LONG)
  883. * @param string Timezone to be converted from. If null, UTC will be assumed.
  884. * @return string Converted and localized date
  885. *
  886. * @author Guillaume Viguier <guillaume.viguier@beeznest.com>
  887. */
  888. function api_convert_and_format_date($time = null, $format = null, $from_timezone = null)
  889. {
  890. // First, convert the datetime to the right timezone
  891. $time = api_get_local_time($time, null, $from_timezone);
  892. // Second, localize the date
  893. return api_format_date($time, $format);
  894. }
  895. /**
  896. * Returns an array of translated week days in short names.
  897. * @param string $language (optional) Language indentificator. If it is omited, the current interface language is assumed.
  898. * @return string Returns an array of week days (short names).
  899. * Example: api_get_week_days_short('english') means array('Sun', 'Mon', ... 'Sat').
  900. * Note: For all languges returned days are in the English order.
  901. */
  902. function api_get_week_days_short($language = null)
  903. {
  904. $days = & _api_get_day_month_names($language);
  905. return $days['days_short'];
  906. }
  907. /**
  908. * Returns an array of translated week days.
  909. * @param string $language (optional) Language indentificator. If it is omited, the current interface language is assumed.
  910. * @return string Returns an array of week days.
  911. * Example: api_get_week_days_long('english') means array('Sunday, 'Monday', ... 'Saturday').
  912. * Note: For all languges returned days are in the English order.
  913. */
  914. function api_get_week_days_long($language = null)
  915. {
  916. $days = & _api_get_day_month_names($language);
  917. return $days['days_long'];
  918. }
  919. /**
  920. * Returns an array of translated months in short names.
  921. * @param string $language (optional) Language indentificator. If it is omited, the current interface language is assumed.
  922. * @return string Returns an array of months (short names).
  923. * Example: api_get_months_short('english') means array('Jan', 'Feb', ... 'Dec').
  924. */
  925. function api_get_months_short($language = null)
  926. {
  927. $months = & _api_get_day_month_names($language);
  928. return $months['months_short'];
  929. }
  930. /**
  931. * Returns an array of translated months.
  932. * @param string $language (optional) Language indentificator. If it is omited, the current interface language is assumed.
  933. * @return string Returns an array of months.
  934. * Example: api_get_months_long('english') means array('January, 'February' ... 'December').
  935. */
  936. function api_get_months_long($language = null)
  937. {
  938. $months = & _api_get_day_month_names($language);
  939. return $months['months_long'];
  940. }
  941. /**
  942. * Name order conventions
  943. */
  944. /**
  945. * Builds a person (full) name depending on the convention for a given language.
  946. * @param string $first_name The first name of the preson.
  947. * @param string $last_name The last name of the person.
  948. * @param string $title The title of the person.
  949. * @param int/string $format (optional) The person name format. It may be a pattern-string (for example '%t %l, %f' or '%T %F %L', ...) or some of the constants PERSON_NAME_COMMON_CONVENTION (default), PERSON_NAME_WESTERN_ORDER, PERSON_NAME_EASTERN_ORDER, PERSON_NAME_LIBRARY_ORDER.
  950. * @param string $language (optional) The language identificator. if it is omitted, the current interface language is assumed. This parameter has meaning with the format PERSON_NAME_COMMON_CONVENTION only.
  951. * @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.
  952. * @return bool The result is sort of full name of the person.
  953. * Sample results:
  954. * Peter Ustinoff or Dr. Peter Ustinoff - the Western order
  955. * Ustinoff Peter or Dr. Ustinoff Peter - the Eastern order
  956. * Ustinoff, Peter or - Dr. Ustinoff, Peter - the library order
  957. * Note: See the file chamilo/main/inc/lib/internationalization_database/name_order_conventions.php where you can revise the convention for your language.
  958. * @author Carlos Vargas <carlos.vargas@dokeos.com> - initial implementation.
  959. * @author Ivan Tcholakov
  960. */
  961. function api_get_person_name($first_name, $last_name, $title = null, $format = null, $language = null, $encoding = null)
  962. {
  963. static $valid = array();
  964. if (empty($format)) {
  965. $format = PERSON_NAME_COMMON_CONVENTION;
  966. }
  967. //We check if the language is supported, otherwise we check the interface language of the parent language of sublanguage
  968. $language_is_supported = api_is_language_supported($language);
  969. if (!$language_is_supported || empty($language)) {
  970. $language = api_get_interface_language(false, true);
  971. }
  972. if (empty($encoding)) {
  973. $encoding = _api_mb_internal_encoding();
  974. }
  975. if (!isset($valid[$format][$language])) {
  976. if (is_int($format)) {
  977. switch ($format) {
  978. case PERSON_NAME_COMMON_CONVENTION:
  979. $valid[$format][$language] = _api_get_person_name_convention($language, 'format');
  980. $usernameOrderFromDatabase = api_get_setting('user_name_order');
  981. if (isset($usernameOrderFromDatabase) && !empty($usernameOrderFromDatabase)) {
  982. $valid[$format][$language] = $usernameOrderFromDatabase;
  983. }
  984. break;
  985. case PERSON_NAME_WESTERN_ORDER:
  986. $valid[$format][$language] = '%t %f %l';
  987. break;
  988. case PERSON_NAME_EASTERN_ORDER:
  989. $valid[$format][$language] = '%t %l %f';
  990. break;
  991. case PERSON_NAME_LIBRARY_ORDER:
  992. $valid[$format][$language] = '%t %l, %f';
  993. break;
  994. default:
  995. $valid[$format][$language] = '%t %f %l';
  996. break;
  997. }
  998. } else {
  999. $valid[$format][$language] = _api_validate_person_name_format($format);
  1000. }
  1001. }
  1002. $format = $valid[$format][$language];
  1003. $person_name = str_replace(array('%f', '%l', '%t'), array($first_name, $last_name, $title), $format);
  1004. if (strpos($format, '%F') !== false || strpos($format, '%L') !== false || strpos($format, '%T') !== false) {
  1005. $person_name = str_replace(
  1006. array(
  1007. '%F',
  1008. '%L',
  1009. '%T'
  1010. ),
  1011. array(
  1012. api_strtoupper($first_name, $encoding),
  1013. api_strtoupper($last_name, $encoding),
  1014. api_strtoupper($title, $encoding)
  1015. ),
  1016. $person_name
  1017. );
  1018. }
  1019. return _api_clean_person_name($person_name);
  1020. }
  1021. /**
  1022. * Checks whether a given format represents person name in Western order (for which first name is first).
  1023. * @param int/string $format (optional) The person name format. It may be a pattern-string (for example '%t. %l, %f') or some of the constants PERSON_NAME_COMMON_CONVENTION (default), PERSON_NAME_WESTERN_ORDER, PERSON_NAME_EASTERN_ORDER, PERSON_NAME_LIBRARY_ORDER.
  1024. * @param string $language (optional) The language indentificator. If it is omited, the current interface language is assumed. This parameter has meaning with the format PERSON_NAME_COMMON_CONVENTION only.
  1025. * @return bool The result TRUE means that the order is first_name last_name, FALSE means last_name first_name.
  1026. * Note: You may use this function for determing the order of the fields or columns "First name" and "Last name" in forms, tables and reports.
  1027. * @author Ivan Tcholakov
  1028. */
  1029. function api_is_western_name_order($format = null, $language = null)
  1030. {
  1031. static $order = array();
  1032. if (empty($format)) {
  1033. $format = PERSON_NAME_COMMON_CONVENTION;
  1034. }
  1035. $language_is_supported = api_is_language_supported($language);
  1036. if (!$language_is_supported || empty($language)) {
  1037. $language = api_get_interface_language(false, true);
  1038. }
  1039. if (!isset($order[$format][$language])) {
  1040. $test_name = api_get_person_name('%f', '%l', '%t', $format, $language);
  1041. $order[$format][$language] = stripos($test_name, '%f') <= stripos($test_name, '%l');
  1042. }
  1043. return $order[$format][$language];
  1044. }
  1045. /**
  1046. * Returns a directive for sorting person names depending on a given language and based on the options in the internationalization "database".
  1047. * @param string $language (optional) The input language. If it is omited, the current interface language is assumed.
  1048. * @return bool Returns boolean value. TRUE means ORDER BY first_name, last_name; FALSE means ORDER BY last_name, first_name.
  1049. * Note: You may use this function:
  1050. * 2. for constructing the ORDER clause of SQL queries, related to first_name and last_name;
  1051. * 3. for adjusting php-implemented sorting in tables and reports.
  1052. * @author Ivan Tcholakov
  1053. */
  1054. function api_sort_by_first_name($language = null)
  1055. {
  1056. $userNameSortBy = api_get_setting('user_name_sort_by');
  1057. if (!empty($userNameSortBy) && in_array($userNameSortBy, array('firstname', 'lastname'))) {
  1058. return $userNameSortBy == 'firstname' ? true : false;
  1059. }
  1060. static $sort_by_first_name = array();
  1061. $language_is_supported = api_is_language_supported($language);
  1062. if (!$language_is_supported || empty($language)) {
  1063. $language = api_get_interface_language(false, true);
  1064. }
  1065. if (!isset($sort_by_first_name[$language])) {
  1066. $sort_by_first_name[$language] = _api_get_person_name_convention($language, 'sort_by');
  1067. }
  1068. return $sort_by_first_name[$language];
  1069. }
  1070. /**
  1071. * A safe way to calculate binary lenght of a string (as number of bytes)
  1072. */
  1073. /**
  1074. * Calculates binary lenght of a string, as number of bytes, regardless the php-setting mbstring.func_overload.
  1075. * This function should work for all multi-byte related changes of PHP5 configuration.
  1076. * @param string $string The input string.
  1077. * @return int Returns the length of the input string (or binary data) as number of bytes.
  1078. */
  1079. function api_byte_count(& $string)
  1080. {
  1081. static $use_mb_strlen;
  1082. if (!isset($use_mb_strlen)) {
  1083. $use_mb_strlen = MBSTRING_INSTALLED && ((int)ini_get('mbstring.func_overload') & 2);
  1084. }
  1085. if ($use_mb_strlen) {
  1086. return mb_strlen($string, '8bit');
  1087. }
  1088. return strlen($string);
  1089. }
  1090. /**
  1091. * Multibyte string conversion functions
  1092. */
  1093. /**
  1094. * Converts character encoding of a given string.
  1095. * @param string $string The string being converted.
  1096. * @param string $to_encoding The encoding that $string is being converted to.
  1097. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  1098. * @return string Returns the converted string.
  1099. * This function is aimed at replacing the function mb_convert_encoding() for human-language strings.
  1100. * @link http://php.net/manual/en/function.mb-convert-encoding
  1101. */
  1102. function api_convert_encoding($string, $to_encoding, $from_encoding = null)
  1103. {
  1104. if (empty($from_encoding)) {
  1105. $from_encoding = _api_mb_internal_encoding();
  1106. }
  1107. if (api_equal_encodings($to_encoding, $from_encoding)) {
  1108. return $string; // When conversion is not needed, the string is returned directly, without validation.
  1109. }
  1110. if (_api_mb_supports($to_encoding) && _api_mb_supports($from_encoding)) {
  1111. return @mb_convert_encoding($string, $to_encoding, $from_encoding);
  1112. }
  1113. if (_api_iconv_supports($to_encoding) && _api_iconv_supports($from_encoding)) {
  1114. return @iconv($from_encoding, $to_encoding, $string);
  1115. }
  1116. if (api_is_utf8($to_encoding) && api_is_latin1($from_encoding, true)) {
  1117. return utf8_encode($string);
  1118. }
  1119. if (api_is_latin1($to_encoding, true) && api_is_utf8($from_encoding)) {
  1120. return utf8_decode($string);
  1121. }
  1122. if (_api_convert_encoding_supports($to_encoding) && _api_convert_encoding_supports($from_encoding)) {
  1123. return _api_convert_encoding($string, $to_encoding, $from_encoding);
  1124. }
  1125. return $string; // Here the function gives up.
  1126. }
  1127. /**
  1128. * Converts a given string into UTF-8 encoded string.
  1129. * @param string $string The string being converted.
  1130. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  1131. * @return string Returns the converted string.
  1132. * This function is aimed at replacing the function utf8_encode() for human-language strings.
  1133. * @link http://php.net/manual/en/function.utf8-encode
  1134. */
  1135. function api_utf8_encode($string, $from_encoding = null)
  1136. {
  1137. if (empty($from_encoding)) {
  1138. $from_encoding = _api_mb_internal_encoding();
  1139. }
  1140. if (api_is_utf8($from_encoding)) {
  1141. return $string; // When conversion is not needed, the string is returned directly, without validation.
  1142. }
  1143. if (_api_mb_supports($from_encoding)) {
  1144. return @mb_convert_encoding($string, 'UTF-8', $from_encoding);
  1145. }
  1146. if (_api_iconv_supports($from_encoding)) {
  1147. return @iconv($from_encoding, 'UTF-8', $string);
  1148. }
  1149. if (api_is_latin1($from_encoding, true)) {
  1150. return utf8_encode($string);
  1151. }
  1152. if (_api_convert_encoding_supports($from_encoding)) {
  1153. return _api_convert_encoding($string, 'UTF-8', $from_encoding);
  1154. }
  1155. return $string; // Here the function gives up.
  1156. }
  1157. /**
  1158. * Converts a given string from UTF-8 encoding to a specified encoding.
  1159. * @param string $string The string being converted.
  1160. * @param string $to_encoding (optional) The encoding that $string is being converted to. If it is omited, the platform character set is assumed.
  1161. * @return string Returns the converted string.
  1162. * This function is aimed at replacing the function utf8_decode() for human-language strings.
  1163. * @link http://php.net/manual/en/function.utf8-decode
  1164. */
  1165. function api_utf8_decode($string, $to_encoding = null)
  1166. {
  1167. if (empty($to_encoding)) {
  1168. $to_encoding = _api_mb_internal_encoding();
  1169. }
  1170. if (api_is_utf8($to_encoding)) {
  1171. return $string; // When conversion is not needed, the string is returned directly, without validation.
  1172. }
  1173. if (_api_mb_supports($to_encoding)) {
  1174. return @mb_convert_encoding($string, $to_encoding, 'UTF-8');
  1175. }
  1176. if (_api_iconv_supports($to_encoding)) {
  1177. return @iconv('UTF-8', $to_encoding, $string);
  1178. }
  1179. if (api_is_latin1($to_encoding, true)) {
  1180. return utf8_decode($string);
  1181. }
  1182. if (_api_convert_encoding_supports($to_encoding)) {
  1183. return _api_convert_encoding($string, $to_encoding, 'UTF-8');
  1184. }
  1185. return $string; // Here the function gives up.
  1186. }
  1187. /**
  1188. * Converts a given string into the system ecoding (or platform character set).
  1189. * When $from encoding is omited on UTF-8 platforms then language dependent encoding
  1190. * is guessed/assumed. On non-UTF-8 platforms omited $from encoding is assumed as UTF-8.
  1191. * When the parameter $check_utf8_validity is true the function checks string's
  1192. * UTF-8 validity and decides whether to try to convert it or not.
  1193. * This function is useful for problem detection or making workarounds.
  1194. * @param string $string The string being converted.
  1195. * @param string $from_encoding (optional) The encoding that $string is being converted from. It is guessed when it is omited.
  1196. * @param bool $check_utf8_validity (optional) A flag for UTF-8 validity check as condition for making conversion.
  1197. * @return string Returns the converted string.
  1198. */
  1199. function api_to_system_encoding($string, $from_encoding = null, $check_utf8_validity = false)
  1200. {
  1201. $system_encoding = api_get_system_encoding();
  1202. if (empty($from_encoding)) {
  1203. if (api_is_utf8($system_encoding)) {
  1204. $from_encoding = api_get_non_utf8_encoding();
  1205. } else {
  1206. $from_encoding = 'UTF-8';
  1207. }
  1208. }
  1209. if (api_equal_encodings($system_encoding, $from_encoding)) {
  1210. return $string;
  1211. }
  1212. if ($check_utf8_validity) {
  1213. if (api_is_utf8($system_encoding)) {
  1214. if (api_is_valid_utf8($string)) {
  1215. return $string;
  1216. }
  1217. } elseif (api_is_utf8($from_encoding)) {
  1218. if (!api_is_valid_utf8($string)) {
  1219. return $string;
  1220. }
  1221. }
  1222. }
  1223. return api_convert_encoding($string, $system_encoding, $from_encoding);
  1224. }
  1225. /**
  1226. * Converts all applicable characters to HTML entities.
  1227. * @param string $string The input string.
  1228. * @param int $quote_style (optional) The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES.
  1229. * @param string $encoding (optional) The encoding (of the input string) used in conversion. If it is omited, the platform character set is assumed.
  1230. * @return string Returns the converted string.
  1231. * This function is aimed at replacing the function htmlentities() for human-language strings.
  1232. * @link http://php.net/manual/en/function.htmlentities
  1233. */
  1234. function api_htmlentities($string, $quote_style = ENT_COMPAT, $encoding = null)
  1235. {
  1236. if (empty($encoding)) {
  1237. $encoding = _api_mb_internal_encoding();
  1238. }
  1239. if (!api_is_utf8($encoding) && _api_html_entity_supports($encoding)) {
  1240. return htmlentities($string, $quote_style, $encoding);
  1241. }
  1242. switch ($quote_style) {
  1243. case ENT_COMPAT:
  1244. $string = str_replace(array('&', '"', '<', '>'), array('&amp;', '&quot;', '&lt;', '&gt;'), $string);
  1245. break;
  1246. case ENT_QUOTES:
  1247. $string = str_replace(
  1248. array('&', '\'', '"', '<', '>'),
  1249. array('&amp;', '&#039;', '&quot;', '&lt;', '&gt;'),
  1250. $string
  1251. );
  1252. break;
  1253. }
  1254. if (_api_mb_supports($encoding)) {
  1255. if (!api_is_utf8($encoding)) {
  1256. $string = api_utf8_encode($string, $encoding);
  1257. }
  1258. $string = @mb_convert_encoding(api_utf8_encode($string, $encoding), 'HTML-ENTITIES', 'UTF-8');
  1259. if (!api_is_utf8($encoding)) { // Just in case.
  1260. $string = api_utf8_decode($string, $encoding);
  1261. }
  1262. } elseif (_api_convert_encoding_supports($encoding)) {
  1263. if (!api_is_utf8($encoding)) {
  1264. $string = _api_convert_encoding($string, 'UTF-8', $encoding);
  1265. }
  1266. $string = implode(array_map('_api_html_entity_from_unicode', _api_utf8_to_unicode($string)));
  1267. if (!api_is_utf8($encoding)) { // Just in case.
  1268. $string = _api_convert_encoding($string, $encoding, 'UTF-8');
  1269. }
  1270. }
  1271. return $string;
  1272. }
  1273. /**
  1274. * Checks whether the specified encoding is supported by the html-entitiy related functions.
  1275. * @param string $encoding The specified encoding.
  1276. * @return bool Returns TRUE when the specified encoding is supported, FALSE othewise.
  1277. */
  1278. function _api_html_entity_supports($encoding) {
  1279. static $supports = array();
  1280. if (!isset($supports[$encoding])) {
  1281. // See http://php.net/manual/en/function.htmlentities.php
  1282. $html_entity_encodings = array(
  1283. 'ISO-8859-1',
  1284. 'ISO-8859-15',
  1285. 'UTF-8',
  1286. 'CP866',
  1287. 'CP1251',
  1288. 'CP1252',
  1289. 'KOI8-R',
  1290. 'BIG5', '950',
  1291. 'GB2312', '936',
  1292. 'BIG5-HKSCS',
  1293. 'Shift_JIS', 'SJIS', '932',
  1294. 'EUC-JP', 'EUCJP'
  1295. );
  1296. $supports[$encoding] = api_equal_encodings($encoding, $html_entity_encodings);
  1297. }
  1298. return $supports[$encoding];
  1299. }
  1300. /**
  1301. * Convers HTML entities into normal characters.
  1302. * @param string $string The input string.
  1303. * @param int $quote_style (optional) The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES.
  1304. * @param string $encoding (optional) The encoding (of the result) used in conversion. If it is omited, the platform character set is assumed.
  1305. * @return string Returns the converted string.
  1306. * This function is aimed at replacing the function html_entity_decode() for human-language strings.
  1307. * @link http://php.net/html_entity_decode
  1308. */
  1309. function api_html_entity_decode($string, $quote_style = ENT_COMPAT, $encoding = null)
  1310. {
  1311. if (empty($encoding)) {
  1312. $encoding = _api_mb_internal_encoding();
  1313. }
  1314. if (_api_html_entity_supports($encoding)) {
  1315. return html_entity_decode($string, $quote_style, $encoding);
  1316. }
  1317. if (api_is_encoding_supported($encoding)) {
  1318. if (!api_is_utf8($encoding)) {
  1319. $string = api_utf8_encode($string, $encoding);
  1320. }
  1321. $string = html_entity_decode($string, $quote_style, 'UTF-8');
  1322. if (!api_is_utf8($encoding)) {
  1323. return api_utf8_decode($string, $encoding);
  1324. }
  1325. return $string;
  1326. }
  1327. return $string; // Here the function guves up.
  1328. }
  1329. /**
  1330. * This function encodes (conditionally) a given string to UTF-8 if XmlHttp-request has been detected.
  1331. * @param string $string The string being converted.
  1332. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  1333. * @return string Returns the converted string.
  1334. */
  1335. function api_xml_http_response_encode($string, $from_encoding = null)
  1336. {
  1337. if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
  1338. if (empty($from_encoding)) {
  1339. $from_encoding = _api_mb_internal_encoding();
  1340. }
  1341. if (!api_is_utf8($from_encoding)) {
  1342. return api_utf8_encode($string, $from_encoding);
  1343. }
  1344. }
  1345. return $string;
  1346. }
  1347. /**
  1348. * This function converts a given string to the encoding that filesystem uses for representing file/folder names.
  1349. * @param string $string The string being converted.
  1350. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  1351. * @return string Returns the converted string.
  1352. */
  1353. function api_file_system_encode($string, $from_encoding = null)
  1354. {
  1355. if (empty($from_encoding)) {
  1356. $from_encoding = _api_mb_internal_encoding();
  1357. }
  1358. return api_convert_encoding($string, api_get_file_system_encoding(), $from_encoding);
  1359. }
  1360. /**
  1361. * This function converts a given string from the encoding that filesystem uses for representing file/folder names.
  1362. * @param string $string The string being converted.
  1363. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  1364. * @return string Returns the converted string.
  1365. */
  1366. function api_file_system_decode($string, $to_encoding = null)
  1367. {
  1368. if (empty($to_encoding)) {
  1369. $to_encoding = _api_mb_internal_encoding();
  1370. }
  1371. return api_convert_encoding($string, $to_encoding, api_get_file_system_encoding());
  1372. }
  1373. /**
  1374. * Transliterates a string with arbitrary encoding into a plain ASCII string.
  1375. *
  1376. * Example:
  1377. * echo api_transliterate(api_html_entity_decode(
  1378. * '&#1060;&#1105;&#1076;&#1086;&#1088; '.
  1379. * '&#1052;&#1080;&#1093;&#1072;&#1081;&#1083;&#1086;&#1074;&#1080;&#1095; '.
  1380. * '&#1044;&#1086;&#1089;&#1090;&#1086;&#1077;&#1074;&#1082;&#1080;&#1081;',
  1381. * ENT_QUOTES, 'UTF-8'), 'X', 'UTF-8');
  1382. * The output should be: Fyodor Mihaylovich Dostoevkiy
  1383. *
  1384. * @param string $string The input string.
  1385. * @param string $unknown (optional) Replacement character for unknown characters and illegal UTF-8 sequences.
  1386. * @param string $from_encoding (optional) The encoding of the input string. If it is omited, the platform character set is assumed.
  1387. * @return string Plain ASCII output.
  1388. *
  1389. * Based on Drupal's module "Transliteration", version 6.x-2.1, 09-JUN-2009:
  1390. * @author Stefan M. Kudwien (smk-ka)
  1391. * @author Daniel F. Kudwien (sun)
  1392. * @link http://drupal.org/project/transliteration
  1393. *
  1394. * See also MediaWiki's UtfNormal.php and CPAN's Text::Unidecode library
  1395. * @link http://www.mediawiki.org
  1396. * @link http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm).
  1397. *
  1398. * Adaptation for Chamilo 1.8.7, 2010
  1399. * Initial implementation for Dokeos 1.8.6.1, 12-JUN-2009
  1400. * @author Ivan Tcholakov
  1401. */
  1402. function api_transliterate($string, $unknown = '?', $from_encoding = null)
  1403. {
  1404. static $map = array();
  1405. $string = api_utf8_encode($string, $from_encoding);
  1406. // Screen out some characters that eg won't be allowed in XML.
  1407. $string = preg_replace('/[\x00-\x08\x0b\x0c\x0e-\x1f]/', $unknown, $string);
  1408. // ASCII is always valid NFC!
  1409. // If we're only ever given plain ASCII, we can avoid the overhead
  1410. // of initializing the decomposition tables by skipping out early.
  1411. if (api_is_valid_ascii($string)) {
  1412. return $string;
  1413. }
  1414. static $tail_bytes;
  1415. if (!isset($tail_bytes)) {
  1416. // Each UTF-8 head byte is followed by a certain
  1417. // number of tail bytes.
  1418. $tail_bytes = array();
  1419. for ($n = 0; $n < 256; $n++) {
  1420. if ($n < 0xc0) {
  1421. $remaining = 0;
  1422. } elseif ($n < 0xe0) {
  1423. $remaining = 1;
  1424. } elseif ($n < 0xf0) {
  1425. $remaining = 2;
  1426. } elseif ($n < 0xf8) {
  1427. $remaining = 3;
  1428. } elseif ($n < 0xfc) {
  1429. $remaining = 4;
  1430. } elseif ($n < 0xfe) {
  1431. $remaining = 5;
  1432. } else {
  1433. $remaining = 0;
  1434. }
  1435. $tail_bytes[chr($n)] = $remaining;
  1436. }
  1437. }
  1438. // Chop the text into pure-ASCII and non-ASCII areas;
  1439. // large ASCII parts can be handled much more quickly.
  1440. // Don't chop up Unicode areas for punctuation, though,
  1441. // that wastes energy.
  1442. preg_match_all('/[\x00-\x7f]+|[\x80-\xff][\x00-\x40\x5b-\x5f\x7b-\xff]*/', $string, $matches);
  1443. $result = '';
  1444. foreach ($matches[0] as $str) {
  1445. if ($str{0} < "\x80") {
  1446. // ASCII chunk: guaranteed to be valid UTF-8
  1447. // and in normal form C, so skip over it.
  1448. $result .= $str;
  1449. continue;
  1450. }
  1451. // We'll have to examine the chunk byte by byte to ensure
  1452. // that it consists of valid UTF-8 sequences, and to see
  1453. // if any of them might not be normalized.
  1454. //
  1455. // Since PHP is not the fastest language on earth, some of
  1456. // this code is a little ugly with inner loop optimizations.
  1457. $head = '';
  1458. $chunk = api_byte_count($str);
  1459. // Counting down is faster. I'm *so* sorry.
  1460. $len = $chunk + 1;
  1461. for ($i = -1; --$len;) {
  1462. $c = $str{++$i};
  1463. if ($remaining = $tail_bytes[$c]) {
  1464. // UTF-8 head byte!
  1465. $sequence = $head = $c;
  1466. do {
  1467. // Look for the defined number of tail bytes...
  1468. if (--$len && ($c = $str{++$i}) >= "\x80" && $c < "\xc0") {
  1469. // Legal tail bytes are nice.
  1470. $sequence .= $c;
  1471. } else {
  1472. if ($len == 0) {
  1473. // Premature end of string!
  1474. // Drop a replacement character into output to
  1475. // represent the invalid UTF-8 sequence.
  1476. $result .= $unknown;
  1477. break 2;
  1478. } else {
  1479. // Illegal tail byte; abandon the sequence.
  1480. $result .= $unknown;
  1481. // Back up and reprocess this byte; it may itself
  1482. // be a legal ASCII or UTF-8 sequence head.
  1483. --$i;
  1484. ++$len;
  1485. continue 2;
  1486. }
  1487. }
  1488. } while (--$remaining);
  1489. $n = ord($head);
  1490. if ($n <= 0xdf) {
  1491. $ord = ($n - 192) * 64 + (ord($sequence{1}) - 128);
  1492. } else {
  1493. if ($n <= 0xef) {
  1494. $ord = ($n - 224) * 4096 + (ord($sequence{1}) - 128) * 64 + (ord($sequence{2}) - 128);
  1495. } else {
  1496. if ($n <= 0xf7) {
  1497. $ord = ($n - 240) * 262144 + (ord($sequence{1}) - 128) * 4096 + (ord(
  1498. $sequence{2}
  1499. ) - 128) * 64 + (ord($sequence{3}) - 128);
  1500. } else {
  1501. if ($n <= 0xfb) {
  1502. $ord = ($n - 248) * 16777216 + (ord($sequence{1}) - 128) * 262144 + (ord(
  1503. $sequence{2}
  1504. ) - 128) * 4096 + (ord($sequence{3}) - 128) * 64 + (ord($sequence{4}) - 128);
  1505. } else {
  1506. if ($n <= 0xfd) {
  1507. $ord = ($n - 252) * 1073741824 + (ord($sequence{1}) - 128) * 16777216 + (ord(
  1508. $sequence{2}
  1509. ) - 128) * 262144 + (ord($sequence{3}) - 128) * 4096 + (ord(
  1510. $sequence{4}
  1511. ) - 128) * 64 + (ord(
  1512. $sequence{5}
  1513. ) - 128);
  1514. }
  1515. }
  1516. }
  1517. }
  1518. }
  1519. // Lookup and replace a character from the transliteration database.
  1520. $bank = $ord >> 8;
  1521. // Check if we need to load a new bank
  1522. if (!isset($map[$bank])) {
  1523. $file = dirname(__FILE__).'/internationalization_database/transliteration/'.sprintf(
  1524. 'x%02x',
  1525. $bank
  1526. ).'.php';
  1527. if (file_exists($file)) {
  1528. $map[$bank] = include ($file);
  1529. } else {
  1530. $map[$bank] = array('en' => array());
  1531. }
  1532. }
  1533. $ord = $ord & 255;
  1534. $result .= isset($map[$bank]['en'][$ord]) ? $map[$bank]['en'][$ord] : $unknown;
  1535. $head = '';
  1536. } elseif ($c < "\x80") {
  1537. // ASCII byte.
  1538. $result .= $c;
  1539. $head = '';
  1540. } elseif ($c < "\xc0") {
  1541. // Illegal tail bytes.
  1542. if ($head == '') {
  1543. $result .= $unknown;
  1544. }
  1545. } else {
  1546. // Miscellaneous freaks.
  1547. $result .= $unknown;
  1548. $head = '';
  1549. }
  1550. }
  1551. }
  1552. return $result;
  1553. }
  1554. /**
  1555. * Common multibyte string functions
  1556. */
  1557. /**
  1558. * Takes the first character in a string and returns its Unicode codepoint.
  1559. * @param string $character The input string.
  1560. * @param string $encoding (optional) The encoding of the input string. If it is omitted, the platform character set will be used by default.
  1561. * @return int Returns: the codepoint of the first character; or 0xFFFD (unknown character) when the input string is empty.
  1562. * This is a multibyte aware version of the function ord().
  1563. * @link http://php.net/manual/en/function.ord.php
  1564. * Note the difference with the original funtion ord(): ord('') returns 0, api_ord('') returns 0xFFFD (unknown character).
  1565. */
  1566. function api_ord($character, $encoding)
  1567. {
  1568. return _api_utf8_ord(api_utf8_encode($character, $encoding));
  1569. }
  1570. /**
  1571. * Takes a Unicode codepoint and returns its correspondent character, encoded in given encoding.
  1572. * @param int $codepoint The Unicode codepoint.
  1573. * @param string $encoding (optional) The encoding of the returned character. If it is omitted, the platform character set will be used by default.
  1574. * @return string Returns the corresponding character, encoded as it has been requested.
  1575. * This is a multibyte aware version of the function chr().
  1576. * @link http://php.net/manual/en/function.chr.php
  1577. */
  1578. function api_chr($codepoint, $encoding)
  1579. {
  1580. return api_utf8_decode(_api_utf8_chr($codepoint), $encoding);
  1581. }
  1582. /**
  1583. * This function returns a string or an array with all occurrences of search in subject (ignoring case) replaced with the given replace value.
  1584. * @param mixed $search String or array of strings to be found.
  1585. * @param mixed $replace String or array of strings used for replacement.
  1586. * @param mixed $subject String or array of strings being searced.
  1587. * @param int $count (optional) The number of matched and replaced needles will be returned in count, which is passed by reference.
  1588. * @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.
  1589. * @return mixed String or array as a result.
  1590. * Notes:
  1591. * If $subject is an array, then the search and replace is performed with every entry of subject, the return value is an array.
  1592. * If $search and $replace are arrays, then the function takes a value from each array and uses it to do search and replace on subject.
  1593. * If $replace has fewer values than search, then an empty string is used for the rest of replacement values.
  1594. * If $search is an array and $replace is a string, then this replacement string is used for every value of search.
  1595. * This function is aimed at replacing the function str_ireplace() for human-language strings.
  1596. * @link http://php.net/manual/en/function.str-ireplace
  1597. * @author Henri Sivonen, mailto:hsivonen@iki.fi
  1598. * @link http://hsivonen.iki.fi/php-utf8/
  1599. * Adaptation for Chamilo 1.8.7, 2010
  1600. * Initial implementation Dokeos LMS, August 2009
  1601. * @author Ivan Tcholakov
  1602. */
  1603. function api_str_ireplace($search, $replace, $subject, & $count = null, $encoding = null)
  1604. {
  1605. if (empty($encoding)) {
  1606. $encoding = _api_mb_internal_encoding();
  1607. }
  1608. if (api_is_encoding_supported($encoding)) {
  1609. if (!is_array($search) && !is_array($replace)) {
  1610. if (!api_is_utf8($encoding)) {
  1611. $search = api_utf8_encode($search, $encoding);
  1612. }
  1613. $slen = api_byte_count($search);
  1614. if ($slen == 0) {
  1615. return $subject;
  1616. }
  1617. if (!api_is_utf8($encoding)) {
  1618. $replace = api_utf8_encode($replace, $encoding);
  1619. $subject = api_utf8_encode($subject, $encoding);
  1620. }
  1621. $lendif = api_byte_count($replace) - api_byte_count($search);
  1622. $search = api_strtolower($search, 'UTF-8');
  1623. $search = preg_quote($search);
  1624. $lstr = api_strtolower($subject, 'UTF-8');
  1625. $i = 0;
  1626. $matched = 0;
  1627. while (preg_match('/(.*)'.$search.'/Us', $lstr, $matches)) {
  1628. if ($i === $count) {
  1629. break;
  1630. }
  1631. $mlen = api_byte_count($matches[0]);
  1632. $lstr = substr($lstr, $mlen);
  1633. $subject = substr_replace($subject, $replace, $matched + api_byte_count($matches[1]), $slen);
  1634. $matched += $mlen + $lendif;
  1635. $i++;
  1636. }
  1637. if (!api_is_utf8($encoding)) {
  1638. $subject = api_utf8_decode($subject, $encoding);
  1639. }
  1640. return $subject;
  1641. } else {
  1642. foreach (array_keys($search) as $k) {
  1643. if (is_array($replace)) {
  1644. if (array_key_exists($k, $replace)) {
  1645. $subject = api_str_ireplace($search[$k], $replace[$k], $subject, $count);
  1646. } else {
  1647. $subject = api_str_ireplace($search[$k], '', $subject, $count);
  1648. }
  1649. } else {
  1650. $subject = api_str_ireplace($search[$k], $replace, $subject, $count);
  1651. }
  1652. }
  1653. return $subject;
  1654. }
  1655. }
  1656. if (is_null($count)) {
  1657. return str_ireplace($search, $replace, $subject);
  1658. }
  1659. return str_ireplace($search, $replace, $subject, $count);
  1660. }
  1661. /**
  1662. * Converts a string to an array.
  1663. * @param string $string The input string.
  1664. * @param int $split_length Maximum character-length of the chunk, one character by default.
  1665. * @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.
  1666. * @return array The result array of chunks with the spcified length.
  1667. * Notes:
  1668. * If the optional split_length parameter is specified, the returned array will be broken down into chunks
  1669. * with each being split_length in length, otherwise each chunk will be one character in length.
  1670. * FALSE is returned if split_length is less than 1.
  1671. * If the split_length length exceeds the length of string, the entire string is returned as the first (and only) array element.
  1672. * This function is aimed at replacing the function str_split() for human-language strings.
  1673. * @link http://php.net/str_split
  1674. */
  1675. function api_str_split($string, $split_length = 1, $encoding = null)
  1676. {
  1677. if (empty($encoding)) {
  1678. $encoding = _api_mb_internal_encoding();
  1679. }
  1680. if (empty($string)) {
  1681. return array();
  1682. }
  1683. if ($split_length < 1) {
  1684. return false;
  1685. }
  1686. if (_api_is_single_byte_encoding($encoding)) {
  1687. return str_split($string, $split_length);
  1688. }
  1689. if (api_is_encoding_supported($encoding)) {
  1690. $len = api_strlen($string);
  1691. if ($len <= $split_length) {
  1692. return array($string);
  1693. }
  1694. if (!api_is_utf8($encoding)) {
  1695. $string = api_utf8_encode($string, $encoding);
  1696. }
  1697. if (preg_match_all('/.{'.$split_length.'}|[^\x00]{1,'.$split_length.'}$/us', $string, $result) === false) {
  1698. return array();
  1699. }
  1700. if (!api_is_utf8($encoding)) {
  1701. global $_api_encoding;
  1702. $_api_encoding = $encoding;
  1703. $result = _api_array_utf8_decode($result[0]);
  1704. }
  1705. return $result[0];
  1706. }
  1707. return str_split($string, $split_length);
  1708. }
  1709. /**
  1710. * Finds position of first occurrence of a string within another, case insensitive.
  1711. * @param string $haystack The string from which to get the position of the first occurrence.
  1712. * @param string $needle The string to be found.
  1713. * @param int $offset The position in $haystack to start searching from. If it is omitted, searching starts from the beginning.
  1714. * @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.
  1715. * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
  1716. * Note: The first character's position is 0, the second character position is 1, and so on.
  1717. * This function is aimed at replacing the functions stripos() and mb_stripos() for human-language strings.
  1718. * @link http://php.net/manual/en/function.stripos
  1719. * @link http://php.net/manual/en/function.mb-stripos
  1720. */
  1721. function api_stripos($haystack, $needle, $offset = 0, $encoding = null)
  1722. {
  1723. if (empty($encoding)) {
  1724. $encoding = _api_mb_internal_encoding();
  1725. }
  1726. if (!is_string($needle)) {
  1727. $needle = (int)$needle;
  1728. if (api_is_utf8($encoding)) {
  1729. $needle = _api_utf8_chr($needle);
  1730. } else {
  1731. $needle = chr($needle);
  1732. }
  1733. }
  1734. if ($needle == '') {
  1735. return false;
  1736. }
  1737. if (_api_mb_supports($encoding)) {
  1738. return @mb_stripos($haystack, $needle, $offset, $encoding);
  1739. } elseif (api_is_encoding_supported($encoding)) {
  1740. if (MBSTRING_INSTALLED) {
  1741. if (!api_is_utf8($encoding)) {
  1742. $haystack = api_utf8_encode($haystack, $encoding);
  1743. $needle = api_utf8_encode($needle, $encoding);
  1744. }
  1745. return @mb_stripos($haystack, $needle, $offset, 'UTF-8');
  1746. }
  1747. return api_strpos(api_strtolower($haystack, $encoding), api_strtolower($needle, $encoding), $offset, $encoding);
  1748. }
  1749. return stripos($haystack, $needle, $offset);
  1750. }
  1751. /**
  1752. * Finds first occurrence of a string within another, case insensitive.
  1753. * @param string $haystack The string from which to get the first occurrence.
  1754. * @param mixed $needle The string to be found.
  1755. * @param bool $before_needle (optional) Determines which portion of $haystack this function returns. The default value is FALSE.
  1756. * @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.
  1757. * @return mixed Returns the portion of $haystack, or FALSE if $needle is not found.
  1758. * Notes:
  1759. * 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.
  1760. * If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence of $needle.
  1761. * If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence of $needle to the end.
  1762. * This function is aimed at replacing the functions stristr() and mb_stristr() for human-language strings.
  1763. * @link http://php.net/manual/en/function.stristr
  1764. * @link http://php.net/manual/en/function.mb-stristr
  1765. */
  1766. function api_stristr($haystack, $needle, $before_needle = false, $encoding = null)
  1767. {
  1768. if (empty($encoding)) {
  1769. $encoding = _api_mb_internal_encoding();
  1770. }
  1771. if (!is_string($needle)) {
  1772. $needle = (int)$needle;
  1773. if (api_is_utf8($encoding)) {
  1774. $needle = _api_utf8_chr($needle);
  1775. } else {
  1776. $needle = chr($needle);
  1777. }
  1778. }
  1779. if ($needle == '') {
  1780. return false;
  1781. }
  1782. if (_api_mb_supports($encoding)) {
  1783. return @mb_stristr($haystack, $needle, $before_needle, $encoding);
  1784. } elseif (api_is_encoding_supported($encoding)) {
  1785. if (MBSTRING_INSTALLED) {
  1786. if (!api_is_utf8($encoding)) {
  1787. $haystack = api_utf8_encode($haystack, $encoding);
  1788. $needle = api_utf8_encode($needle, $encoding);
  1789. }
  1790. $result = @mb_stristr($haystack, $needle, $before_needle, 'UTF-8');
  1791. if ($result === false) {
  1792. return false;
  1793. }
  1794. if (!api_is_utf8($encoding)) {
  1795. return api_utf8_decode($result, $encoding);
  1796. }
  1797. return $result;
  1798. }
  1799. $result = api_strstr(
  1800. api_strtolower($haystack, $encoding),
  1801. api_strtolower($needle, $encoding),
  1802. $before_needle,
  1803. $encoding
  1804. );
  1805. if ($result === false) {
  1806. return false;
  1807. }
  1808. if ($before_needle) {
  1809. return api_substr($haystack, 0, api_strlen($result, $encoding), $encoding);
  1810. }
  1811. return api_substr(
  1812. $haystack,
  1813. api_strlen($haystack, $encoding) - api_strlen($result, $encoding),
  1814. null,
  1815. $encoding
  1816. );
  1817. }
  1818. if (!IS_PHP_53) {
  1819. return stristr($haystack, $needle);
  1820. }
  1821. return stristr($haystack, $needle, $before_needle);
  1822. }
  1823. /**
  1824. * Returns length of the input string.
  1825. * @param string $string The string which length is to be calculated.
  1826. * @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.
  1827. * @return int Returns the number of characters within the string. A multi-byte character is counted as 1.
  1828. * This function is aimed at replacing the functions strlen() and mb_strlen() for human-language strings.
  1829. * @link http://php.net/manual/en/function.strlen
  1830. * @link http://php.net/manual/en/function.mb-strlen
  1831. * Note: When you use strlen() to test for an empty string, you needn't change it to api_strlen().
  1832. * For example, in lines like the following:
  1833. * if (strlen($string) > 0)
  1834. * if (strlen($string) != 0)
  1835. * there is no need the original function strlen() to be changed, it works correctly and faster for these cases.
  1836. */
  1837. function api_strlen($string, $encoding = null)
  1838. {
  1839. if (empty($encoding)) {
  1840. $encoding = _api_mb_internal_encoding();
  1841. }
  1842. if (_api_is_single_byte_encoding($encoding)) {
  1843. return strlen($string);
  1844. }
  1845. if (_api_mb_supports($encoding)) {
  1846. return @mb_strlen($string, $encoding);
  1847. }
  1848. if (_api_iconv_supports($encoding)) {
  1849. return @iconv_strlen($string, $encoding);
  1850. }
  1851. if (api_is_utf8($encoding)) {
  1852. return api_byte_count(preg_replace("/[\x80-\xBF]/", '', $string));
  1853. }
  1854. return strlen($string);
  1855. }
  1856. /**
  1857. * Finds position of first occurrence of a string within another.
  1858. * @param string $haystack The string from which to get the position of the first occurrence.
  1859. * @param string $needle The string to be found.
  1860. * @param int $offset (optional) The position in $haystack to start searching from. If it is omitted, searching starts from the beginning.
  1861. * @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.
  1862. * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
  1863. * Note: The first character's position is 0, the second character position is 1, and so on.
  1864. * This function is aimed at replacing the functions strpos() and mb_strpos() for human-language strings.
  1865. * @link http://php.net/manual/en/function.strpos
  1866. * @link http://php.net/manual/en/function.mb-strpos
  1867. */
  1868. function api_strpos($haystack, $needle, $offset = 0, $encoding = null)
  1869. {
  1870. if (empty($encoding)) {
  1871. $encoding = _api_mb_internal_encoding();
  1872. }
  1873. if (!is_string($needle)) {
  1874. $needle = (int)$needle;
  1875. if (api_is_utf8($encoding)) {
  1876. $needle = _api_utf8_chr($needle);
  1877. } else {
  1878. $needle = chr($needle);
  1879. }
  1880. }
  1881. if ($needle == '') {
  1882. return false;
  1883. }
  1884. if (_api_is_single_byte_encoding($encoding)) {
  1885. return strpos($haystack, $needle, $offset);
  1886. } elseif (_api_mb_supports($encoding)) {
  1887. return @mb_strpos($haystack, $needle, $offset, $encoding);
  1888. } elseif (api_is_encoding_supported($encoding)) {
  1889. if (!api_is_utf8($encoding)) {
  1890. $haystack = api_utf8_encode($haystack, $encoding);
  1891. $needle = api_utf8_encode($needle, $encoding);
  1892. }
  1893. if (MBSTRING_INSTALLED) {
  1894. return @mb_strpos($haystack, $needle, $offset, 'UTF-8');
  1895. }
  1896. if (empty($offset)) {
  1897. $haystack = explode($needle, $haystack, 2);
  1898. if (count($haystack) > 1) {
  1899. return api_strlen($haystack[0]);
  1900. }
  1901. return false;
  1902. }
  1903. $haystack = api_substr($haystack, $offset);
  1904. if (($pos = api_strpos($haystack, $needle)) !== false) {
  1905. return $pos + $offset;
  1906. }
  1907. return false;
  1908. }
  1909. return strpos($haystack, $needle, $offset);
  1910. }
  1911. /**
  1912. * Finds the last occurrence of a character in a string.
  1913. * @param string $haystack The string from which to get the last occurrence.
  1914. * @param mixed $needle The string which first character is to be found.
  1915. * @param bool $before_needle (optional) Determines which portion of $haystack this function returns. The default value is FALSE.
  1916. * @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.
  1917. * @return mixed Returns the portion of $haystack, or FALSE if the first character from $needle is not found.
  1918. * Notes:
  1919. * 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.
  1920. * If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence.
  1921. * If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence to the end.
  1922. * This function is aimed at replacing the functions strrchr() and mb_strrchr() for human-language strings.
  1923. * @link http://php.net/manual/en/function.strrchr
  1924. * @link http://php.net/manual/en/function.mb-strrchr
  1925. */
  1926. function api_strrchr($haystack, $needle, $before_needle = false, $encoding = null)
  1927. {
  1928. if (empty($encoding)) {
  1929. $encoding = _api_mb_internal_encoding();
  1930. }
  1931. if (!is_string($needle)) {
  1932. $needle = (int)$needle;
  1933. if (api_is_utf8($encoding)) {
  1934. $needle = _api_utf8_chr($needle);
  1935. } else {
  1936. $needle = chr($needle);
  1937. }
  1938. }
  1939. if ($needle == '') {
  1940. return false;
  1941. }
  1942. if (_api_is_single_byte_encoding($encoding)) {
  1943. if (!$before_needle) {
  1944. return strrchr($haystack, $needle);
  1945. }
  1946. $result = strrchr($haystack, $needle);
  1947. if ($result === false) {
  1948. return false;
  1949. }
  1950. return api_substr($haystack, 0, api_strlen($haystack, $encoding) - api_strlen($result, $encoding), $encoding);
  1951. } elseif (_api_mb_supports($encoding)) {
  1952. return @mb_strrchr($haystack, $needle, $before_needle, $encoding);
  1953. } elseif (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  1954. if (!api_is_utf8($encoding)) {
  1955. $haystack = api_utf8_encode($haystack, $encoding);
  1956. $needle = api_utf8_encode($needle, $encoding);
  1957. }
  1958. $result = @mb_strrchr($haystack, $needle, $before_needle, 'UTF-8');
  1959. if ($result === false) {
  1960. return false;
  1961. }
  1962. if (!api_is_utf8($encoding)) {
  1963. return api_utf8_decode($result, $encoding);
  1964. }
  1965. return $result;
  1966. }
  1967. if (!$before_needle) {
  1968. return strrchr($haystack, $needle);
  1969. }
  1970. $result = strrchr($haystack, $needle);
  1971. if ($result === false) {
  1972. return false;
  1973. }
  1974. return api_substr($haystack, 0, api_strlen($haystack, $encoding) - api_strlen($result, $encoding), $encoding);
  1975. }
  1976. /**
  1977. * Reverses a string.
  1978. * @param string $string The string to be reversed.
  1979. * @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.
  1980. * @return string Returns the reversed string.
  1981. * This function is aimed at replacing the function strrev() for human-language strings.
  1982. * @link http://php.net/manual/en/function.strrev
  1983. */
  1984. function api_strrev($string, $encoding = null)
  1985. {
  1986. if (empty($encoding)) {
  1987. $encoding = _api_mb_internal_encoding();
  1988. }
  1989. if (empty($string)) {
  1990. return '';
  1991. }
  1992. if (_api_is_single_byte_encoding($encoding)) {
  1993. return strrev($string);
  1994. }
  1995. if (api_is_encoding_supported($encoding)) {
  1996. return implode(array_reverse(api_str_split($string, 1, $encoding)));
  1997. }
  1998. return strrev($string);
  1999. }
  2000. /**
  2001. * Finds the position of last occurrence (case insensitive) of a string in a string.
  2002. * @param string $haystack The string from which to get the position of the last occurrence.
  2003. * @param string $needle The string to be found.
  2004. * @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.
  2005. * @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.
  2006. * @return mixed Returns the numeric position of the first occurrence (case insensitive) of $needle in the $haystack, or FALSE if $needle is not found.
  2007. * Note: The first character's position is 0, the second character position is 1, and so on.
  2008. * This function is aimed at replacing the functions strripos() and mb_strripos() for human-language strings.
  2009. * @link http://php.net/manual/en/function.strripos
  2010. * @link http://php.net/manual/en/function.mb-strripos
  2011. */
  2012. function api_strripos($haystack, $needle, $offset = 0, $encoding = null)
  2013. {
  2014. return api_strrpos(api_strtolower($haystack, $encoding), api_strtolower($needle, $encoding), $offset, $encoding);
  2015. }
  2016. /**
  2017. * Finds the position of last occurrence of a string in a string.
  2018. * @param string $haystack The string from which to get the position of the last occurrence.
  2019. * @param string $needle The string to be found.
  2020. * @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.
  2021. * @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.
  2022. * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
  2023. * Note: The first character's position is 0, the second character position is 1, and so on.
  2024. * This function is aimed at replacing the functions strrpos() and mb_strrpos() for human-language strings.
  2025. * @link http://php.net/manual/en/function.strrpos
  2026. * @link http://php.net/manual/en/function.mb-strrpos
  2027. */
  2028. function api_strrpos($haystack, $needle, $offset = 0, $encoding = null)
  2029. {
  2030. if (empty($encoding)) {
  2031. $encoding = _api_mb_internal_encoding();
  2032. }
  2033. if (!is_string($needle)) {
  2034. $needle = (int)$needle;
  2035. if (api_is_utf8($encoding)) {
  2036. $needle = _api_utf8_chr($needle);
  2037. } else {
  2038. $needle = chr($needle);
  2039. }
  2040. }
  2041. if ($needle == '') {
  2042. return false;
  2043. }
  2044. if (_api_is_single_byte_encoding($encoding)) {
  2045. return strrpos($haystack, $needle, $offset);
  2046. }
  2047. if (_api_mb_supports($encoding) && IS_PHP_52) {
  2048. return @mb_strrpos($haystack, $needle, $offset, $encoding);
  2049. } elseif (api_is_encoding_supported($encoding)) {
  2050. if (!api_is_utf8($encoding)) {
  2051. $haystack = api_utf8_encode($haystack, $encoding);
  2052. $needle = api_utf8_encode($needle, $encoding);
  2053. }
  2054. // In PHP 5.1 the $offset parameter didn't exist see http://php.net/manual/en/function.mb-strrpos.php
  2055. if (MBSTRING_INSTALLED && IS_PHP_SUP_OR_EQ_51) {
  2056. //return @mb_strrpos($haystack, $needle, $offset, 'UTF-8');
  2057. //@todo fix the missing $offset parameter
  2058. return @mb_strrpos($haystack, $needle, 'UTF-8');
  2059. }
  2060. if (MBSTRING_INSTALLED && IS_PHP_SUP_OR_EQ_52) {
  2061. return @mb_strrpos($haystack, $needle, $offset, 'UTF-8');
  2062. }
  2063. // This branch (this fragment of code) is an adaptation from the CakePHP(tm) Project, http://www.cakefoundation.org
  2064. $found = false;
  2065. $haystack = _api_utf8_to_unicode($haystack);
  2066. $haystack_count = count($haystack);
  2067. $matches = array_count_values($haystack);
  2068. $needle = _api_utf8_to_unicode($needle);
  2069. $needle_count = count($needle);
  2070. $position = $offset;
  2071. while (($found === false) && ($position < $haystack_count)) {
  2072. if (isset($needle[0]) && $needle[0] === $haystack[$position]) {
  2073. for ($i = 1; $i < $needle_count; $i++) {
  2074. if ($needle[$i] !== $haystack[$position + $i]) {
  2075. if ($needle[$i] === $haystack[($position + $i) - 1]) {
  2076. $position--;
  2077. $found = true;
  2078. continue;
  2079. }
  2080. }
  2081. }
  2082. if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
  2083. $matches[$needle[0]] = $matches[$needle[0]] - 1;
  2084. } elseif ($i === $needle_count) {
  2085. $found = true;
  2086. $position--;
  2087. }
  2088. }
  2089. $position++;
  2090. }
  2091. return ($found) ? $position : false;
  2092. }
  2093. return strrpos($haystack, $needle, $offset);
  2094. }
  2095. /**
  2096. * Finds first occurrence of a string within another.
  2097. * @param string $haystack The string from which to get the first occurrence.
  2098. * @param mixed $needle The string to be found.
  2099. * @param bool $before_needle (optional) Determines which portion of $haystack this function returns. The default value is FALSE.
  2100. * @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.
  2101. * @return mixed Returns the portion of $haystack, or FALSE if $needle is not found.
  2102. * Notes:
  2103. * 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.
  2104. * If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence of $needle.
  2105. * If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence of $needle to the end.
  2106. * This function is aimed at replacing the functions strstr() and mb_strstr() for human-language strings.
  2107. * @link http://php.net/manual/en/function.strstr
  2108. * @link http://php.net/manual/en/function.mb-strstr
  2109. */
  2110. function api_strstr($haystack, $needle, $before_needle = false, $encoding = null)
  2111. {
  2112. if (empty($encoding)) {
  2113. $encoding = _api_mb_internal_encoding();
  2114. }
  2115. if (!is_string($needle)) {
  2116. $needle = (int)$needle;
  2117. if (api_is_utf8($encoding)) {
  2118. $needle = _api_utf8_chr($needle);
  2119. } else {
  2120. $needle = chr($needle);
  2121. }
  2122. }
  2123. if ($needle == '') {
  2124. return false;
  2125. }
  2126. if (_api_is_single_byte_encoding($encoding)) {
  2127. // Adding the missing parameter $before_needle to the original function strstr(), PHP_VERSION < 5.3
  2128. if (!$before_needle) {
  2129. return strstr($haystack, $needle);
  2130. }
  2131. if (!IS_PHP_53) {
  2132. $result = explode($needle, $haystack, 2);
  2133. if ($result === false || count($result) < 2) {
  2134. return false;
  2135. }
  2136. return $result[0];
  2137. }
  2138. return strstr($haystack, $needle, $before_needle);
  2139. }
  2140. if (_api_mb_supports($encoding)) {
  2141. return @mb_strstr($haystack, $needle, $before_needle, $encoding);
  2142. } elseif (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  2143. if (!api_is_utf8($encoding)) {
  2144. $haystack = api_utf8_encode($haystack, $encoding);
  2145. $needle = api_utf8_encode($needle, $encoding);
  2146. }
  2147. $result = @mb_strstr($haystack, $needle, $before_needle, 'UTF-8');
  2148. if ($result !== false) {
  2149. if (!api_is_utf8($encoding)) {
  2150. return api_utf8_decode($result, $encoding);
  2151. }
  2152. return $result;
  2153. }
  2154. return false;
  2155. }
  2156. // Adding the missing parameter $before_needle to the original function strstr(), PHP_VERSION < 5.3
  2157. if (!$before_needle) {
  2158. return strstr($haystack, $needle);
  2159. }
  2160. if (!IS_PHP_53) {
  2161. $result = explode($needle, $haystack, 2);
  2162. if ($result === false || count($result) < 2) {
  2163. return false;
  2164. }
  2165. return $result[0];
  2166. }
  2167. return strstr($haystack, $needle, $before_needle);
  2168. }
  2169. /**
  2170. * Makes a string lowercase.
  2171. * @param string $string The string being lowercased.
  2172. * @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.
  2173. * @return string Returns the string with all alphabetic characters converted to lowercase.
  2174. * This function is aimed at replacing the functions strtolower() and mb_strtolower() for human-language strings.
  2175. * @link http://php.net/manual/en/function.strtolower
  2176. * @link http://php.net/manual/en/function.mb-strtolower
  2177. */
  2178. function api_strtolower($string, $encoding = null)
  2179. {
  2180. if (empty($encoding)) {
  2181. $encoding = _api_mb_internal_encoding();
  2182. }
  2183. if (_api_mb_supports($encoding)) {
  2184. return @mb_strtolower($string, $encoding);
  2185. } elseif (api_is_encoding_supported($encoding)) {
  2186. if (!api_is_utf8($encoding)) {
  2187. $string = api_utf8_encode($string, $encoding);
  2188. }
  2189. if (MBSTRING_INSTALLED) {
  2190. $string = @mb_strtolower($string, 'UTF-8');
  2191. } else {
  2192. // This branch (this fragment of code) is an adaptation from the CakePHP(tm) Project, http://www.cakefoundation.org
  2193. $codepoints = _api_utf8_to_unicode($string);
  2194. $length = count($codepoints);
  2195. $matched = false;
  2196. $result = array();
  2197. for ($i = 0; $i < $length; $i++) {
  2198. $codepoint = $codepoints[$i];
  2199. if ($codepoint < 128) {
  2200. $str = strtolower(chr($codepoint));
  2201. $strlen = api_byte_count($str);
  2202. for ($ii = 0; $ii < $strlen; $ii++) {
  2203. $lower = ord($str[$ii]);
  2204. }
  2205. $result[] = $lower;
  2206. $matched = true;
  2207. } else {
  2208. $matched = false;
  2209. $properties = & _api_utf8_get_letter_case_properties($codepoint, 'upper');
  2210. if (!empty($properties)) {
  2211. foreach ($properties as $key => $value) {
  2212. if ($properties[$key]['upper'] == $codepoint && count(
  2213. $properties[$key]['lower'][0]
  2214. ) === 1
  2215. ) {
  2216. $result[] = $properties[$key]['lower'][0];
  2217. $matched = true;
  2218. break 1;
  2219. }
  2220. }
  2221. }
  2222. }
  2223. if ($matched === false) {
  2224. $result[] = $codepoint;
  2225. }
  2226. }
  2227. $string = _api_utf8_from_unicode($result);
  2228. }
  2229. if (!api_is_utf8($encoding)) {
  2230. return api_utf8_decode($string, $encoding);
  2231. }
  2232. return $string;
  2233. }
  2234. return strtolower($string);
  2235. }
  2236. /**
  2237. * Makes a string uppercase.
  2238. * @param string $string The string being uppercased.
  2239. * @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.
  2240. * @return string Returns the string with all alphabetic characters converted to uppercase.
  2241. * This function is aimed at replacing the functions strtoupper() and mb_strtoupper() for human-language strings.
  2242. * @link http://php.net/manual/en/function.strtoupper
  2243. * @link http://php.net/manual/en/function.mb-strtoupper
  2244. */
  2245. function api_strtoupper($string, $encoding = null)
  2246. {
  2247. if (empty($encoding)) {
  2248. $encoding = _api_mb_internal_encoding();
  2249. }
  2250. if (_api_mb_supports($encoding)) {
  2251. return @mb_strtoupper($string, $encoding);
  2252. } elseif (api_is_encoding_supported($encoding)) {
  2253. if (!api_is_utf8($encoding)) {
  2254. $string = api_utf8_encode($string, $encoding);
  2255. }
  2256. if (MBSTRING_INSTALLED) {
  2257. $string = @mb_strtoupper($string, 'UTF-8');
  2258. } else {
  2259. // This branch (this fragment of code) is an adaptation from the CakePHP(tm) Project, http://www.cakefoundation.org
  2260. $codepoints = _api_utf8_to_unicode($string);
  2261. $length = count($codepoints);
  2262. $matched = false;
  2263. $replaced = array();
  2264. $result = array();
  2265. for ($i = 0; $i < $length; $i++) {
  2266. $codepoint = $codepoints[$i];
  2267. if ($codepoint < 128) {
  2268. $str = strtoupper(chr($codepoint));
  2269. $strlen = api_byte_count($str);
  2270. for ($ii = 0; $ii < $strlen; $ii++) {
  2271. $lower = ord($str[$ii]);
  2272. }
  2273. $result[] = $lower;
  2274. $matched = true;
  2275. } else {
  2276. $matched = false;
  2277. $properties = & _api_utf8_get_letter_case_properties($codepoint);
  2278. $property_count = count($properties);
  2279. if (!empty($properties)) {
  2280. foreach ($properties as $key => $value) {
  2281. $matched = false;
  2282. $replace = 0;
  2283. if ($length > 1 && count($properties[$key]['lower']) > 1) {
  2284. $j = 0;
  2285. for ($ii = 0; $ii < count($properties[$key]['lower']); $ii++) {
  2286. $next_codepoint = $next_codepoints[$i + $ii];
  2287. if (isset($next_codepoint) && ($next_codepoint == $properties[$key]['lower'][$j + $ii])) {
  2288. $replace++;
  2289. }
  2290. }
  2291. if ($replace == count($properties[$key]['lower'])) {
  2292. $result[] = $properties[$key]['upper'];
  2293. $replaced = array_merge($replaced, array_values($properties[$key]['lower']));
  2294. $matched = true;
  2295. break 1;
  2296. }
  2297. } elseif ($length > 1 && $property_count > 1) {
  2298. $j = 0;
  2299. for ($ii = 1; $ii < $property_count; $ii++) {
  2300. $next_codepoint = $next_codepoints[$i + $ii - 1];
  2301. if (in_array($next_codepoint, $properties[$ii]['lower'])) {
  2302. for ($jj = 0; $jj < count($properties[$ii]['lower']); $jj++) {
  2303. $next_codepoint = $next_codepoints[$i + $jj];
  2304. if (isset($next_codepoint) && ($next_codepoint == $properties[$ii]['lower'][$j + $jj])) {
  2305. $replace++;
  2306. }
  2307. }
  2308. if ($replace == count($properties[$ii]['lower'])) {
  2309. $result[] = $properties[$ii]['upper'];
  2310. $replaced = array_merge($replaced, array_values($properties[$ii]['lower']));
  2311. $matched = true;
  2312. break 2;
  2313. }
  2314. }
  2315. }
  2316. }
  2317. if ($properties[$key]['lower'][0] == $codepoint) {
  2318. $result[] = $properties[$key]['upper'];
  2319. $matched = true;
  2320. break 1;
  2321. }
  2322. }
  2323. }
  2324. }
  2325. if ($matched === false && !in_array($codepoint, $replaced, true)) {
  2326. $result[] = $codepoint;
  2327. }
  2328. }
  2329. $string = _api_utf8_from_unicode($result);
  2330. }
  2331. if (!api_is_utf8($encoding)) {
  2332. return api_utf8_decode($string, $encoding);
  2333. }
  2334. return $string;
  2335. }
  2336. return strtoupper($string);
  2337. }
  2338. /**
  2339. // Gets part of a string.
  2340. * @param string $string The input string.
  2341. * @param int $start The first position from which the extracted part begins.
  2342. * @param int $length The length in character of the extracted part.
  2343. * @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.
  2344. * @return string Returns the part of the string specified by the start and length parameters.
  2345. * Note: First character's position is 0. Second character position is 1, and so on.
  2346. * This function is aimed at replacing the functions substr() and mb_substr() for human-language strings.
  2347. * @link http://php.net/manual/en/function.substr
  2348. * @link http://php.net/manual/en/function.mb-substr
  2349. */
  2350. function api_substr($string, $start, $length = null, $encoding = null)
  2351. {
  2352. if (empty($encoding)) {
  2353. $encoding = _api_mb_internal_encoding();
  2354. }
  2355. // Passing null as $length would mean 0. This behaviour has been corrected here.
  2356. if (is_null($length)) {
  2357. $length = api_strlen($string, $encoding);
  2358. }
  2359. if (_api_is_single_byte_encoding($encoding)) {
  2360. return substr($string, $start, $length);
  2361. }
  2362. if (_api_mb_supports($encoding)) {
  2363. return @mb_substr($string, $start, $length, $encoding);
  2364. } elseif (api_is_encoding_supported($encoding)) {
  2365. if (!api_is_utf8($encoding)) {
  2366. $string = api_utf8_encode($string, $encoding);
  2367. }
  2368. if (MBSTRING_INSTALLED) {
  2369. $string = @mb_substr($string, $start, $length, 'UTF-8');
  2370. } else {
  2371. // The following branch of code is from the Drupal CMS, see the function drupal_substr().
  2372. $strlen = api_byte_count($string);
  2373. // Find the starting byte offset
  2374. $bytes = 0;
  2375. if ($start > 0) {
  2376. // Count all the continuation bytes from the start until we have found
  2377. // $start characters
  2378. $bytes = -1;
  2379. $chars = -1;
  2380. while ($bytes < $strlen && $chars < $start) {
  2381. $bytes++;
  2382. $c = ord($string[$bytes]);
  2383. if ($c < 0x80 || $c >= 0xC0) {
  2384. $chars++;
  2385. }
  2386. }
  2387. } else {
  2388. if ($start < 0) {
  2389. // Count all the continuation bytes from the end until we have found
  2390. // abs($start) characters
  2391. $start = abs($start);
  2392. $bytes = $strlen;
  2393. $chars = 0;
  2394. while ($bytes > 0 && $chars < $start) {
  2395. $bytes--;
  2396. $c = ord($string[$bytes]);
  2397. if ($c < 0x80 || $c >= 0xC0) {
  2398. $chars++;
  2399. }
  2400. }
  2401. }
  2402. }
  2403. $istart = $bytes;
  2404. // Find the ending byte offset
  2405. if ($length === null) {
  2406. $bytes = $strlen - 1;
  2407. } else {
  2408. if ($length > 0) {
  2409. // Count all the continuation bytes from the starting index until we have
  2410. // found $length + 1 characters. Then backtrack one byte.
  2411. $bytes = $istart;
  2412. $chars = 0;
  2413. while ($bytes < $strlen && $chars < $length) {
  2414. $bytes++;
  2415. $c = ord($string[$bytes]);
  2416. if ($c < 0x80 || $c >= 0xC0) {
  2417. $chars++;
  2418. }
  2419. }
  2420. $bytes--;
  2421. } else {
  2422. if ($length < 0) {
  2423. // Count all the continuation bytes from the end until we have found
  2424. // abs($length) characters
  2425. $length = abs($length);
  2426. $bytes = $strlen - 1;
  2427. $chars = 0;
  2428. while ($bytes >= 0 && $chars < $length) {
  2429. $c = ord($string[$bytes]);
  2430. if ($c < 0x80 || $c >= 0xC0) {
  2431. $chars++;
  2432. }
  2433. $bytes--;
  2434. }
  2435. }
  2436. }
  2437. }
  2438. $iend = $bytes;
  2439. $string = substr($string, $istart, max(0, $iend - $istart + 1));
  2440. }
  2441. if (!api_is_utf8($encoding)) {
  2442. $string = api_utf8_decode($string, $encoding);
  2443. }
  2444. return $string;
  2445. }
  2446. return substr($string, $start, $length);
  2447. }
  2448. /**
  2449. * Counts the number of substring occurrences.
  2450. * @param string $haystack The string being checked.
  2451. * @param string $needle The string being found.
  2452. * @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.
  2453. * @return int The number of times the needle substring occurs in the haystack string.
  2454. * @link http://php.net/manual/en/function.mb-substr-count.php
  2455. */
  2456. function api_substr_count($haystack, $needle, $encoding = null)
  2457. {
  2458. if (empty($encoding)) {
  2459. $encoding = _api_mb_internal_encoding();
  2460. }
  2461. if (_api_mb_supports($encoding)) {
  2462. return @mb_substr_count($haystack, $needle, $encoding);
  2463. }
  2464. return substr_count($haystack, $needle);
  2465. }
  2466. /**
  2467. * Replaces text within a portion of a string.
  2468. * @param string $string The input string.
  2469. * @param string $replacement The replacement string.
  2470. * @param int $start The position from which replacing will begin.
  2471. * Notes:
  2472. * If $start is positive, the replacing will begin at the $start'th offset into the string.
  2473. * If $start is negative, the replacing will begin at the $start'th character from the end of the string.
  2474. * @param int $length (optional) The position where replacing will end.
  2475. * Notes:
  2476. * If given and is positive, it represents the length of the portion of the string which is to be replaced.
  2477. * If it is negative, it represents the number of characters from the end of string at which to stop replacing.
  2478. * If it is not given, then it will default to api_strlen($string); i.e. end the replacing at the end of string.
  2479. * If $length is zero, then this function will have the effect of inserting replacement into the string at the given start offset.
  2480. * @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.
  2481. * @return string The result string is returned.
  2482. * This function is aimed at replacing the function substr_replace() for human-language strings.
  2483. * @link http://php.net/manual/function.substr-replace
  2484. */
  2485. function api_substr_replace($string, $replacement, $start, $length = null, $encoding = null)
  2486. {
  2487. if (empty($encoding)) {
  2488. $encoding = _api_mb_internal_encoding();
  2489. }
  2490. if (_api_is_single_byte_encoding($encoding)) {
  2491. if (is_null($length)) {
  2492. return substr_replace($string, $replacement, $start);
  2493. }
  2494. return substr_replace($string, $replacement, $start, $length);
  2495. }
  2496. if (api_is_encoding_supported($encoding)) {
  2497. if (is_null($length)) {
  2498. $length = api_strlen($string);
  2499. }
  2500. if (!api_is_utf8($encoding)) {
  2501. $string = api_utf8_encode($string, $encoding);
  2502. $replacement = api_utf8_encode($replacement, $encoding);
  2503. }
  2504. $string = _api_utf8_to_unicode($string);
  2505. array_splice($string, $start, $length, _api_utf8_to_unicode($replacement));
  2506. $string = _api_utf8_from_unicode($string);
  2507. if (!api_is_utf8($encoding)) {
  2508. $string = api_utf8_decode($string, $encoding);
  2509. }
  2510. return $string;
  2511. }
  2512. if (is_null($length)) {
  2513. return substr_replace($string, $replacement, $start);
  2514. }
  2515. return substr_replace($string, $replacement, $start, $length);
  2516. }
  2517. /**
  2518. * Makes a string's first character uppercase.
  2519. * @param string $string The input string.
  2520. * @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.
  2521. * @return string Returns a string with the first character capitalized, if that character is alphabetic.
  2522. * This function is aimed at replacing the function ucfirst() for human-language strings.
  2523. * @link http://php.net/manual/en/function.ucfirst
  2524. */
  2525. function api_ucfirst($string, $encoding = null)
  2526. {
  2527. if (empty($encoding)) {
  2528. $encoding = _api_mb_internal_encoding();
  2529. }
  2530. return api_strtoupper(api_substr($string, 0, 1, $encoding), $encoding).api_substr(
  2531. $string,
  2532. 1,
  2533. api_strlen($string, $encoding),
  2534. $encoding
  2535. );
  2536. }
  2537. /**
  2538. * Uppercases the first character of each word in a string.
  2539. * @param string $string The input string.
  2540. * @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.
  2541. * @return string Returns the modified string.
  2542. * This function is aimed at replacing the function ucwords() for human-language strings.
  2543. * @link http://php.net/manual/en/function.ucwords
  2544. */
  2545. function api_ucwords($string, $encoding = null)
  2546. {
  2547. if (empty($encoding)) {
  2548. $encoding = _api_mb_internal_encoding();
  2549. }
  2550. if (_api_mb_supports($encoding)) {
  2551. return @mb_convert_case($string, MB_CASE_TITLE, $encoding);
  2552. }
  2553. if (api_is_encoding_supported($encoding)) {
  2554. if (!api_is_utf8($encoding)) {
  2555. $string = api_utf8_encode($string, $encoding);
  2556. }
  2557. if (MBSTRING_INSTALLED) {
  2558. $string = @mb_convert_case($string, MB_CASE_TITLE, 'UTF-8');
  2559. } else {
  2560. // The following fragment (branch) of code is based on the function utf8_ucwords() by Harry Fuecks
  2561. // See http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
  2562. // Note: [\x0c\x09\x0b\x0a\x0d\x20] matches - form feeds, horizontal tabs, vertical tabs, linefeeds and carriage returns.
  2563. // This corresponds to the definition of a "word" defined at http://www.php.net/ucwords
  2564. $pattern = '/(^|([\x0c\x09\x0b\x0a\x0d\x20]+))([^\x0c\x09\x0b\x0a\x0d\x20]{1})[^\x0c\x09\x0b\x0a\x0d\x20]*/u';
  2565. $string = preg_replace_callback($pattern, '_api_utf8_ucwords_callback', $string);
  2566. }
  2567. if (!api_is_utf8($encoding)) {
  2568. return api_utf8_decode($string, $encoding);
  2569. }
  2570. return $string;
  2571. }
  2572. return ucwords($string);
  2573. }
  2574. /**
  2575. * String operations using regular expressions
  2576. */
  2577. /**
  2578. * Performs a regular expression match, UTF-8 aware when it is applicable.
  2579. * @param string $pattern The pattern to search for, as a string.
  2580. * @param string $subject The input string.
  2581. * @param array &$matches (optional) If matches is provided, then it is filled with the results of search (as an array).
  2582. * $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.
  2583. * @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.
  2584. * 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.
  2585. * @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.
  2586. * @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.
  2587. * @return int|boolean Returns the number of times pattern matches or FALSE if an error occurred.
  2588. * @link http://php.net/preg_match
  2589. */
  2590. function api_preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0, $encoding = null)
  2591. {
  2592. if (empty($encoding)) {
  2593. $encoding = _api_mb_internal_encoding();
  2594. }
  2595. return preg_match(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $matches, $flags, $offset);
  2596. }
  2597. /**
  2598. * Performs a global regular expression match, UTF-8 aware when it is applicable.
  2599. * @param string $pattern The pattern to search for, as a string.
  2600. * @param string $subject The input string.
  2601. * @param array &$matches (optional) Array of all matches in multi-dimensional array ordered according to $flags.
  2602. * @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):
  2603. * 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;
  2604. * 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;
  2605. * 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
  2606. * 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.
  2607. * If no order flag is given, PREG_PATTERN_ORDER is assumed.
  2608. * @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.
  2609. * @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.
  2610. * @return int|boolean Returns the number of full pattern matches (which might be zero), or FALSE if an error occurred.
  2611. * @link http://php.net/preg_match_all
  2612. */
  2613. function api_preg_match_all($pattern, $subject, &$matches, $flags = PREG_PATTERN_ORDER, $offset = 0, $encoding = null)
  2614. {
  2615. if (empty($encoding)) {
  2616. $encoding = _api_mb_internal_encoding();
  2617. }
  2618. if (is_null($flags)) {
  2619. $flags = PREG_PATTERN_ORDER;
  2620. }
  2621. return preg_match_all(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $matches, $flags, $offset);
  2622. }
  2623. /**
  2624. * Performs a regular expression search and replace, UTF-8 aware when it is applicable.
  2625. * @param string|array $pattern The pattern to search for. It can be either a string or an array with strings.
  2626. * @param string|array $replacement The string or an array with strings to replace.
  2627. * @param string|array $subject The string or an array with strings to search and replace.
  2628. * @param int $limit The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit).
  2629. * @param int &$count If specified, this variable will be filled with the number of replacements done.
  2630. * @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.
  2631. * @return array|string|null returns an array if the subject parameter is an array, or a string otherwise.
  2632. * If matches are found, the new subject will be returned, otherwise subject will be returned unchanged or NULL if an error occurred.
  2633. * @link http://php.net/preg_replace
  2634. */
  2635. function api_preg_replace($pattern, $replacement, $subject, $limit = -1, &$count = 0, $encoding = null)
  2636. {
  2637. if (empty($encoding)) {
  2638. $encoding = _api_mb_internal_encoding();
  2639. }
  2640. $is_utf8 = api_is_utf8($encoding);
  2641. if (is_array($pattern)) {
  2642. foreach ($pattern as &$p) {
  2643. $p = $is_utf8 ? $p.'u' : $p;
  2644. }
  2645. } else {
  2646. $pattern = $is_utf8 ? $pattern.'u' : $pattern;
  2647. }
  2648. return preg_replace($pattern, $replacement, $subject, $limit, $count);
  2649. }
  2650. /**
  2651. * Performs a regular expression search and replace using a callback function, UTF-8 aware when it is applicable.
  2652. * @param string|array $pattern The pattern to search for. It can be either a string or an array with strings.
  2653. * @param function $callback A callback that will be called and passed an array of matched elements in the $subject string. The callback should return the replacement string.
  2654. * @param string|array $subject The string or an array with strings to search and replace.
  2655. * @param int $limit (optional) The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit).
  2656. * @param int &$count (optional) If specified, this variable will be filled with the number of replacements done.
  2657. * @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.
  2658. * @return array|string Returns an array if the subject parameter is an array, or a string otherwise.
  2659. * @link http://php.net/preg_replace_callback
  2660. */
  2661. function api_preg_replace_callback($pattern, $callback, $subject, $limit = -1, &$count = 0, $encoding = null)
  2662. {
  2663. if (empty($encoding)) {
  2664. $encoding = _api_mb_internal_encoding();
  2665. }
  2666. if (is_array($pattern)) {
  2667. foreach ($pattern as &$p) {
  2668. $p = api_is_utf8($encoding) ? $p.'u' : $p;
  2669. }
  2670. } else {
  2671. $pattern = api_is_utf8($encoding) ? $pattern.'u' : $pattern;
  2672. }
  2673. return preg_replace_callback($pattern, $callback, $subject, $limit, $count);
  2674. }
  2675. /**
  2676. * Splits a string by a regular expression, UTF-8 aware when it is applicable.
  2677. * @param string $pattern The pattern to search for, as a string.
  2678. * @param string $subject The input string.
  2679. * @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.
  2680. * @param int $flags (optional) $flags can be any combination of the following flags (combined with bitwise | operator):
  2681. * PREG_SPLIT_NO_EMPTY - if this flag is set, only non-empty pieces will be returned;
  2682. * PREG_SPLIT_DELIM_CAPTURE - if this flag is set, parenthesized expression in the delimiter pattern will be captured and returned as well;
  2683. * PREG_SPLIT_OFFSET_CAPTURE - If this flag is set, for every occurring match the appendant string offset will also be returned.
  2684. * 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.
  2685. * @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.
  2686. * @return array Returns an array containing substrings of $subject split along boundaries matched by $pattern.
  2687. * @link http://php.net/preg_split
  2688. */
  2689. function api_preg_split($pattern, $subject, $limit = -1, $flags = 0, $encoding = null)
  2690. {
  2691. if (empty($encoding)) {
  2692. $encoding = _api_mb_internal_encoding();
  2693. }
  2694. return preg_split(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $limit, $flags);
  2695. }
  2696. /**
  2697. * Obsolete string operations using regular expressions, to be deprecated
  2698. */
  2699. /**
  2700. * Note: Try to avoid using this function. Use api_preg_match() with Perl-compatible regular expression syntax.
  2701. *
  2702. * Executes a regular expression match with extended multibyte support.
  2703. * By default this function uses the platform character set.
  2704. * @param string $pattern The regular expression pattern.
  2705. * @param string $string The searched string.
  2706. * @param array $regs (optional) If specified, by this passed by reference parameter an array containing found match and its substrings is returned.
  2707. * @return mixed 1 if match is found, FALSE if not. If $regs has been specified, byte-length of the found match is returned, or FALSE if no match has been found.
  2708. * This function is aimed at replacing the functions ereg() and mb_ereg() for human-language strings.
  2709. * @link http://php.net/manual/en/function.ereg
  2710. * @link http://php.net/manual/en/function.mb-ereg
  2711. */
  2712. function api_ereg($pattern, $string, & $regs = null)
  2713. {
  2714. $count = func_num_args();
  2715. $encoding = _api_mb_regex_encoding();
  2716. if (_api_mb_supports($encoding)) {
  2717. if ($count < 3) {
  2718. return @mb_ereg($pattern, $string);
  2719. }
  2720. return @mb_ereg($pattern, $string, $regs);
  2721. }
  2722. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  2723. global $_api_encoding;
  2724. $_api_encoding = $encoding;
  2725. _api_mb_regex_encoding('UTF-8');
  2726. if ($count < 3) {
  2727. $result = @mb_ereg(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding));
  2728. } else {
  2729. $result = @mb_ereg(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding), $regs);
  2730. $regs = _api_array_utf8_decode($regs);
  2731. }
  2732. _api_mb_regex_encoding($encoding);
  2733. return $result;
  2734. }
  2735. if ($count < 3) {
  2736. return ereg($pattern, $string);
  2737. }
  2738. return ereg($pattern, $string, $regs);
  2739. }
  2740. /**
  2741. * Note: Try to avoid using this function. Use api_preg_replace() with Perl-compatible regular expression syntax.
  2742. *
  2743. * Scans string for matches to pattern, then replaces the matched text with replacement, with extended multibyte support.
  2744. * By default this function uses the platform character set.
  2745. * @param string $pattern The regular expression pattern.
  2746. * @param string $replacement The replacement text.
  2747. * @param string $string The searched string.
  2748. * @param string $option (optional) Matching condition.
  2749. * If i is specified for the matching condition parameter, the case will be ignored.
  2750. * If x is specified, white space will be ignored.
  2751. * If m is specified, match will be executed in multiline mode and line break will be included in '.'.
  2752. * If p is specified, match will be executed in POSIX mode, line break will be considered as normal character.
  2753. * If e is specified, replacement string will be evaluated as PHP expression.
  2754. * @return mixed The modified string is returned. If no matches are found within the string, then it will be returned unchanged. FALSE will be returned on error.
  2755. * This function is aimed at replacing the functions ereg_replace() and mb_ereg_replace() for human-language strings.
  2756. * @link http://php.net/manual/en/function.ereg-replace
  2757. * @link http://php.net/manual/en/function.mb-ereg-replace
  2758. */
  2759. function api_ereg_replace($pattern, $replacement, $string, $option = null)
  2760. {
  2761. $encoding = _api_mb_regex_encoding();
  2762. if (_api_mb_supports($encoding)) {
  2763. if (is_null($option)) {
  2764. return @mb_ereg_replace($pattern, $replacement, $string);
  2765. }
  2766. return @mb_ereg_replace($pattern, $replacement, $string, $option);
  2767. }
  2768. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  2769. _api_mb_regex_encoding('UTF-8');
  2770. if (is_null($option)) {
  2771. $result = api_utf8_decode(
  2772. @mb_ereg_replace(
  2773. api_utf8_encode($pattern, $encoding),
  2774. api_utf8_encode($replacement, $encoding),
  2775. api_utf8_encode($string, $encoding)
  2776. ),
  2777. $encoding
  2778. );
  2779. } else {
  2780. $result = api_utf8_decode(
  2781. @mb_ereg_replace(
  2782. api_utf8_encode($pattern, $encoding),
  2783. api_utf8_encode($replacement, $encoding),
  2784. api_utf8_encode($string, $encoding),
  2785. $option
  2786. ),
  2787. $encoding
  2788. );
  2789. }
  2790. _api_mb_regex_encoding($encoding);
  2791. return $result;
  2792. }
  2793. return ereg_replace($pattern, $replacement, $string);
  2794. }
  2795. /**
  2796. * Note: Try to avoid using this function. Use api_preg_match() with Perl-compatible regular expression syntax.
  2797. *
  2798. * Executes a regular expression match, ignoring case, with extended multibyte support.
  2799. * By default this function uses the platform character set.
  2800. * @param string $pattern The regular expression pattern.
  2801. * @param string $string The searched string.
  2802. * @param array $regs (optional) If specified, by this passed by reference parameter an array containing found match and its substrings is returned.
  2803. * @return mixed 1 if match is found, FALSE if not. If $regs has been specified, byte-length of the found match is returned, or FALSE if no match has been found.
  2804. * This function is aimed at replacing the functions eregi() and mb_eregi() for human-language strings.
  2805. * @link http://php.net/manual/en/function.eregi
  2806. * @link http://php.net/manual/en/function.mb-eregi
  2807. */
  2808. function api_eregi($pattern, $string, & $regs = null)
  2809. {
  2810. $count = func_num_args();
  2811. $encoding = _api_mb_regex_encoding();
  2812. if (_api_mb_supports($encoding)) {
  2813. if ($count < 3) {
  2814. return @mb_eregi($pattern, $string);
  2815. }
  2816. return @mb_eregi($pattern, $string, $regs);
  2817. }
  2818. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  2819. global $_api_encoding;
  2820. $_api_encoding = $encoding;
  2821. _api_mb_regex_encoding('UTF-8');
  2822. if ($count < 3) {
  2823. $result = @mb_eregi(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding));
  2824. } else {
  2825. $result = @mb_eregi(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding), $regs);
  2826. $regs = _api_array_utf8_decode($regs);
  2827. }
  2828. _api_mb_regex_encoding($encoding);
  2829. return $result;
  2830. }
  2831. if ($count < 3) {
  2832. return eregi($pattern, $string);
  2833. }
  2834. return eregi($pattern, $string, $regs);
  2835. }
  2836. /**
  2837. * Note: Try to avoid using this function. Use api_preg_replace() with Perl-compatible regular expression syntax.
  2838. *
  2839. * Scans string for matches to pattern, then replaces the matched text with replacement, ignoring case, with extended multibyte support.
  2840. * By default this function uses the platform character set.
  2841. * @param string $pattern The regular expression pattern.
  2842. * @param string $replacement The replacement text.
  2843. * @param string $string The searched string.
  2844. * @param string $option (optional) Matching condition.
  2845. * If i is specified for the matching condition parameter, the case will be ignored.
  2846. * If x is specified, white space will be ignored.
  2847. * If m is specified, match will be executed in multiline mode and line break will be included in '.'.
  2848. * If p is specified, match will be executed in POSIX mode, line break will be considered as normal character.
  2849. * If e is specified, replacement string will be evaluated as PHP expression.
  2850. * @return mixed The modified string is returned. If no matches are found within the string, then it will be returned unchanged. FALSE will be returned on error.
  2851. * This function is aimed at replacing the functions eregi_replace() and mb_eregi_replace() for human-language strings.
  2852. * @link http://php.net/manual/en/function.eregi-replace
  2853. * @link http://php.net/manual/en/function.mb-eregi-replace
  2854. */
  2855. function api_eregi_replace($pattern, $replacement, $string, $option = null)
  2856. {
  2857. $encoding = _api_mb_regex_encoding();
  2858. if (_api_mb_supports($encoding)) {
  2859. if (is_null($option)) {
  2860. return @mb_eregi_replace($pattern, $replacement, $string);
  2861. }
  2862. return @mb_eregi_replace($pattern, $replacement, $string, $option);
  2863. }
  2864. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  2865. _api_mb_regex_encoding('UTF-8');
  2866. if (is_null($option)) {
  2867. $result = api_utf8_decode(
  2868. @mb_eregi_replace(
  2869. api_utf8_encode($pattern, $encoding),
  2870. api_utf8_encode($replacement, $encoding),
  2871. api_utf8_encode($string, $encoding)
  2872. ),
  2873. $encoding
  2874. );
  2875. } else {
  2876. $result = api_utf8_decode(
  2877. @mb_eregi_replace(
  2878. api_utf8_encode($pattern, $encoding),
  2879. api_utf8_encode($replacement, $encoding),
  2880. api_utf8_encode($string, $encoding),
  2881. $option
  2882. ),
  2883. $encoding
  2884. );
  2885. }
  2886. _api_mb_regex_encoding($encoding);
  2887. return $result;
  2888. }
  2889. return eregi_replace($pattern, $replacement, $string);
  2890. }
  2891. /**
  2892. * Note: Try to avoid using this function. Use api_preg_split() with Perl-compatible regular expression syntax.
  2893. *
  2894. * Splits a multibyte string using regular expression pattern and returns the result as an array.
  2895. * By default this function uses the platform character set.
  2896. * @param string $pattern The regular expression pattern.
  2897. * @param string $string The string being split.
  2898. * @param int $limit (optional) If this optional parameter $limit is specified, the string will be split in $limit elements as maximum.
  2899. * @return array The result as an array.
  2900. * This function is aimed at replacing the functions split() and mb_split() for human-language strings.
  2901. * @link http://php.net/manual/en/function.split
  2902. * @link http://php.net/manual/en/function.mb-split
  2903. */
  2904. function api_split($pattern, $string, $limit = null)
  2905. {
  2906. $encoding = _api_mb_regex_encoding();
  2907. if (_api_mb_supports($encoding)) {
  2908. if (is_null($limit)) {
  2909. return @mb_split($pattern, $string);
  2910. }
  2911. return @mb_split($pattern, $string, $limit);
  2912. }
  2913. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  2914. global $_api_encoding;
  2915. $_api_encoding = $encoding;
  2916. _api_mb_regex_encoding('UTF-8');
  2917. if (is_null($limit)) {
  2918. $result = @mb_split(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding));
  2919. } else {
  2920. $result = @mb_split(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding), $limit);
  2921. }
  2922. $result = _api_array_utf8_decode($result);
  2923. _api_mb_regex_encoding($encoding);
  2924. return $result;
  2925. }
  2926. if (is_null($limit)) {
  2927. return split($pattern, $string);
  2928. }
  2929. return split($pattern, $string, $limit);
  2930. }
  2931. /**
  2932. * String comparison
  2933. */
  2934. /**
  2935. * Performs string comparison, case insensitive, language sensitive, with extended multibyte support.
  2936. * @param string $string1 The first string.
  2937. * @param string $string2 The second string.
  2938. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  2939. * @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.
  2940. * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
  2941. * This function is aimed at replacing the function strcasecmp() for human-language strings.
  2942. * @link http://php.net/manual/en/function.strcasecmp
  2943. */
  2944. function api_strcasecmp($string1, $string2, $language = null, $encoding = null)
  2945. {
  2946. return api_strcmp(api_strtolower($string1, $encoding), api_strtolower($string2, $encoding), $language, $encoding);
  2947. }
  2948. /**
  2949. * Performs string comparison, case sensitive, language sensitive, with extended multibyte support.
  2950. * @param string $string1 The first string.
  2951. * @param string $string2 The second string.
  2952. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  2953. * @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.
  2954. * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
  2955. * This function is aimed at replacing the function strcmp() for human-language strings.
  2956. * @link http://php.net/manual/en/function.strcmp.php
  2957. * @link http://php.net/manual/en/collator.compare.php
  2958. */
  2959. function api_strcmp($string1, $string2, $language = null, $encoding = null)
  2960. {
  2961. if (INTL_INSTALLED) {
  2962. $collator = _api_get_collator($language);
  2963. if (is_object($collator)) {
  2964. $result = collator_compare(
  2965. $collator,
  2966. api_utf8_encode($string1, $encoding),
  2967. api_utf8_encode($string2, $encoding)
  2968. );
  2969. return $result === false ? 0 : $result;
  2970. }
  2971. }
  2972. return strcmp($string1, $string2);
  2973. }
  2974. /**
  2975. * Performs string comparison in so called "natural order", case insensitive, language sensitive, with extended multibyte support.
  2976. * @param string $string1 The first string.
  2977. * @param string $string2 The second string.
  2978. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  2979. * @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.
  2980. * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
  2981. * This function is aimed at replacing the function strnatcasecmp() for human-language strings.
  2982. * @link http://php.net/manual/en/function.strnatcasecmp
  2983. */
  2984. function api_strnatcasecmp($string1, $string2, $language = null, $encoding = null)
  2985. {
  2986. return api_strnatcmp(
  2987. api_strtolower($string1, $encoding),
  2988. api_strtolower($string2, $encoding),
  2989. $language,
  2990. $encoding
  2991. );
  2992. }
  2993. /**
  2994. * Performs string comparison in so called "natural order", case sensitive, language sensitive, with extended multibyte support.
  2995. * @param string $string1 The first string.
  2996. * @param string $string2 The second string.
  2997. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  2998. * @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.
  2999. * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
  3000. * This function is aimed at replacing the function strnatcmp() for human-language strings.
  3001. * @link http://php.net/manual/en/function.strnatcmp.php
  3002. * @link http://php.net/manual/en/collator.compare.php
  3003. */
  3004. function api_strnatcmp($string1, $string2, $language = null, $encoding = null)
  3005. {
  3006. if (INTL_INSTALLED) {
  3007. $collator = _api_get_alpha_numerical_collator($language);
  3008. if (is_object($collator)) {
  3009. $result = collator_compare(
  3010. $collator,
  3011. api_utf8_encode($string1, $encoding),
  3012. api_utf8_encode($string2, $encoding)
  3013. );
  3014. return $result === false ? 0 : $result;
  3015. }
  3016. }
  3017. return strnatcmp($string1, $string2);
  3018. }
  3019. /**
  3020. * Sorting arrays
  3021. */
  3022. /**
  3023. * Sorts an array with maintaining index association, elements will be arranged from the lowest to the highest.
  3024. * @param array $array The input array.
  3025. * @param int $sort_flag (optional) Shows how elements of the array to be compared.
  3026. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3027. * @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.
  3028. * @return bool Returns TRUE on success, FALSE on error.
  3029. * Note: $sort_flag may have the following values:
  3030. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  3031. * SORT_NUMERIC - items will be compared as numbers;
  3032. * SORT_STRING - items will be compared as strings. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale;
  3033. * SORT_LOCALE_STRING - items will be compared as strings depending on the current POSIX locale. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale.
  3034. * This function is aimed at replacing the function asort() for sorting human-language strings.
  3035. * @link http://php.net/manual/en/function.asort.php
  3036. * @link http://php.net/manual/en/collator.asort.php
  3037. */
  3038. function api_asort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null)
  3039. {
  3040. if (INTL_INSTALLED) {
  3041. if (empty($encoding)) {
  3042. $encoding = _api_mb_internal_encoding();
  3043. }
  3044. $collator = _api_get_collator($language);
  3045. if (is_object($collator)) {
  3046. if (api_is_utf8($encoding)) {
  3047. $sort_flag = ($sort_flag == SORT_LOCALE_STRING) ? SORT_STRING : $sort_flag;
  3048. return collator_asort($collator, $array, _api_get_collator_sort_flag($sort_flag));
  3049. } elseif ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  3050. global $_api_collator, $_api_encoding;
  3051. $_api_collator = $collator;
  3052. $_api_encoding = $encoding;
  3053. return uasort($array, '_api_cmp');
  3054. }
  3055. }
  3056. }
  3057. return asort($array, $sort_flag);
  3058. }
  3059. /**
  3060. * Sorts an array with maintaining index association, elements will be arranged from the highest to the lowest (in reverse order).
  3061. * @param array $array The input array.
  3062. * @param int $sort_flag (optional) Shows how elements of the array to be compared.
  3063. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3064. * @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.
  3065. * @return bool Returns TRUE on success, FALSE on error.
  3066. * Note: $sort_flag may have the following values:
  3067. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  3068. * SORT_NUMERIC - items will be compared as numbers;
  3069. * SORT_STRING - items will be compared as strings. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale;
  3070. * SORT_LOCALE_STRING - items will be compared as strings depending on the current POSIX locale. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale.
  3071. * This function is aimed at replacing the function arsort() for sorting human-language strings.
  3072. * @link http://php.net/manual/en/function.arsort.php
  3073. */
  3074. function api_arsort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null)
  3075. {
  3076. if (INTL_INSTALLED) {
  3077. if (empty($encoding)) {
  3078. $encoding = _api_mb_internal_encoding();
  3079. }
  3080. $collator = _api_get_collator($language);
  3081. if (is_object($collator)) {
  3082. if ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  3083. global $_api_collator, $_api_encoding;
  3084. $_api_collator = $collator;
  3085. $_api_encoding = $encoding;
  3086. return uasort($array, '_api_rcmp');
  3087. }
  3088. }
  3089. }
  3090. return arsort($array, $sort_flag);
  3091. }
  3092. /**
  3093. * Sorts an array using natural order algorithm.
  3094. * @param array $array The input array.
  3095. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3096. * @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.
  3097. * @return bool Returns TRUE on success, FALSE on error.
  3098. * This function is aimed at replacing the function natsort() for sorting human-language strings.
  3099. * @link http://php.net/manual/en/function.natsort.php
  3100. */
  3101. function api_natsort(&$array, $language = null, $encoding = null)
  3102. {
  3103. if (INTL_INSTALLED) {
  3104. if (empty($encoding)) {
  3105. $encoding = _api_mb_internal_encoding();
  3106. }
  3107. $collator = _api_get_alpha_numerical_collator($language);
  3108. if (is_object($collator)) {
  3109. global $_api_collator, $_api_encoding;
  3110. $_api_collator = $collator;
  3111. $_api_encoding = $encoding;
  3112. return uasort($array, '_api_cmp');
  3113. }
  3114. }
  3115. return natsort($array);
  3116. }
  3117. /**
  3118. * Sorts an array using natural order algorithm in reverse order.
  3119. * @param array $array The input array.
  3120. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3121. * @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.
  3122. * @return bool Returns TRUE on success, FALSE on error.
  3123. */
  3124. function api_natrsort(&$array, $language = null, $encoding = null)
  3125. {
  3126. if (INTL_INSTALLED) {
  3127. if (empty($encoding)) {
  3128. $encoding = _api_mb_internal_encoding();
  3129. }
  3130. $collator = _api_get_alpha_numerical_collator($language);
  3131. if (is_object($collator)) {
  3132. global $_api_collator, $_api_encoding;
  3133. $_api_collator = $collator;
  3134. $_api_encoding = $encoding;
  3135. return uasort($array, '_api_rcmp');
  3136. }
  3137. }
  3138. return uasort($array, '_api_strnatrcmp');
  3139. }
  3140. /**
  3141. * Sorts an array using natural order algorithm, case-insensitive.
  3142. * @param array $array The input array.
  3143. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3144. * @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.
  3145. * @return bool Returns TRUE on success, FALSE on error.
  3146. * This function is aimed at replacing the function natcasesort() for sorting human-language strings.
  3147. * @link http://php.net/manual/en/function.natcasesort.php
  3148. */
  3149. function api_natcasesort(&$array, $language = null, $encoding = null)
  3150. {
  3151. if (INTL_INSTALLED) {
  3152. if (empty($encoding)) {
  3153. $encoding = _api_mb_internal_encoding();
  3154. }
  3155. $collator = _api_get_alpha_numerical_collator($language);
  3156. if (is_object($collator)) {
  3157. global $_api_collator, $_api_encoding;
  3158. $_api_collator = $collator;
  3159. $_api_encoding = $encoding;
  3160. return uasort($array, '_api_casecmp');
  3161. }
  3162. }
  3163. return natcasesort($array);
  3164. }
  3165. /**
  3166. * Sorts an array using natural order algorithm, case-insensitive, reverse order.
  3167. * @param array $array The input array.
  3168. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3169. * @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.
  3170. * @return bool Returns TRUE on success, FALSE on error.
  3171. */
  3172. function api_natcasersort(&$array, $language = null, $encoding = null)
  3173. {
  3174. if (INTL_INSTALLED) {
  3175. if (empty($encoding)) {
  3176. $encoding = _api_mb_internal_encoding();
  3177. }
  3178. $collator = _api_get_alpha_numerical_collator($language);
  3179. if (is_object($collator)) {
  3180. global $_api_collator, $_api_encoding;
  3181. $_api_collator = $collator;
  3182. $_api_encoding = $encoding;
  3183. return uasort($array, '_api_casercmp');
  3184. }
  3185. }
  3186. return uasort($array, '_api_strnatcasercmp');
  3187. }
  3188. /**
  3189. * Sorts an array by keys, elements will be arranged from the lowest key to the highest key.
  3190. * @param array $array The input array.
  3191. * @param int $sort_flag (optional) Shows how keys of the array to be compared.
  3192. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3193. * @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.
  3194. * @return bool Returns TRUE on success, FALSE on error.
  3195. * Note: $sort_flag may have the following values:
  3196. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  3197. * SORT_NUMERIC - keys will be compared as numbers;
  3198. * SORT_STRING - keys will be compared as strings. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale;
  3199. * SORT_LOCALE_STRING - keys will be compared as strings depending on the current POSIX locale. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale.
  3200. * This function is aimed at replacing the function ksort() for sorting human-language key strings.
  3201. * @link http://php.net/manual/en/function.ksort.php
  3202. */
  3203. function api_ksort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null)
  3204. {
  3205. if (INTL_INSTALLED) {
  3206. if (empty($encoding)) {
  3207. $encoding = _api_mb_internal_encoding();
  3208. }
  3209. $collator = _api_get_collator($language);
  3210. if (is_object($collator)) {
  3211. if ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  3212. global $_api_collator, $_api_encoding;
  3213. $_api_collator = $collator;
  3214. $_api_encoding = $encoding;
  3215. return uksort($array, '_api_cmp');
  3216. }
  3217. }
  3218. }
  3219. return ksort($array, $sort_flag);
  3220. }
  3221. /**
  3222. * Sorts an array by keys, elements will be arranged from the highest key to the lowest key (in reverse order).
  3223. * @param array $array The input array.
  3224. * @param int $sort_flag (optional) Shows how keys of the array to be compared.
  3225. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3226. * @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.
  3227. * @return bool Returns TRUE on success, FALSE on error.
  3228. * Note: $sort_flag may have the following values:
  3229. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  3230. * SORT_NUMERIC - keys will be compared as numbers;
  3231. * SORT_STRING - keys will be compared as strings. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale;
  3232. * SORT_LOCALE_STRING - keys will be compared as strings depending on the current POSIX locale. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale.
  3233. * This function is aimed at replacing the function krsort() for sorting human-language key strings.
  3234. * @link http://php.net/manual/en/function.krsort.php
  3235. */
  3236. function api_krsort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null)
  3237. {
  3238. if (INTL_INSTALLED) {
  3239. if (empty($encoding)) {
  3240. $encoding = _api_mb_internal_encoding();
  3241. }
  3242. $collator = _api_get_collator($language);
  3243. if (is_object($collator)) {
  3244. if ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  3245. global $_api_collator, $_api_encoding;
  3246. $_api_collator = $collator;
  3247. $_api_encoding = $encoding;
  3248. return uksort($array, '_api_rcmp');
  3249. }
  3250. }
  3251. }
  3252. return krsort($array, $sort_flag);
  3253. }
  3254. /**
  3255. * Sorts an array by keys using natural order algorithm.
  3256. * @param array $array The input array.
  3257. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3258. * @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.
  3259. * @return bool Returns TRUE on success, FALSE on error.
  3260. */
  3261. function api_knatsort(&$array, $language = null, $encoding = null)
  3262. {
  3263. if (INTL_INSTALLED) {
  3264. if (empty($encoding)) {
  3265. $encoding = _api_mb_internal_encoding();
  3266. }
  3267. $collator = _api_get_alpha_numerical_collator($language);
  3268. if (is_object($collator)) {
  3269. global $_api_collator, $_api_encoding;
  3270. $_api_collator = $collator;
  3271. $_api_encoding = $encoding;
  3272. return uksort($array, '_api_cmp');
  3273. }
  3274. }
  3275. return uksort($array, 'strnatcmp');
  3276. }
  3277. /**
  3278. * Sorts an array by keys using natural order algorithm in reverse order.
  3279. * @param array $array The input array.
  3280. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3281. * @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.
  3282. * @return bool Returns TRUE on success, FALSE on error.
  3283. */
  3284. function api_knatrsort(&$array, $language = null, $encoding = null)
  3285. {
  3286. if (INTL_INSTALLED) {
  3287. if (empty($encoding)) {
  3288. $encoding = _api_mb_internal_encoding();
  3289. }
  3290. $collator = _api_get_alpha_numerical_collator($language);
  3291. if (is_object($collator)) {
  3292. global $_api_collator, $_api_encoding;
  3293. $_api_collator = $collator;
  3294. $_api_encoding = $encoding;
  3295. return uksort($array, '_api_rcmp');
  3296. }
  3297. }
  3298. return uksort($array, '_api_strnatrcmp');
  3299. }
  3300. /**
  3301. * Sorts an array by keys using natural order algorithm, case insensitive.
  3302. * @param array $array The input array.
  3303. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3304. * @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.
  3305. * @return bool Returns TRUE on success, FALSE on error.
  3306. */
  3307. function api_knatcasesort(&$array, $language = null, $encoding = null)
  3308. {
  3309. if (INTL_INSTALLED) {
  3310. if (empty($encoding)) {
  3311. $encoding = _api_mb_internal_encoding();
  3312. }
  3313. $collator = _api_get_alpha_numerical_collator($language);
  3314. if (is_object($collator)) {
  3315. global $_api_collator, $_api_encoding;
  3316. $_api_collator = $collator;
  3317. $_api_encoding = $encoding;
  3318. return uksort($array, '_api_casecmp');
  3319. }
  3320. }
  3321. return uksort($array, 'strnatcasecmp');
  3322. }
  3323. /**
  3324. * Sorts an array by keys using natural order algorithm, case insensitive, reverse order.
  3325. * @param array $array The input array.
  3326. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3327. * @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.
  3328. * @return bool Returns TRUE on success, FALSE on error.
  3329. */
  3330. function api_knatcasersort(&$array, $language = null, $encoding = null)
  3331. {
  3332. if (INTL_INSTALLED) {
  3333. if (empty($encoding)) {
  3334. $encoding = _api_mb_internal_encoding();
  3335. }
  3336. $collator = _api_get_alpha_numerical_collator($language);
  3337. if (is_object($collator)) {
  3338. global $_api_collator, $_api_encoding;
  3339. $_api_collator = $collator;
  3340. $_api_encoding = $encoding;
  3341. return uksort($array, '_api_casercmp');
  3342. }
  3343. }
  3344. return uksort($array, '_api_strnatcasercmp');
  3345. }
  3346. /**
  3347. * Sorts an array, elements will be arranged from the lowest to the highest.
  3348. * @param array $array The input array.
  3349. * @param int $sort_flag (optional) Shows how elements of the array to be compared.
  3350. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3351. * @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.
  3352. * @return bool Returns TRUE on success, FALSE on error.
  3353. * Note: $sort_flag may have the following values:
  3354. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  3355. * SORT_NUMERIC - items will be compared as numbers;
  3356. * SORT_STRING - items will be compared as strings. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale;
  3357. * SORT_LOCALE_STRING - items will be compared as strings depending on the current POSIX locale. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale.
  3358. * This function is aimed at replacing the function sort() for sorting human-language strings.
  3359. * @link http://php.net/manual/en/function.sort.php
  3360. * @link http://php.net/manual/en/collator.sort.php
  3361. */
  3362. function api_sort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null)
  3363. {
  3364. if (INTL_INSTALLED) {
  3365. if (empty($encoding)) {
  3366. $encoding = _api_mb_internal_encoding();
  3367. }
  3368. $collator = _api_get_collator($language);
  3369. if (is_object($collator)) {
  3370. if (api_is_utf8($encoding)) {
  3371. $sort_flag = ($sort_flag == SORT_LOCALE_STRING) ? SORT_STRING : $sort_flag;
  3372. return collator_sort($collator, $array, _api_get_collator_sort_flag($sort_flag));
  3373. } elseif ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  3374. global $_api_collator, $_api_encoding;
  3375. $_api_collator = $collator;
  3376. $_api_encoding = $encoding;
  3377. return usort($array, '_api_cmp');
  3378. }
  3379. }
  3380. }
  3381. return sort($array, $sort_flag);
  3382. }
  3383. /**
  3384. * Sorts an array, elements will be arranged from the highest to the lowest (in reverse order).
  3385. * @param array $array The input array.
  3386. * @param int $sort_flag (optional) Shows how elements of the array to be compared.
  3387. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  3388. * @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.
  3389. * @return bool Returns TRUE on success, FALSE on error.
  3390. * Note: $sort_flag may have the following values:
  3391. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  3392. * SORT_NUMERIC - items will be compared as numbers;
  3393. * SORT_STRING - items will be compared as strings. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale;
  3394. * SORT_LOCALE_STRING - items will be compared as strings depending on the current POSIX locale. If intl extension is enabled, then comparison will be language-sensitive using internally a created ICU locale.
  3395. * This function is aimed at replacing the function rsort() for sorting human-language strings.
  3396. * @link http://php.net/manual/en/function.rsort.php
  3397. */
  3398. function api_rsort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null)
  3399. {
  3400. if (INTL_INSTALLED) {
  3401. if (empty($encoding)) {
  3402. $encoding = _api_mb_internal_encoding();
  3403. }
  3404. $collator = _api_get_collator($language);
  3405. if (is_object($collator)) {
  3406. if ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  3407. global $_api_collator, $_api_encoding;
  3408. $_api_collator = $collator;
  3409. $_api_encoding = $encoding;
  3410. return usort($array, '_api_rcmp');
  3411. }
  3412. }
  3413. }
  3414. return rsort($array, $sort_flag);
  3415. }
  3416. /**
  3417. * Common sting operations with arrays
  3418. */
  3419. /**
  3420. * Checks if a value exists in an array, a case insensitive version of in_array() function with extended multibyte support.
  3421. * @param mixed $needle The searched value. If needle is a string, the comparison is done in a case-insensitive manner.
  3422. * @param array $haystack The array.
  3423. * @param bool $strict (optional) If is set to TRUE then the function will also check the types of the $needle in the $haystack. The default value if FALSE.
  3424. * @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.
  3425. * @return bool Returns TRUE if $needle is found in the array, FALSE otherwise.
  3426. * @link http://php.net/manual/en/function.in-array.php
  3427. */
  3428. function api_in_array_nocase($needle, $haystack, $strict = false, $encoding = null)
  3429. {
  3430. if (is_array($needle)) {
  3431. foreach ($needle as $item) {
  3432. if (api_in_array_nocase($item, $haystack, $strict, $encoding)) {
  3433. return true;
  3434. }
  3435. }
  3436. return false;
  3437. }
  3438. if (!is_string($needle)) {
  3439. return in_array($needle, $haystack, $strict);
  3440. }
  3441. $needle = api_strtolower($needle, $encoding);
  3442. if (!is_array($haystack)) {
  3443. return false;
  3444. }
  3445. foreach ($haystack as $item) {
  3446. if ($strict && !is_string($item)) {
  3447. continue;
  3448. }
  3449. if (api_strtolower($item, $encoding) == $needle) {
  3450. return true;
  3451. }
  3452. }
  3453. return false;
  3454. }
  3455. /**
  3456. * Encoding management functions
  3457. */
  3458. /**
  3459. * This function unifies the encoding identificators, so they could be compared.
  3460. * @param string/array $encoding The specified encoding.
  3461. * @return string Returns the encoding identificator modified in suitable for comparison way.
  3462. */
  3463. function api_refine_encoding_id($encoding)
  3464. {
  3465. if (is_array($encoding)) {
  3466. return array_map('api_refine_encoding_id', $encoding);
  3467. }
  3468. return strtoupper(str_replace('_', '-', $encoding));
  3469. }
  3470. /**
  3471. * This function checks whether two $encoding are equal (same, equvalent).
  3472. * @param string/array $encoding1 The first encoding
  3473. * @param string/array $encoding2 The second encoding
  3474. * @param bool $strict When this parameter is TRUE the comparison ignores aliases of encodings. When the parameter is FALSE, aliases are taken into account.
  3475. * @return bool Returns TRUE if the encodings are equal, FALSE otherwise.
  3476. */
  3477. function api_equal_encodings($encoding1, $encoding2, $strict = false)
  3478. {
  3479. static $equal_encodings = array();
  3480. if (is_array($encoding1)) {
  3481. foreach ($encoding1 as $encoding) {
  3482. if (api_equal_encodings($encoding, $encoding2, $strict)) {
  3483. return true;
  3484. }
  3485. }
  3486. return false;
  3487. } elseif (is_array($encoding2)) {
  3488. foreach ($encoding2 as $encoding) {
  3489. if (api_equal_encodings($encoding1, $encoding, $strict)) {
  3490. return true;
  3491. }
  3492. }
  3493. return false;
  3494. }
  3495. if (!isset($equal_encodings[$encoding1][$encoding2][$strict])) {
  3496. $encoding_1 = api_refine_encoding_id($encoding1);
  3497. $encoding_2 = api_refine_encoding_id($encoding2);
  3498. if ($encoding_1 == $encoding_2) {
  3499. $result = true;
  3500. } else {
  3501. if ($strict) {
  3502. $result = false;
  3503. } else {
  3504. $alias1 = _api_get_character_map_name($encoding_1);
  3505. $alias2 = _api_get_character_map_name($encoding_2);
  3506. $result = !empty($alias1) && !empty($alias2) && $alias1 == $alias2;
  3507. }
  3508. }
  3509. $equal_encodings[$encoding1][$encoding2][$strict] = $result;
  3510. }
  3511. return $equal_encodings[$encoding1][$encoding2][$strict];
  3512. }
  3513. /**
  3514. * This function checks whether a given encoding is UTF-8.
  3515. * @param string $encoding The tested encoding.
  3516. * @return bool Returns TRUE if the given encoding id means UTF-8, otherwise returns false.
  3517. */
  3518. function api_is_utf8($encoding)
  3519. {
  3520. static $result = array();
  3521. if (!isset($result[$encoding])) {
  3522. $result[$encoding] = api_equal_encodings($encoding, 'UTF-8');
  3523. }
  3524. return $result[$encoding];
  3525. }
  3526. /**
  3527. * This function checks whether a given encoding represents (is an alias of) ISO Latin 1 character set.
  3528. * @param string/array $encoding The tested encoding.
  3529. * @param bool $strict Flag for check precision. ISO-8859-1 is always Latin 1. When $strict is false, ISO-8859-15 is assumed as Latin 1 too.
  3530. * @return bool Returns TRUE if the given encoding id means Latin 1 character set, otherwise returns false.
  3531. */
  3532. function api_is_latin1($encoding, $strict = false)
  3533. {
  3534. static $latin1 = array();
  3535. static $latin1_strict = array();
  3536. if ($strict) {
  3537. if (!isset($latin1_strict[$encoding])) {
  3538. $latin1_strict[$encoding] = api_equal_encodings(
  3539. $encoding,
  3540. array('ISO-8859-1', 'ISO8859-1', 'CP819', 'LATIN1')
  3541. );
  3542. }
  3543. return $latin1_strict[$encoding];
  3544. }
  3545. if (!isset($latin1[$encoding])) {
  3546. $latin1[$encoding] = api_equal_encodings(
  3547. $encoding,
  3548. array(
  3549. 'ISO-8859-1',
  3550. 'ISO8859-1',
  3551. 'CP819',
  3552. 'LATIN1',
  3553. 'ISO-8859-15',
  3554. 'ISO8859-15',
  3555. 'CP923',
  3556. 'LATIN0',
  3557. 'LATIN-9',
  3558. 'WINDOWS-1252',
  3559. 'CP1252',
  3560. 'WIN-1252',
  3561. 'WIN1252'
  3562. )
  3563. );
  3564. }
  3565. return $latin1[$encoding];
  3566. }
  3567. /**
  3568. * This function returns the encoding, currently used by the system.
  3569. * @return string The system's encoding, set in the configuration file
  3570. */
  3571. function api_get_system_encoding()
  3572. {
  3573. global $configuration;
  3574. return isset($configuration['platform_charset']) ? $configuration['platform_charset'] : 'utf-8';
  3575. /*
  3576. static $system_encoding;
  3577. if (!isset($system_encoding)) {
  3578. $encoding_setting = api_get_setting('platform_charset');
  3579. if (empty($encoding_setting)) {
  3580. global $charset;
  3581. if (empty($charset)) {
  3582. return _api_mb_internal_encoding();
  3583. }
  3584. return $charset;
  3585. }
  3586. $system_encoding = $encoding_setting;
  3587. }
  3588. return $system_encoding;*/
  3589. }
  3590. /**
  3591. * This function returns the encoding, currently used by the file system.
  3592. * @return string The file system's encoding, it depends on the locale that OS currently uses.
  3593. * @link http://php.net/manual/en/function.setlocale.php
  3594. * Note: For Linux systems, to see all installed locales type in a terminal locale -a
  3595. */
  3596. function api_get_file_system_encoding()
  3597. {
  3598. static $file_system_encoding;
  3599. if (!isset($file_system_encoding)) {
  3600. $locale = setlocale(LC_CTYPE, '0');
  3601. $seek_pos = strpos($locale, '.');
  3602. if ($seek_pos !== false) {
  3603. $file_system_encoding = substr($locale, $seek_pos + 1);
  3604. if (IS_WINDOWS_OS) {
  3605. $file_system_encoding = 'CP'.$file_system_encoding;
  3606. }
  3607. }
  3608. // Dealing with some aliases.
  3609. $file_system_encoding = str_ireplace('utf8', 'UTF-8', $file_system_encoding);
  3610. $file_system_encoding = preg_replace('/^CP65001$/', 'UTF-8', $file_system_encoding);
  3611. $file_system_encoding = preg_replace('/^CP(125[0-9])$/', 'WINDOWS-\1', $file_system_encoding);
  3612. $file_system_encoding = str_replace('WINDOWS-1252', 'ISO-8859-15', $file_system_encoding);
  3613. if (empty($file_system_encoding)) {
  3614. if (IS_WINDOWS_OS) {
  3615. // Not expected for Windows, this assignment is here just in case.
  3616. $file_system_encoding = api_get_system_encoding();
  3617. } else {
  3618. // For Ububntu and other UTF-8 enabled Linux systems this fits with the default settings.
  3619. $file_system_encoding = 'UTF-8';
  3620. }
  3621. }
  3622. }
  3623. return $file_system_encoding;
  3624. }
  3625. /**
  3626. * Checks whether a specified encoding is supported by this API.
  3627. * @param string $encoding The specified encoding.
  3628. * @return bool Returns TRUE when the specified encoding is supported, FALSE othewise.
  3629. */
  3630. function api_is_encoding_supported($encoding)
  3631. {
  3632. static $supported = array();
  3633. if (!isset($supported[$encoding])) {
  3634. $supported[$encoding] = _api_mb_supports($encoding) || _api_iconv_supports(
  3635. $encoding
  3636. ) || _api_convert_encoding_supports($encoding);
  3637. }
  3638. return $supported[$encoding];
  3639. }
  3640. /**
  3641. * Returns in an array the most-probably used non-UTF-8 encoding for the given language.
  3642. * The first (leading) value is actually used by the system at the moment.
  3643. * @param string $language (optional) The specified language, the default value is the user intrface language.
  3644. * @return string The correspondent encoding to the specified language.
  3645. * Note: See the file chamilo/main/inc/lib/internationalization_database/non_utf8_encodings.php
  3646. * if you wish to revise the leading non-UTF-8 encoding for your language.
  3647. */
  3648. function api_get_non_utf8_encoding($language = null)
  3649. {
  3650. $language_is_supported = api_is_language_supported($language);
  3651. if (!$language_is_supported || empty($language)) {
  3652. $language = api_get_interface_language(false, true);
  3653. }
  3654. $language = api_purify_language_id($language);
  3655. $encodings = & _api_non_utf8_encodings();
  3656. if (is_array($encodings[$language])) {
  3657. if (!empty($encodings[$language][0])) {
  3658. return $encodings[$language][0];
  3659. }
  3660. return null;
  3661. }
  3662. return null;
  3663. }
  3664. /**
  3665. * Return a list of valid encodings for setting platform character set.
  3666. * @return array List of valid encodings, preferably IANA-registared.
  3667. */
  3668. function api_get_valid_encodings()
  3669. {
  3670. $encodings = & _api_non_utf8_encodings();
  3671. if (!is_array($encodings)) {
  3672. $encodings = array('english', array('ISO-8859-15'));
  3673. }
  3674. $result1 = array();
  3675. $result2 = array();
  3676. $result3 = array();
  3677. foreach ($encodings as $value) {
  3678. if (isset($value[0])) {
  3679. $encoding = api_refine_encoding_id(trim($value[0]));
  3680. if (!empty($encoding)) {
  3681. if (strpos($encoding, 'ISO-') === 0) {
  3682. $result1[] = $encoding;
  3683. } elseif (strpos($encoding, 'WINDOWS-') === 0) {
  3684. $result2[] = $encoding;
  3685. } else {
  3686. $result3[] = $encoding;
  3687. }
  3688. }
  3689. }
  3690. }
  3691. $result1 = array_unique($result1);
  3692. $result2 = array_unique($result2);
  3693. $result3 = array_unique($result3);
  3694. natsort($result1);
  3695. natsort($result2);
  3696. natsort($result3);
  3697. return array_merge(array('UTF-8'), $result1, $result2, $result3);
  3698. }
  3699. /**
  3700. * Detects encoding of plain text.
  3701. * @param string $string The input text.
  3702. * @param string $language (optional) The language of the input text, provided if it is known.
  3703. * @return string Returns the detected encoding.
  3704. */
  3705. function api_detect_encoding($string, $language = null)
  3706. {
  3707. // Testing against valid UTF-8 first.
  3708. if (api_is_valid_utf8($string)) {
  3709. return 'UTF-8';
  3710. }
  3711. $result = null;
  3712. $delta_points_min = LANGUAGE_DETECT_MAX_DELTA;
  3713. // Testing non-UTF-8 encodings.
  3714. $encodings = api_get_valid_encodings();
  3715. foreach ($encodings as & $encoding) {
  3716. if (api_is_encoding_supported($encoding) && !api_is_utf8($encoding)) {
  3717. $result_array = & _api_compare_n_grams(
  3718. _api_generate_n_grams(api_substr($string, 0, LANGUAGE_DETECT_MAX_LENGTH, $encoding), $encoding),
  3719. $encoding
  3720. );
  3721. if (!empty($result_array)) {
  3722. list($key, $delta_points) = each($result_array);
  3723. if ($delta_points < $delta_points_min) {
  3724. $pos = strpos($key, ':');
  3725. $result_encoding = api_refine_encoding_id(substr($key, $pos + 1));
  3726. if (api_equal_encodings($encoding, $result_encoding)) {
  3727. if ($string == api_utf8_decode(api_utf8_encode($string, $encoding), $encoding)) {
  3728. $delta_points_min = $delta_points;
  3729. $result = $encoding;
  3730. }
  3731. }
  3732. }
  3733. }
  3734. }
  3735. }
  3736. // "Broken" UTF-8 texts are to be detected as UTF-8.
  3737. // This functionality is enabled when language of the text is known.
  3738. $language = api_purify_language_id((string)$language);
  3739. if (!empty($language)) {
  3740. $encoding = 'UTF-8';
  3741. $result_array = & _api_compare_n_grams(
  3742. _api_generate_n_grams(api_substr($string, 0, LANGUAGE_DETECT_MAX_LENGTH, $encoding), $encoding),
  3743. $encoding
  3744. );
  3745. if (!empty($result_array)) {
  3746. list($key, $delta_points) = each($result_array);
  3747. if ($delta_points < $delta_points_min) {
  3748. $pos = strpos($key, ':');
  3749. $result_encoding = api_refine_encoding_id(substr($key, $pos + 1));
  3750. $result_language = substr($key, 0, $pos);
  3751. if ($language == $result_language && api_is_utf8($result_encoding)) {
  3752. $delta_points_min = $delta_points;
  3753. $result = $encoding;
  3754. }
  3755. }
  3756. }
  3757. }
  3758. return $result;
  3759. }
  3760. /**
  3761. * String validation functions concerning certain encodings
  3762. */
  3763. /**
  3764. * Checks a string for UTF-8 validity.
  3765. *
  3766. * @deprecated Use Encoding::utf8()->is_valid() instead
  3767. */
  3768. function api_is_valid_utf8(&$string)
  3769. {
  3770. return Encoding::utf8()->is_valid($string);
  3771. }
  3772. /**
  3773. * Checks whether a string contains 7-bit ASCII characters only.
  3774. * @param string $string The string to be tested/validated.
  3775. * @return bool Returns TRUE when the tested string contains 7-bit ASCII characters only, FALSE othewise.
  3776. */
  3777. function api_is_valid_ascii(&$string)
  3778. {
  3779. if (MBSTRING_INSTALLED) {
  3780. return @mb_detect_encoding($string, 'ASCII', true) == 'ASCII' ? true : false;
  3781. }
  3782. return !preg_match('/[^\x00-\x7F]/S', $string);
  3783. }
  3784. /**
  3785. *
  3786. * Experimental translation feature for Chamilo
  3787. *
  3788. * Install this in Ubuntu
  3789. *
  3790. * sudo locale-gen es_ES
  3791. * sudo apt-get install php-gettext
  3792. *
  3793. * Install Spanish locale: $ sudo locale-gen es_ES
  3794. * Install English locale: $ sudo locale-gen en_US
  3795. *
  3796. * To view the list of locales installed in ubuntu
  3797. * locale -a
  3798. *
  3799. * In Debian check this file More info: http://algorytmy.pl/doc/php/ref.gettext.php
  3800. * sudo vim /etc/locale.gen
  3801. *
  3802. * Translate po files using this GUI
  3803. * sudo apt-get install poedit
  3804. *
  3805. * Some help here:
  3806. *
  3807. * Config getext
  3808. * http://zez.org/article/articleview/42/3/
  3809. * *
  3810. * Using getext in ubuntu
  3811. * http://www.sourcerally.net/regin/49-How-to-get-PHP-and-gettext-working-%28ubuntu,-debian%29
  3812. *
  3813. * Getext tutorial
  3814. * http://mel.melaxis.com/devblog/2005/08/06/localizing-php-web-sites-using-gettext/
  3815. *
  3816. */
  3817. function setting_gettext()
  3818. {
  3819. $domain = 'default';
  3820. $locale = api_get_language_isocode();
  3821. $locale = 'es_ES';
  3822. putenv("LC_ALL=$locale");
  3823. setlocale(LC_ALL, $locale);
  3824. bindtextdomain($domain, api_get_path(SYS_LANG_PATH));
  3825. bind_textdomain_codeset($domain, 'UTF-8');
  3826. textdomain($domain);
  3827. }