multibyte_string_functions.lib.php 126 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064
  1. <?php
  2. /**
  3. * ==============================================================================
  4. * File: multibyte_string_functions.lib.php
  5. * Main API extension library for Dokeos 1.8.6+ LMS
  6. * A common purpose library for supporting multibyte string aware functions.
  7. * License: GNU/GPL version 2 or later (Free Software Foundation)
  8. * @author: Ivan Tcholakov, ivantcholakov@gmail.com
  9. * October 2008 - initial implementation.
  10. * May 2009 - refactoring and minor corrections have been implemented.
  11. * August 2009 - PCRE-related functions have been added,
  12. * dependancy on mbstring extension has been lowered.
  13. * @package dokeos.library
  14. * ==============================================================================
  15. */
  16. /**
  17. * Notes:
  18. *
  19. * 1. For all the functions from this library witn optional encoding
  20. * parameters, the system's encoding is assumed by default, i.e. the
  21. * value that is returned by api_get_setting('platform_charset') or
  22. * the value of the global variable $charset.
  23. *
  24. * 2. In other aspects, most of the functions in this library try to copy
  25. * behaviour of some core PHP functions and some functions from the
  26. * mbstring extension. Mostly they have similar names prefixed with "api_".
  27. * For your convenience, links have been given to the documentation of the
  28. * original PHP functions. Thus, you may exploit on your previous habits.
  29. *
  30. * 3. Why these function have been introduced? Because they are able to
  31. * support more encodings than the original ones. And which is more
  32. * important - they are UTF-8 aware. So, they should be used for strings
  33. * in natural language. For internal system identificators of file names
  34. * which are supposed to contain only English letters you may use the
  35. * original PHP string functions.
  36. *
  37. * 4. This library requires PHP mbstring extension to be activated.
  38. * When encodings to be used are not supported by mbstring, this library
  39. * is able to exploit the PHP iconv extesion, which in this case should
  40. * be activated too.
  41. *
  42. * 5. For improved sorting of multibyte strings the library uses the intl
  43. * php-extension if it is installed.
  44. */
  45. /**
  46. * ----------------------------------------------------------------------------
  47. * A safe way to calculate binary lenght of a string (as number of bytes)
  48. * ----------------------------------------------------------------------------
  49. */
  50. /**
  51. * Calculates binary lenght of a string, as number of bytes, regardless the php-setting mbstring.func_overload.
  52. * This function should work for all multi-byte related changes of PHP5 configuration.
  53. * @param string $string The input string.
  54. * @return int Returns the length of the input string (or binary data) as number of bytes.
  55. */
  56. function api_byte_count($string) {
  57. static $use_mb_strlen;
  58. if (!isset($use_mb_strlen)) {
  59. $use_mb_strlen = MBSTRING_INSTALLED && ((int) ini_get('mbstring.func_overload') & 2);
  60. }
  61. if ($use_mb_strlen) {
  62. return mb_strlen($string, '8bit');
  63. }
  64. return strlen($string);
  65. // For PHP6 this function probably will contain:
  66. //return strlen((binary)$string);
  67. }
  68. /**
  69. * ----------------------------------------------------------------------------
  70. * Multibyte string conversion functions
  71. * ----------------------------------------------------------------------------
  72. */
  73. /**
  74. * Converts character encoding of a given string.
  75. * @param string $string The string being converted.
  76. * @param string $to_encoding The encoding that $string is being converted to.
  77. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  78. * @return string Returns the converted string.
  79. * This function is aimed at replacing the function mb_convert_encoding() for human-language strings.
  80. * @link http://php.net/manual/en/function.mb-convert-encoding
  81. */
  82. function api_convert_encoding($string, $to_encoding, $from_encoding = null) {
  83. static $equal_encodings = array();
  84. if (empty($from_encoding)) {
  85. $from_encoding = api_mb_internal_encoding();
  86. }
  87. if (!isset($equal_encodings[$to_encoding][$from_encoding])) {
  88. $equal_encodings[$to_encoding][$from_encoding] = api_equal_encodings($to_encoding, $from_encoding);
  89. }
  90. if ($equal_encodings[$to_encoding][$from_encoding]) {
  91. return $string; // When conversion is not needed, the string is returned directly, without validation.
  92. }
  93. if (api_mb_supports($to_encoding) && api_mb_supports($from_encoding)) {
  94. return @mb_convert_encoding($string, $to_encoding, $from_encoding);
  95. }
  96. if (api_iconv_supports($to_encoding) && api_iconv_supports($from_encoding)) {
  97. return @iconv($from_encoding, $to_encoding, $string);
  98. }
  99. if (api_is_utf8($to_encoding) && api_is_latin1($from_encoding, true)) {
  100. return utf8_encode($string);
  101. }
  102. if (api_is_latin1($to_encoding, true) && api_is_utf8($from_encoding)) {
  103. return utf8_decode($string);
  104. }
  105. if (_api_convert_encoding_supports($to_encoding) && _api_convert_encoding_supports($from_encoding)) {
  106. return _api_convert_encoding($string, $to_encoding, $from_encoding);
  107. }
  108. return $string; // Here the function gives up.
  109. }
  110. /**
  111. * Converts a given string into UTF-8 encoded string.
  112. * @param string $string The string being converted.
  113. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  114. * @return string Returns the converted string.
  115. * This function is aimed at replacing the function utf8_encode() for human-language strings.
  116. * @link http://php.net/manual/en/function.utf8-encode
  117. */
  118. function api_utf8_encode($string, $from_encoding = null) {
  119. if (empty($from_encoding)) {
  120. $from_encoding = api_mb_internal_encoding();
  121. }
  122. if (api_is_utf8($from_encoding)) {
  123. return $string; // When conversion is not needed, the string is returned directly, without validation.
  124. }
  125. if (api_mb_supports($from_encoding)) {
  126. return @mb_convert_encoding($string, 'UTF-8', $from_encoding);
  127. }
  128. if (api_iconv_supports($from_encoding)) {
  129. return @iconv($from_encoding, 'UTF-8', $string);
  130. }
  131. if (api_is_latin1($from_encoding, true)) {
  132. return utf8_encode($string);
  133. }
  134. if (_api_convert_encoding_supports($from_encoding)) {
  135. return _api_convert_encoding($string, 'UTF-8', $from_encoding);
  136. }
  137. return $string; // Here the function gives up.
  138. }
  139. /**
  140. * Converts a given string from UTF-8 encoding to a specified encoding.
  141. * @param string $string The string being converted.
  142. * @param string $to_encoding (optional) The encoding that $string is being converted to. If it is omited, the platform character set is assumed.
  143. * @return string Returns the converted string.
  144. * This function is aimed at replacing the function utf8_decode() for human-language strings.
  145. * @link http://php.net/manual/en/function.utf8-decode
  146. */
  147. function api_utf8_decode($string, $to_encoding = null) {
  148. if (empty($to_encoding)) {
  149. $to_encoding = api_mb_internal_encoding();
  150. }
  151. if (api_is_utf8($to_encoding)) {
  152. return $string; // When conversion is not needed, the string is returned directly, without validation.
  153. }
  154. if (api_mb_supports($to_encoding)) {
  155. return @mb_convert_encoding($string, $to_encoding, 'UTF-8');
  156. }
  157. if (api_iconv_supports($to_encoding)) {
  158. return @iconv('UTF-8', $to_encoding, $string);
  159. }
  160. if (api_is_latin1($to_encoding, true)) {
  161. return utf8_decode($string);
  162. }
  163. if (_api_convert_encoding_supports($to_encoding)) {
  164. return _api_convert_encoding($string, $to_encoding, 'UTF-8');
  165. }
  166. return $string; // Here the function gives up.
  167. }
  168. /**
  169. * Converts a given string into the system ecoding (or platform character set).
  170. * When $from encoding is omited on UTF-8 platforms then language dependent encoding
  171. * is guessed/assumed. On non-UTF-8 platforms omited $from encoding is assumed as UTF-8.
  172. * When the parameter $check_utf8_validity is true the function checks string's
  173. * UTF-8 validity and decides whether to try to convert it or not.
  174. * This function is useful for problem detection or making workarounds.
  175. * @param string $string The string being converted.
  176. * @param string $from_encoding (optional) The encoding that $string is being converted from. It is guessed when it is omited.
  177. * @param bool $check_utf8_validity (optional) A flag for UTF-8 validity check as condition for making conversion.
  178. * @return string Returns the converted string.
  179. */
  180. function api_to_system_encoding($string, $from_encoding = null, $check_utf8_validity = false) {
  181. $system_encoding = api_get_system_encoding();
  182. if (empty($from_encoding)) {
  183. if (api_is_utf8($system_encoding)) {
  184. $from_encoding = api_get_non_utf8_encoding();
  185. } else {
  186. $from_encoding = 'UTF-8';
  187. }
  188. }
  189. if (api_equal_encodings($system_encoding, $from_encoding)) {
  190. return $string;
  191. }
  192. if ($check_utf8_validity) {
  193. if (api_is_utf8($system_encoding)) {
  194. if (api_is_valid_utf8($string)) {
  195. return $string;
  196. }
  197. }
  198. elseif (api_is_utf8($from_encoding)) {
  199. if (!api_is_valid_utf8($string)) {
  200. return $string;
  201. }
  202. }
  203. }
  204. return api_convert_encoding($string, $system_encoding, $from_encoding);
  205. }
  206. /**
  207. * Converts all applicable characters to HTML entities.
  208. * @param string $string The input string.
  209. * @param int $quote_style (optional) The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES.
  210. * @param string $encoding (optional) The encoding (of the input string) used in conversion. If it is omited, the platform character set is assumed.
  211. * @return string Returns the converted string.
  212. * This function is aimed at replacing the function htmlentities() for human-language strings.
  213. * @link http://php.net/manual/en/function.htmlentities
  214. */
  215. function api_htmlentities($string, $quote_style = ENT_COMPAT, $encoding = null) {
  216. if (empty($encoding)) {
  217. $encoding = api_mb_internal_encoding();
  218. }
  219. if (!api_is_utf8($encoding) && api_html_entity_supports($encoding)) {
  220. return htmlentities($string, $quote_style, $encoding);
  221. }
  222. if (api_mb_supports($encoding) || api_iconv_supports($encoding)) {
  223. $string = api_convert_encoding(api_utf8_encode($string, $encoding), 'HTML-ENTITIES', 'UTF-8');
  224. }
  225. elseif (api_is_utf8($encoding)) {
  226. $string = _api_utf8_to_htmlentities($string);
  227. }
  228. elseif (_api_convert_encoding_supports($encoding)) {
  229. if (!api_is_utf8($encoding)) {
  230. $string = _api_convert_encoding($string, 'UTF-8', $encoding);
  231. }
  232. $string = _api_utf8_to_unicode($string);
  233. foreach ($string as $key => &$value) {
  234. if ($value < 128) {
  235. $value = chr($value);
  236. } else {
  237. $value = '&#'.$value.';';
  238. }
  239. }
  240. $string = implode($string);
  241. if (!api_is_utf8($encoding)) {
  242. $string = _api_convert_encoding($string, $encoding, 'UTF-8');
  243. }
  244. }
  245. else {
  246. // Here the function gives up.
  247. return $string;
  248. }
  249. switch($quote_style) {
  250. case ENT_COMPAT:
  251. $string = str_replace('"', '&quot;', $string);
  252. break;
  253. case ENT_QUOTES:
  254. $string = str_replace(array('\'', '"'), array('&#039;', '&quot;'), $string);
  255. break;
  256. }
  257. return $string;
  258. }
  259. /**
  260. * Convers HTML entities into normal characters.
  261. * @param string $string The input string.
  262. * @param int $quote_style (optional) The quote style - ENT_COMPAT (default), ENT_QUOTES, ENT_NOQUOTES.
  263. * @param string $encoding (optional) The encoding (of the result) used in conversion. If it is omited, the platform character set is assumed.
  264. * @return string Returns the converted string.
  265. * This function is aimed at replacing the function html_entity_decode() for human-language strings.
  266. * @link http://php.net/html_entity_decode
  267. */
  268. function api_html_entity_decode($string, $quote_style = ENT_COMPAT, $encoding = null) {
  269. if (empty($encoding)) {
  270. $encoding = api_mb_internal_encoding();
  271. }
  272. if (api_html_entity_supports($encoding)) {
  273. return html_entity_decode($string, $quote_style, $encoding);
  274. }
  275. if (api_is_encoding_supported($encoding)) {
  276. if (!api_is_utf8($encoding)) {
  277. $string = api_utf8_encode($string, $encoding);
  278. }
  279. $string = html_entity_decode($string, $quote_style, 'UTF-8');
  280. if (!api_is_utf8($encoding)) {
  281. return api_utf8_decode($string, $encoding);
  282. }
  283. return $string;
  284. }
  285. return $string; // Here the function guves up.
  286. }
  287. /**
  288. * This function encodes (conditionally) a given string to UTF-8 if XmlHttp-request has been detected.
  289. * @param string $string The string being converted.
  290. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  291. * @return string Returns the converted string.
  292. */
  293. function api_xml_http_response_encode($string, $from_encoding = null) {
  294. if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
  295. if (empty($from_encoding)) {
  296. $from_encoding = api_mb_internal_encoding();
  297. }
  298. if (!api_is_utf8($from_encoding)) {
  299. return api_utf8_encode($string, $from_encoding);
  300. }
  301. }
  302. return $string;
  303. }
  304. /**
  305. * This function converts a given string to the encoding that filesystem uses for representing file/folder names.
  306. * @param string $string The string being converted.
  307. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  308. * @return string Returns the converted string.
  309. */
  310. function api_file_system_encode($string, $from_encoding = null) {
  311. if (empty($from_encoding)) {
  312. $from_encoding = api_mb_internal_encoding();
  313. }
  314. return api_convert_encoding($string, api_get_file_system_encoding(), $from_encoding);
  315. }
  316. /**
  317. * This function converts a given string from the encoding that filesystem uses for representing file/folder names.
  318. * @param string $string The string being converted.
  319. * @param string $from_encoding (optional) The encoding that $string is being converted from. If it is omited, the platform character set is assumed.
  320. * @return string Returns the converted string.
  321. */
  322. function api_file_system_decode($string, $to_encoding = null) {
  323. if (empty($to_encoding)) {
  324. $to_encoding = api_mb_internal_encoding();
  325. }
  326. return api_convert_encoding($string, $to_encoding, api_get_file_system_encoding());
  327. }
  328. /**
  329. * ----------------------------------------------------------------------------
  330. * Common multibyte string functions
  331. * ----------------------------------------------------------------------------
  332. */
  333. /**
  334. * This function returns a string or an array with all occurrences of search in subject (ignoring case) replaced with the given replace value.
  335. * @param mixed $search String or array of strings to be found.
  336. * @param mixed $replace String or array of strings used for replacement.
  337. * @param mixed $subject String or array of strings being searced.
  338. * @param int $count (optional) The number of matched and replaced needles will be returned in count, which is passed by reference.
  339. * @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.
  340. * @return mixed String or array as a result.
  341. * Notes:
  342. * If $subject is an array, then the search and replace is performed with every entry of subject, the return value is an array.
  343. * 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.
  344. * If $replace has fewer values than search, then an empty string is used for the rest of replacement values.
  345. * If $search is an array and $replace is a string, then this replacement string is used for every value of search.
  346. * This function is aimed at replacing the function str_ireplace() for human-language strings.
  347. * @link http://php.net/manual/en/function.str-ireplace
  348. */
  349. function api_str_ireplace($search, $replace, $subject, & $count = null, $encoding = null) {
  350. if (empty($encoding)) {
  351. $encoding = api_mb_internal_encoding();
  352. }
  353. if (is_array($subject)) {
  354. foreach ($subject as $key => $val) {
  355. $subject[$key] = api_str_ireplace($search, $replace, $val, $count, $encoding);
  356. }
  357. return $subject;
  358. }
  359. if (is_array($search)) {
  360. foreach (array_keys($search) as $key) {
  361. if (is_array($replace)) {
  362. if (array_key_exists($key, $replace)) {
  363. $subject = api_str_ireplace($search[$key], $replace[$key], $subject, $count, $encoding);
  364. } else {
  365. $subject = api_str_ireplace($search[$key], '', $subject, $count, $encoding);
  366. }
  367. } else {
  368. $subject = api_str_ireplace($search[$key], $replace, $subject, $count, $encoding);
  369. }
  370. }
  371. return $subject;
  372. }
  373. $search = api_strtolower($search, $encoding);
  374. $subject_lower = api_strtolower($subject, $encoding);
  375. $total_matched_strlen = 0;
  376. $i = 0;
  377. while (api_preg_match('/(.*?)'.preg_quote($search, '/').'/s', $subject_lower, $matches, 0, 0, $encoding)) {
  378. $matched_strlen = api_strlen($matches[0], $encoding);
  379. $subject_lower = api_substr($subject_lower, $matched_strlen, api_strlen($subject_lower, $encoding), $encoding);
  380. $offset = $total_matched_strlen + api_strlen($matches[1], $encoding) + ($i * (api_strlen($replace, $encoding) - 1));
  381. $subject = api_substr_replace($subject, $replace, $offset, api_strlen($search), $encoding);
  382. $total_matched_strlen += $matched_strlen;
  383. $i++;
  384. }
  385. $count += $i;
  386. return $subject;
  387. }
  388. /**
  389. * Converts a string to an array.
  390. * @param string $string The input string.
  391. * @param int $split_length Maximum character-length of the chunk, one character by default.
  392. * @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.
  393. * @return array The result array of chunks with the spcified length.
  394. * Notes:
  395. * If the optional split_length parameter is specified, the returned array will be broken down into chunks
  396. * with each being split_length in length, otherwise each chunk will be one character in length.
  397. * FALSE is returned if split_length is less than 1.
  398. * If the split_length length exceeds the length of string, the entire string is returned as the first (and only) array element.
  399. * This function is aimed at replacing the function str_split() for human-language strings.
  400. * @link http://php.net/str_split
  401. */
  402. function api_str_split($string, $split_length = 1, $encoding = null) {
  403. if (empty($encoding)) {
  404. $encoding = api_mb_internal_encoding();
  405. }
  406. if (empty($string)) {
  407. return array();
  408. }
  409. if ($split_length < 1) {
  410. return false;
  411. }
  412. if (_api_is_single_byte_encoding($encoding)) {
  413. return str_split($string, $split_length);
  414. }
  415. if (api_is_encoding_supported($encoding)) {
  416. $len = api_strlen($string);
  417. if ($len <= $split_length) {
  418. return array($string);
  419. }
  420. if (!api_is_utf8($encoding)) {
  421. $string = api_utf8_encode($string, $encoding);
  422. }
  423. if (preg_match_all('/.{'.$split_length.'}|[^\x00]{1,'.$split_length.'}$/us', $string, $result) === false) {
  424. return array();
  425. }
  426. if (!api_is_utf8($encoding)) {
  427. global $_api_encoding;
  428. $_api_encoding = $encoding;
  429. $result = _api_array_utf8_decode($result[0]);
  430. }
  431. return $result[0];
  432. }
  433. return str_split($string, $split_length);
  434. }
  435. /**
  436. * Finds position of first occurrence of a string within another, case insensitive.
  437. * @param string $haystack The string from which to get the position of the first occurrence.
  438. * @param string $needle The string to be found.
  439. * @param int $offset The position in $haystack to start searching from. If it is omitted, searching starts from the beginning.
  440. * @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.
  441. * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
  442. * Note: The first character's position is 0, the second character position is 1, and so on.
  443. * This function is aimed at replacing the functions stripos() and mb_stripos() for human-language strings.
  444. * @link http://php.net/manual/en/function.stripos
  445. * @link http://php.net/manual/en/function.mb-stripos
  446. */
  447. function api_stripos($haystack, $needle, $offset = 0, $encoding = null) {
  448. if (empty($encoding)){
  449. $encoding = api_mb_internal_encoding();
  450. }
  451. if (api_mb_supports($encoding)) {
  452. return @mb_stripos($haystack, $needle, $offset, $encoding);
  453. }
  454. elseif (api_is_encoding_supported($encoding)) {
  455. if (MBSTRING_INSTALLED) {
  456. if (!api_is_utf8($encoding)) {
  457. $haystack = api_utf8_encode($haystack, $encoding);
  458. $needle = api_utf8_encode($needle, $encoding);
  459. }
  460. return @mb_stripos($haystack, $needle, $offset, 'UTF-8');
  461. }
  462. return api_strpos(api_strtolower($haystack, $encoding), api_strtolower($needle, $encoding), $offset, $encoding);
  463. }
  464. return stripos($haystack, $needle, $offset);
  465. }
  466. /**
  467. * Finds first occurrence of a string within another, case insensitive.
  468. * @param string $haystack The string from which to get the first occurrence.
  469. * @param mixed $needle The string to be found.
  470. * @param bool $before_needle (optional) Determines which portion of $haystack this function returns. The default value is FALSE.
  471. * @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.
  472. * @return mixed Returns the portion of $haystack, or FALSE if $needle is not found.
  473. * Notes:
  474. * 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.
  475. * If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence of $needle.
  476. * If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence of $needle to the end.
  477. * This function is aimed at replacing the functions stristr() and mb_stristr() for human-language strings.
  478. * @link http://php.net/manual/en/function.stristr
  479. * @link http://php.net/manual/en/function.mb-stristr
  480. */
  481. function api_stristr($haystack, $needle, $before_needle = false, $encoding = null) {
  482. if (empty($encoding)) {
  483. $encoding = api_mb_internal_encoding();
  484. }
  485. if (!is_string($needle)) {
  486. $needle = (int)$needle;
  487. if (api_is_utf8($encoding)) {
  488. $needle = _api_utf8_chr($needle);
  489. } else {
  490. $needle = chr($needle);
  491. }
  492. }
  493. if (api_mb_supports($encoding)) {
  494. return @mb_stristr($haystack, $needle, $before_needle, $encoding);
  495. }
  496. elseif (api_is_encoding_supported($encoding)) {
  497. if (MBSTRING_INSTALLED) {
  498. if (!api_is_utf8($encoding)) {
  499. $haystack = api_utf8_encode($haystack, $encoding);
  500. $needle = api_utf8_encode($needle, $encoding);
  501. }
  502. $result = @mb_stristr($haystack, $needle, $before_needle, 'UTF-8');
  503. if ($result === false) {
  504. return false;
  505. }
  506. if (!api_is_utf8($encoding)) {
  507. return api_utf8_decode($result, $encoding);
  508. }
  509. return $result;
  510. }
  511. $result = api_strstr(api_strtolower($haystack, $encoding), api_strtolower($needle, $encoding), $before_needle, $encoding);
  512. if ($result === false) {
  513. return false;
  514. }
  515. if ($before_needle) {
  516. return api_substr($haystack, 0, api_strlen($result, $encoding), $encoding);
  517. }
  518. return api_substr($haystack, api_strlen($haystack, $encoding) - api_strlen($result, $encoding), null, $encoding);
  519. }
  520. if (PHP_VERSION < 5.3) {
  521. return stristr($haystack, $needle);
  522. }
  523. return stristr($haystack, $needle, $before_needle);
  524. }
  525. /**
  526. * Returns length of the input string.
  527. * @param string $string The string which length is to be calculated.
  528. * @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.
  529. * @return int Returns the number of characters within the string. A multi-byte character is counted as 1.
  530. * This function is aimed at replacing the functions strlen() and mb_strlen() for human-language strings.
  531. * @link http://php.net/manual/en/function.strlen
  532. * @link http://php.net/manual/en/function.mb-strlen
  533. * Note: When you use strlen() to test for an empty string, you needn't change it to api_strlen().
  534. * For example, in lines like the following:
  535. * if (strlen($string) > 0)
  536. * if (strlen($string) != 0)
  537. * there is no need the original function strlen() to be changed, it works correctly and faster for these cases.
  538. */
  539. function api_strlen($string, $encoding = null) {
  540. if (empty($encoding)) {
  541. $encoding = api_mb_internal_encoding();
  542. }
  543. if (_api_is_single_byte_encoding($encoding)) {
  544. return strlen($string);
  545. }
  546. if (api_mb_supports($encoding)) {
  547. return @mb_strlen($string, $encoding);
  548. }
  549. if (api_iconv_supports($encoding)) {
  550. return @iconv_strlen($string, $encoding);
  551. }
  552. if (api_is_utf8($encoding)) {
  553. return api_byte_count(preg_replace("/[\x80-\xBF]/", '', $string));
  554. }
  555. return strlen($string);
  556. }
  557. /**
  558. * Finds position of first occurrence of a string within another.
  559. * @param string $haystack The string from which to get the position of the first occurrence.
  560. * @param string $needle The string to be found.
  561. * @param int $offset (optional) The position in $haystack to start searching from. If it is omitted, searching starts from the beginning.
  562. * @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.
  563. * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
  564. * Note: The first character's position is 0, the second character position is 1, and so on.
  565. * This function is aimed at replacing the functions strpos() and mb_strpos() for human-language strings.
  566. * @link http://php.net/manual/en/function.strpos
  567. * @link http://php.net/manual/en/function.mb-strpos
  568. */
  569. function api_strpos($haystack, $needle, $offset = 0, $encoding = null) {
  570. if (empty($encoding)) {
  571. $encoding = api_mb_internal_encoding();
  572. }
  573. if (_api_is_single_byte_encoding($encoding)) {
  574. return strpos($haystack, $needle, $offset);
  575. }
  576. elseif (api_mb_supports($encoding)) {
  577. return @mb_strpos($haystack, $needle, $offset, $encoding);
  578. }
  579. elseif (api_is_encoding_supported($encoding)) {
  580. if (!api_is_utf8($encoding)) {
  581. $haystack = api_utf8_encode($haystack, $encoding);
  582. $needle = api_utf8_encode($needle, $encoding);
  583. }
  584. if (MBSTRING_INSTALLED) {
  585. return @mb_strpos($haystack, $needle, $offset, 'UTF-8');
  586. }
  587. if (empty($offset)) {
  588. $haystack = explode($needle, $haystack, 2);
  589. if (count($haystack) > 1) {
  590. return api_strlen($haystack[0]);
  591. }
  592. return false;
  593. }
  594. $haystack = api_substr($haystack, $offset);
  595. if (($pos = api_strpos($haystack, $needle)) !== false ) {
  596. return $pos + $offset;
  597. }
  598. return false;
  599. }
  600. return strpos($haystack, $needle, $offset);
  601. }
  602. /**
  603. * Finds the last occurrence of a character in a string.
  604. * @param string $haystack The string from which to get the last occurrence.
  605. * @param mixed $needle The string which first character is to be found.
  606. * @param bool $before_needle (optional) Determines which portion of $haystack this function returns. The default value is FALSE.
  607. * @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.
  608. * @return mixed Returns the portion of $haystack, or FALSE if the first character from $needle is not found.
  609. * Notes:
  610. * 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.
  611. * If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence.
  612. * If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence to the end.
  613. * This function is aimed at replacing the functions strrchr() and mb_strrchr() for human-language strings.
  614. * @link http://php.net/manual/en/function.strrchr
  615. * @link http://php.net/manual/en/function.mb-strrchr
  616. */
  617. function api_strrchr($haystack, $needle, $before_needle = false, $encoding = null) {
  618. if (empty($encoding)) {
  619. $encoding = api_mb_internal_encoding();
  620. }
  621. if (!is_string($needle)) {
  622. $needle = (int)$needle;
  623. if (api_is_utf8($encoding)) {
  624. $needle = _api_utf8_chr($needle);
  625. } else {
  626. $needle = chr($needle);
  627. }
  628. }
  629. if (_api_is_single_byte_encoding($encoding)) {
  630. if (!$before_needle) {
  631. return strrchr($haystack, $needle);
  632. }
  633. $result = strrchr($haystack, $needle);
  634. if ($result === false) {
  635. return false;
  636. }
  637. return api_substr($haystack, 0, api_strlen($haystack, $encoding) - api_strlen($result, $encoding), $encoding);
  638. }
  639. elseif (api_mb_supports($encoding)) {
  640. return @mb_strrchr($haystack, $needle, $before_needle, $encoding);
  641. }
  642. elseif (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  643. if (!api_is_utf8($encoding)) {
  644. $haystack = api_utf8_encode($haystack, $encoding);
  645. $needle = api_utf8_encode($needle, $encoding);
  646. }
  647. $result = @mb_strrchr($haystack, $needle, $before_needle, 'UTF-8');
  648. if ($result === false) {
  649. return false;
  650. }
  651. if (!api_is_utf8($encoding)) {
  652. return api_utf8_decode($result, $encoding);
  653. }
  654. return $result;
  655. }
  656. if (!$before_needle) {
  657. return strrchr($haystack, $needle);
  658. }
  659. $result = strrchr($haystack, $needle);
  660. if ($result === false) {
  661. return false;
  662. }
  663. return api_substr($haystack, 0, api_strlen($haystack, $encoding) - api_strlen($result, $encoding), $encoding);
  664. }
  665. /**
  666. * Reverses a string.
  667. * @param string $string The string to be reversed.
  668. * @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.
  669. * @return string Returns the reversed string.
  670. * This function is aimed at replacing the function strrev() for human-language strings.
  671. * @link http://php.net/manual/en/function.strrev
  672. */
  673. function api_strrev($string, $encoding = null) {
  674. if (empty($encoding)) {
  675. $encoding = api_mb_internal_encoding();
  676. }
  677. if (empty($string)) {
  678. return '';
  679. }
  680. if (_api_is_single_byte_encoding($encoding)) {
  681. return strrev($string);
  682. }
  683. if (api_is_encoding_supported($encoding)) {
  684. return implode(array_reverse(api_str_split($string, 1, $encoding)));
  685. }
  686. return strrev($string);
  687. }
  688. /**
  689. * Finds the position of last occurrence of a string in a string.
  690. * @param string $haystack The string from which to get the position of the last occurrence.
  691. * @param string $needle The string to be found.
  692. * @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.
  693. * @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.
  694. * @return mixed Returns the numeric position of the first occurrence of $needle in the $haystack, or FALSE if $needle is not found.
  695. * Note: The first character's position is 0, the second character position is 1, and so on.
  696. * This function is aimed at replacing the functions strrpos() and mb_strrpos() for human-language strings.
  697. * @link http://php.net/manual/en/function.strrpos
  698. * @link http://php.net/manual/en/function.mb-strrpos
  699. */
  700. function api_strrpos($haystack, $needle, $offset = 0, $encoding = null) {
  701. if (empty($encoding)) {
  702. $encoding = api_mb_internal_encoding();
  703. }
  704. if (_api_is_single_byte_encoding($encoding)) {
  705. return strrpos($haystack, $needle, $offset);
  706. }
  707. if (api_mb_supports($encoding)) {
  708. return @mb_strrpos($haystack, $needle, $offset, $encoding);
  709. }
  710. elseif (api_is_encoding_supported($encoding)) {
  711. if (!api_is_utf8($encoding)) {
  712. $haystack = api_utf8_encode($haystack, $encoding);
  713. $needle = api_utf8_encode($needle, $encoding);
  714. }
  715. if (MBSTRING_INSTALLED) {
  716. return @mb_strrpos($haystack, $needle, $offset, 'UTF-8');
  717. }
  718. // This branch (this fragment of code) is an adaptation from the CakePHP(tm) Project, http://www.cakefoundation.org
  719. $found = false;
  720. $haystack = _api_utf8_to_unicode($haystack);
  721. $haystack_count = count($haystack);
  722. $matches = array_count_values($haystack);
  723. $needle = _api_utf8_to_unicode($needle);
  724. $needle_count = count($needle);
  725. $position = $offset;
  726. while (($found === false) && ($position < $haystack_count)) {
  727. if (isset($needle[0]) && $needle[0] === $haystack[$position]) {
  728. for ($i = 1; $i < $needle_count; $i++) {
  729. if ($needle[$i] !== $haystack[$position + $i]) {
  730. if ($needle[$i] === $haystack[($position + $i) -1]) {
  731. $position--;
  732. $found = true;
  733. continue;
  734. }
  735. }
  736. }
  737. if (!$offset && isset($matches[$needle[0]]) && $matches[$needle[0]] > 1) {
  738. $matches[$needle[0]] = $matches[$needle[0]] - 1;
  739. } elseif ($i === $needle_count) {
  740. $found = true;
  741. $position--;
  742. }
  743. }
  744. $position++;
  745. }
  746. return ($found) ? $position : false;
  747. }
  748. return strrpos($haystack, $needle, $offset);
  749. }
  750. /**
  751. * Finds first occurrence of a string within another.
  752. * @param string $haystack The string from which to get the first occurrence.
  753. * @param mixed $needle The string to be found.
  754. * @param bool $before_needle (optional) Determines which portion of $haystack this function returns. The default value is FALSE.
  755. * @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.
  756. * @return mixed Returns the portion of $haystack, or FALSE if $needle is not found.
  757. * Notes:
  758. * 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.
  759. * If $before_needle is set to TRUE, the function returns all of $haystack from the beginning to the first occurrence of $needle.
  760. * If $before_needle is set to FALSE, the function returns all of $haystack from the first occurrence of $needle to the end.
  761. * This function is aimed at replacing the functions strstr() and mb_strstr() for human-language strings.
  762. * @link http://php.net/manual/en/function.strstr
  763. * @link http://php.net/manual/en/function.mb-strstr
  764. */
  765. function api_strstr($haystack, $needle, $before_needle = false, $encoding = null) {
  766. if (empty($encoding)) {
  767. $encoding = api_mb_internal_encoding();
  768. }
  769. if (!is_string($needle)) {
  770. $needle = (int)$needle;
  771. if (api_is_utf8($encoding)) {
  772. $needle = _api_utf8_chr($needle);
  773. } else {
  774. $needle = chr($needle);
  775. }
  776. }
  777. if (_api_is_single_byte_encoding($encoding)) {
  778. // Adding the missing parameter $before_needle to the original function strstr(), PHP_VERSION < 5.3
  779. if (!$before_needle) {
  780. return strstr($haystack, $needle);
  781. }
  782. if (PHP_VERSION < 5.3) {
  783. $result = explode($needle, $haystack, 2);
  784. if ($result === false || count($result) < 2) {
  785. return false;
  786. }
  787. return $result[0];
  788. }
  789. return strstr($haystack, $needle, $before_needle);
  790. }
  791. if (api_mb_supports($encoding)) {
  792. return @mb_strstr($haystack, $needle, $before_needle, $encoding);
  793. }
  794. elseif (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  795. if (!api_is_utf8($encoding)) {
  796. $haystack = api_utf8_encode($haystack, $encoding);
  797. $needle = api_utf8_encode($needle, $encoding);
  798. }
  799. $result = @mb_strstr($haystack, $needle, $before_needle, 'UTF-8');
  800. if ($result !== false) {
  801. if (!api_is_utf8($encoding)) {
  802. return api_utf8_decode($result, $encoding);
  803. }
  804. return $result;
  805. }
  806. return false;
  807. }
  808. // Adding the missing parameter $before_needle to the original function strstr(), PHP_VERSION < 5.3
  809. if (!$before_needle) {
  810. return strstr($haystack, $needle);
  811. }
  812. if (PHP_VERSION < 5.3) {
  813. $result = explode($needle, $haystack, 2);
  814. if ($result === false || count($result) < 2) {
  815. return false;
  816. }
  817. return $result[0];
  818. }
  819. return strstr($haystack, $needle, $before_needle);
  820. }
  821. /**
  822. * Makes a string lowercase.
  823. * @param string $string The string being lowercased.
  824. * @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.
  825. * @return string Returns the string with all alphabetic characters converted to lowercase.
  826. * This function is aimed at replacing the functions strtolower() and mb_strtolower() for human-language strings.
  827. * @link http://php.net/manual/en/function.strtolower
  828. * @link http://php.net/manual/en/function.mb-strtolower
  829. */
  830. function api_strtolower($string, $encoding = null) {
  831. if (empty($encoding)) {
  832. $encoding = api_mb_internal_encoding();
  833. }
  834. if (api_mb_supports($encoding)) {
  835. return @mb_strtolower($string, $encoding);
  836. }
  837. elseif (api_is_encoding_supported($encoding)) {
  838. if (!api_is_utf8($encoding)) {
  839. $string = api_utf8_encode($string, $encoding);
  840. }
  841. if (MBSTRING_INSTALLED) {
  842. $string = @mb_strtolower($string, 'UTF-8');
  843. } else {
  844. // This branch (this fragment of code) is an adaptation from the CakePHP(tm) Project, http://www.cakefoundation.org
  845. $codepoints = _api_utf8_to_unicode($string);
  846. $length = count($codepoints);
  847. $matched = false;
  848. $result = array();
  849. for ($i = 0 ; $i < $length; $i++) {
  850. $codepoint = $codepoints[$i];
  851. if ($codepoint < 128) {
  852. $str = strtolower(chr($codepoint));
  853. $strlen = api_byte_count($str);
  854. for ($ii = 0 ; $ii < $strlen; $ii++) {
  855. $lower = ord($str[$ii]);
  856. }
  857. $result[] = $lower;
  858. $matched = true;
  859. } else {
  860. $matched = false;
  861. $properties = _api_utf8_get_letter_case_properties($codepoint, 'upper');
  862. if (!empty($properties)) {
  863. foreach ($properties as $key => $value) {
  864. if ($properties[$key]['upper'] == $codepoint && count($properties[$key]['lower'][0]) === 1) {
  865. $result[] = $properties[$key]['lower'][0];
  866. $matched = true;
  867. break 1;
  868. }
  869. }
  870. }
  871. }
  872. if ($matched === false) {
  873. $result[] = $codepoint;
  874. }
  875. }
  876. $string = _api_utf8_from_unicode($result);
  877. }
  878. if (!api_is_utf8($encoding)) {
  879. return api_utf8_decode($string, $encoding);
  880. }
  881. return $string;
  882. }
  883. return strtolower($string);
  884. }
  885. /**
  886. * Makes a string uppercase.
  887. * @param string $string The string being uppercased.
  888. * @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.
  889. * @return string Returns the string with all alphabetic characters converted to uppercase.
  890. * This function is aimed at replacing the functions strtoupper() and mb_strtoupper() for human-language strings.
  891. * @link http://php.net/manual/en/function.strtoupper
  892. * @link http://php.net/manual/en/function.mb-strtoupper
  893. */
  894. function api_strtoupper($string, $encoding = null) {
  895. if (empty($encoding)) {
  896. $encoding = api_mb_internal_encoding();
  897. }
  898. if (api_mb_supports($encoding)) {
  899. return @mb_strtoupper($string, $encoding);
  900. }
  901. elseif (api_is_encoding_supported($encoding)) {
  902. if (!api_is_utf8($encoding)) {
  903. $string = api_utf8_encode($string, $encoding);
  904. }
  905. if (MBSTRING_INSTALLED) {
  906. $string = @mb_strtoupper($string, 'UTF-8');
  907. } else {
  908. // This branch (this fragment of code) is an adaptation from the CakePHP(tm) Project, http://www.cakefoundation.org
  909. $codepoints = _api_utf8_to_unicode($string);
  910. $length = count($codepoints);
  911. $matched = false;
  912. $replaced = array();
  913. $result = array();
  914. for ($i = 0 ; $i < $length; $i++) {
  915. $codepoint = $codepoints[$i];
  916. if ($codepoint < 128) {
  917. $str = strtoupper(chr($codepoint));
  918. $strlen = api_byte_count($str);
  919. for ($ii = 0 ; $ii < $strlen; $ii++) {
  920. $lower = ord($str[$ii]);
  921. }
  922. $result[] = $lower;
  923. $matched = true;
  924. } else {
  925. $matched = false;
  926. $properties = _api_utf8_get_letter_case_properties($codepoint);
  927. $property_count = count($properties);
  928. if (!empty($properties)) {
  929. foreach ($properties as $key => $value) {
  930. $matched = false;
  931. $replace = 0;
  932. if ($length > 1 && count($properties[$key]['lower']) > 1) {
  933. $j = 0;
  934. for ($ii = 0; $ii < count($properties[$key]['lower']); $ii++) {
  935. $next_codepoint = $next_codepoints[$i + $ii];
  936. if (isset($next_codepoint) && ($next_codepoint == $properties[$key]['lower'][$j + $ii])) {
  937. $replace++;
  938. }
  939. }
  940. if ($replace == count($properties[$key]['lower'])) {
  941. $result[] = $properties[$key]['upper'];
  942. $replaced = array_merge($replaced, array_values($properties[$key]['lower']));
  943. $matched = true;
  944. break 1;
  945. }
  946. } elseif ($length > 1 && $property_count > 1) {
  947. $j = 0;
  948. for ($ii = 1; $ii < $property_count; $ii++) {
  949. $next_codepoint = $next_codepoints[$i + $ii - 1];
  950. if (in_array($next_codepoint, $properties[$ii]['lower'])) {
  951. for ($jj = 0; $jj < count($properties[$ii]['lower']); $jj++) {
  952. $next_codepoint = $next_codepoints[$i + $jj];
  953. if (isset($next_codepoint) && ($next_codepoint == $properties[$ii]['lower'][$j + $jj])) {
  954. $replace++;
  955. }
  956. }
  957. if ($replace == count($properties[$ii]['lower'])) {
  958. $result[] = $properties[$ii]['upper'];
  959. $replaced = array_merge($replaced, array_values($properties[$ii]['lower']));
  960. $matched = true;
  961. break 2;
  962. }
  963. }
  964. }
  965. }
  966. if ($properties[$key]['lower'][0] == $codepoint) {
  967. $result[] = $properties[$key]['upper'];
  968. $matched = true;
  969. break 1;
  970. }
  971. }
  972. }
  973. }
  974. if ($matched === false && !in_array($codepoint, $replaced, true)) {
  975. $result[] = $codepoint;
  976. }
  977. }
  978. $string = _api_utf8_from_unicode($result);
  979. }
  980. if (!api_is_utf8($encoding)) {
  981. return api_utf8_decode($string, $encoding);
  982. }
  983. return $string;
  984. }
  985. return strtoupper($string);
  986. }
  987. /**
  988. * Translates certain characters.
  989. * @param string $string The string being translated.
  990. * @param mixed $from A string that contains the character to be replaced. This parameter can be also an array with pairs of characters 'from' => 'to'.
  991. * @param string $to (optional) A string that contains the replacing characters.
  992. * @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.
  993. * @return string Returns a copy of $string, translating all occurrences of each character in $from to the corresponding character in $to.
  994. * This function is aimed at replacing the function strtr() for human-language strings.
  995. * @link http://php.net/manual/en/function.strtr
  996. * TODO: To be revised and tested. Probably this function will not be needed.
  997. * TODO: This function will be removed. It is not needed. 21-AUG-2009.
  998. */
  999. function api_strtr($string, $from, $to = null, $encoding = null) {
  1000. if (empty($string)) {
  1001. return '';
  1002. }
  1003. if (is_array($from)) {
  1004. if (empty($from)) {
  1005. return $string;
  1006. }
  1007. $encoding = $to;
  1008. if (empty($encoding)){
  1009. $encoding = api_mb_internal_encoding();
  1010. }
  1011. $translator = $from;
  1012. } else {
  1013. if (empty($from) || empty($to)) {
  1014. return $string;
  1015. }
  1016. if (empty($encoding)) {
  1017. $encoding = api_mb_internal_encoding();
  1018. }
  1019. $translator = array();
  1020. $arr_from = api_str_split($from, 1, $encoding);
  1021. $arr_to = api_str_split($to, 1, $encoding);
  1022. $n = count($arr_from);
  1023. $n2 = count($arr_to);
  1024. if ($n > $n2) $n = $n2;
  1025. for ($i = 0; $i < $n; $i++) {
  1026. $translator[$arr_from[$i]] = $arr_to[$i];
  1027. }
  1028. }
  1029. $arr_string = api_str_split($string, 1, $encoding);
  1030. $n = count($arr_string);
  1031. $result = '';
  1032. for ($i = 0; $i < $n; $i++) {
  1033. if (is_set($translator[$arr_string[$i]])) {
  1034. $result .= $translator[$arr_string[$i]];
  1035. } else {
  1036. $result .= $arr_string[$i];
  1037. }
  1038. }
  1039. return $result;
  1040. }
  1041. /**
  1042. // Gets part of a string.
  1043. * @param string $string The input string.
  1044. * @param int $start The first position from which the extracted part begins.
  1045. * @param int $length The length in character of the extracted part.
  1046. * @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.
  1047. * @return string Returns the part of the string specified by the start and length parameters.
  1048. * Note: First character's position is 0. Second character position is 1, and so on.
  1049. * This function is aimed at replacing the functions substr() and mb_substr() for human-language strings.
  1050. * @link http://php.net/manual/en/function.substr
  1051. * @link http://php.net/manual/en/function.mb-substr
  1052. */
  1053. function api_substr($string, $start, $length = null, $encoding = null) {
  1054. if (empty($encoding)) {
  1055. $encoding = api_mb_internal_encoding();
  1056. }
  1057. // Passing null as $length would mean 0. This behaviour has been corrected here.
  1058. if (is_null($length)) {
  1059. $length = api_strlen($string, $encoding);
  1060. }
  1061. if (_api_is_single_byte_encoding($encoding)) {
  1062. return substr($string, $start, $length);
  1063. }
  1064. if (api_mb_supports($encoding)) {
  1065. return @mb_substr($string, $start, $length, $encoding);
  1066. }
  1067. elseif (api_is_encoding_supported($encoding)) {
  1068. if (!api_is_utf8($encoding)) {
  1069. $string = api_utf8_encode($string, $encoding);
  1070. }
  1071. if (MBSTRING_INSTALLED) {
  1072. $string = @mb_substr($string, $start, $length, 'UTF-8');
  1073. } else {
  1074. // The following branch of code is from the Drupal CMS, see the function drupal_substr().
  1075. $strlen = api_byte_count($string);
  1076. // Find the starting byte offset
  1077. $bytes = 0;
  1078. if ($start > 0) {
  1079. // Count all the continuation bytes from the start until we have found
  1080. // $start characters
  1081. $bytes = -1; $chars = -1;
  1082. while ($bytes < $strlen && $chars < $start) {
  1083. $bytes++;
  1084. $c = ord($string[$bytes]);
  1085. if ($c < 0x80 || $c >= 0xC0) {
  1086. $chars++;
  1087. }
  1088. }
  1089. }
  1090. else if ($start < 0) {
  1091. // Count all the continuation bytes from the end until we have found
  1092. // abs($start) characters
  1093. $start = abs($start);
  1094. $bytes = $strlen; $chars = 0;
  1095. while ($bytes > 0 && $chars < $start) {
  1096. $bytes--;
  1097. $c = ord($string[$bytes]);
  1098. if ($c < 0x80 || $c >= 0xC0) {
  1099. $chars++;
  1100. }
  1101. }
  1102. }
  1103. $istart = $bytes;
  1104. // Find the ending byte offset
  1105. if ($length === NULL) {
  1106. $bytes = $strlen - 1;
  1107. }
  1108. else if ($length > 0) {
  1109. // Count all the continuation bytes from the starting index until we have
  1110. // found $length + 1 characters. Then backtrack one byte.
  1111. $bytes = $istart; $chars = 0;
  1112. while ($bytes < $strlen && $chars < $length) {
  1113. $bytes++;
  1114. $c = ord($string[$bytes]);
  1115. if ($c < 0x80 || $c >= 0xC0) {
  1116. $chars++;
  1117. }
  1118. }
  1119. $bytes--;
  1120. }
  1121. else if ($length < 0) {
  1122. // Count all the continuation bytes from the end until we have found
  1123. // abs($length) characters
  1124. $length = abs($length);
  1125. $bytes = $strlen - 1; $chars = 0;
  1126. while ($bytes >= 0 && $chars < $length) {
  1127. $c = ord($string[$bytes]);
  1128. if ($c < 0x80 || $c >= 0xC0) {
  1129. $chars++;
  1130. }
  1131. $bytes--;
  1132. }
  1133. }
  1134. $iend = $bytes;
  1135. $string = substr($string, $istart, max(0, $iend - $istart + 1));
  1136. }
  1137. if (!api_is_utf8($encoding)) {
  1138. $string = api_utf8_decode($string, $encoding);
  1139. }
  1140. return $string;
  1141. }
  1142. return substr($string, $start, $length);
  1143. }
  1144. /**
  1145. * Replaces text within a portion of a string.
  1146. * @param string $string The input string.
  1147. * @param string $replacement The replacement string.
  1148. * @param int $start The position from which replacing will begin.
  1149. * Notes:
  1150. * If $start is positive, the replacing will begin at the $start'th offset into the string.
  1151. * If $start is negative, the replacing will begin at the $start'th character from the end of the string.
  1152. * @param int $length (optional) The position where replacing will end.
  1153. * Notes:
  1154. * If given and is positive, it represents the length of the portion of the string which is to be replaced.
  1155. * If it is negative, it represents the number of characters from the end of string at which to stop replacing.
  1156. * If it is not given, then it will default to api_strlen($string); i.e. end the replacing at the end of string.
  1157. * If $length is zero, then this function will have the effect of inserting replacement into the string at the given start offset.
  1158. * @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.
  1159. * @return string The result string is returned.
  1160. * This function is aimed at replacing the function substr_replace() for human-language strings.
  1161. * @link http://php.net/manual/function.substr-replace
  1162. */
  1163. function api_substr_replace($string, $replacement, $start, $length = null, $encoding = null) {
  1164. if (empty($encoding)) {
  1165. $encoding = api_mb_internal_encoding();
  1166. }
  1167. if (_api_is_single_byte_encoding($encoding)) {
  1168. return substr_replace($string, $replacement, $start, $length);
  1169. }
  1170. if (api_is_encoding_supported($encoding)) {
  1171. // This fragment (branch) of code is adaptation of a published proposition:
  1172. // http://php.net/manual/en/function.substr-replace.php#90146
  1173. $string_length = api_strlen($string, $encoding);
  1174. if ($start < 0) {
  1175. $start = max(0, $string_length + $start);
  1176. }
  1177. else if ($start > $string_length) {
  1178. $start = $string_length;
  1179. }
  1180. if ($length < 0) {
  1181. $length = max(0, $string_length - $start + $length);
  1182. }
  1183. else if (is_null($length) || ($length > $string_length)) {
  1184. $length = $string_length;
  1185. }
  1186. if (($start + $length) > $string_length) {
  1187. $length = $string_length - $start;
  1188. }
  1189. return api_substr($string, 0, $start, $encoding) . $replacement . api_substr($string, $start + $length, $string_length - $start - $length, $encoding);
  1190. }
  1191. if (is_null($length)) {
  1192. return substr_replace($string, $replacement, $start);
  1193. }
  1194. return substr_replace($string, $replacement, $start, $length);
  1195. }
  1196. /**
  1197. * Makes a string's first character uppercase.
  1198. * @param string $string The input string.
  1199. * @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.
  1200. * @return string Returns a string with the first character capitalized, if that character is alphabetic.
  1201. * This function is aimed at replacing the function ucfirst() for human-language strings.
  1202. * @link http://php.net/manual/en/function.ucfirst
  1203. */
  1204. function api_ucfirst($string, $encoding = null) {
  1205. if (empty($encoding)) {
  1206. $encoding = api_mb_internal_encoding();
  1207. }
  1208. return api_strtoupper(api_substr($string, 0, 1, $encoding), $encoding) . api_substr($string, 1, api_strlen($string, $encoding), $encoding);
  1209. }
  1210. /**
  1211. * Uppercases the first character of each word in a string.
  1212. * @param string $string The input string.
  1213. * @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.
  1214. * @return string Returns the modified string.
  1215. * This function is aimed at replacing the function ucwords() for human-language strings.
  1216. * @link http://php.net/manual/en/function.ucwords
  1217. */
  1218. function api_ucwords($string, $encoding = null) {
  1219. if (empty($encoding)) {
  1220. $encoding = api_mb_internal_encoding();
  1221. }
  1222. if (api_mb_supports($encoding)) {
  1223. return @mb_convert_case($string, MB_CASE_TITLE, $encoding);
  1224. }
  1225. if (api_is_encoding_supported($encoding)) {
  1226. if (!api_is_utf8($encoding)) {
  1227. $string = api_utf8_encode($string, $encoding);
  1228. }
  1229. if (MBSTRING_INSTALLED) {
  1230. $string = @mb_convert_case($string, MB_CASE_TITLE, 'UTF-8');
  1231. } else {
  1232. // The following fragment (branch) of code is based on the function utf8_ucwords() by Harry Fuecks
  1233. // See http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
  1234. // Note: [\x0c\x09\x0b\x0a\x0d\x20] matches - form feeds, horizontal tabs, vertical tabs, linefeeds and carriage returns.
  1235. // This corresponds to the definition of a "word" defined at http://www.php.net/ucwords
  1236. $pattern = '/(^|([\x0c\x09\x0b\x0a\x0d\x20]+))([^\x0c\x09\x0b\x0a\x0d\x20]{1})[^\x0c\x09\x0b\x0a\x0d\x20]*/u';
  1237. $string = preg_replace_callback($pattern, '_api_utf8_ucwords_callback', $string);
  1238. }
  1239. if (!api_is_utf8($encoding)) {
  1240. return api_utf8_decode($string, $encoding);
  1241. }
  1242. return $string;
  1243. }
  1244. return ucwords($string);
  1245. }
  1246. /**
  1247. * ----------------------------------------------------------------------------
  1248. * String operations using regular expressions
  1249. * ----------------------------------------------------------------------------
  1250. */
  1251. /**
  1252. * Note: Try to avoid using this function. Use api_preg_match() with Perl-compatible regular expression syntax.
  1253. *
  1254. * Executes a regular expression match with extended multibyte support.
  1255. * By default this function uses the platform character set.
  1256. * @param string $pattern The regular expression pattern.
  1257. * @param string $string The searched string.
  1258. * @param array $regs (optional) If specified, by this passed by reference parameter an array containing found match and its substrings is returned.
  1259. * @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.
  1260. * This function is aimed at replacing the functions ereg() and mb_ereg() for human-language strings.
  1261. * @link http://php.net/manual/en/function.ereg
  1262. * @link http://php.net/manual/en/function.mb-ereg
  1263. */
  1264. function api_ereg($pattern, $string, & $regs = null) {
  1265. $count = func_num_args();
  1266. $encoding = api_mb_regex_encoding();
  1267. if (api_mb_supports($encoding)) {
  1268. if ($count < 3) {
  1269. return @mb_ereg($pattern, $string);
  1270. }
  1271. return @mb_ereg($pattern, $string, $regs);
  1272. }
  1273. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  1274. global $_api_encoding;
  1275. $_api_encoding = $encoding;
  1276. api_mb_regex_encoding('UTF-8');
  1277. if ($count < 3) {
  1278. $result = @mb_ereg(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding));
  1279. } else {
  1280. $result = @mb_ereg(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding), $regs);
  1281. $regs = _api_array_utf8_decode($regs);
  1282. }
  1283. api_mb_regex_encoding($encoding);
  1284. return $result;
  1285. }
  1286. if ($count < 3) {
  1287. return ereg($pattern, $string);
  1288. }
  1289. return ereg($pattern, $string, $regs);
  1290. }
  1291. /**
  1292. * Note: Try to avoid using this function. Use api_preg_replace() with Perl-compatible regular expression syntax.
  1293. *
  1294. * Scans string for matches to pattern, then replaces the matched text with replacement, with extended multibyte support.
  1295. * By default this function uses the platform character set.
  1296. * @param string $pattern The regular expression pattern.
  1297. * @param string $replacement The replacement text.
  1298. * @param string $string The searched string.
  1299. * @param string $option (optional) Matching condition.
  1300. * If i is specified for the matching condition parameter, the case will be ignored.
  1301. * If x is specified, white space will be ignored.
  1302. * If m is specified, match will be executed in multiline mode and line break will be included in '.'.
  1303. * If p is specified, match will be executed in POSIX mode, line break will be considered as normal character.
  1304. * If e is specified, replacement string will be evaluated as PHP expression.
  1305. * @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.
  1306. * This function is aimed at replacing the functions ereg_replace() and mb_ereg_replace() for human-language strings.
  1307. * @link http://php.net/manual/en/function.ereg-replace
  1308. * @link http://php.net/manual/en/function.mb-ereg-replace
  1309. */
  1310. function api_ereg_replace($pattern, $replacement, $string, $option = null) {
  1311. $encoding = api_mb_regex_encoding();
  1312. if (api_mb_supports($encoding)) {
  1313. if (is_null($option)) {
  1314. return @mb_ereg_replace($pattern, $replacement, $string);
  1315. }
  1316. return @mb_ereg_replace($pattern, $replacement, $string, $option);
  1317. }
  1318. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  1319. api_mb_regex_encoding('UTF-8');
  1320. if (is_null($option)) {
  1321. $result = api_utf8_decode(@mb_ereg_replace(api_utf8_encode($pattern, $encoding), api_utf8_encode($replacement, $encoding), api_utf8_encode($string, $encoding)), $encoding);
  1322. } else {
  1323. $result = api_utf8_decode(@mb_ereg_replace(api_utf8_encode($pattern, $encoding), api_utf8_encode($replacement, $encoding), api_utf8_encode($string, $encoding), $option), $encoding);
  1324. }
  1325. api_mb_regex_encoding($encoding);
  1326. return $result;
  1327. }
  1328. return ereg_replace($pattern, $replacement, $string);
  1329. }
  1330. /**
  1331. * Note: Try to avoid using this function. Use api_preg_match() with Perl-compatible regular expression syntax.
  1332. *
  1333. * Executes a regular expression match, ignoring case, with extended multibyte support.
  1334. * By default this function uses the platform character set.
  1335. * @param string $pattern The regular expression pattern.
  1336. * @param string $string The searched string.
  1337. * @param array $regs (optional) If specified, by this passed by reference parameter an array containing found match and its substrings is returned.
  1338. * @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.
  1339. * This function is aimed at replacing the functions eregi() and mb_eregi() for human-language strings.
  1340. * @link http://php.net/manual/en/function.eregi
  1341. * @link http://php.net/manual/en/function.mb-eregi
  1342. */
  1343. function api_eregi($pattern, $string, & $regs = null) {
  1344. $count = func_num_args();
  1345. $encoding = api_mb_regex_encoding();
  1346. if (api_mb_supports($encoding)) {
  1347. if ($count < 3) {
  1348. return @mb_eregi($pattern, $string);
  1349. }
  1350. return @mb_eregi($pattern, $string, $regs);
  1351. }
  1352. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  1353. global $_api_encoding;
  1354. $_api_encoding = $encoding;
  1355. api_mb_regex_encoding('UTF-8');
  1356. if ($count < 3) {
  1357. $result = @mb_eregi(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding));
  1358. } else {
  1359. $result = @mb_eregi(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding), $regs);
  1360. $regs = _api_array_utf8_decode($regs);
  1361. }
  1362. api_mb_regex_encoding($encoding);
  1363. return $result;
  1364. }
  1365. if ($count < 3) {
  1366. return eregi($pattern, $string);
  1367. }
  1368. return eregi($pattern, $string, $regs);
  1369. }
  1370. /**
  1371. * Note: Try to avoid using this function. Use api_preg_replace() with Perl-compatible regular expression syntax.
  1372. *
  1373. * Scans string for matches to pattern, then replaces the matched text with replacement, ignoring case, with extended multibyte support.
  1374. * By default this function uses the platform character set.
  1375. * @param string $pattern The regular expression pattern.
  1376. * @param string $replacement The replacement text.
  1377. * @param string $string The searched string.
  1378. * @param string $option (optional) Matching condition.
  1379. * If i is specified for the matching condition parameter, the case will be ignored.
  1380. * If x is specified, white space will be ignored.
  1381. * If m is specified, match will be executed in multiline mode and line break will be included in '.'.
  1382. * If p is specified, match will be executed in POSIX mode, line break will be considered as normal character.
  1383. * If e is specified, replacement string will be evaluated as PHP expression.
  1384. * @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.
  1385. * This function is aimed at replacing the functions eregi_replace() and mb_eregi_replace() for human-language strings.
  1386. * @link http://php.net/manual/en/function.eregi-replace
  1387. * @link http://php.net/manual/en/function.mb-eregi-replace
  1388. */
  1389. function api_eregi_replace($pattern, $replacement, $string, $option = null) {
  1390. $encoding = api_mb_regex_encoding();
  1391. if (api_mb_supports($encoding)) {
  1392. if (is_null($option)) {
  1393. return @mb_eregi_replace($pattern, $replacement, $string);
  1394. }
  1395. return @mb_eregi_replace($pattern, $replacement, $string, $option);
  1396. }
  1397. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  1398. api_mb_regex_encoding('UTF-8');
  1399. if (is_null($option)) {
  1400. $result = api_utf8_decode(@mb_eregi_replace(api_utf8_encode($pattern, $encoding), api_utf8_encode($replacement, $encoding), api_utf8_encode($string, $encoding)), $encoding);
  1401. } else {
  1402. $result = api_utf8_decode(@mb_eregi_replace(api_utf8_encode($pattern, $encoding), api_utf8_encode($replacement, $encoding), api_utf8_encode($string, $encoding), $option), $encoding);
  1403. }
  1404. api_mb_regex_encoding($encoding);
  1405. return $result;
  1406. }
  1407. return eregi_replace($pattern, $replacement, $string);
  1408. }
  1409. /**
  1410. * Performs a regular expression match, UTF-8 aware when it is applicable.
  1411. * @param string $pattern The pattern to search for, as a string.
  1412. * @param string $subject The input string.
  1413. * @param array &$matches (optional) If matches is provided, then it is filled with the results of search (as an array).
  1414. * $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.
  1415. * @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.
  1416. * 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.
  1417. * @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.
  1418. * @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.
  1419. * @return int|boolean Returns the number of times pattern matches or FALSE if an error occurred.
  1420. * @link http://php.net/preg_match
  1421. */
  1422. function api_preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0, $encoding = null) {
  1423. if (empty($encoding)){
  1424. $encoding = api_get_system_encoding();
  1425. }
  1426. return preg_match(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $matches, $flags, $offset);
  1427. }
  1428. /**
  1429. * Performs a global regular expression match, UTF-8 aware when it is applicable.
  1430. * @param string $pattern The pattern to search for, as a string.
  1431. * @param string $subject The input string.
  1432. * @param array &$matches (optional) Array of all matches in multi-dimensional array ordered according to $flags.
  1433. * @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):
  1434. * 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;
  1435. * 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;
  1436. * 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
  1437. * 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.
  1438. * If no order flag is given, PREG_PATTERN_ORDER is assumed.
  1439. * @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.
  1440. * @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.
  1441. * @return int|boolean Returns the number of full pattern matches (which might be zero), or FALSE if an error occurred.
  1442. * @link http://php.net/preg_match_all
  1443. */
  1444. function api_preg_match_all($pattern, $subject, &$matches, $flags = PREG_PATTERN_ORDER, $offset = 0, $encoding = null) {
  1445. if (empty($encoding)){
  1446. $encoding = api_get_system_encoding();
  1447. }
  1448. return preg_match_all(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $matches, $flags, $offset);
  1449. }
  1450. /**
  1451. * Performs a regular expression search and replace, UTF-8 aware when it is applicable.
  1452. * @param string|array $pattern The pattern to search for. It can be either a string or an array with strings.
  1453. * @param string|array $replacement The string or an array with strings to replace.
  1454. * @param string|array $subject The string or an array with strings to search and replace.
  1455. * @param int $limit The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit).
  1456. * @param int &$count If specified, this variable will be filled with the number of replacements done.
  1457. * @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.
  1458. * @return array|string|null returns an array if the subject parameter is an array, or a string otherwise.
  1459. * If matches are found, the new subject will be returned, otherwise subject will be returned unchanged or NULL if an error occurred.
  1460. * @link http://php.net/preg_replace
  1461. */
  1462. function api_preg_replace($pattern, $replacement, $subject, $limit = -1, &$count = 0, $encoding = null) {
  1463. if (empty($encoding)){
  1464. $encoding = api_get_system_encoding();
  1465. }
  1466. $is_utf8 = api_is_utf8($encoding);
  1467. if (is_array($pattern)) {
  1468. foreach ($pattern as &$p) {
  1469. $p = $is_utf8 ? $p.'u' : $p;
  1470. }
  1471. } else {
  1472. $pattern = $is_utf8 ? $pattern.'u' : $pattern;
  1473. }
  1474. return preg_replace($pattern, $replacement, $subject, $limit, $count);
  1475. }
  1476. /**
  1477. * Performs a regular expression search and replace using a callback function, UTF-8 aware when it is applicable.
  1478. * @param string|array $pattern The pattern to search for. It can be either a string or an array with strings.
  1479. * @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.
  1480. * @param string|array $subject The string or an array with strings to search and replace.
  1481. * @param int $limit (optional) The maximum possible replacements for each pattern in each subject string. Defaults to -1 (no limit).
  1482. * @param int &$count (optional) If specified, this variable will be filled with the number of replacements done.
  1483. * @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.
  1484. * @return array|string Returns an array if the subject parameter is an array, or a string otherwise.
  1485. * @link http://php.net/preg_replace_callback
  1486. */
  1487. function api_preg_replace_callback($pattern, $callback, $subject, $limit = -1, &$count = 0, $encoding = null) {
  1488. if (empty($encoding)){
  1489. $encoding = api_get_system_encoding();
  1490. }
  1491. if (is_array($pattern)) {
  1492. foreach ($pattern as &$p) {
  1493. $p = api_is_utf8($encoding) ? $p.'u' : $p;
  1494. }
  1495. } else {
  1496. $pattern = api_is_utf8($encoding) ? $pattern.'u' : $pattern;
  1497. }
  1498. return preg_replace_callback($pattern, $callback, $subject, $limit, $count);
  1499. }
  1500. /**
  1501. * Splits a string by a regular expression, UTF-8 aware when it is applicable.
  1502. * @param string $pattern The pattern to search for, as a string.
  1503. * @param string $subject The input string.
  1504. * @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.
  1505. * @param int $flags (optional) $flags can be any combination of the following flags (combined with bitwise | operator):
  1506. * PREG_SPLIT_NO_EMPTY - if this flag is set, only non-empty pieces will be returned;
  1507. * PREG_SPLIT_DELIM_CAPTURE - if this flag is set, parenthesized expression in the delimiter pattern will be captured and returned as well;
  1508. * PREG_SPLIT_OFFSET_CAPTURE - If this flag is set, for every occurring match the appendant string offset will also be returned.
  1509. * 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.
  1510. * @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.
  1511. * @return array Returns an array containing substrings of $subject split along boundaries matched by $pattern.
  1512. * @link http://php.net/preg_split
  1513. */
  1514. function api_preg_split($pattern, $subject, $limit = -1, $flags = 0, $encoding = null) {
  1515. if (empty($encoding)){
  1516. $encoding = api_get_system_encoding();
  1517. }
  1518. return preg_split(api_is_utf8($encoding) ? $pattern.'u' : $pattern, $subject, $limit, $flags);
  1519. }
  1520. /**
  1521. * Note: Try to avoid using this function. Use api_preg_split() with Perl-compatible regular expression syntax.
  1522. *
  1523. * Splits a multibyte string using regular expression pattern and returns the result as an array.
  1524. * By default this function uses the platform character set.
  1525. * @param string $pattern The regular expression pattern.
  1526. * @param string $string The string being split.
  1527. * @param int $limit (optional) If this optional parameter $limit is specified, the string will be split in $limit elements as maximum.
  1528. * @return array The result as an array.
  1529. * This function is aimed at replacing the functions split() and mb_split() for human-language strings.
  1530. * @link http://php.net/manual/en/function.split
  1531. * @link http://php.net/manual/en/function.mb-split
  1532. */
  1533. function api_split($pattern, $string, $limit = null) {
  1534. $encoding = api_mb_regex_encoding();
  1535. if (api_mb_supports($encoding)) {
  1536. if (is_null($limit)) {
  1537. return @mb_split($pattern, $string);
  1538. }
  1539. return @mb_split($pattern, $string, $limit);
  1540. }
  1541. if (MBSTRING_INSTALLED && api_is_encoding_supported($encoding)) {
  1542. global $_api_encoding;
  1543. $_api_encoding = $encoding;
  1544. api_mb_regex_encoding('UTF-8');
  1545. if (is_null($limit)) {
  1546. $result = @mb_split(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding));
  1547. } else {
  1548. $result = @mb_split(api_utf8_encode($pattern, $encoding), api_utf8_encode($string, $encoding), $limit);
  1549. }
  1550. $result = _api_array_utf8_decode($result);
  1551. api_mb_regex_encoding($encoding);
  1552. return $result;
  1553. }
  1554. if (is_null($limit)) {
  1555. return split($pattern, $string);
  1556. }
  1557. return split($pattern, $string, $limit);
  1558. }
  1559. /**
  1560. * ----------------------------------------------------------------------------
  1561. * Common sting operations with arrays
  1562. * ----------------------------------------------------------------------------
  1563. */
  1564. /**
  1565. * Checks if a value exists in an array, a case insensitive version of in_array() function with extended multibyte support.
  1566. * @param mixed $needle The searched value. If needle is a string, the comparison is done in a case-insensitive manner.
  1567. * @param array $haystack The array.
  1568. * @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.
  1569. * @param string $encoding (optional) The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
  1570. * @return bool Returns TRUE if $needle is found in the array, FALSE otherwise.
  1571. * @link http://php.net/manual/en/function.in-array.php
  1572. */
  1573. function api_in_array_nocase($needle, $haystack, $strict = false, $encoding = null) {
  1574. if (is_array($needle)) {
  1575. foreach ($needle as $item) {
  1576. if (api_in_array_nocase($item, $haystack, $strict, $encoding)) return true;
  1577. }
  1578. return false;
  1579. }
  1580. if (!is_string($needle)) {
  1581. return in_array($needle, $haystack, $strict);
  1582. }
  1583. $needle = api_strtolower($needle, $encoding);
  1584. foreach ($haystack as $item) {
  1585. if ($strict && !is_string($item)) {
  1586. continue;
  1587. }
  1588. if (api_strtolower($item, $encoding) == $needle) {
  1589. return true;
  1590. }
  1591. }
  1592. return false;
  1593. }
  1594. /**
  1595. * ----------------------------------------------------------------------------
  1596. * String comparison
  1597. * ----------------------------------------------------------------------------
  1598. */
  1599. /**
  1600. * Performs string comparison, case insensitive, language sensitive, with extended multibyte support.
  1601. * @param string $string1 The first string.
  1602. * @param string $string2 The second string.
  1603. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1604. * @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.
  1605. * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
  1606. * This function is aimed at replacing the function strcasecmp() for human-language strings.
  1607. * @link http://php.net/manual/en/function.strcasecmp
  1608. */
  1609. function api_strcasecmp($string1, $string2, $language = null, $encoding = null) {
  1610. return api_strcmp(api_strtolower($string1, $encoding), api_strtolower($string2, $encoding), $language, $encoding);
  1611. }
  1612. /**
  1613. * Performs string comparison, case sensitive, language sensitive, with extended multibyte support.
  1614. * @param string $string1 The first string.
  1615. * @param string $string2 The second string.
  1616. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1617. * @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.
  1618. * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
  1619. * This function is aimed at replacing the function strcmp() for human-language strings.
  1620. * @link http://php.net/manual/en/function.strcmp.php
  1621. * @link http://php.net/manual/en/collator.compare.php
  1622. */
  1623. function api_strcmp($string1, $string2, $language = null, $encoding = null) {
  1624. if (INTL_INSTALLED) {
  1625. $collator = _api_get_collator($language);
  1626. if (is_object($collator)) {
  1627. $result = collator_compare($collator, api_utf8_encode($string1, $encoding), api_utf8_encode($string2, $encoding));
  1628. return $result === false ? 0 : $result;
  1629. }
  1630. }
  1631. return strcmp($string1, $string2);
  1632. }
  1633. /**
  1634. * Performs string comparison in so called "natural order", case insensitive, language sensitive, with extended multibyte support.
  1635. * @param string $string1 The first string.
  1636. * @param string $string2 The second string.
  1637. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1638. * @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.
  1639. * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
  1640. * This function is aimed at replacing the function strnatcasecmp() for human-language strings.
  1641. * @link http://php.net/manual/en/function.strnatcasecmp
  1642. */
  1643. function api_strnatcasecmp($string1, $string2, $language = null, $encoding = null) {
  1644. return api_strnatcmp(api_strtolower($string1, $encoding), api_strtolower($string2, $encoding), $language, $encoding);
  1645. }
  1646. /**
  1647. * Performs string comparison in so called "natural order", case sensitive, language sensitive, with extended multibyte support.
  1648. * @param string $string1 The first string.
  1649. * @param string $string2 The second string.
  1650. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1651. * @param string $encoding (optional) The used internally by this function character encoding. If it is omitted, the platform character set will be used by default.
  1652. * @return int Returns < 0 if $string1 is less than $string2; > 0 if $string1 is greater than $string2; and 0 if the strings are equal.
  1653. * This function is aimed at replacing the function strnatcmp() for human-language strings.
  1654. * @link http://php.net/manual/en/function.strnatcmp.php
  1655. * @link http://php.net/manual/en/collator.compare.php
  1656. */
  1657. function api_strnatcmp($string1, $string2, $language = null, $encoding = null) {
  1658. if (INTL_INSTALLED) {
  1659. $collator = _api_get_alpha_numerical_collator($language);
  1660. if (is_object($collator)) {
  1661. $result = collator_compare($collator, api_utf8_encode($string1, $encoding), api_utf8_encode($string2, $encoding));
  1662. return $result === false ? 0 : $result;
  1663. }
  1664. }
  1665. return strnatcmp($string1, $string2);
  1666. }
  1667. /**
  1668. * ----------------------------------------------------------------------------
  1669. * Sorting arrays
  1670. * ----------------------------------------------------------------------------
  1671. */
  1672. /**
  1673. * Sorts an array with maintaining index association, elements will be arranged from the lowest to the highest.
  1674. * @param array $array The input array.
  1675. * @param int $sort_flag (optional) Shows how elements of the array to be compared.
  1676. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1677. * @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.
  1678. * @return bool Returns TRUE on success, FALSE on error.
  1679. * Note: $sort_flag may have the following values:
  1680. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  1681. * SORT_NUMERIC - items will be compared as numbers;
  1682. * 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;
  1683. * 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.
  1684. * This function is aimed at replacing the function asort() for sorting human-language strings.
  1685. * @link http://php.net/manual/en/function.asort.php
  1686. * @link http://php.net/manual/en/collator.asort.php
  1687. */
  1688. function api_asort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null) {
  1689. if (INTL_INSTALLED) {
  1690. if (empty($encoding)) {
  1691. $encoding = api_mb_internal_encoding();
  1692. }
  1693. $collator = _api_get_collator($language);
  1694. if (is_object($collator)) {
  1695. if (api_is_utf8($encoding)) {
  1696. $sort_flag = ($sort_flag == SORT_LOCALE_STRING) ? SORT_STRING : $sort_flag;
  1697. return collator_asort($collator, $array, _api_get_collator_sort_flag($sort_flag));
  1698. }
  1699. elseif ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  1700. global $_api_collator, $_api_encoding;
  1701. $_api_collator = $collator;
  1702. $_api_encoding = $encoding;
  1703. return uasort($array, '_api_cmp');
  1704. }
  1705. }
  1706. }
  1707. return asort($array, $sort_flag);
  1708. }
  1709. /**
  1710. * Sorts an array with maintaining index association, elements will be arranged from the highest to the lowest (in reverse order).
  1711. * @param array $array The input array.
  1712. * @param int $sort_flag (optional) Shows how elements of the array to be compared.
  1713. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  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 bool Returns TRUE on success, FALSE on error.
  1716. * Note: $sort_flag may have the following values:
  1717. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  1718. * SORT_NUMERIC - items will be compared as numbers;
  1719. * 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;
  1720. * 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.
  1721. * This function is aimed at replacing the function arsort() for sorting human-language strings.
  1722. * @link http://php.net/manual/en/function.arsort.php
  1723. */
  1724. function api_arsort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null) {
  1725. if (INTL_INSTALLED) {
  1726. if (empty($encoding)) {
  1727. $encoding = api_mb_internal_encoding();
  1728. }
  1729. $collator = _api_get_collator($language);
  1730. if (is_object($collator)) {
  1731. if ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  1732. global $_api_collator, $_api_encoding;
  1733. $_api_collator = $collator;
  1734. $_api_encoding = $encoding;
  1735. return uasort($array, '_api_rcmp');
  1736. }
  1737. }
  1738. }
  1739. return arsort($array, $sort_flag);
  1740. }
  1741. /**
  1742. * Sorts an array using natural order algorithm.
  1743. * @param array $array The input array.
  1744. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1745. * @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.
  1746. * @return bool Returns TRUE on success, FALSE on error.
  1747. * This function is aimed at replacing the function natsort() for sorting human-language strings.
  1748. * @link http://php.net/manual/en/function.natsort.php
  1749. */
  1750. function api_natsort(&$array, $language = null, $encoding = null) {
  1751. if (INTL_INSTALLED) {
  1752. if (empty($encoding)) {
  1753. $encoding = api_mb_internal_encoding();
  1754. }
  1755. $collator = _api_get_alpha_numerical_collator($language);
  1756. if (is_object($collator)) {
  1757. global $_api_collator, $_api_encoding;
  1758. $_api_collator = $collator;
  1759. $_api_encoding = $encoding;
  1760. return uasort($array, '_api_cmp');
  1761. }
  1762. }
  1763. return natsort($array);
  1764. }
  1765. /**
  1766. * Sorts an array using natural order algorithm in reverse order.
  1767. * @param array $array The input array.
  1768. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1769. * @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.
  1770. * @return bool Returns TRUE on success, FALSE on error.
  1771. */
  1772. function api_natrsort(&$array, $language = null, $encoding = null) {
  1773. if (INTL_INSTALLED) {
  1774. if (empty($encoding)) {
  1775. $encoding = api_mb_internal_encoding();
  1776. }
  1777. $collator = _api_get_alpha_numerical_collator($language);
  1778. if (is_object($collator)) {
  1779. global $_api_collator, $_api_encoding;
  1780. $_api_collator = $collator;
  1781. $_api_encoding = $encoding;
  1782. return uasort($array, '_api_rcmp');
  1783. }
  1784. }
  1785. return uasort($array, '_api_strnatrcmp');
  1786. }
  1787. /**
  1788. * Sorts an array using natural order algorithm, case-insensitive.
  1789. * @param array $array The input array.
  1790. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1791. * @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.
  1792. * @return bool Returns TRUE on success, FALSE on error.
  1793. * This function is aimed at replacing the function natcasesort() for sorting human-language strings.
  1794. * @link http://php.net/manual/en/function.natcasesort.php
  1795. */
  1796. function api_natcasesort(&$array, $language = null, $encoding = null) {
  1797. if (INTL_INSTALLED) {
  1798. if (empty($encoding)) {
  1799. $encoding = api_mb_internal_encoding();
  1800. }
  1801. $collator = _api_get_alpha_numerical_collator($language);
  1802. if (is_object($collator)) {
  1803. global $_api_collator, $_api_encoding;
  1804. $_api_collator = $collator;
  1805. $_api_encoding = $encoding;
  1806. return uasort($array, '_api_casecmp');
  1807. }
  1808. }
  1809. return natcasesort($array);
  1810. }
  1811. /**
  1812. * Sorts an array using natural order algorithm, case-insensitive, reverse order.
  1813. * @param array $array The input array.
  1814. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1815. * @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.
  1816. * @return bool Returns TRUE on success, FALSE on error.
  1817. */
  1818. function api_natcasersort(&$array, $language = null, $encoding = null) {
  1819. if (INTL_INSTALLED) {
  1820. if (empty($encoding)) {
  1821. $encoding = api_mb_internal_encoding();
  1822. }
  1823. $collator = _api_get_alpha_numerical_collator($language);
  1824. if (is_object($collator)) {
  1825. global $_api_collator, $_api_encoding;
  1826. $_api_collator = $collator;
  1827. $_api_encoding = $encoding;
  1828. return uasort($array, '_api_casercmp');
  1829. }
  1830. }
  1831. return uasort($array, '_api_strnatcasercmp');
  1832. }
  1833. /**
  1834. * Sorts an array by keys, elements will be arranged from the lowest key to the highest key.
  1835. * @param array $array The input array.
  1836. * @param int $sort_flag (optional) Shows how keys of the array to be compared.
  1837. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1838. * @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.
  1839. * @return bool Returns TRUE on success, FALSE on error.
  1840. * Note: $sort_flag may have the following values:
  1841. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  1842. * SORT_NUMERIC - keys will be compared as numbers;
  1843. * 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;
  1844. * 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.
  1845. * This function is aimed at replacing the function ksort() for sorting human-language key strings.
  1846. * @link http://php.net/manual/en/function.ksort.php
  1847. */
  1848. function api_ksort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null) {
  1849. if (INTL_INSTALLED) {
  1850. if (empty($encoding)) {
  1851. $encoding = api_mb_internal_encoding();
  1852. }
  1853. $collator = _api_get_collator($language);
  1854. if (is_object($collator)) {
  1855. if ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  1856. global $_api_collator, $_api_encoding;
  1857. $_api_collator = $collator;
  1858. $_api_encoding = $encoding;
  1859. return uksort($array, '_api_cmp');
  1860. }
  1861. }
  1862. }
  1863. return ksort($array, $sort_flag);
  1864. }
  1865. /**
  1866. * Sorts an array by keys, elements will be arranged from the highest key to the lowest key (in reverse order).
  1867. * @param array $array The input array.
  1868. * @param int $sort_flag (optional) Shows how keys of the array to be compared.
  1869. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1870. * @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.
  1871. * @return bool Returns TRUE on success, FALSE on error.
  1872. * Note: $sort_flag may have the following values:
  1873. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  1874. * SORT_NUMERIC - keys will be compared as numbers;
  1875. * 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;
  1876. * 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.
  1877. * This function is aimed at replacing the function krsort() for sorting human-language key strings.
  1878. * @link http://php.net/manual/en/function.krsort.php
  1879. */
  1880. function api_krsort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null) {
  1881. if (INTL_INSTALLED) {
  1882. if (empty($encoding)) {
  1883. $encoding = api_mb_internal_encoding();
  1884. }
  1885. $collator = _api_get_collator($language);
  1886. if (is_object($collator)) {
  1887. if ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  1888. global $_api_collator, $_api_encoding;
  1889. $_api_collator = $collator;
  1890. $_api_encoding = $encoding;
  1891. return uksort($array, '_api_rcmp');
  1892. }
  1893. }
  1894. }
  1895. return krsort($array, $sort_flag);
  1896. }
  1897. /**
  1898. * Sorts an array by keys using natural order algorithm.
  1899. * @param array $array The input array.
  1900. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1901. * @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.
  1902. * @return bool Returns TRUE on success, FALSE on error.
  1903. */
  1904. function api_knatsort(&$array, $language = null, $encoding = null) {
  1905. if (INTL_INSTALLED) {
  1906. if (empty($encoding)) {
  1907. $encoding = api_mb_internal_encoding();
  1908. }
  1909. $collator = _api_get_alpha_numerical_collator($language);
  1910. if (is_object($collator)) {
  1911. global $_api_collator, $_api_encoding;
  1912. $_api_collator = $collator;
  1913. $_api_encoding = $encoding;
  1914. return uksort($array, '_api_cmp');
  1915. }
  1916. }
  1917. return uksort($array, 'strnatcmp');
  1918. }
  1919. /**
  1920. * Sorts an array by keys using natural order algorithm in reverse order.
  1921. * @param array $array The input array.
  1922. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1923. * @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.
  1924. * @return bool Returns TRUE on success, FALSE on error.
  1925. */
  1926. function api_knatrsort(&$array, $language = null, $encoding = null) {
  1927. if (INTL_INSTALLED) {
  1928. if (empty($encoding)) {
  1929. $encoding = api_mb_internal_encoding();
  1930. }
  1931. $collator = _api_get_alpha_numerical_collator($language);
  1932. if (is_object($collator)) {
  1933. global $_api_collator, $_api_encoding;
  1934. $_api_collator = $collator;
  1935. $_api_encoding = $encoding;
  1936. return uksort($array, '_api_rcmp');
  1937. }
  1938. }
  1939. return uksort($array, '_api_strnatrcmp');
  1940. }
  1941. /**
  1942. * Sorts an array by keys using natural order algorithm, case insensitive.
  1943. * @param array $array The input array.
  1944. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1945. * @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.
  1946. * @return bool Returns TRUE on success, FALSE on error.
  1947. */
  1948. function api_knatcasesort(&$array, $language = null, $encoding = null) {
  1949. if (INTL_INSTALLED) {
  1950. if (empty($encoding)) {
  1951. $encoding = api_mb_internal_encoding();
  1952. }
  1953. $collator = _api_get_alpha_numerical_collator($language);
  1954. if (is_object($collator)) {
  1955. global $_api_collator, $_api_encoding;
  1956. $_api_collator = $collator;
  1957. $_api_encoding = $encoding;
  1958. return uksort($array, '_api_casecmp');
  1959. }
  1960. }
  1961. return uksort($array, 'strnatcasecmp');
  1962. }
  1963. /**
  1964. * Sorts an array by keys using natural order algorithm, case insensitive, reverse order.
  1965. * @param array $array The input array.
  1966. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1967. * @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.
  1968. * @return bool Returns TRUE on success, FALSE on error.
  1969. */
  1970. function api_knatcasersort(&$array, $language = null, $encoding = null) {
  1971. if (INTL_INSTALLED) {
  1972. if (empty($encoding)) {
  1973. $encoding = api_mb_internal_encoding();
  1974. }
  1975. $collator = _api_get_alpha_numerical_collator($language);
  1976. if (is_object($collator)) {
  1977. global $_api_collator, $_api_encoding;
  1978. $_api_collator = $collator;
  1979. $_api_encoding = $encoding;
  1980. return uksort($array, '_api_casercmp');
  1981. }
  1982. }
  1983. return uksort($array, '_api_strnatcasercmp');
  1984. }
  1985. /**
  1986. * Sorts an array, elements will be arranged from the lowest to the highest.
  1987. * @param array $array The input array.
  1988. * @param int $sort_flag (optional) Shows how elements of the array to be compared.
  1989. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  1990. * @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.
  1991. * @return bool Returns TRUE on success, FALSE on error.
  1992. * Note: $sort_flag may have the following values:
  1993. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  1994. * SORT_NUMERIC - items will be compared as numbers;
  1995. * 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;
  1996. * 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.
  1997. * This function is aimed at replacing the function sort() for sorting human-language strings.
  1998. * @link http://php.net/manual/en/function.sort.php
  1999. * @link http://php.net/manual/en/collator.sort.php
  2000. */
  2001. function api_sort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null) {
  2002. if (INTL_INSTALLED) {
  2003. if (empty($encoding)) {
  2004. $encoding = api_mb_internal_encoding();
  2005. }
  2006. $collator = _api_get_collator($language);
  2007. if (is_object($collator)) {
  2008. if (api_is_utf8($encoding)) {
  2009. $sort_flag = ($sort_flag == SORT_LOCALE_STRING) ? SORT_STRING : $sort_flag;
  2010. return collator_sort($collator, $array, _api_get_collator_sort_flag($sort_flag));
  2011. }
  2012. elseif ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  2013. global $_api_collator, $_api_encoding;
  2014. $_api_collator = $collator;
  2015. $_api_encoding = $encoding;
  2016. return usort($array, '_api_cmp');
  2017. }
  2018. }
  2019. }
  2020. return sort($array, $sort_flag);
  2021. }
  2022. /**
  2023. * Sorts an array, elements will be arranged from the highest to the lowest (in reverse order).
  2024. * @param array $array The input array.
  2025. * @param int $sort_flag (optional) Shows how elements of the array to be compared.
  2026. * @param string $language (optional) The language in which comparison is to be made. If language is omitted, interface language is assumed then.
  2027. * @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.
  2028. * @return bool Returns TRUE on success, FALSE on error.
  2029. * Note: $sort_flag may have the following values:
  2030. * SORT_REGULAR - internal PHP-rules for comparison will be applied, without preliminary changing types;
  2031. * SORT_NUMERIC - items will be compared as numbers;
  2032. * 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;
  2033. * 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.
  2034. * This function is aimed at replacing the function rsort() for sorting human-language strings.
  2035. * @link http://php.net/manual/en/function.rsort.php
  2036. */
  2037. function api_rsort(&$array, $sort_flag = SORT_REGULAR, $language = null, $encoding = null) {
  2038. if (INTL_INSTALLED) {
  2039. if (empty($encoding)) {
  2040. $encoding = api_mb_internal_encoding();
  2041. }
  2042. $collator = _api_get_collator($language);
  2043. if (is_object($collator)) {
  2044. if ($sort_flag == SORT_STRING || $sort_flag == SORT_LOCALE_STRING) {
  2045. global $_api_collator, $_api_encoding;
  2046. $_api_collator = $collator;
  2047. $_api_encoding = $encoding;
  2048. return usort($array, '_api_rcmp');
  2049. }
  2050. }
  2051. }
  2052. return rsort($array, $sort_flag);
  2053. }
  2054. //----------------------------------------------------------------------------
  2055. // Transliteration, converting ANSI and UTF-8 strings to ASCII strings
  2056. //----------------------------------------------------------------------------
  2057. /**
  2058. * Transliterates a string to a plain ASCII string.
  2059. *
  2060. * Example:
  2061. * echo api_transliterate(api_html_entity_decode(
  2062. * '&#1060;&#1105;&#1076;&#1086;&#1088; '.
  2063. * '&#1052;&#1080;&#1093;&#1072;&#1081;&#1083;&#1086;&#1074;&#1080;&#1095; '.
  2064. * '&#1044;&#1086;&#1089;&#1090;&#1086;&#1077;&#1074;&#1082;&#1080;&#1081;',
  2065. * ENT_QUOTES, 'UTF-8'), 'X', 'UTF-8');
  2066. * The output should be: Fyodor Mihaylovich Dostoevkiy
  2067. *
  2068. * @param string $string The input string.
  2069. * @param string $unknown (optional) Replacement character for unknown characters and illegal UTF-8 sequences.
  2070. * @param string $from_encoding (optional) The encoding of the input string. If it is omited, the platform character set is assumed.
  2071. * @return string Plain ASCII output.
  2072. *
  2073. * Based on Drupal's module "Transliteration", version 6.x-2.1, 09-JUN-2009:
  2074. * @author Stefan M. Kudwien (smk-ka)
  2075. * @author Daniel F. Kudwien (sun)
  2076. * @link http://drupal.org/project/transliteration
  2077. *
  2078. * See also MediaWiki's UtfNormal.php and CPAN's Text::Unidecode library
  2079. * @link http://www.mediawiki.org
  2080. * @link http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm).
  2081. *
  2082. * Adaptation for the Dokeos 1.8.6.1, 12-JUN-2009:
  2083. * @author Ivan Tcholakov
  2084. */
  2085. function api_transliterate($string, $unknown = '?', $from_encoding = null) {
  2086. static $map = array();
  2087. $string = api_utf8_encode($string, $from_encoding);
  2088. // Screen out some characters that eg won't be allowed in XML.
  2089. $string = preg_replace('/[\x00-\x08\x0b\x0c\x0e-\x1f]/', $unknown, $string);
  2090. // ASCII is always valid NFC!
  2091. // If we're only ever given plain ASCII, we can avoid the overhead
  2092. // of initializing the decomposition tables by skipping out early.
  2093. if (api_is_valid_ascii($string)) {
  2094. return $string;
  2095. }
  2096. static $tail_bytes;
  2097. if (!isset($tail_bytes)) {
  2098. // Each UTF-8 head byte is followed by a certain
  2099. // number of tail bytes.
  2100. $tail_bytes = array();
  2101. for ($n = 0; $n < 256; $n++) {
  2102. if ($n < 0xc0) {
  2103. $remaining = 0;
  2104. }
  2105. elseif ($n < 0xe0) {
  2106. $remaining = 1;
  2107. }
  2108. elseif ($n < 0xf0) {
  2109. $remaining = 2;
  2110. }
  2111. elseif ($n < 0xf8) {
  2112. $remaining = 3;
  2113. }
  2114. elseif ($n < 0xfc) {
  2115. $remaining = 4;
  2116. }
  2117. elseif ($n < 0xfe) {
  2118. $remaining = 5;
  2119. } else {
  2120. $remaining = 0;
  2121. }
  2122. $tail_bytes[chr($n)] = $remaining;
  2123. }
  2124. }
  2125. // Chop the text into pure-ASCII and non-ASCII areas;
  2126. // large ASCII parts can be handled much more quickly.
  2127. // Don't chop up Unicode areas for punctuation, though,
  2128. // that wastes energy.
  2129. preg_match_all('/[\x00-\x7f]+|[\x80-\xff][\x00-\x40\x5b-\x5f\x7b-\xff]*/', $string, $matches);
  2130. $result = '';
  2131. foreach ($matches[0] as $str) {
  2132. if ($str{0} < "\x80") {
  2133. // ASCII chunk: guaranteed to be valid UTF-8
  2134. // and in normal form C, so skip over it.
  2135. $result .= $str;
  2136. continue;
  2137. }
  2138. // We'll have to examine the chunk byte by byte to ensure
  2139. // that it consists of valid UTF-8 sequences, and to see
  2140. // if any of them might not be normalized.
  2141. //
  2142. // Since PHP is not the fastest language on earth, some of
  2143. // this code is a little ugly with inner loop optimizations.
  2144. $head = '';
  2145. $chunk = api_byte_count($str);
  2146. // Counting down is faster. I'm *so* sorry.
  2147. $len = $chunk + 1;
  2148. for ($i = -1; --$len; ) {
  2149. $c = $str{++$i};
  2150. if ($remaining = $tail_bytes[$c]) {
  2151. // UTF-8 head byte!
  2152. $sequence = $head = $c;
  2153. do {
  2154. // Look for the defined number of tail bytes...
  2155. if (--$len && ($c = $str{++$i}) >= "\x80" && $c < "\xc0") {
  2156. // Legal tail bytes are nice.
  2157. $sequence .= $c;
  2158. } else {
  2159. if ($len == 0) {
  2160. // Premature end of string!
  2161. // Drop a replacement character into output to
  2162. // represent the invalid UTF-8 sequence.
  2163. $result .= $unknown;
  2164. break 2;
  2165. } else {
  2166. // Illegal tail byte; abandon the sequence.
  2167. $result .= $unknown;
  2168. // Back up and reprocess this byte; it may itself
  2169. // be a legal ASCII or UTF-8 sequence head.
  2170. --$i;
  2171. ++$len;
  2172. continue 2;
  2173. }
  2174. }
  2175. } while (--$remaining);
  2176. $n = ord($head);
  2177. if ($n <= 0xdf) {
  2178. $ord = ($n - 192) * 64 + (ord($sequence{1}) - 128);
  2179. }
  2180. else if ($n <= 0xef) {
  2181. $ord = ($n - 224) * 4096 + (ord($sequence{1}) - 128) * 64 + (ord($sequence{2}) - 128);
  2182. }
  2183. else if ($n <= 0xf7) {
  2184. $ord = ($n - 240) * 262144 + (ord($sequence{1}) - 128) * 4096 + (ord($sequence{2}) - 128) * 64 + (ord($sequence{3}) - 128);
  2185. }
  2186. else if ($n <= 0xfb) {
  2187. $ord = ($n - 248) * 16777216 + (ord($sequence{1}) - 128) * 262144 + (ord($sequence{2}) - 128) * 4096 + (ord($sequence{3}) - 128) * 64 + (ord($sequence{4}) - 128);
  2188. }
  2189. else if ($n <= 0xfd) {
  2190. $ord = ($n - 252) * 1073741824 + (ord($sequence{1}) - 128) * 16777216 + (ord($sequence{2}) - 128) * 262144 + (ord($sequence{3}) - 128) * 4096 + (ord($sequence{4}) - 128) * 64 + (ord($sequence{5}) - 128);
  2191. }
  2192. // Lookup and replace a character from the transliteration database.
  2193. $bank = $ord >> 8;
  2194. // Check if we need to load a new bank
  2195. if (!isset($map[$bank])) {
  2196. $file = dirname(__FILE__) . '/multibyte_string_database/transliteration/' . sprintf('x%02x', $bank) . '.php';
  2197. if (file_exists($file)) {
  2198. $map[$bank] = include ($file);
  2199. } else {
  2200. $map[$bank] = array('en' => array());
  2201. }
  2202. }
  2203. $ord = $ord & 255;
  2204. $result .= isset($map[$bank]['en'][$ord]) ? $map[$bank]['en'][$ord] : $unknown;
  2205. $head = '';
  2206. }
  2207. elseif ($c < "\x80") {
  2208. // ASCII byte.
  2209. $result .= $c;
  2210. $head = '';
  2211. }
  2212. elseif ($c < "\xc0") {
  2213. // Illegal tail bytes.
  2214. if ($head == '') {
  2215. $result .= $unknown;
  2216. }
  2217. } else {
  2218. // Miscellaneous freaks.
  2219. $result .= $unknown;
  2220. $head = '';
  2221. }
  2222. }
  2223. }
  2224. return $result;
  2225. }
  2226. /**
  2227. * ----------------------------------------------------------------------------
  2228. * Encoding management functions
  2229. * ----------------------------------------------------------------------------
  2230. */
  2231. /**
  2232. * Returns the most-probably used non-UTF-8 encoding for the given language.
  2233. * @param string $language (optional) The specified language, the default value is the user intrface language.
  2234. * @return string The correspondent encoding to the specified language.
  2235. */
  2236. function api_get_non_utf8_encoding($language = null) {
  2237. if (empty($language)) {
  2238. $language = api_get_interface_language();
  2239. }
  2240. $language = api_refine_language_id($language);
  2241. $encodings = & api_non_utf8_encodings();
  2242. if (is_array($encodings[$language])) {
  2243. if (!empty($encodings[$language][0])) {
  2244. return $encodings[$language][0];
  2245. }
  2246. return 'ISO-8859-15';
  2247. }
  2248. return 'ISO-8859-15';
  2249. }
  2250. /**
  2251. * Returns a table with non-UTF-8 encodings for all system languages.
  2252. * @return array Returns an array in the form array('language1' => array('encoding1', encoding2', ...), ...)
  2253. * Note: The function api_get_non_utf8_encoding() returns the first encoding from this array that is correspondent to the given language.
  2254. */
  2255. function & api_non_utf8_encodings() {
  2256. // The following list may have some inconsistencies.
  2257. // Place the most used for your language encoding at the first place.
  2258. // If you are adding an encoding, check whether it is supported either by
  2259. // mbstring library, either by iconv library.
  2260. // If you modify this list, please, follow the given syntax exactly.
  2261. // The language names must be stripped of any suffixes, such as _unicode, _corporate, _org, etc.
  2262. static $encodings =
  2263. '
  2264. arabic: WINDOWS-1256, ISO-8859-6;
  2265. asturian: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2266. bosnian: WINDOWS-1250;
  2267. brazilian: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2268. bulgarian: WINDOWS-1251;
  2269. catalan: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2270. croatian: WINDOWS-1250;
  2271. czech: WINDOWS-1250, ISO-8859-2;
  2272. danish: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2273. dari: WINDOWS-1256;
  2274. dutch: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2275. english: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2276. euskera: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2277. esperanto: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2278. finnish: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2279. french: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2280. friulian: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2281. galician: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2282. georgian: GEORGIAN-ACADEMY, GEORGIAN-PS;
  2283. german: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2284. greek: WINDOWS-1253, ISO-8859-7;
  2285. hebrew: ISO-8859-8, WINDOWS-1255;
  2286. hungarian: WINDOWS-1250, ISO-8859-2;
  2287. indonesian: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2288. italian: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2289. japanese: EUC-JP, ISO-2022-JP, Shift-JIS;
  2290. korean: EUC-KR, ISO-2022-KR, CP949;
  2291. latvian: WINDOWS-1257, ISO-8859-13;
  2292. lithuanian: WINDOWS-1257, ISO-8859-13;
  2293. macedonian: WINDOWS-1251;
  2294. malay: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2295. norwegian: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2296. occitan: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2297. pashto: WINDOWS-1256;
  2298. persian: WINDOWS-1256;
  2299. polish: WINDOWS-1250, ISO-8859-2;
  2300. portuguese: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2301. quechua_cusco: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2302. romanian: WINDOWS-1250, ISO-8859-2;
  2303. russian: KOI8-R, WINDOWS-1251;
  2304. serbian: ISO-8859-15, WINDOWS-1252, ISO-8859-1, WINDOWS-1251;
  2305. simpl_chinese: GB2312, WINDOWS-936;
  2306. slovak: WINDOWS-1250, ISO-8859-2;
  2307. slovenian: WINDOWS-1250, ISO-8859-2;
  2308. spanish: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2309. swahili: ISO-8859-1;
  2310. swedish: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2311. thai: WINDOWS-874, ISO-8859-11;
  2312. trad_chinese: BIG-5, EUC-TW;
  2313. turkce: WINDOWS-1254, ISO-8859-9;
  2314. ukrainian: KOI8-U;
  2315. vietnamese: WINDOWS-1258, VISCII, TCVN;
  2316. yoruba: ISO-8859-15, WINDOWS-1252, ISO-8859-1;
  2317. ';
  2318. if (!is_array($encodings)) {
  2319. $table = explode(';', str_replace(' ', '', $encodings));
  2320. $encodings = array();
  2321. foreach ($table as & $row) {
  2322. $row = trim($row);
  2323. if (!empty($row)) {
  2324. $row = explode(':', $row);
  2325. $encodings[$row[0]] = explode(',', strtoupper($row[1]));
  2326. }
  2327. }
  2328. }
  2329. return $encodings;
  2330. }
  2331. /**
  2332. * This function unifies the encoding identificators, so they could be compared.
  2333. * @param string/array $encoding The specified encoding.
  2334. * @return string Returns the encoding identificator modified in suitable for comparison way.
  2335. */
  2336. function api_refine_encoding_id($encoding) {
  2337. if (is_array($encoding)){
  2338. return array_map('strtoupper', $encoding);
  2339. }
  2340. return strtoupper($encoding);
  2341. }
  2342. /**
  2343. * This function checks whether two $encoding are equal (same, equvalent).
  2344. * @param string/array $encoding1 The first encoding
  2345. * @param string/array $encoding2 The second encoding
  2346. * @return bool Returns TRUE if the encodings are equal, FALSE otherwise.
  2347. */
  2348. function api_equal_encodings($encoding1, $encoding2) {
  2349. $is_array_encoding2 = is_array($encoding2);
  2350. $encoding1 = api_refine_encoding_id($encoding1);
  2351. $encoding2 = api_refine_encoding_id($encoding2);
  2352. if (!is_array($encoding1) && !$is_array_encoding2) {
  2353. return $encoding1 == $encoding2;
  2354. }
  2355. if ($is_array_encoding2) {
  2356. return in_array($encoding1, $encoding2);
  2357. }
  2358. return in_array($encoding2, $encoding1);
  2359. }
  2360. /**
  2361. * This function checks whether a given encoding is UTF-8.
  2362. * @param string $encoding The tested encoding.
  2363. * @return bool Returns TRUE if the given encoding id means UTF-8, otherwise returns false.
  2364. */
  2365. function api_is_utf8($encoding) {
  2366. static $result = array();
  2367. if (!isset($result[$encoding])) {
  2368. $result[$encoding] = api_equal_encodings($encoding, array('UTF-8', 'CP65001', 'WINDOWS-65001'));
  2369. }
  2370. return $result[$encoding];
  2371. }
  2372. /**
  2373. * This function checks whether a given encoding represents (is an alias of) ISO Latin 1 character set.
  2374. * @param string/array $encoding The tested encoding.
  2375. * @return bool Returns TRUE if the given encoding id means Latin 1 character set, otherwise returns false.
  2376. */
  2377. function api_is_latin1($encoding, $strict = false) {
  2378. static $latin1 = array();
  2379. static $latin1_strict = array();
  2380. if ($strict) {
  2381. if (!isset($latin1_strict[$encoding])) {
  2382. $latin1_strict[$encoding] = api_equal_encodings($encoding, array('ISO-8859-1', 'ISO8859-1', 'CP819', 'LATIN1'));
  2383. }
  2384. return $latin1_strict[$encoding];
  2385. }
  2386. if (!isset($latin1[$encoding])) {
  2387. $latin1[$encoding] = api_equal_encodings($encoding, array(
  2388. 'ISO-8859-1', 'ISO8859-1', 'CP819', 'LATIN1',
  2389. 'ISO-8859-15', 'ISO8859-15', 'CP923', 'LATIN0', 'LATIN-9',
  2390. 'WINDOWS-1252', 'CP1252', 'WIN-1252', 'WIN1252'
  2391. ));
  2392. }
  2393. return $latin1[$encoding];
  2394. }
  2395. /**
  2396. * This function returns the encoding, currently used by the system.
  2397. * @return string The system's encoding.
  2398. * Note: The value of api_get_setting('platform_charset') is tried to be returned first,
  2399. * on the second place the global variable $charset is tried to be returned. If for some
  2400. * reason both attempts fail, 'ISO-8859-15' will be returned.
  2401. */
  2402. function api_get_system_encoding() {
  2403. $system_encoding = api_get_setting('platform_charset');
  2404. if (!empty($system_encoding)) {
  2405. return $system_encoding;
  2406. }
  2407. global $charset;
  2408. return empty($charset) ? 'ISO-8859-15' : $charset;
  2409. }
  2410. /**
  2411. * This function returns the encoding, currently used by the file system.
  2412. * @return string The file system's encoding, it depends on the locale that OS currently uses.
  2413. * @link http://php.net/manual/en/function.setlocale.php
  2414. * Note: For Linux systems, to see all installed locales type in a terminal locale -a
  2415. */
  2416. function api_get_file_system_encoding() {
  2417. static $file_system_encoding;
  2418. if (!isset($file_system_encoding)) {
  2419. $locale = setlocale(LC_CTYPE, '0');
  2420. $seek_pos = strpos($locale, '.');
  2421. if ($seek_pos !== false) {
  2422. $file_system_encoding = substr($locale, $seek_pos + 1);
  2423. if (IS_WINDOWS_OS) {
  2424. $file_system_encoding = 'CP'.$file_system_encoding;
  2425. }
  2426. }
  2427. // Dealing with some aliases.
  2428. $file_system_encoding = str_ireplace('utf8', 'UTF-8', $file_system_encoding);
  2429. $file_system_encoding = preg_replace('/^CP65001$/', 'UTF-8', $file_system_encoding);
  2430. $file_system_encoding = preg_replace('/^CP(125[0-9])$/', 'WINDOWS-\1', $file_system_encoding);
  2431. $file_system_encoding = str_replace('WINDOWS-1252', 'ISO-8859-15', $file_system_encoding);
  2432. if (empty($file_system_encoding)) {
  2433. if (IS_WINDOWS_OS) {
  2434. // Not expected for Windows, this assignment is here just in case.
  2435. $file_system_encoding = api_get_system_encoding();
  2436. } else {
  2437. // For Ububntu and other UTF-8 enabled Linux systems this fits with the default settings.
  2438. $file_system_encoding = 'UTF-8';
  2439. }
  2440. }
  2441. }
  2442. return $file_system_encoding;
  2443. }
  2444. /**
  2445. * Sets all internal default encodings of the multi-byte functions to the given value.
  2446. * @param string $encoding The specified default encoding.
  2447. * @return void
  2448. * Note: This function should be called once the initialization. Please, avoid further manipulation of the internal default encodings.
  2449. */
  2450. function api_set_default_encoding($encoding) {
  2451. if (MBSTRING_INSTALLED) {
  2452. // Additional mbstring-related initialization operations.
  2453. @ini_set('mbstring.func_overload', 0);
  2454. @ini_set('mbstring.encoding_translation', 0);
  2455. @ini_set('mbstring.http_input', 'pass');
  2456. @ini_set('mbstring.http_output', 'pass');
  2457. @ini_set('mbstring.language', 'neutral');
  2458. }
  2459. api_mb_internal_encoding($encoding);
  2460. api_mb_regex_encoding($encoding);
  2461. api_iconv_set_encoding('iconv_internal_encoding', $encoding);
  2462. }
  2463. /**
  2464. * Sets/Gets internal character encoding of the common string functions within the PHP mbstring extension.
  2465. * @param string $encoding (optional) When this parameter is given, the function sets the internal encoding.
  2466. * @return string When $encoding parameter is not given, the function returns the internal encoding.
  2467. * Note: This function is used in the global initialization script for setting the internal encoding to the platform's character set.
  2468. * @link http://php.net/manual/en/function.mb-internal-encoding
  2469. */
  2470. function api_mb_internal_encoding($encoding = null) {
  2471. static $mb_internal_encoding = null;
  2472. if (empty($encoding)) {
  2473. if (is_null($mb_internal_encoding)) {
  2474. if (MBSTRING_INSTALLED) {
  2475. $mb_internal_encoding = @mb_internal_encoding();
  2476. } else {
  2477. $mb_internal_encoding = 'ISO-8859-15';
  2478. }
  2479. }
  2480. return $mb_internal_encoding;
  2481. }
  2482. $mb_internal_encoding = $encoding;
  2483. if (api_mb_supports($encoding)) {
  2484. return @mb_internal_encoding($encoding);
  2485. }
  2486. return false;
  2487. }
  2488. /**
  2489. * Sets/Gets internal character encoding of the regular expression functions (ereg-like) within the PHP mbstring extension.
  2490. * @param string $encoding (optional) When this parameter is given, the function sets the internal encoding.
  2491. * @return string When $encoding parameter is not given, the function returns the internal encoding.
  2492. * Note: This function is used in the global initialization script for setting the internal encoding to the platform's character set.
  2493. * @link http://php.net/manual/en/function.mb-regex-encoding
  2494. */
  2495. function api_mb_regex_encoding($encoding = null) {
  2496. static $mb_regex_encoding = null;
  2497. if (empty($encoding)) {
  2498. if (is_null($mb_regex_encoding)) {
  2499. if (MBSTRING_INSTALLED) {
  2500. $mb_regex_encoding = @mb_regex_encoding();
  2501. } else {
  2502. $mb_regex_encoding = 'ISO-8859-15';
  2503. }
  2504. }
  2505. return $mb_regex_encoding;
  2506. }
  2507. $mb_regex_encoding = $encoding;
  2508. if (api_mb_supports($encoding)) {
  2509. return @mb_regex_encoding($encoding);
  2510. }
  2511. return false;
  2512. }
  2513. /**
  2514. * Retrieves specified internal encoding configuration variable within the PHP iconv extension.
  2515. * @param string $type The parameter $type could be: 'iconv_internal_encoding', 'iconv_input_encoding', or 'iconv_output_encoding'.
  2516. * @return mixed The function returns the requested encoding or FALSE on error.
  2517. * @link http://php.net/manual/en/function.iconv-get-encoding
  2518. */
  2519. function api_iconv_get_encoding($type) {
  2520. return api_iconv_set_encoding($type);
  2521. }
  2522. /**
  2523. * Sets specified internal encoding configuration variables within the PHP iconv extension.
  2524. * @param string $type The parameter $type could be: 'iconv_internal_encoding', 'iconv_input_encoding', or 'iconv_output_encoding'.
  2525. * @param string $encoding (optional) The desired encoding to be set.
  2526. * @return bool Returns TRUE on success, FALSE on error.
  2527. * Note: This function is used in the global initialization script for setting these three internal encodings to the platform's character set.
  2528. * @link http://php.net/manual/en/function.iconv-set-encoding
  2529. */
  2530. // Sets current setting for character encoding conversion.
  2531. // The parameter $type could be: 'iconv_internal_encoding', 'iconv_input_encoding', or 'iconv_output_encoding'.
  2532. function api_iconv_set_encoding($type, $encoding = null) {
  2533. static $iconv_internal_encoding = null;
  2534. static $iconv_input_encoding = null;
  2535. static $iconv_output_encoding = null;
  2536. if (!ICONV_INSTALLED) {
  2537. return false;
  2538. }
  2539. switch ($type) {
  2540. case 'iconv_internal_encoding':
  2541. if (empty($encoding)) {
  2542. if (is_null($iconv_internal_encoding)) {
  2543. $iconv_internal_encoding = @iconv_get_encoding($type);
  2544. }
  2545. return $iconv_internal_encoding;
  2546. }
  2547. if (api_iconv_supports($encoding)) {
  2548. if(@iconv_set_encoding($type, $encoding)) {
  2549. $iconv_internal_encoding = $encoding;
  2550. return true;
  2551. }
  2552. return false;
  2553. }
  2554. return false;
  2555. case 'iconv_input_encoding':
  2556. if (empty($encoding)) {
  2557. if (is_null($iconv_input_encoding)) {
  2558. $iconv_input_encoding = @iconv_get_encoding($type);
  2559. }
  2560. return $iconv_input_encoding;
  2561. }
  2562. if (api_iconv_supports($encoding)) {
  2563. if(@iconv_set_encoding($type, $encoding)) {
  2564. $iconv_input_encoding = $encoding;
  2565. return true;
  2566. }
  2567. return false;
  2568. }
  2569. return false;
  2570. case 'iconv_output_encoding':
  2571. if (empty($encoding)) {
  2572. if (is_null($iconv_output_encoding)) {
  2573. $iconv_output_encoding = @iconv_get_encoding($type);
  2574. }
  2575. return $iconv_output_encoding;
  2576. }
  2577. if (api_iconv_supports($encoding)) {
  2578. if(@iconv_set_encoding($type, $encoding)) {
  2579. $iconv_output_encoding = $encoding;
  2580. return true;
  2581. }
  2582. return false;
  2583. }
  2584. return false;
  2585. }
  2586. return false;
  2587. }
  2588. /**
  2589. * Checks whether a specified encoding is supported by this API.
  2590. * @param string $encoding The specified encoding.
  2591. * @return bool Returns TRUE when the specified encoding is supported, FALSE othewise.
  2592. */
  2593. function api_is_encoding_supported($encoding) {
  2594. static $supported = array();
  2595. if (!isset($supported[$encoding])) {
  2596. $supported[$encoding] = api_mb_supports($encoding) || api_iconv_supports($encoding) || _api_convert_encoding_supports($encoding);
  2597. }
  2598. return $supported[$encoding];
  2599. }
  2600. /**
  2601. * Checks whether the specified encoding is supported by the PHP mbstring extension.
  2602. * @param string $encoding The specified encoding.
  2603. * @return bool Returns TRUE when the specified encoding is supported, FALSE othewise.
  2604. */
  2605. function api_mb_supports($encoding) {
  2606. static $supported = array();
  2607. $encoding = api_refine_encoding_id($encoding);
  2608. if (!isset($supported[$encoding])) {
  2609. if (MBSTRING_INSTALLED) {
  2610. $mb_encodings = mb_list_encodings();
  2611. $mb_encodings = array_map('api_refine_encoding_id', $mb_encodings);
  2612. } else {
  2613. $mb_encodings = array();
  2614. }
  2615. $supported[$encoding] = in_array($encoding, $mb_encodings);
  2616. }
  2617. return $supported[$encoding];
  2618. }
  2619. /**
  2620. * Checks whether the specified encoding is supported by the PHP iconv extension.
  2621. * @param string $encoding The specified encoding.
  2622. * @return bool Returns TRUE when the specified encoding is supported, FALSE othewise.
  2623. */
  2624. function api_iconv_supports($encoding) {
  2625. static $supported = array();
  2626. $encoding = api_refine_encoding_id($encoding);
  2627. if (!isset($supported[$encoding])) {
  2628. if (ICONV_INSTALLED) {
  2629. $test_string = '';
  2630. for ($i = 32; $i < 128; $i++) {
  2631. $test_string .= chr($i);
  2632. }
  2633. $supported[$encoding] = (@iconv_strlen($test_string, $encoding)) ? true : false;
  2634. } else {
  2635. $supported[$encoding] = false;
  2636. }
  2637. }
  2638. return $supported[$encoding];
  2639. }
  2640. /**
  2641. * Checks whether the specified encoding is supported by the html-entitiy related functions.
  2642. * @param string $encoding The specified encoding.
  2643. * @return bool Returns TRUE when the specified encoding is supported, FALSE othewise.
  2644. */
  2645. function api_html_entity_supports($encoding) {
  2646. static $supported = array();
  2647. $encoding = api_refine_encoding_id($encoding);
  2648. if (!isset($supported[$encoding])) {
  2649. // See http://php.net/manual/en/function.htmlentities.php
  2650. $html_entity_encodings = array(explode(',',
  2651. '
  2652. ISO-8859-1, ISO8859-1,
  2653. ISO-8859-15, ISO8859-15,
  2654. UTF-8,
  2655. cp866, ibm866, 866,
  2656. cp1251, Windows-1251, win-1251, 1251,
  2657. cp1252, Windows-1252, 1252,
  2658. KOI8-R, koi8-ru, koi8r,
  2659. BIG5, 950,
  2660. GB2312, 936,
  2661. BIG5-HKSCS,
  2662. Shift_JIS, SJIS, 932,
  2663. EUC-JP, EUCJP
  2664. '));
  2665. $html_entity_encodings = array_map('trim', $html_entity_encodings);
  2666. $html_entity_encodings = array_map('api_refine_encoding_id', $html_entity_encodings);
  2667. $supported[$encoding] = in_array($encoding, $html_entity_encodings);
  2668. }
  2669. return $supported[$encoding] ? true : false;
  2670. }
  2671. /**
  2672. * ----------------------------------------------------------------------------
  2673. * String validation functions concerning some encodings
  2674. * ----------------------------------------------------------------------------
  2675. */
  2676. /**
  2677. * Checks a string for UTF-8 validity.
  2678. * @param string $string The string to be tested/validated.
  2679. * @return bool Returns TRUE when the tested string is valid UTF-8 one, FALSE othewise.
  2680. * @link http://en.wikipedia.org/wiki/UTF-8
  2681. */
  2682. function api_is_valid_utf8($string) {
  2683. //return @mb_detect_encoding($string, 'UTF-8', true) == 'UTF-8' ? true : false;
  2684. // Ivan Tcholakov, 05-OCT-2008: I do not trust mb_detect_encoding(). I have
  2685. // found a string with a single cyrillic letter (single byte), that is
  2686. // wrongly detected as UTF-8. Possibly, there would be problems with other
  2687. // languages too. An alternative implementation will be used.
  2688. $len = api_byte_count($string);
  2689. $i = 0;
  2690. while ($i < $len) {
  2691. $byte1 = ord($string[$i++]); // Here the current character begins. Its size is
  2692. // determined by the senior bits in the first byte.
  2693. if (($byte1 & 0x80) == 0x00) { // 0xxxxxxx
  2694. // &
  2695. // 10000000
  2696. // --------
  2697. // 00000000
  2698. // This is s valid character and it contains a single byte.
  2699. }
  2700. elseif (($byte1 & 0xE0) == 0xC0) { // 110xxxxx 10xxxxxx
  2701. // & &
  2702. // 11100000 11000000
  2703. // -------- --------
  2704. // 11000000 10000000
  2705. // The character contains two bytes.
  2706. if ($i == $len) {
  2707. return false; // Here the string ends unexpectedly.
  2708. }
  2709. if (!((ord($string[$i++]) & 0xC0) == 0x80))
  2710. return false; // Invalid second byte, invalid string.
  2711. }
  2712. elseif(($byte1 & 0xF0) == 0xE0) { // 1110xxxx 10xxxxxx 10xxxxxx
  2713. // & & &
  2714. // 11110000 11000000 11000000
  2715. // -------- -------- --------
  2716. // 11100000 10000000 10000000
  2717. // This is a character of three bytes.
  2718. if ($i == $len) {
  2719. return false; // Unexpected end of the string.
  2720. }
  2721. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2722. return false; // Invalid second byte.
  2723. }
  2724. if ($i == $len) {
  2725. return false; // Unexpected end of the string.
  2726. }
  2727. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2728. return false; // Invalid third byte, invalid string.
  2729. }
  2730. }
  2731. elseif(($byte1 & 0xF8) == 0xF0) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  2732. // & & & &
  2733. // 11111000 11000000 11000000 11000000
  2734. // -------- -------- -------- --------
  2735. // 11110000 10000000 10000000 10000000
  2736. // This is a character of four bytes.
  2737. if ($i == $len) {
  2738. return false;
  2739. }
  2740. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2741. return false;
  2742. }
  2743. if ($i == $len) {
  2744. return false;
  2745. }
  2746. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2747. return false;
  2748. }
  2749. if ($i == $len) {
  2750. return false;
  2751. }
  2752. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2753. return false;
  2754. }
  2755. }
  2756. elseif(($byte1 & 0xFC) == 0xF8) { // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  2757. // & & & & &
  2758. // 11111100 11000000 11000000 11000000 11000000
  2759. // -------- -------- -------- -------- --------
  2760. // 11111000 10000000 10000000 10000000 10000000
  2761. // This is a character of five bytes.
  2762. if ($i == $len) {
  2763. return false;
  2764. }
  2765. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2766. return false;
  2767. }
  2768. if ($i == $len) {
  2769. return false;
  2770. }
  2771. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2772. return false;
  2773. }
  2774. if ($i == $len) {
  2775. return false;
  2776. }
  2777. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2778. return false;
  2779. }
  2780. if ($i == $len) {
  2781. return false;
  2782. }
  2783. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2784. return false;
  2785. }
  2786. }
  2787. elseif(($byte1 & 0xFE) == 0xFC) { // 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  2788. // & & & & & &
  2789. // 11111110 11000000 11000000 11000000 11000000 11000000
  2790. // -------- -------- -------- -------- -------- --------
  2791. // 11111100 10000000 10000000 10000000 10000000 10000000
  2792. // This is a character of six bytes.
  2793. if ($i == $len) {
  2794. return false;
  2795. }
  2796. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2797. return false;
  2798. }
  2799. if ($i == $len) {
  2800. return false;
  2801. }
  2802. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2803. return false;
  2804. }
  2805. if ($i == $len) {
  2806. return false;
  2807. }
  2808. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2809. return false;
  2810. }
  2811. if ($i == $len) {
  2812. return false;
  2813. }
  2814. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2815. return false;
  2816. }
  2817. if ($i == $len) {
  2818. return false;
  2819. }
  2820. if (!((ord($string[$i++]) & 0xC0) == 0x80)) {
  2821. return false;
  2822. }
  2823. }
  2824. else {
  2825. return false; // In any other case the character is invalid.
  2826. }
  2827. // Here the current character is valid, it
  2828. // matches to some of the cases above.
  2829. // The next character is to be examinated.
  2830. }
  2831. return true; // Empty strings are valid too.
  2832. }
  2833. /**
  2834. * Checks whether a string contains 7-bit ASCII characters only.
  2835. * @param string $string The string to be tested/validated.
  2836. * @return bool Returns TRUE when the tested string contains 7-bit ASCII characters only, FALSE othewise.
  2837. */
  2838. function api_is_valid_ascii($string) {
  2839. if (MBSTRING_INSTALLED) {
  2840. return @mb_detect_encoding($string, 'ASCII', true) == 'ASCII' ? true : false;
  2841. }
  2842. return ! preg_match('/[^\x00-\x7F]/S', $string);
  2843. }
  2844. /**
  2845. * ----------------------------------------------------------------------------
  2846. * Language management functions
  2847. * ----------------------------------------------------------------------------
  2848. */
  2849. /**
  2850. * Returns a purified language id, without possible suffixes that will disturb language identification in certain cases.
  2851. * @param string $language The input language identificator, for example 'french_unicode'.
  2852. * @param string The same purified or filtered language identificator, for example 'french'.
  2853. */
  2854. function api_refine_language_id($language) {
  2855. static $search = array('_unicode', '_latin', '_corporate', '_org', '_KM');
  2856. return strtolower(str_replace($search, '', $language));
  2857. }
  2858. /**
  2859. * This function check whether a given language can use Latin 1 encoding.
  2860. * @param string $language The checked language.
  2861. * @return bool TRUE if the given language can use Latin 1 encoding (ISO-8859-15, ISO-8859-1, WINDOWS-1252, ...), FALSE otherwise.
  2862. */
  2863. function api_is_latin1_compatible($language) {
  2864. static $latin1_languages;
  2865. if (!isset($latin1_languages)) {
  2866. $latin1_languages = api_get_latin1_compatible_languages();
  2867. }
  2868. $language = api_refine_language_id($language);
  2869. return in_array($language, $latin1_languages);
  2870. }
  2871. /**
  2872. * This function returns an array of those languages that can use Latin 1 encoding.
  2873. * @return array The array of languages that can use Latin 1 encoding (ISO-8859-15, ISO-8859-1, WINDOWS-1252, ...).
  2874. * Note: The returned language identificators are purified, without suffixes.
  2875. */
  2876. function api_get_latin1_compatible_languages() {
  2877. static $latin1_languages;
  2878. if (!isset($latin1_languages)) {
  2879. $latin1_languages = array();
  2880. $encodings = & api_non_utf8_encodings();
  2881. foreach ($encodings as $key => $value) {
  2882. if (api_is_latin1($value[0])) {
  2883. $latin1_languages[] = $key;
  2884. }
  2885. }
  2886. }
  2887. return $latin1_languages;
  2888. }
  2889. /**
  2890. * ----------------------------------------------------------------------------
  2891. * ICU locales (accessible through intl extension).
  2892. * ----------------------------------------------------------------------------
  2893. */
  2894. /**
  2895. * Returns isocode (see api_get_language_isocode()) which is purified accordingly to
  2896. * be used by the php intl extension (ICU library).
  2897. * @param string $language (optional) This is the name of the folder containing translations for the corresponding language.
  2898. * If $language is omitted, interface language is assumed then.
  2899. * @return string The found language locale id or null on error. Examples: bg, en, pt_BR, ...
  2900. */
  2901. function api_get_locale_from_language($language = null) {
  2902. static $locale = array();
  2903. if (!isset($locale[$language])) {
  2904. $locale[$language] = Database::get_language_isocode($language);
  2905. if (!is_null($locale[$language])) {
  2906. $locale[$language] = str_replace('-', '_', $locale[$language]);
  2907. }
  2908. }
  2909. return $locale[$language];
  2910. }
  2911. /**
  2912. * Sets/gets the default internal value of the locale id (for the intl extension, ICU).
  2913. * @param string $locale (optional) The locale id to be set. When it is omitted, the function returns (gets, reads) the default internal value.
  2914. * @return mixed When the function sets the default value, it returns TRUE on success or FALSE on error. Otherwise the function returns as string the current default value.
  2915. */
  2916. function api_set_default_locale($locale = null) {
  2917. static $default_locale = 'en';
  2918. if (!empty($locale)) {
  2919. $default_locale = $locale;
  2920. if (INTL_INSTALLED) {
  2921. return @locale_set_default($locale);
  2922. }
  2923. return true;
  2924. } else {
  2925. if (INTL_INSTALLED) {
  2926. $default_locale = @locale_get_default();
  2927. }
  2928. }
  2929. return $default_locale;
  2930. }
  2931. /**
  2932. * Gets the default internal value of the locale id (for the intl extension, ICU).
  2933. * @return string Returns as string the current default value.
  2934. */
  2935. function api_get_default_locale() {
  2936. return api_set_default_locale();
  2937. }
  2938. /**
  2939. * ----------------------------------------------------------------------------
  2940. * Functions for internal use behind this API.
  2941. * ----------------------------------------------------------------------------
  2942. */
  2943. require_once dirname(__FILE__).'/multibyte_string_functions_internal.lib.php';