Str.php 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\Utils;
  12. use Hyperf\Macroable\Macroable;
  13. use Hyperf\Utils\Exception\InvalidArgumentException;
  14. /**
  15. * Most of the methods in this file come from illuminate/support,
  16. * thanks Laravel Team provide such a useful class.
  17. */
  18. class Str
  19. {
  20. use Macroable;
  21. /**
  22. * The cache of snake-cased words.
  23. *
  24. * @var array
  25. */
  26. protected static $snakeCache = [];
  27. /**
  28. * The cache of camel-cased words.
  29. *
  30. * @var array
  31. */
  32. protected static $camelCache = [];
  33. /**
  34. * The cache of studly-cased words.
  35. *
  36. * @var array
  37. */
  38. protected static $studlyCache = [];
  39. /**
  40. * Get a new stringable object from the given string.
  41. *
  42. * @param string $string
  43. * @return Stringable
  44. */
  45. public static function of($string)
  46. {
  47. return new Stringable($string);
  48. }
  49. /**
  50. * Return the remainder of a string after a given value.
  51. *
  52. * @param string $subject
  53. * @param string $search
  54. * @return string
  55. */
  56. public static function after($subject, $search)
  57. {
  58. return $search === '' ? $subject : array_reverse(explode($search, $subject, 2))[0];
  59. }
  60. /**
  61. * Return the remainder of a string after the last occurrence of a given value.
  62. *
  63. * @param string $subject
  64. * @param string $search
  65. * @return string
  66. */
  67. public static function afterLast($subject, $search)
  68. {
  69. if ($search === '') {
  70. return $subject;
  71. }
  72. $position = strrpos($subject, (string) $search);
  73. if ($position === false) {
  74. return $subject;
  75. }
  76. return substr($subject, $position + strlen($search));
  77. }
  78. /**
  79. * Transliterate a UTF-8 value to ASCII.
  80. *
  81. * @param string $value
  82. * @param string $language
  83. * @return string
  84. */
  85. public static function ascii($value, $language = 'en')
  86. {
  87. $languageSpecific = static::languageSpecificCharsArray($language);
  88. if (! is_null($languageSpecific)) {
  89. $value = str_replace($languageSpecific[0], $languageSpecific[1], $value);
  90. }
  91. foreach (static::charsArray() as $key => $val) {
  92. $value = str_replace($val, (string) $key, $value);
  93. }
  94. return preg_replace('/[^\x20-\x7E]/u', '', $value);
  95. }
  96. /**
  97. * Get the portion of a string before a given value.
  98. *
  99. * @param string $subject
  100. * @param string $search
  101. * @return string
  102. */
  103. public static function before($subject, $search)
  104. {
  105. return $search === '' ? $subject : explode($search, $subject)[0];
  106. }
  107. /**
  108. * Get the portion of a string before the last occurrence of a given value.
  109. *
  110. * @param string $subject
  111. * @param string $search
  112. * @return string
  113. */
  114. public static function beforeLast($subject, $search)
  115. {
  116. if ($search === '') {
  117. return $subject;
  118. }
  119. $pos = mb_strrpos($subject, $search);
  120. if ($pos === false) {
  121. return $subject;
  122. }
  123. return static::substr($subject, 0, $pos);
  124. }
  125. /**
  126. * Get the portion of a string between two given values.
  127. *
  128. * @param string $subject
  129. * @param string $from
  130. * @param string $to
  131. * @return string
  132. */
  133. public static function between($subject, $from, $to)
  134. {
  135. if ($from === '' || $to === '') {
  136. return $subject;
  137. }
  138. return static::beforeLast(static::after($subject, $from), $to);
  139. }
  140. /**
  141. * Convert a value to camel case.
  142. *
  143. * @param string $value
  144. * @return string
  145. */
  146. public static function camel($value)
  147. {
  148. if (isset(static::$camelCache[$value])) {
  149. return static::$camelCache[$value];
  150. }
  151. return static::$camelCache[$value] = lcfirst(static::studly($value));
  152. }
  153. /**
  154. * Determine if a given string contains a given substring.
  155. *
  156. * @param string $haystack
  157. * @param array|string $needles
  158. * @return bool
  159. */
  160. public static function contains($haystack, $needles)
  161. {
  162. foreach ((array) $needles as $needle) {
  163. if ($needle !== '' && mb_strpos($haystack, $needle) !== false) {
  164. return true;
  165. }
  166. }
  167. return false;
  168. }
  169. /**
  170. * Determine if a given string contains all array values.
  171. *
  172. * @param string $haystack
  173. * @param string[] $needles
  174. * @return bool
  175. */
  176. public static function containsAll($haystack, array $needles)
  177. {
  178. foreach ($needles as $needle) {
  179. if (! static::contains($haystack, $needle)) {
  180. return false;
  181. }
  182. }
  183. return true;
  184. }
  185. /**
  186. * Determine if a given string ends with a given substring.
  187. *
  188. * @param string $haystack
  189. * @param array|string $needles
  190. * @return bool
  191. */
  192. public static function endsWith($haystack, $needles)
  193. {
  194. foreach ((array) $needles as $needle) {
  195. if (substr($haystack, -strlen($needle)) === (string) $needle) {
  196. return true;
  197. }
  198. }
  199. return false;
  200. }
  201. /**
  202. * Cap a string with a single instance of a given value.
  203. *
  204. * @param string $value
  205. * @param string $cap
  206. * @return string
  207. */
  208. public static function finish($value, $cap)
  209. {
  210. $quoted = preg_quote($cap, '/');
  211. return preg_replace('/(?:' . $quoted . ')+$/u', '', $value) . $cap;
  212. }
  213. /**
  214. * Determine if a given string matches a given pattern.
  215. *
  216. * @param array|string $pattern
  217. * @param string $value
  218. * @return bool
  219. */
  220. public static function is($pattern, $value)
  221. {
  222. $patterns = Arr::wrap($pattern);
  223. if (empty($patterns)) {
  224. return false;
  225. }
  226. foreach ($patterns as $pattern) {
  227. // If the given value is an exact match we can of course return true right
  228. // from the beginning. Otherwise, we will translate asterisks and do an
  229. // actual pattern match against the two strings to see if they match.
  230. if ($pattern == $value) {
  231. return true;
  232. }
  233. $pattern = preg_quote($pattern, '#');
  234. // Asterisks are translated into zero-or-more regular expression wildcards
  235. // to make it convenient to check if the strings starts with the given
  236. // pattern such as "library/*", making any string check convenient.
  237. $pattern = str_replace('\*', '.*', $pattern);
  238. if (preg_match('#^' . $pattern . '\z#u', $value) === 1) {
  239. return true;
  240. }
  241. }
  242. return false;
  243. }
  244. /**
  245. * Convert a string to kebab case.
  246. *
  247. * @param string $value
  248. * @return string
  249. */
  250. public static function kebab($value)
  251. {
  252. return static::snake($value, '-');
  253. }
  254. /**
  255. * Return the length of the given string.
  256. *
  257. * @param string $value
  258. * @param string $encoding
  259. * @return int
  260. */
  261. public static function length($value, $encoding = null)
  262. {
  263. if ($encoding) {
  264. return mb_strlen($value, $encoding);
  265. }
  266. return mb_strlen($value);
  267. }
  268. /**
  269. * Limit the number of characters in a string.
  270. *
  271. * @param string $value
  272. * @param int $limit
  273. * @param string $end
  274. * @return string
  275. */
  276. public static function limit($value, $limit = 100, $end = '...')
  277. {
  278. if (mb_strwidth($value, 'UTF-8') <= $limit) {
  279. return $value;
  280. }
  281. return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')) . $end;
  282. }
  283. /**
  284. * Convert the given string to lower-case.
  285. *
  286. * @param string $value
  287. * @return string
  288. */
  289. public static function lower($value)
  290. {
  291. return mb_strtolower($value, 'UTF-8');
  292. }
  293. /**
  294. * Limit the number of words in a string.
  295. */
  296. public static function words(string $value, int $words = 100, string $end = '...'): string
  297. {
  298. preg_match('/^\s*+(?:\S++\s*+){1,' . $words . '}/u', $value, $matches);
  299. if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) {
  300. return $value;
  301. }
  302. return rtrim($matches[0]) . $end;
  303. }
  304. /**
  305. * Get the string matching the given pattern.
  306. *
  307. * @param string $pattern
  308. * @param string $subject
  309. * @return string
  310. */
  311. public static function match($pattern, $subject)
  312. {
  313. preg_match($pattern, $subject, $matches);
  314. if (! $matches) {
  315. return '';
  316. }
  317. return $matches[1] ?? $matches[0];
  318. }
  319. /**
  320. * Get the string matching the given pattern.
  321. *
  322. * @param string $pattern
  323. * @param string $subject
  324. * @return Collection
  325. */
  326. public static function matchAll($pattern, $subject)
  327. {
  328. preg_match_all($pattern, $subject, $matches);
  329. if (empty($matches[0])) {
  330. return collect();
  331. }
  332. return collect($matches[1] ?? $matches[0]);
  333. }
  334. /**
  335. * Pad both sides of a string with another.
  336. *
  337. * @param string $value
  338. * @param int $length
  339. * @param string $pad
  340. * @return string
  341. */
  342. public static function padBoth($value, $length, $pad = ' ')
  343. {
  344. return str_pad($value, $length, $pad, STR_PAD_BOTH);
  345. }
  346. /**
  347. * Pad the left side of a string with another.
  348. *
  349. * @param string $value
  350. * @param int $length
  351. * @param string $pad
  352. * @return string
  353. */
  354. public static function padLeft($value, $length, $pad = ' ')
  355. {
  356. return str_pad($value, $length, $pad, STR_PAD_LEFT);
  357. }
  358. /**
  359. * Pad the right side of a string with another.
  360. *
  361. * @param string $value
  362. * @param int $length
  363. * @param string $pad
  364. * @return string
  365. */
  366. public static function padRight($value, $length, $pad = ' ')
  367. {
  368. return str_pad($value, $length, $pad, STR_PAD_RIGHT);
  369. }
  370. /**
  371. * Parse a Class@method style callback into class and method.
  372. *
  373. * @param null|string $default
  374. */
  375. public static function parseCallback(string $callback, $default = null): array
  376. {
  377. return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
  378. }
  379. /**
  380. * Pluralize the last word of an English, studly caps case string.
  381. */
  382. public static function pluralStudly(string $value, int $count = 2): string
  383. {
  384. $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
  385. $lastWord = array_pop($parts);
  386. return implode('', $parts) . self::plural($lastWord, $count);
  387. }
  388. /**
  389. * Get the plural form of an English word.
  390. */
  391. public static function plural(string $value, int $count = 2): string
  392. {
  393. return Pluralizer::plural($value, $count);
  394. }
  395. /**
  396. * Generate a more truly "random" alpha-numeric string.
  397. */
  398. public static function random(int $length = 16): string
  399. {
  400. $string = '';
  401. while (($len = strlen($string)) < $length) {
  402. $size = $length - $len;
  403. $bytes = random_bytes($size);
  404. $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
  405. }
  406. return $string;
  407. }
  408. /**
  409. * Repeat the given string.
  410. *
  411. * @return string
  412. */
  413. public static function repeat(string $string, int $times)
  414. {
  415. return str_repeat($string, $times);
  416. }
  417. /**
  418. * Replace a given value in the string sequentially with an array.
  419. */
  420. public static function replaceArray(string $search, array $replace, string $subject): string
  421. {
  422. foreach ($replace as $value) {
  423. $subject = static::replaceFirst($search, (string) $value, $subject);
  424. }
  425. return $subject;
  426. }
  427. /**
  428. * Replace the given value in the given string.
  429. *
  430. * @param string|string[] $search
  431. * @param string|string[] $replace
  432. * @param string|string[] $subject
  433. * @return string
  434. */
  435. public static function replace($search, $replace, $subject)
  436. {
  437. return str_replace($search, $replace, $subject);
  438. }
  439. /**
  440. * Replace the first occurrence of a given value in the string.
  441. */
  442. public static function replaceFirst(string $search, string $replace, string $subject): string
  443. {
  444. if ($search == '') {
  445. return $subject;
  446. }
  447. $position = strpos($subject, $search);
  448. if ($position !== false) {
  449. return substr_replace($subject, $replace, $position, strlen($search));
  450. }
  451. return $subject;
  452. }
  453. /**
  454. * Replace the last occurrence of a given value in the string.
  455. */
  456. public static function replaceLast(string $search, string $replace, string $subject): string
  457. {
  458. $position = strrpos($subject, $search);
  459. if ($position !== false) {
  460. return substr_replace($subject, $replace, $position, strlen($search));
  461. }
  462. return $subject;
  463. }
  464. /**
  465. * Remove any occurrence of the given string in the subject.
  466. *
  467. * @param array<string>|string $search
  468. * @param string $subject
  469. * @param bool $caseSensitive
  470. * @return string
  471. */
  472. public static function remove($search, $subject, $caseSensitive = true)
  473. {
  474. return $caseSensitive
  475. ? str_replace($search, '', $subject)
  476. : str_ireplace($search, '', $subject);
  477. }
  478. /**
  479. * Begin a string with a single instance of a given value.
  480. */
  481. public static function start(string $value, string $prefix): string
  482. {
  483. $quoted = preg_quote($prefix, '/');
  484. return $prefix . preg_replace('/^(?:' . $quoted . ')+/u', '', $value);
  485. }
  486. /**
  487. * Strip HTML and PHP tags from the given string.
  488. *
  489. * @param null|string|string[] $allowedTags
  490. */
  491. public static function stripTags(string $value, $allowedTags = null): string
  492. {
  493. return strip_tags($value, $allowedTags);
  494. }
  495. /**
  496. * Convert the given string to upper-case.
  497. */
  498. public static function upper(string $value): string
  499. {
  500. return mb_strtoupper($value, 'UTF-8');
  501. }
  502. /**
  503. * Convert the given string to title case.
  504. */
  505. public static function title(string $value): string
  506. {
  507. return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
  508. }
  509. /**
  510. * Get the singular form of an English word.
  511. */
  512. public static function singular(string $value): string
  513. {
  514. return Pluralizer::singular($value);
  515. }
  516. /**
  517. * Generate a URL friendly "slug" from a given string.
  518. */
  519. public static function slug(string $title, string $separator = '-', string $language = 'en'): string
  520. {
  521. $title = $language ? static::ascii($title, $language) : $title;
  522. // Convert all dashes/underscores into separator
  523. $flip = $separator === '-' ? '_' : '-';
  524. $title = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $title);
  525. // Replace @ with the word 'at'
  526. $title = str_replace('@', $separator . 'at' . $separator, $title);
  527. // Remove all characters that are not the separator, letters, numbers, or whitespace.
  528. $title = preg_replace('![^' . preg_quote($separator) . '\pL\pN\s]+!u', '', mb_strtolower($title));
  529. // Replace all separator characters and whitespace by a single separator
  530. $title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title);
  531. return trim($title, $separator);
  532. }
  533. /**
  534. * Convert a string to snake case.
  535. */
  536. public static function snake(string $value, string $delimiter = '_'): string
  537. {
  538. $key = $value;
  539. if (isset(static::$snakeCache[$key][$delimiter])) {
  540. return static::$snakeCache[$key][$delimiter];
  541. }
  542. if (! ctype_lower($value)) {
  543. $value = preg_replace('/\s+/u', '', ucwords($value));
  544. $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
  545. }
  546. return static::$snakeCache[$key][$delimiter] = $value;
  547. }
  548. /**
  549. * Determine if a given string starts with a given substring.
  550. *
  551. * @param array|string $needles
  552. */
  553. public static function startsWith(string $haystack, $needles): bool
  554. {
  555. foreach ((array) $needles as $needle) {
  556. if ($needle !== '' && substr($haystack, 0, strlen($needle)) === (string) $needle) {
  557. return true;
  558. }
  559. }
  560. return false;
  561. }
  562. /**
  563. * Convert a value to studly caps case.
  564. */
  565. public static function studly(string $value, string $gap = ''): string
  566. {
  567. $key = $value;
  568. if (isset(static::$studlyCache[$key])) {
  569. return static::$studlyCache[$key];
  570. }
  571. $value = ucwords(str_replace(['-', '_'], ' ', $value));
  572. return static::$studlyCache[$key] = str_replace(' ', $gap, $value);
  573. }
  574. /**
  575. * Returns the portion of string specified by the start and length parameters.
  576. *
  577. * @param string $string
  578. * @param int $start
  579. * @param null|int $length
  580. * @return string
  581. */
  582. public static function substr($string, $start, $length = null)
  583. {
  584. return mb_substr($string, $start, $length, 'UTF-8');
  585. }
  586. /**
  587. * Returns the number of substring occurrences.
  588. *
  589. * @param string $haystack
  590. * @param string $needle
  591. * @param int $offset
  592. * @param null|int $length
  593. * @return int
  594. */
  595. public static function substrCount($haystack, $needle, $offset = 0, $length = null)
  596. {
  597. if (! is_null($length)) {
  598. return substr_count($haystack, $needle, $offset, $length);
  599. }
  600. return substr_count($haystack, $needle, $offset);
  601. }
  602. /**
  603. * Make a string's first character uppercase.
  604. */
  605. public static function ucfirst(string $string): string
  606. {
  607. return static::upper(static::substr($string, 0, 1)) . static::substr($string, 1);
  608. }
  609. /**
  610. * Replaces the first or the last ones chars from a string by a given char.
  611. *
  612. * @param int $offset if is negative it starts from the end
  613. * @param string $replacement default is *
  614. */
  615. public static function mask(string $string, int $offset = 0, int $length = 0, string $replacement = '*')
  616. {
  617. if ($length < 0) {
  618. throw new InvalidArgumentException('The length must equal or greater than zero.');
  619. }
  620. $stringLength = mb_strlen($string);
  621. $absOffset = abs($offset);
  622. if ($absOffset >= $stringLength) {
  623. return $string;
  624. }
  625. $hiddenLength = $length ?: $stringLength - $absOffset;
  626. if ($offset >= 0) {
  627. return mb_substr($string, 0, $offset) . str_repeat($replacement, $hiddenLength) . mb_substr($string, $offset + $hiddenLength);
  628. }
  629. return mb_substr($string, 0, max($stringLength - $hiddenLength - $absOffset, 0)) . str_repeat($replacement, $hiddenLength) . mb_substr($string, $offset);
  630. }
  631. /**
  632. * Returns the replacements for the ascii method.
  633. * Note: Adapted from Stringy\Stringy.
  634. *
  635. * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
  636. */
  637. protected static function charsArray(): array
  638. {
  639. static $charsArray;
  640. if (isset($charsArray)) {
  641. return $charsArray;
  642. }
  643. return $charsArray = [
  644. '0' => ['°', '₀', '۰', '0'],
  645. '1' => ['¹', '₁', '۱', '1'],
  646. '2' => ['²', '₂', '۲', '2'],
  647. '3' => ['³', '₃', '۳', '3'],
  648. '4' => ['⁴', '₄', '۴', '٤', '4'],
  649. '5' => ['⁵', '₅', '۵', '٥', '5'],
  650. '6' => ['⁶', '₆', '۶', '٦', '6'],
  651. '7' => ['⁷', '₇', '۷', '7'],
  652. '8' => ['⁸', '₈', '۸', '8'],
  653. '9' => ['⁹', '₉', '۹', '9'],
  654. 'a' => [
  655. 'à',
  656. 'á',
  657. 'ả',
  658. 'ã',
  659. 'ạ',
  660. 'ă',
  661. 'ắ',
  662. 'ằ',
  663. 'ẳ',
  664. 'ẵ',
  665. 'ặ',
  666. 'â',
  667. 'ấ',
  668. 'ầ',
  669. 'ẩ',
  670. 'ẫ',
  671. 'ậ',
  672. 'ā',
  673. 'ą',
  674. 'å',
  675. 'α',
  676. 'ά',
  677. 'ἀ',
  678. 'ἁ',
  679. 'ἂ',
  680. 'ἃ',
  681. 'ἄ',
  682. 'ἅ',
  683. 'ἆ',
  684. 'ἇ',
  685. 'ᾀ',
  686. 'ᾁ',
  687. 'ᾂ',
  688. 'ᾃ',
  689. 'ᾄ',
  690. 'ᾅ',
  691. 'ᾆ',
  692. 'ᾇ',
  693. 'ὰ',
  694. 'ά',
  695. 'ᾰ',
  696. 'ᾱ',
  697. 'ᾲ',
  698. 'ᾳ',
  699. 'ᾴ',
  700. 'ᾶ',
  701. 'ᾷ',
  702. 'а',
  703. 'أ',
  704. 'အ',
  705. 'ာ',
  706. 'ါ',
  707. 'ǻ',
  708. 'ǎ',
  709. 'ª',
  710. 'ა',
  711. 'अ',
  712. 'ا',
  713. 'a',
  714. 'ä',
  715. ],
  716. 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'],
  717. 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'],
  718. 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'],
  719. 'e' => [
  720. 'é',
  721. 'è',
  722. 'ẻ',
  723. 'ẽ',
  724. 'ẹ',
  725. 'ê',
  726. 'ế',
  727. 'ề',
  728. 'ể',
  729. 'ễ',
  730. 'ệ',
  731. 'ë',
  732. 'ē',
  733. 'ę',
  734. 'ě',
  735. 'ĕ',
  736. 'ė',
  737. 'ε',
  738. 'έ',
  739. 'ἐ',
  740. 'ἑ',
  741. 'ἒ',
  742. 'ἓ',
  743. 'ἔ',
  744. 'ἕ',
  745. 'ὲ',
  746. 'έ',
  747. 'е',
  748. 'ё',
  749. 'э',
  750. 'є',
  751. 'ə',
  752. 'ဧ',
  753. 'ေ',
  754. 'ဲ',
  755. 'ე',
  756. 'ए',
  757. 'إ',
  758. 'ئ',
  759. 'e',
  760. ],
  761. 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'],
  762. 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'],
  763. 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'],
  764. 'i' => [
  765. 'í',
  766. 'ì',
  767. 'ỉ',
  768. 'ĩ',
  769. 'ị',
  770. 'î',
  771. 'ï',
  772. 'ī',
  773. 'ĭ',
  774. 'į',
  775. 'ı',
  776. 'ι',
  777. 'ί',
  778. 'ϊ',
  779. 'ΐ',
  780. 'ἰ',
  781. 'ἱ',
  782. 'ἲ',
  783. 'ἳ',
  784. 'ἴ',
  785. 'ἵ',
  786. 'ἶ',
  787. 'ἷ',
  788. 'ὶ',
  789. 'ί',
  790. 'ῐ',
  791. 'ῑ',
  792. 'ῒ',
  793. 'ΐ',
  794. 'ῖ',
  795. 'ῗ',
  796. 'і',
  797. 'ї',
  798. 'и',
  799. 'ဣ',
  800. 'ိ',
  801. 'ီ',
  802. 'ည်',
  803. 'ǐ',
  804. 'ი',
  805. 'इ',
  806. 'ی',
  807. 'i',
  808. ],
  809. 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'],
  810. 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k'],
  811. 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'],
  812. 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'],
  813. 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'],
  814. 'o' => [
  815. 'ó',
  816. 'ò',
  817. 'ỏ',
  818. 'õ',
  819. 'ọ',
  820. 'ô',
  821. 'ố',
  822. 'ồ',
  823. 'ổ',
  824. 'ỗ',
  825. 'ộ',
  826. 'ơ',
  827. 'ớ',
  828. 'ờ',
  829. 'ở',
  830. 'ỡ',
  831. 'ợ',
  832. 'ø',
  833. 'ō',
  834. 'ő',
  835. 'ŏ',
  836. 'ο',
  837. 'ὀ',
  838. 'ὁ',
  839. 'ὂ',
  840. 'ὃ',
  841. 'ὄ',
  842. 'ὅ',
  843. 'ὸ',
  844. 'ό',
  845. 'о',
  846. 'و',
  847. 'θ',
  848. 'ို',
  849. 'ǒ',
  850. 'ǿ',
  851. 'º',
  852. 'ო',
  853. 'ओ',
  854. 'o',
  855. 'ö',
  856. ],
  857. 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'],
  858. 'q' => ['ყ', 'q'],
  859. 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'],
  860. 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'],
  861. 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'],
  862. 'u' => [
  863. 'ú',
  864. 'ù',
  865. 'ủ',
  866. 'ũ',
  867. 'ụ',
  868. 'ư',
  869. 'ứ',
  870. 'ừ',
  871. 'ử',
  872. 'ữ',
  873. 'ự',
  874. 'û',
  875. 'ū',
  876. 'ů',
  877. 'ű',
  878. 'ŭ',
  879. 'ų',
  880. 'µ',
  881. 'у',
  882. 'ဉ',
  883. 'ု',
  884. 'ူ',
  885. 'ǔ',
  886. 'ǖ',
  887. 'ǘ',
  888. 'ǚ',
  889. 'ǜ',
  890. 'უ',
  891. 'उ',
  892. 'u',
  893. 'ў',
  894. 'ü',
  895. ],
  896. 'v' => ['в', 'ვ', 'ϐ', 'v'],
  897. 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'],
  898. 'x' => ['χ', 'ξ', 'x'],
  899. 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'],
  900. 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'],
  901. 'aa' => ['ع', 'आ', 'آ'],
  902. 'ae' => ['æ', 'ǽ'],
  903. 'ai' => ['ऐ'],
  904. 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'],
  905. 'dj' => ['ђ', 'đ'],
  906. 'dz' => ['џ', 'ძ'],
  907. 'ei' => ['ऍ'],
  908. 'gh' => ['غ', 'ღ'],
  909. 'ii' => ['ई'],
  910. 'ij' => ['ij'],
  911. 'kh' => ['х', 'خ', 'ხ'],
  912. 'lj' => ['љ'],
  913. 'nj' => ['њ'],
  914. 'oe' => ['ö', 'œ', 'ؤ'],
  915. 'oi' => ['ऑ'],
  916. 'oii' => ['ऒ'],
  917. 'ps' => ['ψ'],
  918. 'sh' => ['ш', 'შ', 'ش'],
  919. 'shch' => ['щ'],
  920. 'ss' => ['ß'],
  921. 'sx' => ['ŝ'],
  922. 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'],
  923. 'ts' => ['ц', 'ც', 'წ'],
  924. 'ue' => ['ü'],
  925. 'uu' => ['ऊ'],
  926. 'ya' => ['я'],
  927. 'yu' => ['ю'],
  928. 'zh' => ['ж', 'ჟ', 'ژ'],
  929. '(c)' => ['©'],
  930. 'A' => [
  931. 'Á',
  932. 'À',
  933. 'Ả',
  934. 'Ã',
  935. 'Ạ',
  936. 'Ă',
  937. 'Ắ',
  938. 'Ằ',
  939. 'Ẳ',
  940. 'Ẵ',
  941. 'Ặ',
  942. 'Â',
  943. 'Ấ',
  944. 'Ầ',
  945. 'Ẩ',
  946. 'Ẫ',
  947. 'Ậ',
  948. 'Å',
  949. 'Ā',
  950. 'Ą',
  951. 'Α',
  952. 'Ά',
  953. 'Ἀ',
  954. 'Ἁ',
  955. 'Ἂ',
  956. 'Ἃ',
  957. 'Ἄ',
  958. 'Ἅ',
  959. 'Ἆ',
  960. 'Ἇ',
  961. 'ᾈ',
  962. 'ᾉ',
  963. 'ᾊ',
  964. 'ᾋ',
  965. 'ᾌ',
  966. 'ᾍ',
  967. 'ᾎ',
  968. 'ᾏ',
  969. 'Ᾰ',
  970. 'Ᾱ',
  971. 'Ὰ',
  972. 'Ά',
  973. 'ᾼ',
  974. 'А',
  975. 'Ǻ',
  976. 'Ǎ',
  977. 'A',
  978. 'Ä',
  979. ],
  980. 'B' => ['Б', 'Β', 'ब', 'B'],
  981. 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'],
  982. 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'],
  983. 'E' => [
  984. 'É',
  985. 'È',
  986. 'Ẻ',
  987. 'Ẽ',
  988. 'Ẹ',
  989. 'Ê',
  990. 'Ế',
  991. 'Ề',
  992. 'Ể',
  993. 'Ễ',
  994. 'Ệ',
  995. 'Ë',
  996. 'Ē',
  997. 'Ę',
  998. 'Ě',
  999. 'Ĕ',
  1000. 'Ė',
  1001. 'Ε',
  1002. 'Έ',
  1003. 'Ἐ',
  1004. 'Ἑ',
  1005. 'Ἒ',
  1006. 'Ἓ',
  1007. 'Ἔ',
  1008. 'Ἕ',
  1009. 'Έ',
  1010. 'Ὲ',
  1011. 'Е',
  1012. 'Ё',
  1013. 'Э',
  1014. 'Є',
  1015. 'Ə',
  1016. 'E',
  1017. ],
  1018. 'F' => ['Ф', 'Φ', 'F'],
  1019. 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'],
  1020. 'H' => ['Η', 'Ή', 'Ħ', 'H'],
  1021. 'I' => [
  1022. 'Í',
  1023. 'Ì',
  1024. 'Ỉ',
  1025. 'Ĩ',
  1026. 'Ị',
  1027. 'Î',
  1028. 'Ï',
  1029. 'Ī',
  1030. 'Ĭ',
  1031. 'Į',
  1032. 'İ',
  1033. 'Ι',
  1034. 'Ί',
  1035. 'Ϊ',
  1036. 'Ἰ',
  1037. 'Ἱ',
  1038. 'Ἳ',
  1039. 'Ἴ',
  1040. 'Ἵ',
  1041. 'Ἶ',
  1042. 'Ἷ',
  1043. 'Ῐ',
  1044. 'Ῑ',
  1045. 'Ὶ',
  1046. 'Ί',
  1047. 'И',
  1048. 'І',
  1049. 'Ї',
  1050. 'Ǐ',
  1051. 'ϒ',
  1052. 'I',
  1053. ],
  1054. 'J' => ['J'],
  1055. 'K' => ['К', 'Κ', 'K'],
  1056. 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'],
  1057. 'M' => ['М', 'Μ', 'M'],
  1058. 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'],
  1059. 'O' => [
  1060. 'Ó',
  1061. 'Ò',
  1062. 'Ỏ',
  1063. 'Õ',
  1064. 'Ọ',
  1065. 'Ô',
  1066. 'Ố',
  1067. 'Ồ',
  1068. 'Ổ',
  1069. 'Ỗ',
  1070. 'Ộ',
  1071. 'Ơ',
  1072. 'Ớ',
  1073. 'Ờ',
  1074. 'Ở',
  1075. 'Ỡ',
  1076. 'Ợ',
  1077. 'Ø',
  1078. 'Ō',
  1079. 'Ő',
  1080. 'Ŏ',
  1081. 'Ο',
  1082. 'Ό',
  1083. 'Ὀ',
  1084. 'Ὁ',
  1085. 'Ὂ',
  1086. 'Ὃ',
  1087. 'Ὄ',
  1088. 'Ὅ',
  1089. 'Ὸ',
  1090. 'Ό',
  1091. 'О',
  1092. 'Θ',
  1093. 'Ө',
  1094. 'Ǒ',
  1095. 'Ǿ',
  1096. 'O',
  1097. 'Ö',
  1098. ],
  1099. 'P' => ['П', 'Π', 'P'],
  1100. 'Q' => ['Q'],
  1101. 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'],
  1102. 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'],
  1103. 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'],
  1104. 'U' => [
  1105. 'Ú',
  1106. 'Ù',
  1107. 'Ủ',
  1108. 'Ũ',
  1109. 'Ụ',
  1110. 'Ư',
  1111. 'Ứ',
  1112. 'Ừ',
  1113. 'Ử',
  1114. 'Ữ',
  1115. 'Ự',
  1116. 'Û',
  1117. 'Ū',
  1118. 'Ů',
  1119. 'Ű',
  1120. 'Ŭ',
  1121. 'Ų',
  1122. 'У',
  1123. 'Ǔ',
  1124. 'Ǖ',
  1125. 'Ǘ',
  1126. 'Ǚ',
  1127. 'Ǜ',
  1128. 'U',
  1129. 'Ў',
  1130. 'Ü',
  1131. ],
  1132. 'V' => ['В', 'V'],
  1133. 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'],
  1134. 'X' => ['Χ', 'Ξ', 'X'],
  1135. 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'],
  1136. 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'],
  1137. 'AE' => ['Æ', 'Ǽ'],
  1138. 'Ch' => ['Ч'],
  1139. 'Dj' => ['Ђ'],
  1140. 'Dz' => ['Џ'],
  1141. 'Gx' => ['Ĝ'],
  1142. 'Hx' => ['Ĥ'],
  1143. 'Ij' => ['IJ'],
  1144. 'Jx' => ['Ĵ'],
  1145. 'Kh' => ['Х'],
  1146. 'Lj' => ['Љ'],
  1147. 'Nj' => ['Њ'],
  1148. 'Oe' => ['Œ'],
  1149. 'Ps' => ['Ψ'],
  1150. 'Sh' => ['Ш'],
  1151. 'Shch' => ['Щ'],
  1152. 'Ss' => ['ẞ'],
  1153. 'Th' => ['Þ'],
  1154. 'Ts' => ['Ц'],
  1155. 'Ya' => ['Я'],
  1156. 'Yu' => ['Ю'],
  1157. 'Zh' => ['Ж'],
  1158. ' ' => [
  1159. "\xC2\xA0",
  1160. "\xE2\x80\x80",
  1161. "\xE2\x80\x81",
  1162. "\xE2\x80\x82",
  1163. "\xE2\x80\x83",
  1164. "\xE2\x80\x84",
  1165. "\xE2\x80\x85",
  1166. "\xE2\x80\x86",
  1167. "\xE2\x80\x87",
  1168. "\xE2\x80\x88",
  1169. "\xE2\x80\x89",
  1170. "\xE2\x80\x8A",
  1171. "\xE2\x80\xAF",
  1172. "\xE2\x81\x9F",
  1173. "\xE3\x80\x80",
  1174. "\xEF\xBE\xA0",
  1175. ],
  1176. ];
  1177. }
  1178. /**
  1179. * Returns the language specific replacements for the ascii method.
  1180. * Note: Adapted from Stringy\Stringy.
  1181. *
  1182. * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt
  1183. * @return null|array
  1184. */
  1185. protected static function languageSpecificCharsArray(string $language)
  1186. {
  1187. static $languageSpecific;
  1188. if (! isset($languageSpecific)) {
  1189. $languageSpecific = [
  1190. 'bg' => [
  1191. ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'],
  1192. ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'],
  1193. ],
  1194. 'de' => [
  1195. ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'],
  1196. ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'],
  1197. ],
  1198. ];
  1199. }
  1200. return $languageSpecific[$language] ?? null;
  1201. }
  1202. }