private function getLength(string $string) : ?int
    {
        if (extension_loaded('mbstring') && ! defined('NO_PHP_EXTENSIONS')) { // constant used for tests
            if (! mb_check_encoding($string, 'UTF-8')) {
                return null;
            }

            return mb_strlen($string, 'UTF-8');
        }

        $charLength = 0;

        $length = strlen($string);

        for ($i = 0; $i < $length; $i++) {
            $charLength++;

            $char = ord($string[$i]);

            if ($char < 0x80) { // 0bbbbbbb
                continue;
            }

            if (($char & 0xE0) === 0xC0) { // 110bbbbb
                $n = 1;
            } elseif (($char & 0xF0) === 0xE0) { // 1110bbbb
                $n = 2;
            } elseif (($char & 0xF0) === 0xF0) { // 11110bbb
                $n = 3;
            } else { // invalid char
                return null;
            }

            for ($j = 0; $j < $n; $j++) {
                if (++$i === $length) { // unexpected end of string
                    return null;
                }

                if ((ord($string[$i]) & 0xC0) !== 0x80) { // does not match 10bbbbbb
                    return null;
                }
            }
        }

        return $charLength;
    }