ether-code 0.7.8 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,8 @@ class PHPGenerator {
5
5
  this.i18n = null
6
6
  this.indent = 0
7
7
  this.output = ''
8
+ this.translationMap = {}
9
+ this.supportedLanguages = ['fr', 'en', 'es', 'ru', 'zh', 'ja']
8
10
 
9
11
  if (i18nPath) {
10
12
  this.loadI18n(i18nPath)
@@ -14,447 +16,260 @@ class PHPGenerator {
14
16
  loadI18n(filePath) {
15
17
  const content = fs.readFileSync(filePath, 'utf-8')
16
18
  this.i18n = JSON.parse(content)
17
- this.buildMaps()
19
+ this.buildTranslationMap()
18
20
  }
19
21
 
20
- buildMaps() {
21
- this.keywordMap = {}
22
- this.functionMap = {}
22
+ buildTranslationMap() {
23
+ this.translationMap = {}
23
24
 
24
25
  if (!this.i18n) return
25
26
 
26
- const addToMap = (map, section) => {
27
- if (!section) return
28
- for (const [key, translations] of Object.entries(section)) {
29
- if (translations && translations.fr) {
30
- map[translations.fr.toLowerCase()] = key
31
- }
32
- }
33
- }
34
-
35
- const addAllSections = (map) => {
36
- for (const [sectionName, section] of Object.entries(this.i18n)) {
37
- if (typeof section === 'object' && section !== null) {
38
- addToMap(map, section)
27
+ for (const [sectionName, section] of Object.entries(this.i18n)) {
28
+ if (sectionName === 'metadata' || typeof section !== 'object') continue
29
+
30
+ for (const lang of this.supportedLanguages) {
31
+ if (!section[lang]) continue
32
+
33
+ for (const [etherWord, phpCode] of Object.entries(section[lang])) {
34
+ const normalizedKey = etherWord.toLowerCase().trim()
35
+ const cleanPhp = this.cleanPhpValue(phpCode)
36
+ this.translationMap[normalizedKey] = cleanPhp
39
37
  }
40
38
  }
41
39
  }
40
+ }
42
41
 
43
- addAllSections(this.keywordMap)
44
- addAllSections(this.functionMap)
42
+ cleanPhpValue(value) {
43
+ if (typeof value !== 'string') return value
44
+ let cleaned = value.replace(/\(\)$/, '')
45
+ return cleaned
45
46
  }
46
47
 
47
48
  translate(word) {
48
- const lower = word.toLowerCase()
49
- return this.translateGeneric(lower)
50
- }
51
-
52
- translateGeneric(word) {
53
- const translations = {
54
- 'variable': '',
55
- 'constante': 'const',
56
- 'fonction': 'function',
57
- 'retourner': 'return',
58
- 'si': 'if',
59
- 'sinon': 'else',
60
- 'sinon si': 'elseif',
61
- 'pour': 'for',
62
- 'pour chaque': 'foreach',
63
- 'tant que': 'while',
64
- 'faire': 'do',
65
- 'selon': 'switch',
66
- 'cas': 'case',
67
- 'defaut': 'default',
68
- 'sortir': 'break',
69
- 'continuer': 'continue',
70
- 'essayer': 'try',
71
- 'attraper': 'catch',
72
- 'finalement': 'finally',
73
- 'lancer': 'throw',
74
- 'nouveau': 'new',
75
- 'classe': 'class',
76
- 'interface': 'interface',
77
- 'trait': 'trait',
78
- 'etend': 'extends',
79
- 'implemente': 'implements',
80
- 'utiliser': 'use',
81
- 'public': 'public',
82
- 'prive': 'private',
83
- 'protege': 'protected',
84
- 'statique': 'static',
85
- 'final': 'final',
86
- 'abstrait': 'abstract',
87
- 'constructeur': '__construct',
88
- 'destructeur': '__destruct',
89
- 'obtenir': '__get',
90
- 'definir': '__set',
91
- 'appeler': '__call',
92
- 'vers chaine': '__toString',
93
- 'espace de noms': 'namespace',
94
- 'comme': 'as',
95
- 'vrai': 'true',
96
- 'faux': 'false',
97
- 'nul': 'null',
98
- 'ceci': '$this',
99
- 'parent': 'parent',
100
- 'soi': 'self',
101
- 'et': '&&',
102
- 'ou': '||',
103
- 'non': '!',
104
- 'egal': '===',
105
- 'different': '!==',
106
- 'concatener': '.',
107
- 'tableau': 'array',
108
- 'chaine': 'string',
109
- 'entier': 'int',
110
- 'flottant': 'float',
111
- 'booleen': 'bool',
112
- 'objet': 'object',
113
- 'vide': 'void',
114
- 'mixte': 'mixed',
115
- 'afficher': 'echo',
116
- 'imprimer': 'print',
117
- 'longueur': 'strlen',
118
- 'compter': 'count',
119
- 'exploser': 'explode',
120
- 'imploser': 'implode',
121
- 'remplacer': 'str_replace',
122
- 'position': 'strpos',
123
- 'sous chaine': 'substr',
124
- 'majuscules': 'strtoupper',
125
- 'minuscules': 'strtolower',
126
- 'supprimer espaces': 'trim',
127
- 'ajouter': 'array_push',
128
- 'retirer': 'array_pop',
129
- 'fusionner': 'array_merge',
130
- 'filtrer': 'array_filter',
131
- 'mapper': 'array_map',
132
- 'reduire': 'array_reduce',
133
- 'trier': 'sort',
134
- 'inverser': 'array_reverse',
135
- 'cles': 'array_keys',
136
- 'valeurs': 'array_values',
137
- 'existe': 'isset',
138
- 'vide test': 'empty',
139
- 'est tableau': 'is_array',
140
- 'est chaine': 'is_string',
141
- 'est entier': 'is_int',
142
- 'est flottant': 'is_float',
143
- 'est numerique': 'is_numeric',
144
- 'est nul': 'is_null',
145
- 'type de': 'gettype',
146
- 'convertir chaine': 'strval',
147
- 'convertir entier': 'intval',
148
- 'convertir flottant': 'floatval',
149
- 'convertir booleen': 'boolval',
150
- 'definir type': 'settype',
151
- 'date': 'date',
152
- 'temps': 'time',
153
- 'maintenant': 'now',
154
- 'fichier existe': 'file_exists',
155
- 'lire fichier': 'file_get_contents',
156
- 'ecrire fichier': 'file_put_contents',
157
- 'ouvrir': 'fopen',
158
- 'fermer': 'fclose',
159
- 'lire': 'fread',
160
- 'ecrire': 'fwrite',
161
- 'json encoder': 'json_encode',
162
- 'json decoder': 'json_decode',
163
- 'regex correspondance': 'preg_match',
164
- 'regex toutes': 'preg_match_all',
165
- 'regex remplacer': 'preg_replace',
166
- 'en tete': 'header',
167
- 'rediriger': 'header',
168
- 'session demarrer': 'session_start',
169
- 'session': '$_SESSION',
170
- 'post': '$_POST',
171
- 'get': '$_GET',
172
- 'requete': '$_REQUEST',
173
- 'serveur': '$_SERVER',
174
- 'fichiers': '$_FILES',
175
- 'cookie': '$_COOKIE',
176
- 'mourir': 'die',
177
- 'sortie': 'exit',
178
- 'inclure': 'include',
179
- 'inclure une fois': 'include_once',
180
- 'requiert': 'require',
181
- 'requiert une fois': 'require_once',
182
- 'mapper tableau': 'array_map',
183
- 'filtrer tableau': 'array_filter',
184
- 'reduire tableau': 'array_reduce',
185
- 'parcourir tableau': 'array_walk',
186
- 'parcourir recursif': 'array_walk_recursive',
187
- 'decaler tableau': 'array_shift',
188
- 'depiler tableau': 'array_pop',
189
- 'empiler tableau': 'array_push',
190
- 'prefixer tableau': 'array_unshift',
191
- 'chercher tableau': 'array_search',
192
- 'cle existe tableau': 'array_key_exists',
193
- 'dans tableau': 'in_array',
194
- 'somme tableau': 'array_sum',
195
- 'produit tableau': 'array_product',
196
- 'compter valeurs': 'array_count_values',
197
- 'aleatoire tableau': 'array_rand',
198
- 'element courant': 'current',
199
- 'element suivant': 'next',
200
- 'element precedent': 'prev',
201
- 'cle courante': 'key',
202
- 'premier element': 'reset',
203
- 'dernier element': 'end',
204
- 'remplir tableau': 'array_fill',
205
- 'remplir cles tableau': 'array_fill_keys',
206
- 'rembourrer tableau': 'array_pad',
207
- 'plage': 'range',
208
- 'fonction existe': 'function_exists',
209
- 'classe existe': 'class_exists',
210
- 'methode existe': 'method_exists',
211
- 'propriete existe': 'property_exists',
212
- 'interface existe': 'interface_exists',
213
- 'trait existe': 'trait_exists',
214
- 'obtenir classe': 'get_class',
215
- 'obtenir parent classe': 'get_parent_class',
216
- 'obtenir methodes classe': 'get_class_methods',
217
- 'obtenir variables classe': 'get_class_vars',
218
- 'obtenir variables objet': 'get_object_vars',
219
- 'est instance de': 'is_a',
220
- 'est sous-classe de': 'is_subclass_of',
221
- 'classes declarees': 'get_declared_classes',
222
- 'interfaces declarees': 'get_declared_interfaces',
223
- 'traits declares': 'get_declared_traits',
224
- 'entete http': 'header',
225
- 'entetes envoyes': 'headers_sent',
226
- 'liste entetes': 'headers_list',
227
- 'retirer entete': 'header_remove',
228
- 'code reponse http': 'http_response_code',
229
- 'analyser url': 'parse_url',
230
- 'construire requete http': 'http_build_query',
231
- 'analyser parametres': 'parse_str',
232
- 'encoder url': 'urlencode',
233
- 'decoder url': 'urldecode',
234
- 'encoder url brut': 'rawurlencode',
235
- 'decoder url brut': 'rawurldecode',
236
- 'encoder base64': 'base64_encode',
237
- 'decoder base64': 'base64_decode',
238
- 'hacher mot de passe': 'password_hash',
239
- 'verifier mot de passe': 'password_verify',
240
- 'rehachage necessaire': 'password_needs_rehash',
241
- 'info mot de passe': 'password_get_info',
242
- 'bcrypt par defaut': 'PASSWORD_DEFAULT',
243
- 'bcrypt': 'PASSWORD_BCRYPT',
244
- 'argon2i': 'PASSWORD_ARGON2I',
245
- 'argon2id': 'PASSWORD_ARGON2ID',
246
- 'appeler fonction utilisateur': 'call_user_func',
247
- 'appeler avec arguments': 'call_user_func_array',
248
- 'creer fonction': 'create_function',
249
- 'filtrer variable': 'filter_var',
250
- 'filtrer entree': 'filter_input',
251
- 'filtrer tableau entree': 'filter_input_array',
252
- 'filtrer tableau variable': 'filter_var_array',
253
- 'liste filtres': 'filter_list',
254
- 'id filtre': 'filter_id',
255
- 'valider email': 'FILTER_VALIDATE_EMAIL',
256
- 'valider url': 'FILTER_VALIDATE_URL',
257
- 'valider ip': 'FILTER_VALIDATE_IP',
258
- 'valider entier': 'FILTER_VALIDATE_INT',
259
- 'valider flottant': 'FILTER_VALIDATE_FLOAT',
260
- 'valider booleen': 'FILTER_VALIDATE_BOOLEAN',
261
- 'nettoyer chaine': 'FILTER_SANITIZE_STRING',
262
- 'nettoyer email': 'FILTER_SANITIZE_EMAIL',
263
- 'nettoyer url': 'FILTER_SANITIZE_URL',
264
- 'nettoyer entier': 'FILTER_SANITIZE_NUMBER_INT',
265
- 'nettoyer flottant': 'FILTER_SANITIZE_NUMBER_FLOAT',
266
- 'obtenir config': 'ini_get',
267
- 'definir config': 'ini_set',
268
- 'restaurer config': 'ini_restore',
269
- 'toutes configs': 'ini_get_all',
270
- 'obtenir environnement': 'getenv',
271
- 'definir environnement': 'putenv',
272
- 'version php': 'phpversion',
273
- 'info php': 'phpinfo',
274
- 'comparer versions': 'version_compare',
275
- 'extension chargee': 'extension_loaded',
276
- 'extensions chargees': 'get_loaded_extensions',
277
- 'formater chaine': 'sprintf',
278
- 'afficher formate': 'printf',
279
- 'formater vers flux': 'fprintf',
280
- 'formater avec tableau': 'vsprintf',
281
- 'afficher avec tableau': 'vprintf',
282
- 'analyser formate': 'sscanf',
283
- 'formater nombre': 'number_format',
284
- 'formater monnaie': 'money_format',
285
- 'ajouter echappements': 'addslashes',
286
- 'retirer echappements': 'stripslashes',
287
- 'ajouter echappements c': 'addcslashes',
288
- 'retirer echappements c': 'stripcslashes',
289
- 'citer meta': 'quotemeta',
290
- 'compacter': 'compact',
291
- 'extraire': 'extract',
292
- 'variables definies': 'get_defined_vars',
293
- 'enregistrer autoload': 'spl_autoload_register',
294
- 'desenregistrer autoload': 'spl_autoload_unregister',
295
- 'fonctions autoload': 'spl_autoload_functions',
296
- 'autoload': 'spl_autoload',
297
- 'extensions autoload': 'spl_autoload_extensions',
298
- 'hacher': 'hash',
299
- 'hacher fichier': 'hash_file',
300
- 'hacher hmac': 'hash_hmac',
301
- 'algorithmes hachage': 'hash_algos',
302
- 'md5': 'md5',
303
- 'md5 fichier': 'md5_file',
304
- 'sha1': 'sha1',
305
- 'sha1 fichier': 'sha1_file',
306
- 'crc32': 'crc32',
307
- 'octets aleatoires': 'random_bytes',
308
- 'entier aleatoire': 'random_int',
309
- 'traduire caracteres': 'strtr',
310
- 'sous-chaine compte': 'substr_count',
311
- 'sous-chaine remplacer': 'substr_replace',
312
- 'comparer sous-chaine': 'substr_compare',
313
- 'chercher chaine': 'strstr',
314
- 'chercher insensible': 'stristr',
315
- 'chercher caractere': 'strchr',
316
- 'chercher caractere inverse': 'strrchr',
317
- 'dormir': 'sleep',
318
- 'dormir micro': 'usleep',
319
- 'memoire utilisee': 'memory_get_usage',
320
- 'memoire pic': 'memory_get_peak_usage',
321
- 'limite memoire': "ini_get('memory_limit')",
322
- 'chemin inclus': 'get_include_path',
323
- 'definir chemin inclus': 'set_include_path'
324
- }
325
-
326
- const lower = word.toLowerCase()
327
- return translations[lower] || word
49
+ if (!word || typeof word !== 'string') return word
50
+
51
+ const normalized = word.toLowerCase().trim()
52
+
53
+ if (this.translationMap[normalized]) {
54
+ return this.translationMap[normalized]
55
+ }
56
+
57
+ const withoutAccents = this.removeAccents(normalized)
58
+ if (this.translationMap[withoutAccents]) {
59
+ return this.translationMap[withoutAccents]
60
+ }
61
+
62
+ return word
63
+ }
64
+
65
+ removeAccents(str) {
66
+ return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
328
67
  }
329
68
 
330
69
  generate(ast) {
331
- this.output = '<?php\n\n'
70
+ this.output = '<?php\n'
332
71
  this.indent = 0
72
+
73
+ if (ast.strict) {
74
+ this.writeLine("declare(strict_types=1);")
75
+ this.writeLine('')
76
+ }
333
77
 
334
- if (Array.isArray(ast)) {
335
- for (const node of ast) {
336
- this.generateNode(node)
337
- }
338
- } else if (ast && ast.type) {
339
- this.generateNode(ast)
340
- } else if (ast && ast.body) {
78
+ if (Array.isArray(ast.body)) {
341
79
  for (const node of ast.body) {
342
80
  this.generateNode(node)
343
81
  }
82
+ } else if (ast.body) {
83
+ this.generateNode(ast.body)
84
+ } else {
85
+ this.generateNode(ast)
344
86
  }
345
87
 
346
- return this.output.trim()
88
+ return this.output
347
89
  }
348
90
 
349
91
  generateNode(node) {
350
92
  if (!node) return ''
351
93
 
352
- switch (node.type) {
353
- case 'VariableDeclaration':
354
- return this.generateVariableDeclaration(node)
355
- case 'FunctionDeclaration':
356
- return this.generateFunctionDeclaration(node)
357
- case 'ClassDeclaration':
358
- return this.generateClassDeclaration(node)
359
- case 'IfStatement':
360
- return this.generateIfStatement(node)
361
- case 'ForStatement':
362
- return this.generateForStatement(node)
363
- case 'ForEachStatement':
364
- return this.generateForEachStatement(node)
365
- case 'WhileStatement':
366
- return this.generateWhileStatement(node)
367
- case 'DoWhileStatement':
368
- return this.generateDoWhileStatement(node)
369
- case 'SwitchStatement':
370
- return this.generateSwitchStatement(node)
371
- case 'TryStatement':
372
- return this.generateTryStatement(node)
373
- case 'ReturnStatement':
374
- return this.generateReturnStatement(node)
375
- case 'ThrowStatement':
376
- return this.generateThrowStatement(node)
377
- case 'BreakStatement':
378
- this.writeLine('break;')
379
- return
380
- case 'ContinueStatement':
381
- this.writeLine('continue;')
382
- return
383
- case 'ExpressionStatement':
384
- return this.generateExpressionStatement(node)
385
- case 'EchoStatement':
386
- return this.generateEchoStatement(node)
387
- case 'NamespaceDeclaration':
388
- return this.generateNamespace(node)
389
- case 'UseStatement':
390
- return this.generateUseStatement(node)
391
- case 'IncludeStatement':
392
- return this.generateIncludeStatement(node)
393
- case 'Include':
394
- return this.generateInclude(node)
395
- case 'CallExpression':
396
- return this.generateCallExpression(node)
397
- case 'MemberExpression':
398
- return this.generateMemberExpression(node)
399
- case 'BinaryExpression':
400
- return this.generateBinaryExpression(node)
401
- case 'AssignmentExpression':
402
- return this.generateAssignmentExpression(node)
403
- case 'ArrayExpression':
404
- return this.generateArrayExpression(node)
405
- case 'ObjectExpression':
406
- return this.generateObjectExpression(node)
407
- case 'Identifier':
408
- return this.generateIdentifier(node)
409
- case 'Literal':
410
- return this.generateLiteral(node)
411
- case 'NewExpression':
412
- return this.generateNewExpression(node)
413
- default:
414
- return ''
94
+ if (Array.isArray(node)) {
95
+ return node.map(n => this.generateNode(n)).join('')
96
+ }
97
+
98
+ const type = node.type || node.nodeType
99
+
100
+ const generators = {
101
+ 'Program': () => this.generateProgram(node),
102
+ 'FunctionDeclaration': () => this.generateFunctionDeclaration(node),
103
+ 'ClassDeclaration': () => this.generateClassDeclaration(node),
104
+ 'InterfaceDeclaration': () => this.generateInterfaceDeclaration(node),
105
+ 'TraitDeclaration': () => this.generateTraitDeclaration(node),
106
+ 'EnumDeclaration': () => this.generateEnumDeclaration(node),
107
+ 'MethodDefinition': () => this.generateMethodDefinition(node),
108
+ 'PropertyDeclaration': () => this.generatePropertyDeclaration(node),
109
+ 'VariableDeclaration': () => this.generateVariableDeclaration(node),
110
+ 'ConstantDeclaration': () => this.generateConstantDeclaration(node),
111
+ 'IfStatement': () => this.generateIfStatement(node),
112
+ 'ForStatement': () => this.generateForStatement(node),
113
+ 'ForEachStatement': () => this.generateForEachStatement(node),
114
+ 'ForeachStatement': () => this.generateForEachStatement(node),
115
+ 'WhileStatement': () => this.generateWhileStatement(node),
116
+ 'DoWhileStatement': () => this.generateDoWhileStatement(node),
117
+ 'SwitchStatement': () => this.generateSwitchStatement(node),
118
+ 'MatchExpression': () => this.generateMatchExpression(node),
119
+ 'TryStatement': () => this.generateTryStatement(node),
120
+ 'ReturnStatement': () => this.generateReturnStatement(node),
121
+ 'ThrowStatement': () => this.generateThrowStatement(node),
122
+ 'BreakStatement': () => this.writeLine('break;'),
123
+ 'ContinueStatement': () => this.writeLine('continue;'),
124
+ 'ExpressionStatement': () => this.generateExpressionStatement(node),
125
+ 'EchoStatement': () => this.generateEchoStatement(node),
126
+ 'PrintStatement': () => this.generatePrintStatement(node),
127
+ 'Namespace': () => this.generateNamespace(node),
128
+ 'UseStatement': () => this.generateUseStatement(node),
129
+ 'IncludeStatement': () => this.generateIncludeStatement(node),
130
+ 'Include': () => this.generateInclude(node),
131
+ 'CallExpression': () => this.generateCallExpression(node),
132
+ 'MemberExpression': () => this.generateMemberExpression(node),
133
+ 'BinaryExpression': () => this.generateBinaryExpression(node),
134
+ 'LogicalExpression': () => this.generateBinaryExpression(node),
135
+ 'UnaryExpression': () => this.generateUnaryExpression(node),
136
+ 'AssignmentExpression': () => this.generateAssignmentExpression(node),
137
+ 'UpdateExpression': () => this.generateUpdateExpression(node),
138
+ 'ConditionalExpression': () => this.generateConditionalExpression(node),
139
+ 'NullCoalescing': () => this.generateNullCoalescing(node),
140
+ 'ArrayExpression': () => this.generateArrayExpression(node),
141
+ 'ObjectExpression': () => this.generateObjectExpression(node),
142
+ 'ArrowFunction': () => this.generateArrowFunction(node),
143
+ 'Closure': () => this.generateClosure(node),
144
+ 'Identifier': () => this.generateIdentifier(node),
145
+ 'Literal': () => this.generateLiteral(node),
146
+ 'NewExpression': () => this.generateNewExpression(node),
147
+ 'InstanceOf': () => this.generateInstanceOf(node),
148
+ 'BlockStatement': () => this.generateBlockStatement(node),
149
+ 'EmptyStatement': () => '',
150
+ 'Comment': () => ''
415
151
  }
152
+
153
+ if (generators[type]) {
154
+ return generators[type]()
155
+ }
156
+
157
+ if (node.expression) {
158
+ return this.generateNode(node.expression)
159
+ }
160
+
161
+ return ''
416
162
  }
417
163
 
418
- generateVariableDeclaration(node) {
419
- for (const decl of node.declarations || [node]) {
420
- const name = this.generateIdentifier(decl.id || decl.name)
421
- if (decl.init) {
422
- const value = this.generateNode(decl.init)
423
- this.writeLine(`${name} = ${value};`)
424
- } else {
425
- this.writeLine(`${name} = null;`)
426
- }
164
+ generateProgram(node) {
165
+ for (const stmt of node.body || []) {
166
+ this.generateNode(stmt)
427
167
  }
428
168
  }
429
169
 
430
170
  generateFunctionDeclaration(node) {
431
- const name = node.id ? node.id.name || node.id : node.name
432
- const params = (node.params || []).map(p => {
433
- let param = this.generateIdentifier(p.name || p)
434
- if (p.type) param = p.type + ' ' + param
435
- if (p.default) param += ' = ' + this.generateNode(p.default)
436
- return param
171
+ const name = this.translate(node.name || node.id?.name || 'anonymous')
172
+ const params = this.generateParams(node.params || [])
173
+ const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
174
+
175
+ this.writeLine(`function ${name}(${params})${returnType} {`)
176
+ this.indent++
177
+ this.generateNode(node.body)
178
+ this.indent--
179
+ this.writeLine('}')
180
+ this.writeLine('')
181
+ }
182
+
183
+ generateParams(params) {
184
+ return params.map(p => {
185
+ let result = ''
186
+
187
+ if (p.visibility) {
188
+ result += this.translate(p.visibility) + ' '
189
+ }
190
+
191
+ if (p.type) {
192
+ result += this.translateType(p.type) + ' '
193
+ }
194
+
195
+ if (p.variadic) {
196
+ result += '...'
197
+ }
198
+
199
+ if (p.reference) {
200
+ result += '&'
201
+ }
202
+
203
+ const name = p.name || p.id?.name || p
204
+ result += '$' + this.translate(typeof name === 'string' ? name : name.name || 'param')
205
+
206
+ if (p.default !== undefined) {
207
+ result += ' = ' + this.generateNode(p.default)
208
+ }
209
+
210
+ return result
437
211
  }).join(', ')
212
+ }
213
+
214
+ translateType(type) {
215
+ if (!type) return ''
438
216
 
439
- const returnType = node.returnType ? ': ' + node.returnType : ''
440
- const visibility = node.visibility ? node.visibility + ' ' : ''
441
- const isStatic = node.static ? 'static ' : ''
217
+ if (typeof type === 'string') {
218
+ const translated = this.translate(type)
219
+ const typeMap = {
220
+ 'chaîne': 'string', 'chaine': 'string', 'string': 'string',
221
+ 'entier': 'int', 'integer': 'int', 'int': 'int',
222
+ 'flottant': 'float', 'float': 'float',
223
+ 'booléen': 'bool', 'booleen': 'bool', 'boolean': 'bool', 'bool': 'bool',
224
+ 'tableau': 'array', 'array': 'array',
225
+ 'objet': 'object', 'object': 'object',
226
+ 'nul': 'null', 'null': 'null',
227
+ 'vide': 'void', 'void': 'void',
228
+ 'mixte': 'mixed', 'mixed': 'mixed',
229
+ 'appelable': 'callable', 'callable': 'callable',
230
+ 'itérable': 'iterable', 'iterable': 'iterable',
231
+ 'jamais': 'never', 'never': 'never'
232
+ }
233
+ return typeMap[translated.toLowerCase()] || typeMap[type.toLowerCase()] || type
234
+ }
442
235
 
443
- this.writeLine(`${visibility}${isStatic}function ${name}(${params})${returnType} {`)
236
+ if (type.union) {
237
+ return type.types.map(t => this.translateType(t)).join('|')
238
+ }
239
+
240
+ if (type.intersection) {
241
+ return type.types.map(t => this.translateType(t)).join('&')
242
+ }
243
+
244
+ if (type.nullable) {
245
+ return '?' + this.translateType(type.type)
246
+ }
247
+
248
+ return type.name || type
249
+ }
250
+
251
+ generateClassDeclaration(node) {
252
+ let declaration = ''
253
+
254
+ if (node.abstract) declaration += 'abstract '
255
+ if (node.final) declaration += 'final '
256
+ if (node.readonly) declaration += 'readonly '
257
+
258
+ declaration += 'class ' + this.translate(node.name || node.id?.name)
259
+
260
+ if (node.extends) {
261
+ declaration += ' extends ' + this.translate(node.extends)
262
+ }
263
+
264
+ if (node.implements && node.implements.length > 0) {
265
+ declaration += ' implements ' + node.implements.map(i => this.translate(i)).join(', ')
266
+ }
267
+
268
+ this.writeLine(declaration + ' {')
444
269
  this.indent++
445
270
 
446
- if (node.body) {
447
- if (Array.isArray(node.body)) {
448
- for (const stmt of node.body) {
449
- this.generateNode(stmt)
450
- }
451
- } else if (node.body.body) {
452
- for (const stmt of node.body.body) {
453
- this.generateNode(stmt)
454
- }
455
- } else {
456
- this.generateNode(node.body)
457
- }
271
+ for (const member of node.body || node.members || []) {
272
+ this.generateNode(member)
458
273
  }
459
274
 
460
275
  this.indent--
@@ -462,57 +277,201 @@ class PHPGenerator {
462
277
  this.writeLine('')
463
278
  }
464
279
 
465
- generateClassDeclaration(node) {
466
- const name = node.id ? node.id.name || node.id : node.name
467
- let declaration = 'class ' + name
280
+ generateInterfaceDeclaration(node) {
281
+ let declaration = 'interface ' + this.translate(node.name || node.id?.name)
468
282
 
469
- if (node.superClass) {
470
- declaration += ' extends ' + (node.superClass.name || node.superClass)
283
+ if (node.extends && node.extends.length > 0) {
284
+ declaration += ' extends ' + node.extends.map(e => this.translate(e)).join(', ')
471
285
  }
472
- if (node.implements && node.implements.length > 0) {
473
- const interfaces = node.implements.map(i => i.name || i).join(', ')
474
- declaration += ' implements ' + interfaces
286
+
287
+ this.writeLine(declaration + ' {')
288
+ this.indent++
289
+
290
+ for (const member of node.body || node.members || []) {
291
+ this.generateNode(member)
292
+ }
293
+
294
+ this.indent--
295
+ this.writeLine('}')
296
+ this.writeLine('')
297
+ }
298
+
299
+ generateTraitDeclaration(node) {
300
+ this.writeLine('trait ' + this.translate(node.name || node.id?.name) + ' {')
301
+ this.indent++
302
+
303
+ for (const member of node.body || node.members || []) {
304
+ this.generateNode(member)
305
+ }
306
+
307
+ this.indent--
308
+ this.writeLine('}')
309
+ this.writeLine('')
310
+ }
311
+
312
+ generateEnumDeclaration(node) {
313
+ let declaration = 'enum ' + this.translate(node.name || node.id?.name)
314
+
315
+ if (node.backingType) {
316
+ declaration += ': ' + this.translateType(node.backingType)
475
317
  }
476
318
 
477
319
  this.writeLine(declaration + ' {')
478
320
  this.indent++
479
321
 
480
- if (node.body) {
481
- const body = node.body.body || node.body
482
- if (Array.isArray(body)) {
483
- for (const member of body) {
484
- this.generateNode(member)
485
- }
322
+ for (const member of node.cases || node.members || []) {
323
+ if (member.value !== undefined) {
324
+ this.writeLine(`case ${member.name} = ${this.generateNode(member.value)};`)
486
325
  } else {
487
- this.generateNode(body)
326
+ this.writeLine(`case ${member.name};`)
488
327
  }
489
328
  }
490
329
 
330
+ for (const method of node.methods || []) {
331
+ this.generateNode(method)
332
+ }
333
+
491
334
  this.indent--
492
335
  this.writeLine('}')
493
336
  this.writeLine('')
494
337
  }
495
338
 
339
+ generateMethodDefinition(node) {
340
+ let declaration = ''
341
+
342
+ const visibility = node.visibility || node.access || 'public'
343
+ declaration += this.translate(visibility) + ' '
344
+
345
+ if (node.static) declaration += 'static '
346
+ if (node.final) declaration += 'final '
347
+ if (node.abstract) declaration += 'abstract '
348
+
349
+ const name = this.translateMethodName(node.name || node.key?.name)
350
+ const params = this.generateParams(node.params || node.value?.params || [])
351
+ const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
352
+
353
+ if (node.abstract) {
354
+ this.writeLine(`${declaration}function ${name}(${params})${returnType};`)
355
+ } else {
356
+ this.writeLine(`${declaration}function ${name}(${params})${returnType} {`)
357
+ this.indent++
358
+ this.generateNode(node.body || node.value?.body)
359
+ this.indent--
360
+ this.writeLine('}')
361
+ }
362
+ this.writeLine('')
363
+ }
364
+
365
+ translateMethodName(name) {
366
+ if (!name) return 'method'
367
+
368
+ const magicMethods = {
369
+ 'constructeur': '__construct',
370
+ 'constructor': '__construct',
371
+ 'destructeur': '__destruct',
372
+ 'destructor': '__destruct',
373
+ 'obtenir': '__get',
374
+ 'get': '__get',
375
+ 'définir': '__set',
376
+ 'definir': '__set',
377
+ 'set': '__set',
378
+ 'appeler': '__call',
379
+ 'call': '__call',
380
+ 'appeler statique': '__callStatic',
381
+ 'call static': '__callStatic',
382
+ 'vers chaîne': '__toString',
383
+ 'vers chaine': '__toString',
384
+ 'to string': '__toString',
385
+ 'invoquer': '__invoke',
386
+ 'invoke': '__invoke',
387
+ 'cloner': '__clone',
388
+ 'clone': '__clone',
389
+ 'dormir': '__sleep',
390
+ 'sleep': '__sleep',
391
+ 'réveiller': '__wakeup',
392
+ 'reveiller': '__wakeup',
393
+ 'wakeup': '__wakeup',
394
+ 'sérialiser': '__serialize',
395
+ 'serialiser': '__serialize',
396
+ 'serialize': '__serialize',
397
+ 'désérialiser': '__unserialize',
398
+ 'deserialiser': '__unserialize',
399
+ 'unserialize': '__unserialize',
400
+ 'isset': '__isset',
401
+ 'existe propriété': '__isset',
402
+ 'existe propriete': '__isset',
403
+ 'unset': '__unset',
404
+ 'détruire propriété': '__unset',
405
+ 'detruire propriete': '__unset',
406
+ 'déboguer info': '__debugInfo',
407
+ 'deboguer info': '__debugInfo',
408
+ 'debug info': '__debugInfo'
409
+ }
410
+
411
+ const normalized = name.toLowerCase().trim()
412
+ return magicMethods[normalized] || this.translate(name)
413
+ }
414
+
415
+ generatePropertyDeclaration(node) {
416
+ let declaration = ''
417
+
418
+ const visibility = node.visibility || node.access || 'public'
419
+ declaration += this.translate(visibility) + ' '
420
+
421
+ if (node.static) declaration += 'static '
422
+ if (node.readonly) declaration += 'readonly '
423
+
424
+ if (node.type) {
425
+ declaration += this.translateType(node.type) + ' '
426
+ }
427
+
428
+ const name = this.translate(node.name || node.key?.name)
429
+ declaration += '$' + name
430
+
431
+ if (node.value !== undefined) {
432
+ declaration += ' = ' + this.generateNode(node.value)
433
+ }
434
+
435
+ this.writeLine(declaration + ';')
436
+ }
437
+
438
+ generateVariableDeclaration(node) {
439
+ for (const decl of node.declarations || [node]) {
440
+ const name = decl.name || decl.id?.name || decl.id
441
+ const varName = '$' + this.translate(typeof name === 'string' ? name : name.name || 'var')
442
+
443
+ if (decl.init !== undefined) {
444
+ const value = this.generateNode(decl.init)
445
+ this.writeLine(`${varName} = ${value};`)
446
+ } else {
447
+ this.writeLine(`${varName} = null;`)
448
+ }
449
+ }
450
+ }
451
+
452
+ generateConstantDeclaration(node) {
453
+ const name = node.name || node.id?.name
454
+ const value = this.generateNode(node.value || node.init)
455
+
456
+ if (node.classLevel) {
457
+ const visibility = node.visibility ? this.translate(node.visibility) + ' ' : ''
458
+ this.writeLine(`${visibility}const ${name.toUpperCase()} = ${value};`)
459
+ } else {
460
+ this.writeLine(`define('${name.toUpperCase()}', ${value});`)
461
+ }
462
+ }
463
+
496
464
  generateIfStatement(node) {
497
465
  const test = this.generateNode(node.test)
498
466
  this.writeLine(`if (${test}) {`)
499
467
  this.indent++
500
468
  this.generateNode(node.consequent)
501
469
  this.indent--
502
-
470
+
503
471
  if (node.alternate) {
504
472
  if (node.alternate.type === 'IfStatement') {
505
- this.output = this.output.trimEnd() + ' elseif '
506
- const altTest = this.generateNode(node.alternate.test)
507
- this.output += `(${altTest}) {\n`
508
- this.indent++
509
- this.generateNode(node.alternate.consequent)
510
- this.indent--
511
- if (node.alternate.alternate) {
512
- this.generateIfStatement({ alternate: node.alternate.alternate })
513
- } else {
514
- this.writeLine('}')
515
- }
473
+ this.write('} else')
474
+ this.generateIfStatement({ ...node.alternate, isElseIf: true })
516
475
  } else {
517
476
  this.writeLine('} else {')
518
477
  this.indent++
@@ -538,9 +497,9 @@ class PHPGenerator {
538
497
  }
539
498
 
540
499
  generateForEachStatement(node) {
541
- const array = this.generateNode(node.array || node.right)
542
- const value = this.generateIdentifier(node.value || node.left)
543
- const key = node.key ? this.generateIdentifier(node.key) + ' => ' : ''
500
+ const array = this.generateNode(node.array || node.right || node.iterable)
501
+ const value = this.generateIdentifier(node.value || node.left || node.valueVar)
502
+ const key = node.key || node.keyVar ? this.generateIdentifier(node.key || node.keyVar) + ' => ' : ''
544
503
 
545
504
  this.writeLine(`foreach (${array} as ${key}${value}) {`)
546
505
  this.indent++
@@ -590,18 +549,37 @@ class PHPGenerator {
590
549
  this.writeLine('}')
591
550
  }
592
551
 
552
+ generateMatchExpression(node) {
553
+ const subject = this.generateNode(node.subject || node.discriminant)
554
+ let result = `match (${subject}) {\n`
555
+
556
+ for (const arm of node.arms || node.cases || []) {
557
+ this.indent++
558
+ if (arm.conditions) {
559
+ const conditions = arm.conditions.map(c => this.generateNode(c)).join(', ')
560
+ result += this.getIndent() + `${conditions} => ${this.generateNode(arm.body)},\n`
561
+ } else if (arm.default) {
562
+ result += this.getIndent() + `default => ${this.generateNode(arm.body)},\n`
563
+ }
564
+ this.indent--
565
+ }
566
+
567
+ result += this.getIndent() + '}'
568
+ return result
569
+ }
570
+
593
571
  generateTryStatement(node) {
594
572
  this.writeLine('try {')
595
573
  this.indent++
596
574
  this.generateNode(node.block)
597
575
  this.indent--
598
576
 
599
- if (node.handler) {
600
- const exceptionType = node.handler.type || 'Exception'
601
- const param = node.handler.param ? this.generateIdentifier(node.handler.param) : '$e'
577
+ for (const handler of node.handlers || (node.handler ? [node.handler] : [])) {
578
+ const exceptionType = handler.type || handler.param?.type || 'Exception'
579
+ const param = handler.param ? this.generateIdentifier(handler.param) : '$e'
602
580
  this.writeLine(`} catch (${exceptionType} ${param}) {`)
603
581
  this.indent++
604
- this.generateNode(node.handler.body)
582
+ this.generateNode(handler.body)
605
583
  this.indent--
606
584
  }
607
585
 
@@ -635,10 +613,15 @@ class PHPGenerator {
635
613
  }
636
614
 
637
615
  generateEchoStatement(node) {
638
- const args = (node.arguments || [node.argument]).map(a => this.generateNode(a)).join(', ')
616
+ const args = (node.arguments || [node.argument]).filter(Boolean).map(a => this.generateNode(a)).join(', ')
639
617
  this.writeLine(`echo ${args};`)
640
618
  }
641
619
 
620
+ generatePrintStatement(node) {
621
+ const arg = this.generateNode(node.argument)
622
+ this.writeLine(`print ${arg};`)
623
+ }
624
+
642
625
  generateNamespace(node) {
643
626
  this.writeLine(`namespace ${node.name};`)
644
627
  this.writeLine('')
@@ -647,11 +630,14 @@ class PHPGenerator {
647
630
  generateUseStatement(node) {
648
631
  const name = node.name || node.source
649
632
  const alias = node.alias ? ' as ' + node.alias : ''
650
- this.writeLine(`use ${name}${alias};`)
633
+ const type = node.type === 'function' ? 'function ' : (node.type === 'const' ? 'const ' : '')
634
+ this.writeLine(`use ${type}${name}${alias};`)
651
635
  }
652
636
 
653
637
  generateIncludeStatement(node) {
654
- const type = node.once ? (node.required ? 'require_once' : 'include_once') : (node.required ? 'require' : 'include')
638
+ const type = node.once
639
+ ? (node.required ? 'require_once' : 'include_once')
640
+ : (node.required ? 'require' : 'include')
655
641
  const path = typeof node.path === 'string' ? `'${node.path}'` : this.generateNode(node.path)
656
642
  this.writeLine(`${type} ${path};`)
657
643
  }
@@ -661,16 +647,30 @@ class PHPGenerator {
661
647
  for (const child of node.children) {
662
648
  this.generateNode(child)
663
649
  }
664
- } else {
665
- this.writeLine(`// Include non résolu: ${node.path || 'inconnu'}`)
666
650
  }
667
651
  }
668
652
 
669
653
  generateCallExpression(node) {
670
- const callee = typeof node.callee === 'string'
671
- ? this.translate(node.callee)
672
- : this.generateNode(node.callee)
673
- const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
654
+ let callee
655
+
656
+ if (typeof node.callee === 'string') {
657
+ callee = this.translate(node.callee)
658
+ } else if (node.callee.type === 'MemberExpression') {
659
+ callee = this.generateMemberExpression(node.callee)
660
+ } else {
661
+ callee = this.generateNode(node.callee)
662
+ }
663
+
664
+ const args = (node.arguments || []).map(a => {
665
+ if (a.spread) {
666
+ return '...' + this.generateNode(a.argument || a)
667
+ }
668
+ if (a.name && a.value) {
669
+ return `${a.name}: ${this.generateNode(a.value)}`
670
+ }
671
+ return this.generateNode(a)
672
+ }).join(', ')
673
+
674
674
  return `${callee}(${args})`
675
675
  }
676
676
 
@@ -686,6 +686,9 @@ class PHPGenerator {
686
686
  if (node.computed) {
687
687
  return `${object}[${property}]`
688
688
  }
689
+ if (node.nullSafe) {
690
+ return `${object}?->${property}`
691
+ }
689
692
  return `${object}->${property}`
690
693
  }
691
694
 
@@ -695,15 +698,41 @@ class PHPGenerator {
695
698
  let op = node.operator
696
699
 
697
700
  const opMap = {
698
- 'et': '&&',
699
- 'ou': '||',
700
- 'concatener': '.'
701
+ 'et': '&&', 'and': '&&',
702
+ 'ou': '||', 'or': '||',
703
+ 'non': '!', 'not': '!',
704
+ 'égal': '===', 'egal': '===', 'equal': '===',
705
+ 'différent': '!==', 'different': '!==', 'not equal': '!==',
706
+ 'concaténer': '.', 'concatener': '.', 'concatenate': '.',
707
+ 'vaisseau spatial': '<=>', 'spaceship': '<=>',
708
+ 'fusion null': '??', 'null coalescing': '??'
701
709
  }
702
- op = opMap[op] || op
710
+
711
+ op = opMap[op?.toLowerCase()] || op
703
712
 
704
713
  return `${left} ${op} ${right}`
705
714
  }
706
715
 
716
+ generateUnaryExpression(node) {
717
+ const argument = this.generateNode(node.argument)
718
+ const op = node.operator === 'non' || node.operator === 'not' ? '!' : node.operator
719
+
720
+ if (node.prefix) {
721
+ return `${op}${argument}`
722
+ }
723
+ return `${argument}${op}`
724
+ }
725
+
726
+ generateUpdateExpression(node) {
727
+ const argument = this.generateNode(node.argument)
728
+ const op = node.operator
729
+
730
+ if (node.prefix) {
731
+ return `${op}${argument}`
732
+ }
733
+ return `${argument}${op}`
734
+ }
735
+
707
736
  generateAssignmentExpression(node) {
708
737
  const left = this.generateNode(node.left)
709
738
  const right = this.generateNode(node.right)
@@ -711,18 +740,35 @@ class PHPGenerator {
711
740
  return `${left} ${op} ${right}`
712
741
  }
713
742
 
743
+ generateConditionalExpression(node) {
744
+ const test = this.generateNode(node.test)
745
+ const consequent = this.generateNode(node.consequent)
746
+ const alternate = this.generateNode(node.alternate)
747
+ return `${test} ? ${consequent} : ${alternate}`
748
+ }
749
+
750
+ generateNullCoalescing(node) {
751
+ const left = this.generateNode(node.left)
752
+ const right = this.generateNode(node.right)
753
+ return `${left} ?? ${right}`
754
+ }
755
+
714
756
  generateArrayExpression(node) {
715
757
  const elements = (node.elements || []).map(e => {
716
- if (e.key) {
758
+ if (!e) return 'null'
759
+ if (e.key !== undefined) {
717
760
  const key = this.generateNode(e.key)
718
761
  const value = this.generateNode(e.value || e)
719
762
  return `${key} => ${value}`
720
763
  }
764
+ if (e.spread) {
765
+ return '...' + this.generateNode(e.argument || e)
766
+ }
721
767
  return this.generateNode(e)
722
768
  })
723
769
 
724
770
  if (elements.length === 0) return '[]'
725
- if (elements.length <= 3) {
771
+ if (elements.length <= 3 && !elements.some(e => e.includes('=>'))) {
726
772
  return `[${elements.join(', ')}]`
727
773
  }
728
774
  return `[\n${this.getIndent()} ${elements.join(',\n' + this.getIndent() + ' ')}\n${this.getIndent()}]`
@@ -732,26 +778,75 @@ class PHPGenerator {
732
778
  return this.generateArrayExpression(node)
733
779
  }
734
780
 
781
+ generateArrowFunction(node) {
782
+ const params = this.generateParams(node.params || [])
783
+ const body = this.generateNode(node.body)
784
+ return `fn(${params}) => ${body}`
785
+ }
786
+
787
+ generateClosure(node) {
788
+ const params = this.generateParams(node.params || [])
789
+ const useVars = node.use && node.use.length > 0
790
+ ? ` use (${node.use.map(u => (u.byRef ? '&' : '') + '$' + this.translate(u.name || u)).join(', ')})`
791
+ : ''
792
+ const returnType = node.returnType ? ': ' + this.translateType(node.returnType) : ''
793
+
794
+ let result = `function(${params})${useVars}${returnType} {\n`
795
+ this.indent++
796
+
797
+ if (Array.isArray(node.body)) {
798
+ for (const stmt of node.body) {
799
+ result += this.getIndent() + this.generateNode(stmt)
800
+ }
801
+ } else {
802
+ result += this.getIndent() + 'return ' + this.generateNode(node.body) + ';\n'
803
+ }
804
+
805
+ this.indent--
806
+ result += this.getIndent() + '}'
807
+ return result
808
+ }
809
+
735
810
  generateIdentifier(node) {
736
- const name = node.name || node
811
+ const name = node.name || node.id?.name || node
737
812
  if (typeof name !== 'string') return '$var'
738
813
 
739
814
  const translated = this.translate(name)
740
815
 
741
816
  if (translated.startsWith('$')) return translated
742
- if (['true', 'false', 'null', 'self', 'parent', 'static'].includes(translated)) {
817
+
818
+ const keywords = [
819
+ 'true', 'false', 'null',
820
+ 'self', 'parent', 'static',
821
+ 'this', '$this'
822
+ ]
823
+ if (keywords.includes(translated.toLowerCase())) {
824
+ if (translated.toLowerCase() === 'this') return '$this'
743
825
  return translated
744
826
  }
827
+
745
828
  if (/^[A-Z]/.test(translated)) return translated
746
- if (translated.includes('(')) return translated
829
+
830
+ if (translated.includes('(') || translated.includes('::')) return translated
831
+
832
+ const superglobals = ['_GET', '_POST', '_SERVER', '_SESSION', '_COOKIE', '_FILES', '_REQUEST', '_ENV', 'GLOBALS']
833
+ if (superglobals.includes(translated) || superglobals.includes(translated.toUpperCase())) {
834
+ return '$' + translated.toUpperCase()
835
+ }
747
836
 
748
837
  return '$' + translated
749
838
  }
750
839
 
751
840
  generateLiteral(node) {
752
- if (node.raw) return node.raw
841
+ if (node.raw !== undefined) return node.raw
753
842
  if (typeof node.value === 'string') {
754
- return `"${node.value.replace(/"/g, '\\"')}"`
843
+ const escaped = node.value
844
+ .replace(/\\/g, '\\\\')
845
+ .replace(/"/g, '\\"')
846
+ .replace(/\n/g, '\\n')
847
+ .replace(/\r/g, '\\r')
848
+ .replace(/\t/g, '\\t')
849
+ return `"${escaped}"`
755
850
  }
756
851
  if (node.value === null) return 'null'
757
852
  if (node.value === true) return 'true'
@@ -761,12 +856,28 @@ class PHPGenerator {
761
856
 
762
857
  generateNewExpression(node) {
763
858
  const callee = typeof node.callee === 'string'
764
- ? node.callee
859
+ ? this.translate(node.callee)
765
860
  : this.generateNode(node.callee)
766
861
  const args = (node.arguments || []).map(a => this.generateNode(a)).join(', ')
767
862
  return `new ${callee}(${args})`
768
863
  }
769
864
 
865
+ generateInstanceOf(node) {
866
+ const left = this.generateNode(node.left)
867
+ const right = this.generateNode(node.right)
868
+ return `${left} instanceof ${right}`
869
+ }
870
+
871
+ generateBlockStatement(node) {
872
+ for (const stmt of node.body || []) {
873
+ this.generateNode(stmt)
874
+ }
875
+ }
876
+
877
+ write(text) {
878
+ this.output += text
879
+ }
880
+
770
881
  writeLine(text) {
771
882
  this.output += this.getIndent() + text + '\n'
772
883
  }
@@ -776,6 +887,6 @@ class PHPGenerator {
776
887
  }
777
888
  }
778
889
 
779
- module.exports = {
780
- PHPGenerator
890
+ module.exports = {
891
+ PHPGenerator
781
892
  }