使用带有CodeIgniter的正则表达式缩小最终的HTML输出

2022-08-31 00:03:15

Google网页建议您缩小HTML,即删除所有不必要的空格。CodeIgniter确实具有对输出进行加密的功能,或者可以通过 来完成。但我仍然想从最终的HTML输出中删除不必要的空格。.htaccess

我用这段代码玩了一会儿来做这件事,它似乎有效。这确实会导致 HTML 没有多余的空格,并删除其他制表符格式。

class Welcome extends CI_Controller 
{
    function _output()
    {
        echo preg_replace('!\s+!', ' ', $output);
    }

    function index(){
    ...
    }
}

问题是可能有像,等这样的标签。其中可能包含空格,正则表达式应将其删除。那么,如何从最终的HTML中删除多余的空间,而不影响使用正则表达式为这些特定标签设置空格或格式?<pre><textarea>

感谢@Alan摩尔得到了答案,这对我有用

echo preg_replace('#(?ix)(?>[^\S ]\s*|\s{2,})(?=(?:(?:[^<]++|<(?!/?(?:textarea|pre)\b))*+)(?:<(?>textarea|pre)\b|\z))#', ' ', $output);

ridgerunner在分析这个正则表达式方面做得很好。我最终使用了他的解决方案。为山脊奔跑者干杯。


答案 1

对于那些对Alan Moore的正则表达式如何工作感到好奇的人(是的,它确实有效),我冒昧地评论了它,以便凡人可以阅读它:

function process_data_alan($text) // 
{
    $re = '%# Collapse ws everywhere but in blacklisted elements.
        (?>             # Match all whitespans other than single space.
          [^\S ]\s*     # Either one [\t\r\n\f\v] and zero or more ws,
        | \s{2,}        # or two or more consecutive-any-whitespace.
        ) # Note: The remaining regex consumes no text at all...
        (?=             # Ensure we are not in a blacklist tag.
          (?:           # Begin (unnecessary) group.
            (?:         # Zero or more of...
              [^<]++    # Either one or more non-"<"
            | <         # or a < starting a non-blacklist tag.
              (?!/?(?:textarea|pre)\b)
            )*+         # (This could be "unroll-the-loop"ified.)
          )             # End (unnecessary) group.
          (?:           # Begin alternation group.
            <           # Either a blacklist start tag.
            (?>textarea|pre)\b
          | \z          # or end of file.
          )             # End alternation group.
        )  # If we made it here, we are not in a blacklist tag.
        %ix';
    $text = preg_replace($re, " ", $text);
    return $text;
}

