From eb092734a11c8e99e913c3a8f2216b737e10497a Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:27:15 +0400 Subject: [PATCH 01/13] feat(php) - type support --- src/baseTranspiler.ts | 2 ++ src/phpTranspiler.ts | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/baseTranspiler.ts b/src/baseTranspiler.ts index 495e5d0c..c1d8bfae 100644 --- a/src/baseTranspiler.ts +++ b/src/baseTranspiler.ts @@ -186,6 +186,7 @@ class BaseTranspiler { uncamelcaseIdentifiers; asyncTranspiling; requiresReturnType; + supportVariableType; requiresParameterType; supportsFalsyOrTruthyValues; requiresCallExpressionCast; @@ -198,6 +199,7 @@ class BaseTranspiler { this.id = "base"; this.uncamelcaseIdentifiers = false; this.requiresReturnType = false; + this.supportVariableType = false; this.requiresParameterType = false; this.supportsFalsyOrTruthyValues = true; this.requiresCallExpressionCast = false; diff --git a/src/phpTranspiler.ts b/src/phpTranspiler.ts index e7c8fe9c..6f811a6a 100644 --- a/src/phpTranspiler.ts +++ b/src/phpTranspiler.ts @@ -99,6 +99,14 @@ export class PhpTranspiler extends BaseTranspiler { return `'${identifier}'`; // Transpile function reference as string } } + // add type support to variable + const type = this.getTypeFromRawType(global.checker.getTypeAtLocation(node)); + if ( + ts.isPropertyDeclaration(valueDecl) || + (ts.isParameter(valueDecl) && ts.isParameter(node.parent)) + ) { + return `${type} $${identifier}`; + } } // below is commented, due to : https://github.com/ccxt/ast-transpiler/pull/15 From 30fd9b6874340c1d1757b9153b751eb5b00f1a4f Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:42:01 +0400 Subject: [PATCH 02/13] type support --- src/phpTranspiler.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/phpTranspiler.ts b/src/phpTranspiler.ts index 6f811a6a..4f334c89 100644 --- a/src/phpTranspiler.ts +++ b/src/phpTranspiler.ts @@ -100,12 +100,14 @@ export class PhpTranspiler extends BaseTranspiler { } } // add type support to variable - const type = this.getTypeFromRawType(global.checker.getTypeAtLocation(node)); - if ( - ts.isPropertyDeclaration(valueDecl) || - (ts.isParameter(valueDecl) && ts.isParameter(node.parent)) - ) { - return `${type} $${identifier}`; + if (this.supportVariableType) { + const type = this.getTypeFromRawType(global.checker.getTypeAtLocation(node)); + if ( + ts.isPropertyDeclaration(valueDecl) || + (ts.isParameter(valueDecl) && ts.isParameter(node.parent)) + ) { + return `${type} $${identifier}`; + } } } From 5e4873f1394979271654b2c762f3744a1ee96699 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:21:31 +0400 Subject: [PATCH 03/13] php support integration --- .../php/transpilable_with_types.php | 72 +++++++++++++++++++ tests/integration/source/transpilable.ts | 9 +++ tests/integration/test.ts | 20 +++++- 3 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 tests/integration/php/transpilable_with_types.php diff --git a/tests/integration/php/transpilable_with_types.php b/tests/integration/php/transpilable_with_types.php new file mode 100644 index 00000000..f323c647 --- /dev/null +++ b/tests/integration/php/transpilable_with_types.php @@ -0,0 +1,72 @@ +stringifyNumber(4)); // should print 4 + $arr = [1, 2, 3, 4]; + custom_echo(count($arr)); // should print 4 + $first = $arr[0]; + custom_echo($first); // should print 1 + $dict = array( + 'a' => 'b', + ); + custom_echo($dict['a']); // should print "b" + $i = 0; + for ($w = 0; $w < 10; $w++) { + $i = $i + 1; + } + custom_echo(((string) $i)); // should print 10 + $list2 = [1, 2, 3, 4, 5]; + $list2 = array_reverse($list2); + custom_echo($list2[0]); // should print 5 + //should delete key from dict + $dict2 = array( + 'a' => 1, + 'b' => 2, + ); + unset($dict2['a']); + $dictKeys = is_array($dict2) ? array_keys($dict2) : array(); + custom_echo(count($dictKeys)); // should print 1 + custom_echo($dictKeys[0]); // should print "b" + $firstConcat = ['a', 'b']; + $secondConcat = ['c', 'd']; + $both = array_merge($firstConcat, $secondConcat); + custom_echo(count($both)); // should print 4 + custom_echo($both[2]); // should print "c" + } + + public function someMethod($arg1, $arg2) { + $res = $arg1; + custom_echo($res); + } +} + +?> \ No newline at end of file diff --git a/tests/integration/source/transpilable.ts b/tests/integration/source/transpilable.ts index dd59729a..3b4db5ff 100644 --- a/tests/integration/source/transpilable.ts +++ b/tests/integration/source/transpilable.ts @@ -8,6 +8,10 @@ class Second { class Test { + strprop: string = "test"; + numprop: number = 1; + boolprop: boolean = false; + public test() { var a = 1; var b = 2; @@ -64,6 +68,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 { diff --git a/tests/integration/test.ts b/tests/integration/test.ts index 687af3ec..ce474907 100644 --- a/tests/integration/test.ts +++ b/tests/integration/test.ts @@ -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"; @@ -35,6 +36,13 @@ const langConfig = [ language: "go", async: true }, + { + language: "php", + async: true, + parser: { + supportVariableType: true + } + }, ] function transpileTests() { @@ -50,13 +58,16 @@ function transpileTests() { const transpiler = new Transpiler(parseConfig); const result = transpiler.transpileDifferentLanguagesByPath(langConfig as any, TS_TRANSPILABLE_FILE); - let phpRes = `` as string; - phpRes = (phpRes as any).replaceAll('var_dump', 'custom_echo'); + let phpResWrapper = (content) => { + const res = `` 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'); - const goImports = [ '\n', 'import (', @@ -67,6 +78,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); @@ -77,6 +89,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; From 3a8502c9b414688e529e55a3a69b39baf5554175 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:22:26 +0400 Subject: [PATCH 04/13] remove --- .../php/transpilable_with_types.php | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 tests/integration/php/transpilable_with_types.php diff --git a/tests/integration/php/transpilable_with_types.php b/tests/integration/php/transpilable_with_types.php deleted file mode 100644 index f323c647..00000000 --- a/tests/integration/php/transpilable_with_types.php +++ /dev/null @@ -1,72 +0,0 @@ -stringifyNumber(4)); // should print 4 - $arr = [1, 2, 3, 4]; - custom_echo(count($arr)); // should print 4 - $first = $arr[0]; - custom_echo($first); // should print 1 - $dict = array( - 'a' => 'b', - ); - custom_echo($dict['a']); // should print "b" - $i = 0; - for ($w = 0; $w < 10; $w++) { - $i = $i + 1; - } - custom_echo(((string) $i)); // should print 10 - $list2 = [1, 2, 3, 4, 5]; - $list2 = array_reverse($list2); - custom_echo($list2[0]); // should print 5 - //should delete key from dict - $dict2 = array( - 'a' => 1, - 'b' => 2, - ); - unset($dict2['a']); - $dictKeys = is_array($dict2) ? array_keys($dict2) : array(); - custom_echo(count($dictKeys)); // should print 1 - custom_echo($dictKeys[0]); // should print "b" - $firstConcat = ['a', 'b']; - $secondConcat = ['c', 'd']; - $both = array_merge($firstConcat, $secondConcat); - custom_echo(count($both)); // should print 4 - custom_echo($both[2]); // should print "c" - } - - public function someMethod($arg1, $arg2) { - $res = $arg1; - custom_echo($res); - } -} - -?> \ No newline at end of file From 6049d1969bec95dd9c75f7bb75fabd6b27079053 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:22:56 +0400 Subject: [PATCH 05/13] space --- tests/integration/test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/test.ts b/tests/integration/test.ts index ce474907..9e7f052a 100644 --- a/tests/integration/test.ts +++ b/tests/integration/test.ts @@ -68,6 +68,7 @@ function transpileTests() { let csharp = 'namespace tests;\n' + result[0].content; csharp = csharp.replace('class Test', 'partial class Test'); + const goImports = [ '\n', 'import (', From c9f7456a2a07e5d23cdcc446e89f5b93fde44016 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:22:32 +0400 Subject: [PATCH 06/13] rawTypeSupport --- src/phpTranspiler.ts | 8 +++++++ tests/phpTranspiler.test.ts | 48 ++++++++++++++++++++++++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/phpTranspiler.ts b/src/phpTranspiler.ts index 4f334c89..28701c00 100644 --- a/src/phpTranspiler.ts +++ b/src/phpTranspiler.ts @@ -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; @@ -126,6 +127,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 = '.'; diff --git a/tests/phpTranspiler.test.ts b/tests/phpTranspiler.test.ts index e1405910..8980279c 100644 --- a/tests/phpTranspiler.test.ts +++ b/tests/phpTranspiler.test.ts @@ -7,6 +7,8 @@ jest.mock('module',()=>({ })); let transpiler: Transpiler; +let transpilerWithType: Transpiler; + beforeAll(() => { const config = { @@ -18,6 +20,14 @@ beforeAll(() => { } } transpiler = new Transpiler(config); + + + const config2 ={ + 'php': { + 'supportVariableType': true + } + } + transpilerWithType = new Transpiler(config2); }) describe('php transpiling tests', () => { @@ -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');"; @@ -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); + }); }); From fdf9639cb5842418e630d9edc90295e8f081ed2e Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:06:19 +0400 Subject: [PATCH 07/13] float support --- src/phpTranspiler.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/phpTranspiler.ts b/src/phpTranspiler.ts index 28701c00..a4d5d210 100644 --- a/src/phpTranspiler.ts +++ b/src/phpTranspiler.ts @@ -102,11 +102,15 @@ export class PhpTranspiler extends BaseTranspiler { } // add type support to variable if (this.supportVariableType) { - const type = this.getTypeFromRawType(global.checker.getTypeAtLocation(node)); + const typeRaw = global.checker.getTypeAtLocation(node); + let type = this.getTypeFromRawType(typeRaw); if ( ts.isPropertyDeclaration(valueDecl) || (ts.isParameter(valueDecl) && ts.isParameter(node.parent)) ) { + if (type === undefined && typeRaw?.intrinsicName === 'number') { + type = 'float'; + } return `${type} $${identifier}`; } } From e4eea7dcc452ed4b0b654e70cb3b2743df7afdd1 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:07:27 +0400 Subject: [PATCH 08/13] obj --- src/phpTranspiler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phpTranspiler.ts b/src/phpTranspiler.ts index a4d5d210..9c572b27 100644 --- a/src/phpTranspiler.ts +++ b/src/phpTranspiler.ts @@ -108,7 +108,7 @@ export class PhpTranspiler extends BaseTranspiler { ts.isPropertyDeclaration(valueDecl) || (ts.isParameter(valueDecl) && ts.isParameter(node.parent)) ) { - if (type === undefined && typeRaw?.intrinsicName === 'number') { + if (type === 'object' && typeRaw?.intrinsicName === 'number') { type = 'float'; } return `${type} $${identifier}`; From 39946fba8244cc852ea7e0dacac28cd1cfe7a6f1 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:46:24 +0400 Subject: [PATCH 09/13] fixes --- tests/phpTranspiler.test.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/phpTranspiler.test.ts b/tests/phpTranspiler.test.ts index 8980279c..8ded0092 100644 --- a/tests/phpTranspiler.test.ts +++ b/tests/phpTranspiler.test.ts @@ -311,8 +311,9 @@ describe('php transpiling tests', () => { " public static x: number = 10;\n" + " public static y: string = \"test\";\n" + " public static a1: string[] = [ 'a', 'b' ];\n" + - " public static a2: any = whatever;\n" + - " public static a3: any = {};\n" + + " public static a2: any = Abc;\n" + + " public static a3: any = abc;\n" + + " public static a4: any = {};\n" + " mainFeature(message) {\n" + " console.log(\"Hello! I'm inside main class:\" + message)\n" + " }\n" + @@ -322,8 +323,9 @@ describe('php transpiling tests', () => { " public static $x = 10;\n" + " public static $y = 'test';\n" + " public static $a1 = ['a', 'b'];\n" + - " public static $a2 = whatever;\n" + - " public static $a3 = array();\n" + + " public static $a2 = Abc;\n" + + " public static $a3 = $abc;\n" + + " public static $a4 = array();\n" + "\n" + " public function mainFeature($message) {\n" + " var_dump('Hello! I\\'m inside main class:' . $message);\n" + @@ -873,7 +875,7 @@ describe('php transpiling tests', () => { const output = transpiler.transpilePhp(ts).content; expect(output).toBe(php); }); - test('should transpile file from path', () => { + test.only('should transpile file from path', () => { transpiler.setPhpUncamelCaseIdentifiers(true); const php = readFileSync ('./tests/files/output/php/test1.php', "utf8"); const output = transpiler.transpilePhpByPath('./tests/files/input/test1.ts').content; From cc8637ce46213391d546433a57668976e1872e72 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Wed, 6 Nov 2024 20:50:29 +0400 Subject: [PATCH 10/13] not only --- tests/phpTranspiler.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpTranspiler.test.ts b/tests/phpTranspiler.test.ts index 8ded0092..75e9ec39 100644 --- a/tests/phpTranspiler.test.ts +++ b/tests/phpTranspiler.test.ts @@ -875,7 +875,7 @@ describe('php transpiling tests', () => { const output = transpiler.transpilePhp(ts).content; expect(output).toBe(php); }); - test.only('should transpile file from path', () => { + test('should transpile file from path', () => { transpiler.setPhpUncamelCaseIdentifiers(true); const php = readFileSync ('./tests/files/output/php/test1.php', "utf8"); const output = transpiler.transpilePhpByPath('./tests/files/input/test1.ts').content; From 6b0988f7b2d8baaf5bf0ede3647d9f4cb268a14d Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Wed, 6 Nov 2024 21:43:55 +0400 Subject: [PATCH 11/13] varname --- tests/csharpTranspiler.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/csharpTranspiler.test.ts b/tests/csharpTranspiler.test.ts index 2e0b8195..27ca73d0 100644 --- a/tests/csharpTranspiler.test.ts +++ b/tests/csharpTranspiler.test.ts @@ -93,7 +93,7 @@ describe('csharp transpiling tests', () => { " public object x = 10;\n" + " public string y = \"test\";\n" + " public List z1 = new List() {\"a\", \"b\"};\n" + - " public Dictionary z2 = whatever;\n" + + " public Dictionary z2 = whatever;\n" + " public Dictionary z3 = new Dictionary() {};\n" + "\n" + " public virtual void mainFeature(object message)\n" + From d65593e4649707ff7ec010c585200b719e0987f8 Mon Sep 17 00:00:00 2001 From: "T. Todua" <7117978+ttodua@users.noreply.github.com> Date: Wed, 6 Nov 2024 22:35:41 +0400 Subject: [PATCH 12/13] Discard changes to tests/csharpTranspiler.test.ts --- tests/csharpTranspiler.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/csharpTranspiler.test.ts b/tests/csharpTranspiler.test.ts index 27ca73d0..2e0b8195 100644 --- a/tests/csharpTranspiler.test.ts +++ b/tests/csharpTranspiler.test.ts @@ -93,7 +93,7 @@ describe('csharp transpiling tests', () => { " public object x = 10;\n" + " public string y = \"test\";\n" + " public List z1 = new List() {\"a\", \"b\"};\n" + - " public Dictionary z2 = whatever;\n" + + " public Dictionary z2 = whatever;\n" + " public Dictionary z3 = new Dictionary() {};\n" + "\n" + " public virtual void mainFeature(object message)\n" + From 7d108ba4c3d725fe1b966db3f833e2cd4e898349 Mon Sep 17 00:00:00 2001 From: "t.t" <7117978+ttodua@users.noreply.github.com> Date: Wed, 6 Nov 2024 22:49:09 +0400 Subject: [PATCH 13/13] revert --- tests/phpTranspiler.test.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/phpTranspiler.test.ts b/tests/phpTranspiler.test.ts index 75e9ec39..8980279c 100644 --- a/tests/phpTranspiler.test.ts +++ b/tests/phpTranspiler.test.ts @@ -311,9 +311,8 @@ describe('php transpiling tests', () => { " public static x: number = 10;\n" + " public static y: string = \"test\";\n" + " public static a1: string[] = [ 'a', 'b' ];\n" + - " public static a2: any = Abc;\n" + - " public static a3: any = abc;\n" + - " public static a4: any = {};\n" + + " public static a2: any = whatever;\n" + + " public static a3: any = {};\n" + " mainFeature(message) {\n" + " console.log(\"Hello! I'm inside main class:\" + message)\n" + " }\n" + @@ -323,9 +322,8 @@ describe('php transpiling tests', () => { " public static $x = 10;\n" + " public static $y = 'test';\n" + " public static $a1 = ['a', 'b'];\n" + - " public static $a2 = Abc;\n" + - " public static $a3 = $abc;\n" + - " public static $a4 = array();\n" + + " public static $a2 = whatever;\n" + + " public static $a3 = array();\n" + "\n" + " public function mainFeature($message) {\n" + " var_dump('Hello! I\\'m inside main class:' . $message);\n" +