Skip to content
This repository has been archived by the owner on Jul 1, 2021. It is now read-only.

examples_php

Marcel Kloubert edited this page May 6, 2017 · 6 revisions

Home >> Examples >> PHP

PHP

ionCube

Encode files with ionCube BEFORE deploy

The following example shows how to use a data transformer script to encode files with ionCube BEFORE they are uploaded to a FTP server.

The example is also compatible with all other targets that support transformer setting.

First define the path(s) of the ionCube command line tool in your settings.json file inside your .vscode folder, by using placeholders:

{
    "deploy": {
        // ...

        "values": [
            {
                "name": "ionCubePath",
                "type": "static",
                "platforms": [ "win32" ],

                "value": "<THE PATH TO IONCUBE ON WINDOWS>"
            },
            {
                "name": "ionCubePath",
                "type": "static",
                "platforms": [ "darwin" ],

                "value": "<THE PATH TO IONCUBE ON MAC>"
            },
            {
                "name": "ionCubePath",
                "type": "static",
                "platforms": [ "linux" ],

                "value": "<THE PATH TO IONCUBE ON LINUX>"
            }
        ],

        // ...
    }
}

You can remove the entries, you do not need, of course!

The next step is to setup package definition:

{
    "deploy": {
        // ...

        "packages": [
            {
                "name": "My PHP files to encode",
                "files": [
                    "/**/*.php"
                ]
            }
        ],

        // ...
    }
}

Then define the target to deploy to:

{
    "deploy": {
        // ...

        "targets": [
            {
                "name": "My FTP server",
                "type": "ftp",

                "host": "mysite.example.com",
                "user": "mkloubert", "password": "P@assword123!",

                "transformer": "./.deploy/ioncube.js",
                "transformerOptions": {
                    "encoderPath": "${ionCubePath}",
                    "exclude": [
                        "/vendor/**/*.php"
                    ]
                }
            }
        ],

        // ...
    }
}

As you can see, the transformer property defines a script, called ioncube.js, in the .deploy sub folder of your workspace:

var ChildProcess = require('child_process');  // https://nodejs.org/api/child_process.html
var FS = require('fs');  // https://nodejs.org/api/fs.html
var Path = require('path');  // https://nodejs.org/api/path.html
var vscode = require('vscode');  // https://code.visualstudio.com/docs/extensionAPI/vscode-api

/**
 * Is invoked BEFORE file is going to
 * be send to remote target.
 * 
 * @return {Promise} The promise with the (encoded) data.
 */
