diff options
Diffstat (limited to 'system/core/Output.php')
-rw-r--r-- | system/core/Output.php | 167 |
1 files changed, 141 insertions, 26 deletions
diff --git a/system/core/Output.php b/system/core/Output.php index 98deff55c..ce0500e71 100644 --- a/system/core/Output.php +++ b/system/core/Output.php @@ -18,7 +18,7 @@ * * @package CodeIgniter * @author EllisLab Dev Team - * @copyright Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/) + * @copyright Copyright (c) 2008 - 2013, EllisLab, Inc. (http://ellislab.com/) * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) * @link http://codeigniter.com * @since Version 1.0 @@ -143,7 +143,7 @@ class CI_Output { * Sets the output string. * * @param string $output Output data - * @return object $this + * @return CI_Output */ public function set_output($output) { @@ -159,7 +159,7 @@ class CI_Output { * Appends data onto the output string. * * @param string $output Data to append - * @return object $this + * @return CI_Output */ public function append_output($output) { @@ -187,7 +187,7 @@ class CI_Output { * * @param string $header Header * @param bool $replace Whether to replace the old header value, if already set - * @return object $this + * @return CI_Output */ public function set_header($header, $replace = TRUE) { @@ -211,7 +211,7 @@ class CI_Output { * * @param string $mime_type Extension of the file we're outputting * @param string $charset Character set (default: NULL) - * @return object $this + * @return CI_Output */ public function set_content_type($mime_type, $charset = NULL) { @@ -239,7 +239,7 @@ class CI_Output { } $header = 'Content-Type: '.$mime_type - .(empty($charset) ? NULL : '; charset='.strtolower($charset)); + .(empty($charset) ? NULL : '; charset='.$charset); $this->headers[] = array($header, TRUE); return $this; @@ -308,7 +308,7 @@ class CI_Output { * * @param int $code Status code (default: 200) * @param string $text Optional message - * @return object $this + * @return CI_Output */ public function set_status_header($code = 200, $text = '') { @@ -322,7 +322,7 @@ class CI_Output { * Enable/disable Profiler * * @param bool $val TRUE to enable or FALSE to disable - * @return object $this + * @return CI_Output */ public function enable_profiler($val = TRUE) { @@ -339,7 +339,7 @@ class CI_Output { * Profiler section display. * * @param array $sections Profiler sections - * @return object $this + * @return CI_Output */ public function set_profiler_sections($sections) { @@ -363,7 +363,7 @@ class CI_Output { * Set Cache * * @param int $time Cache expiration time in seconds - * @return object $this + * @return CI_Output */ public function cache($time) { @@ -710,9 +710,7 @@ class CI_Output { { case 'text/html': - $size_before = strlen($output); - - if ($size_before === 0) + if (($size_before = strlen($output)) === 0) { return ''; } @@ -728,13 +726,13 @@ class CI_Output { preg_match_all('{<style.+</style>}msU', $output, $style_clean); foreach ($style_clean[0] as $s) { - $output = str_replace($s, $this->minify($s, 'text/css'), $output); + $output = str_replace($s, $this->_minify_script_style($s, TRUE), $output); } // Minify the javascript in <script> tags. foreach ($javascript_clean[0] as $s) { - $javascript_mini[] = $this->minify($s, 'text/javascript'); + $javascript_mini[] = $this->_minify_script_style($s, TRUE); } // Replace multiple spaces with a single space. @@ -780,19 +778,10 @@ class CI_Output { break; case 'text/css': - - //Remove CSS comments - $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $output); - - // Remove spaces around curly brackets, colons, - // semi-colons, parenthesis, commas - $output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!', '$1', $output); - - break; - case 'text/javascript': - // Currently leaves JavaScript untouched. + $output = $this->_minify_script_style($output); + break; default: break; @@ -801,6 +790,132 @@ class CI_Output { return $output; } + // -------------------------------------------------------------------- + + /** + * Minify Style and Script + * + * Reduce excessive size of CSS/JavaScript content. To remove spaces this + * script walks the string as an array and determines if the pointer is inside + * a string created by single quotes or double quotes. spaces inside those + * strings are not stripped. Opening and closing tags are severed from + * the string initially and saved without stripping whitespace to preserve + * the tags and any associated properties if tags are present + * + * Minification logic/workflow is similar to methods used by Douglas Crockford + * in JSMIN. http://www.crockford.com/javascript/jsmin.html + * + * KNOWN ISSUE: ending a line with a closing parenthesis ')' and no semicolon + * where there should be one will break the Javascript. New lines after a + * closing parenthesis are not recognized by the script. For best results + * be sure to terminate lines with a semicolon when appropriate. + * + * @param string $output Output to minify + * @param bool $has_tags Specify if the output has style or script tags + * @return string Minified output + */ + protected function _minify_script_style($output, $has_tags = FALSE) + { + // We only need this if there are tags in the file + if ($has_tags === TRUE) + { + // Remove opening tag and save for later + $pos = strpos($output, '>') + 1; + $open_tag = substr($output, 0, $pos); + $output = substr_replace($output, '', 0, $pos); + + // Remove closing tag and save it for later + $end_pos = strlen($output); + $pos = strpos($output, '</'); + $closing_tag = substr($output, $pos, $end_pos); + $output = substr_replace($output, '', $pos); + } + + // Remove CSS comments + $output = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!i', '', $output); + + // Remove spaces around curly brackets, colons, + // semi-colons, parenthesis, commas + $output = preg_replace('!\s*(:|;|,|}|{|\(|\))\s*!i', '$1', $output); + + // Replace tabs with spaces + // Replace carriage returns & multiple new lines with single new line + // and trim any leading or trailing whitespace + $output = trim(preg_replace(array('/\t+/', '/\r/', '/\n+/'), array(' ', "\n", "\n"), $output)); + + // Remove spaces when safe to do so. + $in_string = $in_dstring = $prev = FALSE; + $array_output = str_split($output); + foreach ($array_output as $key => $value) + { + if ($in_string === FALSE && $in_dstring === FALSE) + { + if ($value === ' ') + { + // Get the next element in the array for comparisons + $next = $array_output[$key + 1]; + + // Strip spaces preceded/followed by a non-ASCII character + // or not preceded/followed by an alphanumeric + // or not preceded/followed \ $ and _ + if ((preg_match('/^[\x20-\x7f]*$/D', $next) OR preg_match('/^[\x20-\x7f]*$/D', $prev)) + && ( ! ctype_alnum($next) OR ! ctype_alnum($prev)) + && ! in_array($next, array('\\', '_', '$'), TRUE) + && ! in_array($prev, array('\\', '_', '$'), TRUE) + ) + { + unset($array_output[$key]); + } + } + else + { + // Save this value as previous for the next iteration + // if it is not a blank space + $prev = $value; + } + } + + if ($value === "'") + { + $in_string = ! $in_string; + } + elseif ($value === '"') + { + $in_dstring = ! $in_dstring; + } + } + + // Put the string back together after spaces have been stripped + $output = implode($array_output); + + // Remove new line characters unless previous or next character is + // printable or Non-ASCII + preg_match_all('/[\n]/', $output, $lf, PREG_OFFSET_CAPTURE); + $removed_lf = 0; + foreach ($lf as $feed_position) + { + foreach ($feed_position as $position) + { + $position = $position[1] - $removed_lf; + $next = $output[$position + 1]; + $prev = $output[$position - 1]; + if ( ! ctype_print($next) && ! ctype_print($prev) + && ! preg_match('/^[\x20-\x7f]*$/D', $next) + && ! preg_match('/^[\x20-\x7f]*$/D', $prev) + ) + { + $output = substr_replace($output, '', $position, 1); + $removed_lf++; + } + } + } + + // Put the opening and closing tags back if applicable + return isset($open_tag) + ? $open_tag.$output.$closing_tag + : $output; + } + } /* End of file Output.php */ |