1717 */
1818class MbString extends \ArrayObject implements \Stringable
1919{
20+ public const MBSTRING_CONVMETHOD_ICONV = 1 ;
21+ public const MBSTRING_CONVMETHOD_MBSTRING = 2 ;
22+
23+ /**
24+ * The way to convert text encoding.
25+ *
26+ * @var int
27+ */
28+ public static $ convMethod ;
29+
2030 /**
2131 * UTF-32 string without endian bytes.
2232 *
@@ -46,7 +56,8 @@ class MbString extends \ArrayObject implements \Stringable
4656 */
4757 public function __construct (string $ str = '' , string $ encoding = 'UTF-8 ' )
4858 {
49- static ::$ utf32Header = static ::$ utf32Header ?? static ::getUtf32Header ();
59+ static ::$ convMethod ??= static ::detectConvEncoding ();
60+ static ::$ utf32Header ??= static ::getUtf32Header ();
5061
5162 $ this ->encoding = $ encoding ;
5263 $ this ->set ($ str );
@@ -328,11 +339,37 @@ public function count(): int
328339 protected static function getUtf32Header (): string
329340 {
330341 // just use any string to get the endian header, here we use "A"
331- $ tmp = iconv ( ' UTF-8 ' , 'UTF-32 ' , 'A ' );
342+ $ tmp = self :: convEncoding ( ' A ' , 'UTF-8 ' , 'UTF-32 ' );
332343 // some distributions like "php alpine" docker image won't generate the header
333344 return $ tmp && \strlen ($ tmp ) > 4 ? substr ($ tmp , 0 , 4 ) : '' ;
334345 }
335346
347+ protected static function detectConvEncoding (): int
348+ {
349+ if (\function_exists ('iconv ' ) && iconv ('UTF-8 ' , 'UTF-32 ' , 'A ' ) !== false ) {
350+ return static ::MBSTRING_CONVMETHOD_ICONV ;
351+ }
352+
353+ if (\function_exists ('mb_convert_encoding ' ) && mb_convert_encoding ('A ' , 'UTF-32 ' , 'UTF-8 ' ) !== false ) {
354+ return static ::MBSTRING_CONVMETHOD_MBSTRING ;
355+ }
356+
357+ throw new \RuntimeException ('Either "iconv" or "mbstring" extension is required. ' );
358+ }
359+
360+ protected static function convEncoding (string $ str , string $ from , string $ to ): string
361+ {
362+ if (static ::$ convMethod === static ::MBSTRING_CONVMETHOD_ICONV ) {
363+ return iconv ($ from , $ to , $ str );
364+ }
365+
366+ if (static ::$ convMethod === static ::MBSTRING_CONVMETHOD_MBSTRING ) {
367+ return mb_convert_encoding ($ str , $ to , $ from );
368+ }
369+
370+ throw new \RuntimeException ('Unknown conversion method. ' );
371+ }
372+
336373 /**
337374 * Convert the output string to its original encoding.
338375 *
@@ -344,7 +381,7 @@ protected function outputConv(string $str): string
344381 return '' ;
345382 }
346383
347- return iconv ( 'UTF-32 ' , $ this ->encoding , static :: $ utf32Header . $ str );
384+ return static :: convEncoding ( static :: $ utf32Header . $ str , 'UTF-32 ' , $ this ->encoding );
348385 }
349386
350387 /**
@@ -358,6 +395,6 @@ protected function inputConv(string $str): string
358395 return '' ;
359396 }
360397
361- return substr (iconv ( $ this ->encoding , 'UTF-32 ' , $ str ), \strlen (static ::$ utf32Header ));
398+ return substr (static :: convEncoding ( $ str , $ this ->encoding , 'UTF-32 ' ), \strlen (static ::$ utf32Header ));
362399 }
363400}
0 commit comments