Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(php) - type support #18

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions src/baseTranspiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
uncamelcaseIdentifiers;
asyncTranspiling;
requiresReturnType;
supportVariableType;
requiresParameterType;
supportsFalsyOrTruthyValues;
requiresCallExpressionCast;
Expand All @@ -198,6 +199,7 @@
this.id = "base";
this.uncamelcaseIdentifiers = false;
this.requiresReturnType = false;
this.supportVariableType = false;
this.requiresParameterType = false;
this.supportsFalsyOrTruthyValues = true;
this.requiresCallExpressionCast = false;
Expand Down Expand Up @@ -338,7 +340,7 @@

let method = undefined;

let parentClass = (ts as any).getAllSuperTypeNodes(node.parent)[0];

Check warning on line 343 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Unexpected any. Specify a different type

while (parentClass !== undefined) {
const parentClassType = global.checker.getTypeAtLocation(parentClass);
Expand All @@ -355,13 +357,13 @@
if (ts.isMethodDeclaration(elem)) {

const name = elem.name.getText().trim();
if ((node as any).name.escapedText === name) {

Check warning on line 360 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Unexpected any. Specify a different type
method = elem;
}
}
});

parentClass = (ts as any).getAllSuperTypeNodes(parentClassDecl)[0] ?? undefined;

Check warning on line 366 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Unexpected any. Specify a different type
}


Expand All @@ -374,7 +376,7 @@
return this.DEFAULT_IDENTATION.repeat(parseInt(num));
}

getBlockOpen(identation){

Check warning on line 379 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'identation' is defined but never used
return this.SPACE_BEFORE_BLOCK_OPENING + this.BLOCK_OPENING_TOKEN + "\n";
}

Expand Down Expand Up @@ -439,11 +441,11 @@
return this.getIden(identation) + `${left} instanceof ${right}`;
}

getCustomOperatorIfAny(left, right, operator) {

Check warning on line 444 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'left' is defined but never used

Check warning on line 444 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'right' is defined but never used

Check warning on line 444 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'operator' is defined but never used
return undefined;
}

printCustomBinaryExpressionIfAny(node, identation) {

Check warning on line 448 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'node' is defined but never used

Check warning on line 448 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'identation' is defined but never used
return undefined; // stub to override
}

Expand Down Expand Up @@ -491,7 +493,7 @@
return leftVar +" "+ operator + " " + rightVar.trim();
}

transformPropertyAcessExpressionIfNeeded (node) {

Check warning on line 496 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'node' is defined but never used
return undefined;
}

Expand Down
22 changes: 22 additions & 0 deletions src/phpTranspiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class PhpTranspiler extends BaseTranspiler {
super(config);
this.id = "php";
this.asyncTranspiling = config['async'] ?? true;
this.supportVariableType = config['supportVariableType'] ?? false;
this.uncamelcaseIdentifiers = config['uncamelcaseIdentifiers'] ?? false;
this.removeVariableDeclarationForFunctionExpression = config['removeFunctionAssignToVariable'] ?? false;
this.includeFunctionNameInFunctionExpressionDeclaration = config['includeFunctionNameInFunctionExpressionDeclaration'] ?? false;
Expand Down Expand Up @@ -99,6 +100,20 @@ export class PhpTranspiler extends BaseTranspiler {
return `'${identifier}'`; // Transpile function reference as string
}
}
// add type support to variable
if (this.supportVariableType) {
const typeRaw = global.checker.getTypeAtLocation(node);
let type = this.getTypeFromRawType(typeRaw);
if (
ts.isPropertyDeclaration(valueDecl) ||
(ts.isParameter(valueDecl) && ts.isParameter(node.parent))
) {
if (type === 'object' && typeRaw?.intrinsicName === 'number') {
type = 'float';
}
return `${type} $${identifier}`;
}
}
}

// below is commented, due to : https://github.com/ccxt/ast-transpiler/pull/15
Expand All @@ -116,6 +131,13 @@ export class PhpTranspiler extends BaseTranspiler {
return identifier;
}

getTypeFromRawType(type) {
const typeValue = super.getTypeFromRawType(type);
if (typeValue === undefined && type?.symbol?.escapedName) {
return type.symbol.escapedName;
}
return typeValue;
}

