From 6b32d6bc936b82f0d4f09f47ce6cb49f67dd40a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20S=CC=8Ckoda?= Date: Thu, 1 Aug 2013 23:20:24 +0200 Subject: [PATCH] MDL-40995 simplify minify integration and fix all known issues --- lib/classes/minify.php | 149 ++++++++++++++++++++++++++ lib/csslib.php | 88 ++------------- lib/deprecatedlib.php | 26 +++++ lib/editor/tinymce/plugins/loader.php | 2 +- lib/javascript.php | 3 +- lib/jslib.php | 72 +------------ lib/minify/readme_moodle.txt | 8 +- lib/outputrequirementslib.php | 2 +- lib/tests/minify_test.php | 122 +++++++++++++++++++++ lib/upgrade.txt | 2 + theme/javascript.php | 2 +- 11 files changed, 316 insertions(+), 160 deletions(-) create mode 100644 lib/classes/minify.php create mode 100644 lib/tests/minify_test.php diff --git a/lib/classes/minify.php b/lib/classes/minify.php new file mode 100644 index 0000000000000..19d734f75e2b2 --- /dev/null +++ b/lib/classes/minify.php @@ -0,0 +1,149 @@ +. + +/** + * JS and CSS compression. + * + * @package core + * @copyright 2013 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Collection of JS and CSS compression methods. + */ +class core_minify { + /** + * Minify JS code. + * + * @param string $content + * @return string minified JS code + */ + public static function js($content) { + global $CFG; + require_once("$CFG->libdir/minify/lib/JSMinPlus.php"); + + try { + ob_start(); // JSMinPlus just echos errors, weird... + $compressed = JSMinPlus::minify($content); + if ($compressed !== false) { + ob_end_clean(); + return $compressed; + } + $error = ob_get_clean(); + + } catch (Exception $e) { + ob_end_clean(); + $error = $e->getMessage(); + } + + $return = <<libdir/minify/lib/Minify/CSS/Compressor.php"); + + $error = 'unknown'; + try { + $compressed = Minify_CSS_Compressor::process($content); + if ($compressed !== false) { + return $compressed; + } + + } catch (Exception $e) { + $error = $e->getMessage(); + } + + $return = <<enablecssoptimiser) && $theme->supportscssoptimisation) { // This is an experimental feature introduced in Moodle 2.3 @@ -51,10 +56,6 @@ function css_store_css(theme_config $theme, $csspath, array $cssfiles, $chunk = // the CSS before it is cached removing excess styles and rules and stripping // out any extraneous content such as comments and empty rules. $optimiser = new css_optimiser; - $css = ''; - foreach ($cssfiles as $file) { - $css .= file_get_contents($file)."\n"; - } $css = $theme->post_process($css); $css = $optimiser->process($css); @@ -73,7 +74,8 @@ function css_store_css(theme_config $theme, $csspath, array $cssfiles, $chunk = // However it has the distinct disadvantage of having to minify the CSS // before running the post process functions. Potentially things may break // here if theme designers try to push things with CSS post processing. - $css = $theme->post_process(css_minify_css($cssfiles)); + $css = $theme->post_process($css); + $css = core_minify::css($css); } clearstatcache(); @@ -294,80 +296,6 @@ function css_send_css_not_found() { die('CSS was not found, sorry.'); } -/** - * Uses the minify library to compress CSS. - * - * This is used if $CFG->enablecssoptimiser has been turned off. This was - * the original CSS optimisation library. - * It removes whitespace and shrinks things but does no apparent optimisation. - * Note the minify library is still being used for JavaScript. - * - * @param array $files An array of files to minify - * @return string The minified CSS - */ -function css_minify_css($files) { - global $CFG; - - if (empty($files)) { - return ''; - } - - // We do not really want any 304 here! - // There does not seem to be any better way to prevent them here. - unset($_SERVER['HTTP_IF_NONE_MATCH']); - unset($_SERVER['HTTP_IF_MODIFIED_SINCE']); - - require_once("$CFG->libdir/minify/lib/Minify/Loader.php"); - Minify_Loader::register(); - - if (0 === stripos(PHP_OS, 'win')) { - Minify::setDocRoot(); // IIS may need help - } - // disable all caching, we do it in moodle - Minify::setCache(null, false); - - $options = array( - // JSMin is not GNU GPL compatible, use the plus version instead. - 'minifiers' => array(Minify::TYPE_JS => array('JSMinPlus', 'minify')), - 'bubbleCssImports' => false, - // Don't gzip content we just want text for storage - 'encodeOutput' => false, - // Maximum age to cache, not used but required - 'maxAge' => (60*60*24*20), - // The files to minify - 'files' => $files, - // Turn orr URI rewriting - 'rewriteCssUris' => false, - // This returns the CSS rather than echoing it for display - 'quiet' => true - ); - - $error = 'unknown'; - try { - $result = Minify::serve('Files', $options); - if ($result['success'] and $result['statusCode'] == 200) { - return $result['content']; - } - } catch (Exception $e) { - $error = $e->getMessage(); - $error = str_replace("\r", ' ', $error); - $error = str_replace("\n", ' ', $error); - } - - // minification failed - try to inform the theme developer and include the non-minified version - $css = <<dirroot/lib/jslib.php"); +require_once("$CFG->dirroot/lib/classes/minify.php"); if ($slashargument = min_get_slash_argument()) { $slashargument = ltrim($slashargument, '/'); @@ -90,7 +91,7 @@ js_send_cached($candidate, $etag); } else { - js_write_cache_file_content($candidate, js_minify($jsfiles)); + js_write_cache_file_content($candidate, core_minify::js_files($jsfiles)); // verify nothing failed in cache file creation clearstatcache(); if (file_exists($candidate)) { diff --git a/lib/jslib.php b/lib/jslib.php index f7d7bd34434d6..d3114c104ae58 100644 --- a/lib/jslib.php +++ b/lib/jslib.php @@ -23,7 +23,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -//NOTE: do not verify MOODLE_INTERNAL here, this is used from themes too +defined('MOODLE_INTERNAL') || die(); /** * Send javascript file content with as much caching as possible @@ -93,76 +93,6 @@ function js_send_unmodified($lastmodified, $etag) { die; } -/** - * Minify javascript files - * @param array $files - * @return string - */ -function js_minify($files) { - global $CFG; - - if (empty($files)) { - return ''; - } - - require_once("$CFG->libdir/minify/lib/Minify/Loader.php"); - Minify_Loader::register(); - - // We do not really want any 304 here! - // There does not seem to be any better way to prevent them here. - unset($_SERVER['HTTP_IF_NONE_MATCH']); - unset($_SERVER['HTTP_IF_MODIFIED_SINCE']); - - if (0 === stripos(PHP_OS, 'win')) { - Minify::setDocRoot(); // IIS may need help - } - // disable all caching, we do it in moodle - Minify::setCache(null, false); - - $options = array( - // JSMin is not GNU GPL compatible, use the plus version instead. - 'minifiers' => array(Minify::TYPE_JS => array('JSMinPlus', 'minify')), - 'bubbleCssImports' => false, - // Don't gzip content we just want text for storage - 'encodeOutput' => false, - // Maximum age to cache, not used but required - 'maxAge' => 1800, - // The files to minify - 'files' => $files, - // Turn orr URI rewriting - 'rewriteCssUris' => false, - // This returns the CSS rather than echoing it for display - 'quiet' => true - ); - - $error = 'unknown'; - try { - $result = Minify::serve('Files', $options); - if ($result['success'] and $result['statusCode'] == 200) { - return $result['content']; - } - } catch (Exception $e) { - $error = $e->getMessage(); - $error = str_replace("\r", ' ', $error); - $error = str_replace("\n", ' ', $error); - } - - // minification failed - try to inform the theme developer and include the non-minified version - $js = <<get($keyname); if ($configfn === false) { require_once($CFG->libdir . '/jslib.php'); - $configfn = js_minify($fullpath); + $configfn = core_minify::js_files(array($fullpath)); $cache->set($keyname, $configfn); } } diff --git a/lib/tests/minify_test.php b/lib/tests/minify_test.php new file mode 100644 index 0000000000000..d170d5527f737 --- /dev/null +++ b/lib/tests/minify_test.php @@ -0,0 +1,122 @@ +. + +/** + * core_minify related tests. + * + * @package core + * @category phpunit + * @copyright 2013 Petr Skoda {@link http://skodak.org} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Class core_minify_testcase. + */ +class core_minify_testcase extends advanced_testcase { + public function test_css() { + $css = " +body { +background: #fff; +margin: 0; +padding: 0; +color: #281f18; +}"; + + $this->assertSame("body{background:#fff;margin:0;padding:0;color:#281f18}", core_minify::css($css)); + } + + public function test_css_files() { + global $CFG; + + $testfile1 = "$CFG->tempdir/test1.css"; + $testfile2 = "$CFG->tempdir/test2.css"; + $testfile3 = "$CFG->tempdir/test3.css"; + + $css1 = " +body { +background: #fff; +margin: 0; +padding: 0; +color: #281f18; +}"; + + $css2 = "body{}"; + + file_put_contents($testfile1, $css1); + file_put_contents($testfile2, $css2); + + $files = array($testfile1, $testfile2); + + $this->assertSame("body{background:#fff;margin:0;padding:0;color:#281f18}\nbody{}", core_minify::css_files($files)); + + + $files = array($testfile1, $testfile2, $testfile3); + + $this->assertStringStartsWith("body{background:#fff;margin:0;padding:0;color:#281f18}\nbody{}\n\n\n/* Cannot read CSS file ", @core_minify::css_files($files)); + + unlink($testfile1); + unlink($testfile2); + } + + public function test_js() { + $js = " +function hm() +{ +} +"; + + $this->assertSame("function hm(){}", core_minify::js($js)); + + $js = "function hm{}"; + $result = core_minify::js($js); + $this->assertStringStartsWith("\ntry {console.log('Error: Minimisation of JavaScript failed!');} catch (e) {}", $result); + $this->assertContains($js, $result); + } + + public function test_js_files() { + global $CFG; + + $testfile1 = "$CFG->tempdir/test1.js"; + $testfile2 = "$CFG->tempdir/test2.js"; + $testfile3 = "$CFG->tempdir/test3.js"; + + $js1 = " +function hm() +{ +} +"; + + $js2 = "function oh(){}"; + + file_put_contents($testfile1, $js1); + file_put_contents($testfile2, $js2); + + $files = array($testfile1, $testfile2); + + $this->assertSame("function hm(){}\nfunction oh(){}", core_minify::js_files($files)); + + $files = array($testfile1, $testfile2, $testfile3); + + $this->assertStringStartsWith("function hm(){}\nfunction oh(){}\n\n\n// Cannot read JS file ", @core_minify::js_files($files)); + + unlink($testfile1); + unlink($testfile2); + } +} diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 92b63753769dd..0d02f327f05d8 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -82,6 +82,8 @@ Misc: * httpsrequired() -> $PAGE->https_required() * detect_munged_arguments() -> clean_param([...], PARAM_FILE) * mygroupid() -> groups_get_all_groups() + * js_minify() -> core_minify::js_files() + * css_minify_css() -> core_minify::css_files() YUI: * moodle-core-notification has been deprecated with a recommendation of diff --git a/theme/javascript.php b/theme/javascript.php index 8c3457308a78f..e1b9606e0e3a5 100644 --- a/theme/javascript.php +++ b/theme/javascript.php @@ -95,7 +95,7 @@ make_localcache_directory('theme', false); -js_write_cache_file_content($candidate, js_minify($theme->javascript_files($type))); +js_write_cache_file_content($candidate, core_minify::js_files($theme->javascript_files($type))); // Verify nothing failed in cache file creation. clearstatcache(); if (file_exists($candidate)) {