Минимизация окончательного вывода HTML с использованием регулярных выражений с помощью CodeIgniter

Страницы Google предлагают вам минимизировать HTML, т. Е. Удалить все ненужные пробелы. CodeIgniter имеет функцию выхода giziping, или это можно сделать через .htaccess . Но все же я также хотел бы удалить ненужные пробелы из окончательного вывода HTML.

Я немного поработал с этим куском кода, чтобы сделать это, и, похоже, он работает. Это действительно приводит к тому, что HTML не содержит лишних пробелов и удаляет другое форматирование вкладок.

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

Проблема в том, что могут быть такие теги, как <pre> , <textarea> и т. Д., Которые могут иметь пробелы в них, и регулярное выражение должно их удалить. Итак, как мне удалить лишнее пространство из окончательного HTML, не делая пробелы или форматирование для этих определенных тегов, используя регулярное выражение?

Благодаря @Alan Moore получил ответ, это сработало для меня

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

ridgerunner очень хорошо проанализировал это регулярное выражение. Я закончил тем, что использовал его решение. Приветствую вас.

Для тех, кому интересно, как работает регулярное выражение Алана Мура (и да, это действительно работает), я позволил себе прокомментировать его, чтобы его могли читать простые смертные:

 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; } 

Я новичок здесь, но я сразу вижу, что Алан неплохо подходит к регулярному выражению. Я бы добавил только следующие предложения.

  1. Существует ненужная группа захвата, которую можно удалить.
  2. Хотя ОП не сказал этого, элемент <SCRIPT> должен быть добавлен в черный список <PRE> и <TEXTAREA> .
  3. Добавление модификатора «Исследование» 'S' PCRE ускоряет это регулярное выражение примерно на 20%.
  4. В предложении есть группа чередования, которая созрела для применения конструкции эффективности « разворачивания цикла » Фридла.
  5. В более серьезной ноте эта же группа чередования: (т. Е. (?:[^<]++|<(?!/?(?:textarea|pre)\b))*+ ) подвержена чрезмерной рекурсии PCRE на большие целевые строки, что может привести к переполнению стека, в результате чего исполняемый файл Apache / PHP будет отключен без сбоев и сбой без предупреждения. (Узел Win32 Apache httpd.exe особенно восприимчив к этому, потому что он имеет только 256 КБ стека по сравнению с исполняемыми httpd.exe * nix, которые обычно создаются с помощью стека 8 МБ и более.) Филипп Хейзел (автор механизма регулярного выражения PCRE, используемого в PHP) обсуждает эту проблему в документации: PCR DISCUSSION OF STACK USAGE . Хотя Алан правильно применил ту же ошибку, что и Филипп в этом документе (применяя притяжательный плюс к первому альтернативу), все еще будет много рекурсии, если HTML-файл большой и имеет много нечерных тегов. например, на моем ящике Win32 (с исполняемым файлом, имеющим стек 256 КБ), скрипт взрывается тестовым файлом всего 60 КБ. Обратите также внимание на то, что PHP, к сожалению, не выполняет рекомендации и устанавливает слишком высокий предел рекурсии по умолчанию на уровне 100000. (Согласно документам PCRE это должно быть установлено равным размеру стека, разделенному на 500).

Вот улучшенная версия, которая быстрее оригинала, обрабатывает больший ввод и изящно выходит из строя с сообщением, если входная строка слишком велика для обработки:

 // 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; } 

ps Я знаком с этой проблемой PHP / Apache seg-fault, так как я был вовлечен в помощь сообществу Drupal, когда они боролись с этой проблемой. См .: Оптимизация параметра CSS приводит к тому, что php cgi выполняет segfault в функции pcre «match» . Мы также испытали это с парсером BBCode в проекте программного обеспечения для форума FluxBB.

Надеюсь это поможет.

Я реализовал ответ от @ridgerunner в двух проектах и ​​в конечном итоге столкнулся с некоторыми серьезными замедлениями (10-30 секундных запросов) для организации одного из проектов. Я выяснил, что мне пришлось установить как pcre.recursion_limit и pcre.backtrack_limit достаточно низко, чтобы он даже работал, но даже тогда он откажется после двух секунд обработки и возвращает false.

С тех пор я заменил его на это решение (с более понятным регулярным выражением), которое вдохновлено функцией outputfilter.trimwhitespace от Smarty 2. Она не выполняет никаких операций с возвратом или рекурсией и работает каждый раз (вместо катастрофически неудачной один раз в голубой луне):

 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; }