3

What I'm trying to do is minify inline javascript <script> within php files using Gulp. The reason why is because I'm trying to condense the overall file size (It does matter in my case). The javascript contains php variables.

I've started with the gulp-minify-inline-scripts plugin, but changed it so that it recognizes php files. Unfortunately I haven't successfully been able to output the javascript unless using html files.

My thinking is to keep the php variables and just save the contents back to the php file. Not compiling the actual php.

Plugin code:

var path = require('path');

var gutil = require('gulp-util');
var uglify = require('uglify-js');
var through = require('through2');

var PLUGIN_NAME = 'gulp-minify-inline-scripts';

module.exports = function (options) {
    return through.obj(function (file, enc, cb) {
        var self = this;

        if (file.isNull()) {
            this.push(file);
            return cb();
        }

        if (file.isStream()) {
            this.emit('error', new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
            return cb();
        }

        var fileName = file.path.split(path.sep).pop();

        //html file only
        if (!/^\.php?$/.test(path.extname(file.path))) {
            gutil.log(gutil.colors.red('[WARN] file ' + fileName + ' is not a html file'));
            this.push(file);
            return cb();
        }

        gutil.log("uglify inline scripts in html file: " + file.path);

        var html = file.contents.toString('utf8');
        var reg = /(<script(?![^>]*?\b(type=['"]text\/template['"]|src=["']))[^>]*?>)([\s\S]*?)<\/script>/g;


        html = html.replace(reg, function (str, tagStart, attr, script) {
            try {
                var result = uglify.minify(script, { fromString: true });

                return tagStart + result.code + '</script>';
            } catch (e) {
                self.emit('error', new gutil.PluginError(PLUGIN_NAME, 'uglify inline scripts error: ' + (e && e.stack)));
            }
        });


        file.contents = new Buffer(html);
        this.push(file);
        cb();
    });
};

PHP Inline Javascript to compress:

<?php 

/* Start: This is a php file

?>

<script>
    <?php $var = 'test'; ?>    
    A.config = {
        test: '<?php echo $var; ?>'
    };


    a.visible = function (image, distance) {
        var viewportWidth  = window.innerWidth || document.documentElement.clientWidth;
        var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
        var bounds;
        var gap;

        if (!image.getBoundingClientRect) {
            return false;   
        }
    };
</script>

<?php 

/* End: This is a php file

?>
6
  • 1
    It's not possible to do in general case. Commented Feb 11, 2015 at 5:58
  • I have to ask... why not? @zerkms Commented Feb 11, 2015 at 6:10
  • 1
    Because in general the outcome depends on php runtime. Commented Feb 11, 2015 at 6:21
  • But does it need to? I was thinking of still keeping the php variables, just saving the contents back to the php file. Not compiling the actual php. Commented Feb 11, 2015 at 6:57
  • 1
    That might be possible – but I doubt that gulp is able to handle that. You would have to write your own version that “recognizes” PHP variables inside the JS code, and knows that it has to leave them “untouched”. Plus, depending on your PHP code, that might introduce complexity that simply can not be handled by a reasonable parser. Commented Feb 11, 2015 at 9:13

1 Answer 1

2

For anyone that comes across this, I've written up a solution based on what @Cbroe hints at in a comment. It's really simple: wrap php code so it doesn't break jsminifier, then after the code is minified, remove the wrapped php strings to release the php variables. You could simplify this a little more by choosing better wrap strings.

I'd just be careful of what you use this for because I haven't tested for anything other than my specific use case.

UPDATE: I've been using this for several months and it has never broken. Just make sure to use the same script tag pattern that you place in the regex or else it may not find the tag in the page.

New Gulp Plugin Code:

var path = require('path');
var gutil = require('gulp-util');
var uglify = require('uglify-js');
var through = require('through2');
var PLUGIN_NAME = 'gulp-minify-inline-scripts';

module.exports = function(options) {
    return through.obj(function(file, enc, cb) {
        var self = this;

        if (file.isNull()) {
            this.push(file);
            return cb();
        }

        if (file.isStream()) {
            this.emit('error', new gutil.PluginError(PLUGIN_NAME, 'Streaming not supported'));
            return cb();
        }

        var fileName = file.path.split(path.sep).pop();

        //html file only
        if (!/^\.php?$/.test(path.extname(file.path))) {
            gutil.log(gutil.colors.red('[WARN] file ' + fileName + ' is not a html file'));
            this.push(file);
            return cb();
        }

        gutil.log("uglify inline scripts in html file: " + file.path);

        var html = file.contents.toString('utf8');
        var reg = /(<script(?![^>]*?\b(type=['"]text\/template['"]|src=["']))[^>]*?>)([\s\S]*?)<\/script>/g;

        html = html.replace(reg, function(str, tagStart, attr, script) {
            try {
                var result = uglify.minify(script, {
                    fromString: true
                });

                var code = result.code;

                code = code.split("STRIP__>\"").join('');
                code = code.split("\"<__STRIP").join('');

                return tagStart + code + '</script>';
            } catch (e) {
                self.emit('error', new gutil.PluginError(PLUGIN_NAME, 'uglify inline scripts error: ' + (e && e.stack)));
            }
        });

        file.contents = new Buffer(html);

        this.push(file);

        cb();
    });
};

PHP Inline Javascript to compress:

<?php 

/* Start: This is a php file

?>

<script>
    <?php $var = 'test'; ?>    
    A.config = {
        test: "<__STRIP'<?php echo $var; ?>'STRIP__>"
    };

    a.visible = function (image, distance) {
        var viewportWidth  = window.innerWidth || document.documentElement.clientWidth;
        var viewportHeight = window.innerHeight || document.documentElement.clientHeight;
        var bounds;
        var gap;

        if (!image.getBoundingClientRect) {
            return false;   
        }
    };
</script>

<?php 

/* End: This is a php file

?>
Sign up to request clarification or add additional context in comments.

5 Comments

You're unnecessarily tying a specific implementation of minifying into fixing a code convention that isn't best practice to begin with. You should look into Wordpress' codex.wordpress.org/Function_Reference/wp_localize_script to see an example of adding data to the page for use in script, without turning it into inline php output.
I'm trying to further understand what code convention is a problem here? Because if I call a function from inline script to a JS file, the JS file needs to be loaded for that function to run. Putting the script inline, doesn't require the JS file be loaded yet. I also wouldn't want to have to compile the inline JS in php every time a page is requested.
Putting every script or css you have on the page on the page, not separate files, is also a way to save a little overhead for loading various files, but that's not generally recommended, better to use closure-compiler or something to combine it if you need that speed. Mixing data with model and two different programming languages in one file is not generally best practice either.
For sure. You're totally right. I just see some circumstances where a little bit of inline JS won't hurt. There are definitely tradeoffs when it comes to "best practices" VS speed though. Especially considering a lot of the big sites do near the same thing I'm doing... Google being one of them... that said, I see where you're coming from.
Yes, Google invented Closure Compiler and GWT for optimization. - I'm sure their developers aren't actually writing those 2-letter variables in JS or typing out inline code on the Google homepage :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.