-
Notifications
You must be signed in to change notification settings - Fork 24
examples_php
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.