public static function attrib_string($attrib = array(), $allowed = null)
    {
        if (empty($attrib)) {
            return '';
        }

        $allowed_f  = array_flip((array)$allowed);
        $attrib_arr = array();

        foreach ($attrib as $key => $value) {
            // skip size if not numeric
            if ($key == 'size' && !is_numeric($value)) {
                continue;
            }

            // ignore "internal" or empty attributes
            if ($key == 'nl' || $value === null) {
                continue;
            }

            // ignore not allowed attributes, except aria-* and data-*
            if (!empty($allowed)) {
                $is_data_attr = @substr_compare($key, 'data-', 0, 5) === 0;
                $is_aria_attr = @substr_compare($key, 'aria-', 0, 5) === 0;
                if (!$is_aria_attr && !$is_data_attr && !isset($allowed_f[$key])) {
                    continue;
                }
            }

            // skip empty eventhandlers
            if (preg_match('/^on[a-z]+/', $key) && !$value) {
                continue;
            }

            // attributes with no value
            if (in_array($key, self::$bool_attrib)) {
                if ($value) {
                    $value = $key;
                    if (self::$doctype == 'xhtml') {
                        $value .= '="' . $value . '"';
                    }

                    $attrib_arr[] = $value;
                }
            }
            else {
                $attrib_arr[] = $key . '="' . self::quote($value) . '"';
            }
        }

        return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
    }