我是这里的新手,但我可以立即看到Alan非常擅长正则表达式。我只想补充以下建议。

  1. 有一个不必要的捕获组可以删除。
  2. 虽然OP没有这么说,但该元素应添加到黑名单中。<SCRIPT><PRE><TEXTAREA>
  3. 添加 PCRE“研究”修饰符可将此正则表达式的速度提高约 20%。'S'
  4. 在前瞻中有一个交替组,该组已经成熟,可以应用Friedl的“展开循环”效率结构。
  5. 更严重的是,同样的交替组:(即)容易受到大型目标字符串上过多的PCRE递归的影响,这可能导致堆栈溢出,导致Apache / PHP可执行文件静默地隔离错误并崩溃而没有警告。(Apache的Win32版本特别容易受到这种情况的影响,因为与*nix可执行文件相比,它只有256KB的堆栈,后者通常使用8MB或更多的堆栈构建。Philip Hazel(PHP中使用的PCRE正则表达式引擎的作者)在文档中讨论了这个问题:PCRE堆栈用法的讨论。尽管 Alan 已正确应用了与本文档中显示的 Philip 相同的修复程序(将所有格加号应用于第一个替代项),但如果 HTML 文件很大并且具有许多未列入黑名单的标记,则仍然会有很多递归。例如,在我的Win32盒子上(可执行文件具有256KB的堆栈),脚本以只有60KB的测试文件爆炸。另请注意,PHP 很遗憾没有遵循这些建议,而是将默认递归限制设置得太高,达到 100000。(根据 PCRE 文档,应将其设置为等于堆栈大小除以 500 的值)。(?:[^<]++|<(?!/?(?:textarea|pre)\b))*+httpd.exe

这是一个改进的版本,它比原始版本更快,处理更大的输入,如果输入字符串太大而无法处理,则会优雅地失败并显示消息:

// Set PCRE recursion limit to sane value = STACKSIZE / 500
// ini_set("pcre.recursion_limit", "524"); // 256KB stack. Win32 Apache
ini_set("pcre.recursion_limit", "16777");  // 8MB stack. *nix
function process_data_jmr1($text) // 
{
    $re = '%# Collapse whitespace everywhere but in blacklisted elements.
        (?>             # Match all whitespans other than single space.
          [^\S ]\s*     # Either one [\t\r\n\f\v] and zero or more ws,
        | \s{2,}        # or two or more consecutive-any-whitespace.
        ) # Note: The remaining regex consumes no text at all...
        (?=             # Ensure we are not in a blacklist tag.
          [^<]*+        # Either zero or more non-"<" {normal*}
          (?:           # Begin {(special normal*)*} construct
            <           # or a < starting a non-blacklist tag.
            (?!/?(?:textarea|pre|script)\b)
            [^<]*+      # more non-"<" {normal*}
          )*+           # Finish "unrolling-the-loop"
          (?:           # Begin alternation group.
            <           # Either a blacklist start tag.
            (?>textarea|pre|script)\b
          | \z          # or end of file.
          )             # End alternation group.
        )  # If we made it here, we are not in a blacklist tag.
        %Six';
    $text = preg_replace($re, " ", $text);
    if ($text === null) exit("PCRE Error! File too big.\n");
    return $text;
}

p.s. 我非常熟悉这个 PHP/Apache seg-fault 问题,因为我在 Drupal 社区努力解决这个问题时参与了帮助。参见:优化CSS选项导致php cgi在pcre函数“match”中出现segfault。我们在FluxBB论坛软件项目的BBCode解析器中也体验到了这一点。

希望这有帮助。


答案 2

我在两个项目中实现了@ridgerunner的答案,最终在其中一个项目的暂存中遇到了一些严重的减速(10-30秒的请求时间)。我发现我必须同时将两者都设置得很低才能使其工作,但即使这样,它也会在大约2个仙几毫秒的处理后放弃并返回false。pcre.recursion_limitpcre.backtrack_limit

从那以后,我用这个解决方案(更容易掌握的正则表达式)替换了它,它受到Smarty 2的exputfilter.trimwhitespace函数的启发。它不做回溯或递归,每次都有效(而不是在蓝月亮中灾难性地失败一次):

function filterHtml($input) {
    // Remove HTML comments, but not SSI
    $input = preg_replace('/<!--[^#](.*?)-->/s', '', $input);

    // The content inside these tags will be spared:
    $doNotCompressTags = ['script', 'pre', 'textarea'];
    $matches = [];

    foreach ($doNotCompressTags as $tag) {
        $regex = "!<{$tag}[^>]*?>.*?</{$tag}>!is";

        // It is assumed that this placeholder could not appear organically in your
        // output. If it can, you may have an XSS problem.
        $placeholder = "@@<'-placeholder-$tag'>@@";

        // Replace all the tags (including their content) with a placeholder, and keep their contents for later.
        $input = preg_replace_callback(
            $regex,
            function ($match) use ($tag, &$matches, $placeholder) {
                $matches[$tag][] = $match[0];
                return $placeholder;
            },
            $input
        );
    }

    // Remove whitespace (spaces, newlines and tabs)
    $input = trim(preg_replace('/[ \n\t]+/m', ' ', $input));

    // Iterate the blocks we replaced with placeholders beforehand, and replace the placeholders
    // with the original content.
    foreach ($matches as $tag => $blocks) {
        $placeholder = "@@<'-placeholder-$tag'>@@";
        $placeholderLength = strlen($placeholder);
        $position = 0;

        foreach ($blocks as $block) {
            $position = strpos($input, $placeholder, $position);
            if ($position === false) {
                throw new \RuntimeException("Found too many placeholders of type $tag in input string");
            }
            $input = substr_replace($input, $block, $position, $placeholderLength);
        }
    }

    return $input;
}

推荐