public function attributes($value, array $args = [])
    {
        if (is_array($value) || is_object($value)) {
            foreach ($value as $_key => &$_value) {
                $_value = $this->attributes($_value, $args);
            } // unset($_key, $_value);
            return $value;
        }
        if (!($string = (string) $value)) {
            return $string; // Nothing to do.
        }
        $default_args = [
            'allowed_attributes' => [],
        ];
        $args = array_merge($default_args, $args);
        $args = array_intersect_key($args, $default_args);

        $allowed_attributes = // Force lowercase.
            array_map('mb_strtolower', (array) $args['allowed_attributes']);

        $regex_tags  = '/(?<open>\<[\w\-]+)(?<attrs>[^>]+)(?<close>\>)/ui';
        $regex_attrs = '/\s+(?<attr>[\w\-]+)(?:\s*\=\s*(["\']).*?\\2|\s*\=[^\s]*)?/uis';

        return preg_replace_callback($regex_tags, function ($m) use ($allowed_attributes, $regex_attrs) {
            return $m['open'].preg_replace_callback($regex_attrs, function ($m) use ($allowed_attributes) {
                return in_array(mb_strtolower($m['attr']), $allowed_attributes, true) ? $m[0] : '';
            }, $m['attrs']).$m['close']; // With modified attributes.
        }, $string); // Removes attributes; leaving only those allowed explicitly.
    }