public static function str($obj, $html = false, $depth = 0)
    {
        if (is_null($obj))
            return "NULL";

        if (is_bool($obj))
            return $obj ? "TRUE" : "FALSE";

        if (is_scalar($obj))
            return (string)$obj;


        $str = "";
        if ($obj instanceof Throwable)
        {
            $str = self::exceptionToString($obj);
        }
        else if (is_object($obj) && method_exists($obj, '__toString'))
        {
            $str = (string)$obj;
        }
        else if (is_object($obj) && $obj instanceof \JsonSerializable)
        {
            $obj = $obj->jsonSerialize();
        }

        if (empty($str) && is_array($obj))
        {
            if ($depth > 1)
                return '[...]';
            $vals = [];
            foreach ($obj as $k => $v)
            {
                $repr = "";
                if (!is_int($k))
                    $repr = "'$k' => ";
                
                $repr .= self::str($v, $html, $depth + 1);
                $vals[] = $repr;
            }
            return '[' . implode(', ', $vals) . ']';
        }
        elseif (empty($str))
        {
            $str = self::sprint_r($obj);
        }

        if ($html)
        {
            $str = nl2br($str);
            $str = str_replace('  ', '&nbsp;&nbsp;', $str);
        }

        return $str;
    }