UNPKG

locutus

Version:

Locutus other languages' standard libraries to JavaScript for fun and educational purposes

92 lines (91 loc) 4.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.is_callable = is_callable; const _phpRuntimeState_ts_1 = require("../_helpers/_phpRuntimeState.js"); const _phpTypes_ts_1 = require("../_helpers/_phpTypes.js"); function is_callable(mixedVar, syntaxOnly, callableName) { // discuss at: https://locutus.io/php/is_callable/ // original by: Brett Zamir (https://brett-zamir.me) // input by: François // improved by: Brett Zamir (https://brett-zamir.me) // improved by: KnightYoshi // improved by: Waldo Malqui Silva (https://fayr.us/waldo/) // note 1: The variable callableName cannot work as a string variable passed by // note 1: reference as in PHP (since JavaScript does not support passing // note 1: strings by reference), but instead will take the name of // note 1: a global variable and set that instead. // note 1: When used on an object, depends on a constructor property // note 1: being kept on the object prototype // note 2: Depending on the `callableName` that is passed, this function can use eval. // note 2: The eval input is however checked to only allow valid function names, // note 2: So it should not be unsafer than uses without eval (seeing as you can) // note 2: already pass any function to be executed here. // example 1: is_callable('is_callable') // returns 1: true // example 2: is_callable('bogusFunction', true) // returns 2: true // gives true because does not do strict checking // example 3: function SomeClass () {} // example 3: SomeClass.prototype.someMethod = function (){} // example 3: var testObj = new SomeClass() // example 3: is_callable([testObj, 'someMethod'], true, 'myVar') // example 3: var $result = myVar // returns 3: 'SomeClass::someMethod' // example 4: is_callable(function () {}) // returns 4: true // example 5: is_callable(class MyClass {}) // returns 5: false const validJSFunctionNamePattern = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/; let name = ''; let obj = null; let method = ''; let validFunctionName = false; const getFuncName = function (fn) { const funcNameMatch = /\W*function\s+([\w$]+)\s*\(/.exec(String(fn)); if (!funcNameMatch) { return '(Anonymous)'; } return funcNameMatch[1] ?? '(Anonymous)'; }; if (mixedVar == null) { return false; } if (/(^class|\(this,)/.test(String(mixedVar))) { return false; } if (typeof mixedVar === 'string') { obj = (0, _phpRuntimeState_ts_1.getPhpGlobalScope)(); method = mixedVar; name = mixedVar; validFunctionName = !!name.match(validJSFunctionNamePattern); } else if (typeof mixedVar === 'function') { return true; } else if (Array.isArray(mixedVar) && mixedVar.length === 2 && mixedVar[0] !== null && typeof mixedVar[0] === 'object' && typeof mixedVar[1] === 'string') { const receiver = (0, _phpTypes_ts_1.toPhpArrayObject)(mixedVar[0]); obj = receiver; method = mixedVar[1]; name = (typeof receiver.constructor === 'function' ? getFuncName(receiver.constructor) : '(Anonymous)') + '::' + method; } const callableCandidate = obj ? (0, _phpRuntimeState_ts_1.getPhpObjectEntry)(obj, method) : undefined; if (syntaxOnly || typeof callableCandidate === 'function') { if (callableName) { (0, _phpRuntimeState_ts_1.setPhpGlobalEntry)(callableName, name); } return true; } // validFunctionName avoids exploits // biome-ignore lint/security/noGlobalEval: needed for PHP port if (validFunctionName && typeof eval(method) === 'function') { if (callableName) { (0, _phpRuntimeState_ts_1.setPhpGlobalEntry)(callableName, name); } return true; } return false; }