UNPKG

phpjs

Version:

php.js offers community built php functions in javascript

313 lines (259 loc) 7.81 kB
if (typeof require !== 'undefined') { module.exports = function phpjsutil(config) { var self = new PhpjsUtil(config); return self; }; } function PhpjsUtil(config) { this.injectDependencies = []; // Overwrite properties with config for (var k in config) { this[k] = config[k]; } } // Should be overridden by a more sofisticated function // such as cli.debug when run in node.js PhpjsUtil.prototype.debug = function(a) { return console.log(a); }; // Should be overridden by a more sofisticated function // such as assert.deepEqual when run in node.js PhpjsUtil.prototype.equal = function(a, b) { return JSON.stringify(a) === JSON.stringify(b); }; PhpjsUtil.prototype._commentBlocks = function(code) { var cnt = 0; var comment = []; var commentBlocks = []; var i = 0; var lines = []; var raise = false; for (i in (lines = code.replace('\r', '').split('\n'))) { // Detect if line is a comment, and return the actual comment if ((comment = lines[i].match(/^\s*(\/\/|\/\*|\*)\s*(.*)$/))) { if (raise === true) { cnt = commentBlocks.length; raise = false; } if (!commentBlocks[cnt]) { commentBlocks[cnt] = {clean: [], raw: []}; } commentBlocks[cnt].clean.push(comment[2].trim()); commentBlocks[cnt].raw.push(lines[i]); } else { raise = true; } } return commentBlocks; }; PhpjsUtil.prototype._headKeys = function(headLines) { var i; var keys = {}; var match = []; var dmatch = []; var key = ''; var val = ''; var num = 0; for (i in headLines) { if (!(match = headLines[i].match(/^\s*\W?\s*([a-z\ 0-9]+)\s*:\s*(.*)\s*$/))) { continue; } key = match[1]; val = match[2]; if ((dmatch = key.match(/^(\w+)\s+(\d+)$/))) { // Things like examples and notes can be grouped key = dmatch[1]; num = dmatch[2] - 1; } else { num = 0; } if (!keys[key]) { keys[key] = []; } if (!keys[key][num]) { keys[key][num] = []; } keys[key][num].push(val); } return keys; }; PhpjsUtil.prototype.contains = function(array, value) { var i = array.length; while (i--) { if (array[i] === value) { return true; } } return false; }; PhpjsUtil.prototype._loadDependencies = function(name, headKeys, dependencies, cb) { var self = this; if (!headKeys['depends on'] || !headKeys['depends on'].length) { if (cb) { cb(null, {}); } } var i; var depname; var loaded = 0; for (i in headKeys['depends on']) { depname = headKeys['depends on'][i][0]; self.load(depname, function(err, params) { if (err) { return cb(err); } dependencies[depname] = params; self._loadDependencies(depname, params.headKeys, dependencies); if (cb && ++loaded === headKeys['depends on'].length) { cb(null, dependencies); } }); } }; PhpjsUtil.prototype.parse = function(name, code, cb) { var patFuncStart = /^\s*function\s*([^\s\()]+)\s*\(([^\)]*)\)\s*\{\s*/i; var patFuncEnd = /\s*}\s*$/; var commentBlocks = this._commentBlocks(code); var head = commentBlocks[0].raw.join('\n'); var body = code.replace(head, ''); body = body.replace(patFuncStart, ''); body = body.replace(patFuncEnd, ''); var headKeys = this._headKeys(commentBlocks[0].clean); this._loadDependencies(name, headKeys, {}, function(err, dependencies) { if (err) { return cb(err); } // Parse fucntion signature var funcSigMatch = code.match(patFuncStart); cb(null, { headKeys : headKeys, body : body, head : head, name : name, code : code, dependencies : dependencies, func_signature: funcSigMatch[0], func_name : funcSigMatch[1], func_arguments: funcSigMatch[2].split(/,\s*/), commentBlocks : commentBlocks }); }); }; PhpjsUtil.prototype.opener = function(name, cb) { return cb('Please override with a method that can translate a function-name to code in your environment'); }; PhpjsUtil.prototype.loadMultiple = function(names, cb) { var self = this; var paramsMultiple = {}; var loaded = 0; for (var i in names) { var name = names[i]; self.load(name, function(err, params) { if (err) { return cb(err); } paramsMultiple[params.name] = params; if (++loaded === names.length) { return cb(null, paramsMultiple); } }); } }; PhpjsUtil.prototype.load = function(name, cb) { var self = this; self.opener(name, function(err, code) { if (err) { return cb(err); } self.parse(name, code, function(err, params) { if (err) { return cb(err); } return cb(null, params); }); }); }; PhpjsUtil.prototype.unique = function(array) { var a = array.concat(); for (var i = 0; i < a.length; ++i) { for (var j = i + 1; j < a.length; ++j) { if (a[i] === a[j]) { a.splice(j--, 1); } } } return a; }; PhpjsUtil.prototype.getRecursiveCode = function(params) { var self = this; var codez = []; if ('name' in params) { codez.push(params.code); for (var d in params.dependencies) { codez = codez.concat(self.getRecursiveCode(params.dependencies[d])); } } codez = codez.reverse(); return codez; }; PhpjsUtil.prototype.test = function(params, cb) { var self = this; var codez = []; self.loadMultiple(self.injectDependencies, function(err, paramsMultiple) { codez.unshift('phpjs = {}'); for (var name in paramsMultiple) { codez.unshift(paramsMultiple[name].code); } var extracted = self.getRecursiveCode(params); codez = codez.concat(codez, extracted); codez.unshift('window.window' + ' = window;'); for (var global in self.globals) { codez.unshift(global + ' = ' + self.globals[global] + ';'); } // Load code // self. refers to own function // that. refers to phpjs // this. refers to phpjs var code = codez.join(';\n') .replace(/that\.([a-z_])/g, '$1') .replace(/this\.([a-z_])/g, '$1') .replace(/window\.setTimeout/g, 'setTimeout'); // self.debug(code); eval(code); // Run each example for (var i in params.headKeys.example) { var test = { example: params.headKeys.example[i].join('\n'), number: i }; if (!params.headKeys.returns[i] || !params.headKeys.returns[i].length) { cb('There is no return for example ' + i, test, params); continue; } // Needs an eval so types are cast properly, also, expected may // contain code eval('test.expected = ' + params.headKeys.returns[i].join('\n') + ''); // Let's do something evil. Execute line by line (see date.js why) // We need test.reslult be the last result of the example code for (var j in params.headKeys.example[i]) { if (+j === params.headKeys.example[i].length - 1) { eval('test.result = ' + params.headKeys.example[i][j] + ''); } else { eval(params.headKeys.example[i][j] + ''); } } var jsonExpected = JSON.stringify(test.expected, undefined, 2); var jsonResult = JSON.stringify(test.result, undefined, 2); // We cannot compare using the JSON.stringify as object order is // not guaranteed if (!self.equal(test.expected, test.result)) { err = 'expected: \n' + jsonExpected + '\n\n' + 'returned: \n' + jsonResult + '\n'; cb(err, test, params); continue; } cb(null, test, params); continue; } }); };