htmlwrap - Safely wrap HTML formatted text 1.1

来源:互联网 发布:泰达网络下线 编辑:程序博客网 时间:2024/05/16 02:03
Built for use in the Orca Forum and Blog, the htmlwrap() function safely wraps HTML formatted text by breaking strings of characters over a certain length. It's great for use anywhere where generated HTML output is built from user input.

htmlwrap() won't insert line-breaks within HTML tags or entities, and entities are all treated as single characters when calculating break-points. It will also try to find logical line-break insertion points (like after periods or slashes) instead of pounding them in robotically every certain number of characters. Additionally, you can protect entire elements from line-breaks just by adding it to the protect list.

Now optionally works on multi-byte characters in version 1.1

  1. <?php /* **************************************************************
  2. * htmlwrap() function - v1.1
  3. * Copyright (c) 2004 Brian Huisman AKA GreyWyvern
  4. *
  5. * This program may be distributed under the terms of the GPL
  6. *   - http://www.gnu.org/licenses/gpl.txt
  7. *
  8. *
  9. * htmlwrap -- Safely wraps a string containing HTML formatted text (not
  10. * a full HTML document) to a specified width
  11. *
  12. *
  13. * Changelog
  14. * 1.1  - Now optionally works with multi-byte characters
  15. *
  16. *
  17. * Description
  18. *
  19. * string htmlwrap ( string str [, int width [, string break [, string
  20. * nobreak [, string nobr [, bool utf]]]]])
  21. *
  22. * htmlwrap() is a function which wraps HTML by breaking long words and
  23. * preventing them from damaging your layout.  This function will NOT
  24. * insert <br /> tags every "width" characters as in the PHP wordwrap()
  25. * function.  HTML wraps automatically, so this function only ensures
  26. * wrapping at "width" characters is possible.  Use in places where a
  27. * page will accept user input in order to create HTML output like in
  28. * forums or blog comments.
  29. *
  30. * htmlwrap() won't break text within HTML tags and also preserves any
  31. * existing HTML entities within the string, like &nbsp; and &lt;  It
  32. * will only count these entities as one character.  Output is auto-
  33. * matically nl2br()'ed.
  34. *
  35. * The function also allows you to specify "protected" elements, where
  36. * line-breaks, block-returns or both are not inserted.  This is useful
  37. * for elements like <pre> where you don't want the code to be damaged
  38. * by the insertion of HTML block-returns.  Add the names of the
  39. * elements you wish to protect from line-breaks (nobreak) and/or block-
  40. * returns (nobr) as space separated lists.  Only names of valid HTML
  41. * tags are accepted.  (eg. "code pre blockquote")
  42. *
  43. * The optional "utf" parameter enables the function to treat multi-
  44. * byte characters in UTF-8 as single characters.  The default is false.
  45. * "This modifier is available from PHP 4.1.0 or greater on Unix and
  46. * from PHP 4.2.3 on win32."
  47. *  - http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
  48. *
  49. * htmlwrap() will *always* break long strings of characters at the
  50. * specified width.  In this way, the function behaves as if the
  51. * wordwrap() "cut" flag is always set.  However, the function will try
  52. * to find "safe" characters within strings it breaks, where inserting a
  53. * line-break would make more sense.  You may edit these characters by
  54. * adding or removing them from the $lbrks variable.
  55. *
  56. * htmlwrap() is safe to use on strings containing multi-byte
  57. * characters as of version 1.1.
  58. *
  59. * See the inline comments and http://www.greywyvern.com/php.php
  60. * for more info
  61. ******************************************************************** */
  62.  
  63. function htmlwrap($str, $width = 60, $break = "/n", $nobreak = "", $nobr = "pre", $utf = false) {
  64.  
  65.   // Split HTML content into an array delimited by < and >
  66.   // The flags save the delimeters and remove empty variables
  67.   $content = preg_split("/([<>])/", $str, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
  68.  
  69.   // Transform protected element lists into arrays
  70.   $nobreak = explode(" ", $nobreak);
  71.   $nobr = explode(" ", $nobr);
  72.  
  73.   // Variable setup
  74.   $intag = false;
  75.   $innbk = array();
  76.   $innbr = array();
  77.   $drain = "";
  78.   $utf = ($utf) ? "u" : "";
  79.  
  80.   // List of characters it is "safe" to insert line-breaks at
  81.   // Do not add ampersand (&) as it will mess up HTML Entities
  82.   // It is not necessary to add < and >
  83.   $lbrks = "/?!%)-}]///"':;";
  84.  
  85.   // We use /r for adding <br /> in the right spots so just switch to /n
  86.   if ($break == "/r") $break = "/n";
  87.  
  88.   while (list(, $value) = each($content)) {
  89.     switch ($value) {
  90.  
  91.       // If a < is encountered, set the "in-tag" flag
  92.       case "<": $intag = true; break;
  93.  
  94.       // If a > is encountered, remove the flag
  95.       case ">": $intag = false; break;
  96.  
  97.       default:
  98.  
  99.         // If we are currently within a tag...
  100.         if ($intag) {
  101.  
  102.           // If the first character is not a / then this is an opening tag
  103.           if ($value{0} != "/") {
  104.  
  105.             // Collect the tag name   
  106.             preg_match("/^(.*?)(/s|$)/$utf", $value, $t);
  107.  
  108.             // If this is a protected element, activate the associated protection flag
  109.             if ((!count($innbk) && in_array($t[1], $nobreak)) || in_array($t[1], $innbk)) $innbk[] = $t[1];
  110.             if ((!count($innbr) && in_array($t[1], $nobr)) || in_array($t[1], $innbr)) $innbr[] = $t[1];
  111.  
  112.           // Otherwise this is a closing tag
  113.           } else {
  114.  
  115.             // If this is a closing tag for a protected element, unset the flag
  116.             if (in_array(substr($value, 1), $innbk)) unset($innbk[count($innbk)]);
  117.             if (in_array(substr($value, 1), $innbr)) unset($innbr[count($innbr)]);
  118.           }
  119.  
  120.         // Else if we're outside any tags...
  121.         } else if ($value) {
  122.  
  123.           // If unprotected, remove all existing /r, replace all existing /n with /r
  124.           if (!count($innbr)) $value = str_replace("/n", "/r", str_replace("/r", "", $value));
  125.  
  126.           // If unprotected, enter the line-break loop
  127.           if (!count($innbk)) {
  128.             do {
  129.               $store = $value;
  130.  
  131.               // Find the first stretch of characters over the $width limit
  132.               if (preg_match("/^(.*?/s|^)(([^/s&]|&(/w{2,5}|#/d{2,4});){".$width."})(?!(".preg_quote($break, "/")."|/s))(.*)$/s$utf", $value, $match)) {
  133.  
  134.                 // Determine the last "safe line-break" character within this match
  135.                 for ($x = 0, $ledge = 0; $x < strlen($lbrks); $x++) $ledge = max($ledge, strrpos($match[2], $lbrks{$x}));
  136.                 if (!$ledge) $ledge = strlen($match[2]) - 1;
  137.  
  138.                 // Insert the modified string
  139.                 $value = $match[1].substr($match[2], 0, $ledge + 1).$break.substr($match[2], $ledge + 1).$match[6];
  140.               }
  141.  
  142.             // Loop while overlimit strings are still being found
  143.             } while ($store != $value);
  144.           }
  145.  
  146.           // If unprotected, replace all /r with <br />/n to finish
  147.           if (!count($innbr)) $value = str_replace("/r", "<br />/n", $value);
  148.         }
  149.     }
  150.  
  151.     // Send the modified segment down the drain
  152.     $drain .= $value;
  153.   }
  154.  
  155.   // Return contents of the drain
  156.   return $drain;
  157. }
  158. ?>