getCustomOperatorIfAny(left, right, operator) {
const STRING_CONCAT = '.';
Expand Down
9 changes: 9 additions & 0 deletions tests/integration/source/transpilable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class Second {

class Test {

strprop: string = "test";
numprop: number = 1;
boolprop: boolean = false;

public test() {
var a = 1;
var b = 2;
Expand Down Expand Up @@ -72,6 +76,11 @@ class Test {
console.log(both.length); // should print 4
console.log(both[2]); // should print "c"
}

someMethod(arg1: boolean, arg2: number) {
const res = arg1;
console.log(res);
}
}

export {
Expand Down
19 changes: 17 additions & 2 deletions tests/integration/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const PY_TRANSPILABLE_FILE = "./tests/integration/py/transpilable.py";
const PHP_TRANSPILABLE_FILE = "./tests/integration/php/transpilable.php";
const CS_TRANSPILABLE_FILE = "./tests/integration/cs/transpilable.cs";
const GO_TRANSPILABLE_FILE = "./tests/integration/go/transpilable.go";
const PHP_TRANSPILABLE_FILE_WITH_TYPES = "./tests/integration/php/transpilable_with_types.php";


const TS_FILE = "./tests/integration/source/init.ts";
Expand All @@ -35,6 +36,13 @@ const langConfig = [
language: "go",
async: true
},
{
language: "php",
async: true,
parser: {
supportVariableType: true
}
},
]

function transpileTests() {
Expand All @@ -50,8 +58,12 @@ function transpileTests() {
const transpiler = new Transpiler(parseConfig);
const result = transpiler.transpileDifferentLanguagesByPath(langConfig as any, TS_TRANSPILABLE_FILE);

let phpRes = `<?php\nfunction custom_echo($x){ echo (string)$x . "\n";}\n${result[2].content}\n?>` as string;
phpRes = (phpRes as any).replaceAll('var_dump', 'custom_echo');
let phpResWrapper = (content) => {
const res = `<?php\nfunction custom_echo($x){ echo (string)$x . "\n";}\n${content}\n?>` as string;
return (res as any).replaceAll('var_dump', 'custom_echo');
};
const phpRes = phpResWrapper(result[2].content);
const phpResWithTypes = phpResWrapper(result[4].content);
const pythonAsync = result[1].content;
let csharp = 'namespace tests;\n' + result[0].content;
csharp = csharp.replace('class Test', 'partial class Test');
Expand All @@ -67,6 +79,7 @@ function transpileTests() {
const go = 'package main\n' + goImports + result[3].content;

writeFileSync(PHP_TRANSPILABLE_FILE, phpRes.toString());
writeFileSync(PHP_TRANSPILABLE_FILE_WITH_TYPES, phpResWithTypes.toString());
writeFileSync(PY_TRANSPILABLE_FILE, pythonAsync);
writeFileSync(CS_TRANSPILABLE_FILE, csharp);
writeFileSync(GO_TRANSPILABLE_FILE, go);
Expand All @@ -77,6 +90,8 @@ function runCommand(command) {
exec(command, (error, stdout, stderr) => {
if (stderr !== undefined || stderr !== null) {
stderr = stderr.replace('Debugger attached.\nWaiting for the debugger to disconnect...\n', '');
// fix for windows
stderr = stderr.replace('Debugger attached.\r','').replace('\nWaiting for the debugger to disconnect...\r\n', '');
}
if (stderr.startsWith("Debugger listening") && stderr.includes("For help, see: https://nodejs.org/en/docs/inspector")) {
stderr = undefined;
Expand Down
48 changes: 47 additions & 1 deletion tests/phpTranspiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ jest.mock('module',()=>({
}));

let transpiler: Transpiler;
let transpilerWithType: Transpiler;


beforeAll(() => {
const config = {
Expand All @@ -18,6 +20,14 @@ beforeAll(() => {
}
}
transpiler = new Transpiler(config);


const config2 ={
'php': {
'supportVariableType': true
}
}
transpilerWithType = new Transpiler(config2);
})

describe('php transpiling tests', () => {
Expand Down Expand Up @@ -842,7 +852,7 @@ describe('php transpiling tests', () => {
const output = transpiler.transpilePhp(ts).content;
expect(output).toBe(php);
});
test.only('transpile constants & imports', () => {
test('transpile constants & imports', () => {
const ts = "import { decimalToPrecision, ROUND, TRUNCATE, DECIMAL_PLACES, } from '../../somewhere.js';\n" +
"const exc = new xyz ();\n" +
"assert (exc.decimalToPrecision ('12.3456000', TRUNCATE, 100, DECIMAL_PLACES) === '12.3456');";
Expand Down Expand Up @@ -894,4 +904,40 @@ describe('php transpiling tests', () => {
const output = transpiler.transpilePhp(ts).content;
expect(output).toBe(result);
});
test('should support types', () => {
const nl = '\n';
const ts =
'class RefClass {}' + nl +
'class BasicClass {' + nl +
' public stringProp: string;' + nl +
' public numProp: number;' + nl +
' public boolProp: boolean;' + nl +
' public refProp: RefClass;' + nl +
' public constructor(arg1: string, arg2: number, arg3: boolean, arg4: RefClass) {' + nl +
' this.stringProp = arg1;' + nl +
' this.numProp = arg2;' + nl +
' this.boolProp = arg3;' + nl +
' this.refProp = arg4;' + nl +
' }' + nl +
'}';
const php =
'class RefClass {' + nl +
'' + nl +
'}' + nl +
'class BasicClass {' + nl +
' public string $stringProp;' + nl +
' public object $numProp;' + nl +
' public bool $boolProp;' + nl +
' public RefClass $refProp;' + nl +
'' + nl +
' function __construct(string $arg1, object $arg2, bool $arg3, RefClass $arg4) {' + nl +
' $this->stringProp = $arg1;' + nl +
' $this->numProp = $arg2;' + nl +
' $this->boolProp = $arg3;' + nl +
' $this->refProp = $arg4;' + nl +
' }' + nl +
'}' + nl;
const output = transpilerWithType.transpilePhp(ts).content;
expect(output).toBe(php);
});
});
Loading