Skip to content

Commit

Permalink
feat: use mbstring if iconv is not working
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Cherng <[email protected]>
  • Loading branch information
jfcherng committed Apr 17, 2023
1 parent bbf3ec1 commit 8407bfe
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 10 deletions.
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
"phan/phan": "^5",
"phpunit/phpunit": "^9 || ^10"
},
"suggest": {
"ext-iconv": "Either \"ext-iconv\" or \"ext-mbstring\" is requried.",
"ext-mbstring": "Either \"ext-iconv\" or \"ext-mbstring\" is requried."
},
"scripts": {
"analyze": [
"phan --color"
Expand Down
12 changes: 6 additions & 6 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 41 additions & 4 deletions src/MbString.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@
*/
class MbString extends \ArrayObject implements \Stringable
{
public const MBSTRING_CONVMETHOD_ICONV = 1;
public const MBSTRING_CONVMETHOD_MBSTRING = 2;

/**
* The way to convert text encoding.
*
* @var int
*/
public static $convMethod;

/**
* UTF-32 string without endian bytes.
*
Expand Down Expand Up @@ -46,7 +56,8 @@ class MbString extends \ArrayObject implements \Stringable
*/
public function __construct(string $str = '', string $encoding = 'UTF-8')
{
static::$utf32Header = static::$utf32Header ?? static::getUtf32Header();
static::$convMethod ??= static::detectConvEncoding();
static::$utf32Header ??= static::getUtf32Header();

$this->encoding = $encoding;
$this->set($str);
Expand Down Expand Up @@ -328,11 +339,37 @@ public function count(): int
protected static function getUtf32Header(): string
{
// just use any string to get the endian header, here we use "A"
$tmp = iconv('UTF-8', 'UTF-32', 'A');
$tmp = self::convEncoding('A', 'UTF-8', 'UTF-32');
// some distributions like "php alpine" docker image won't generate the header
return $tmp && \strlen($tmp) > 4 ? substr($tmp, 0, 4) : '';
}

protected static function detectConvEncoding(): int
{
if (\function_exists('iconv') && iconv('UTF-8', 'UTF-32', 'A') !== false) {
return static::MBSTRING_CONVMETHOD_ICONV;
}

if (\function_exists('mb_convert_encoding') && mb_convert_encoding('A', 'UTF-32', 'UTF-8') !== false) {
return static::MBSTRING_CONVMETHOD_MBSTRING;
}

throw new \RuntimeException('Either "iconv" or "mbstring" extension is required.');
}

protected static function convEncoding(string $str, string $from, string $to): string
{
if (static::$convMethod === static::MBSTRING_CONVMETHOD_ICONV) {
return iconv($from, $to, $str);
}

if (static::$convMethod === static::MBSTRING_CONVMETHOD_MBSTRING) {
return mb_convert_encoding($str, $to, $from);
}

throw new \RuntimeException('Unknown conversion method.');
}

/**
* Convert the output string to its original encoding.
*
Expand All @@ -344,7 +381,7 @@ protected function outputConv(string $str): string
return '';
}

return iconv('UTF-32', $this->encoding, static::$utf32Header . $str);
return static::convEncoding(static::$utf32Header . $str, 'UTF-32', $this->encoding);
}

/**
Expand All @@ -358,6 +395,6 @@ protected function inputConv(string $str): string
return '';
}

return substr(iconv($this->encoding, 'UTF-32', $str), \strlen(static::$utf32Header));
return substr(static::convEncoding($str, $this->encoding, 'UTF-32'), \strlen(static::$utf32Header));
}
}

0 comments on commit 8407bfe

Please sign in to comment.