exports.transformData = function(ctx) {
    return new Promise(function(resolve, reject) {
        var glob = ctx.require('glob');  // https://www.npmjs.com/package/glob
        var helpers = ctx.require('./helpers');  // https://mkloubert.github.io/vs-deploy/modules/_helpers_.html
        var tmp = ctx.require('tmp');  // https://www.npmjs.com/package/tmp
        var workflows = ctx.require('node-workflows');  // https://www.npmjs.com/package/node-workflows

        // from "transformerOptions" property
        // of the underlying target
        var options = ctx.options || {};

        var completed = function(err, resultData) {
            if (err) {
                reject(err);
            }
            else {
                resolve(resultData);
            }
        };

        var session = {};
        var destroySession = function(err, resultData) {
            var destroyWF = workflows.create();

            // remove temp files
            [ session.sourceFile, session.outputFile ].filter(function(tempFile) {
                return !helpers.isEmptyString(tempFile);
            }).forEach(function(tempFile) {
                // first check if 'tempFile' exists
                destroyWF.next(function(wfCtx) {
                    return new Promise(function(res, rej) {
                        FS.exists(tempFile, function(exists) {
                            res( exists );  // s. wfCtx.previousValue
                                            // below
                        });
                    });
                });

                // now remove 'tempFile'
                // if exists
                destroyWF.next(function(wfCtx) {
                    var exists = wfCtx.previousValue;
                    
                    return new Promise(function(res, rej) {
                        if (exists) {
                            FS.unlink(tempFile, function(err) {
                                if (err) {
                                    //TODO: log
                                }

                                res();
                            });
                        }
                        else {
                            res();  // does not exist (anymore)
                        }
                    });
                });
            });

            destroyWF.start().then(function() {
                completed(err, resultData);
            }, function(e) {
                //TODO: log

                completed(err, resultData);
            });
        };

        // define the tasks todo
        var wf = workflows.create();

        // [1] initialize workflow result with
        //     input data
        wf.next(function(wfCtx) {
            wfCtx.result = ctx.data;  // initialize with input data
        });

        // [2] check if file is excluded
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                if (options) {
                    // collect glob patterns and files
                    var excludePatterns = helpers.asArray(options.exclude)
                                                 .map(function(p) { 
                                                         return helpers.toStringSafe(p);
                                                      })
                                                 .filter(function(p) {
                                                            return '' !== p.trim();
                                                         });
                    excludePatterns = helpers.distinctArray(excludePatterns);

                    var excludedWF = workflows.create();

                    excludedWF.next(function(wfCtx2) {
                        wfCtx2.result = false;  // isExcluded
                    });

                    // create a glob check
                    // for each pattern
                    excludePatterns.forEach(function(pattern) {
                        excludedWF.next(function(wfCtx2) {
                            return new Promise(function(res2, rej2) {
                                glob(pattern, {
                                    absolute: true,
                                    cwd: vscode.workspace.rootPath,
                                    dot: true,
                                    nodir: true,
                                    root: vscode.workspace.rootPath,
                                }, function(err, filesToExclude) {
                                    if (err) {
                                        rej2(err);
                                    }
                                    else {
                                        // normalize paths of
                                        // found files
                                        filesToExclude = filesToExclude.map(function(f) {
                                            return normalizePath(ctx, f); 
                                        });

                                        if (filesToExclude.indexOf( normalizePath(ctx, ctx.context.file) ) > -1) {
                                            // marked as "excluded"
                                            // and stop next patterns

                                            wfCtx2.result = true;
                                            wfCtx2.finish();
                                        }

                                        res2();
                                    }
                                });
                            });
                        });
                    });

                    excludedWF.start().then(function(isExcluded) {
                        if (isExcluded) {
                            wfCtx.finish();  // do not encode this
                        }

                        res();
                    }, function(err) {
                        rej(err);  // check failed
                    });
                }
                else {
                    res();  // nothing defined
                            // for exclusion
                }
            });
        });

        // [3] define source temp file
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                tmp.tmpName({
                    keep: true,
                    prefix: 'vsd-ioncube-src-',
                    postfix: '.php',
                }, function(err, tempFile) {
                    if (err) {
                        rej(err);  // could not define temp
                                   // file for source data
                    }
                    else {
                        session.sourceFile = tempFile;

                        res();
                    }
                });
            });
        });

        // [4] define temp file for output
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                tmp.tmpName({
                    keep: true,
                    prefix: 'vsd-ioncube-out-',
                    postfix: '.php',
                }, function(err, tempFile) {
                    if (err) {
                        rej(err);  // could not define temp
                                   // file for encoded data
                    }
                    else {
                        session.outputFile = tempFile;

                        res();
                    }
                });
            });
        });

        // [5] fill source file with unhandled
        //     data from ctx.data
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                FS.writeFile(session.sourceFile, ctx.data, function(err) {
                    if (err) {
                        rej(err);  // could not write to source file
                    }
                    else {
                        res();
                    }
                });
            });
        });

        // [6] run ionCube for
        //     session.sourceFile
        //     and write to
        //     session.outputFile
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                try {
                    var ionCube = ctx.replaceWithValues(options['encoderPath']);

                    var cwd = Path.dirname(session.sourceFile);

                    var cp = ChildProcess.spawn(ionCube, [
                        session.sourceFile,
                        '-o',
                        session.outputFile
                    ]);

                    cp.once('error', function(err) {
                        rej( err );
                    });

                    cp.once('close', function(exitCode) {
                        exitCode = helpers.toStringSafe(exitCode).trim();

                        if ('0' === exitCode) {
                            res();
                        }
                        else {
                            rej( new Error("'ionCube' exited with code " + exitCode) );
                        }
                    });
                }
                catch (e) {
                    rej(e);
                }
            });
        });

        // [7] read generated, encoded PHP file
        wf.next(function(wfCtx) {
            return new Promise(function(res, rej) {
                FS.readFile(session.outputFile, function(err, resultData) {
                    if (err) {
                        rej(err);  // could not read generated
                                   // data by ionCube
                    }
                    else {
                        wfCtx.result = resultData;  // update with
                                                    // encoded data

                        res();
                    }
                });
            });
        });

        // start "ionCube" workflow
        wf.start().then(function(resultData) {
            destroySession(null, resultData);
        }, function(err) {
            //TODO: log

            destroySession(err);  // something went wrong
        });
    });
};

/**
 * Is invoked after file
 * has been downloaded from remote.
 * 
 * @return {Buffer} Currently: The source data from remote.
 */
exports.restoreData = function(ctx) {
    // currently there is no chance
    // to decode the data
    return ctx.data;
};


function normalizePath(ctx, p) {
    var helpers = ctx.require('./helpers');  // https://mkloubert.github.io/vs-deploy/modules/_helpers_.html
    var vscode = require('vscode');  // https://code.visualstudio.com/docs/extensionAPI/vscode-api

    if (helpers.isNullOrUndefined(p)) {
        return p;
    }

    p = helpers.toStringSafe(p);

    if (!Path.isAbsolute(p)) {
        p = Path.join(vscode.workspace.rootPath, p);
    }

    p = Path.resolve(p);
    p = helpers.replaceAllStrings(p, Path.sep, '/');

    return p;
}

That's all.

Now all of your .php files will be encoded, except the ones defined by the glob patterns in the transformerOptions.exclude property of the upper target.

It also works for single files.

Clone this wiki locally