1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Generator.php - Jaxon code generator |
5
|
|
|
* |
6
|
|
|
* Generate HTML, CSS and Javascript code for Jaxon. |
7
|
|
|
* |
8
|
|
|
* @package jaxon-core |
9
|
|
|
* @author Thierry Feuzeu <[email protected]> |
10
|
|
|
* @copyright 2016 Thierry Feuzeu <[email protected]> |
11
|
|
|
* @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License |
12
|
|
|
* @link https://github.com/jaxon-php/jaxon-core |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
namespace Jaxon\Plugin; |
16
|
|
|
|
17
|
|
|
class CodeGenerator |
18
|
|
|
{ |
19
|
|
|
use \Jaxon\Utils\Traits\Config; |
20
|
|
|
use \Jaxon\Utils\Traits\Cache; |
21
|
|
|
use \Jaxon\Utils\Traits\Minifier; |
22
|
|
|
use \Jaxon\Utils\Traits\Template; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* The response type. |
26
|
|
|
* |
27
|
|
|
* @var string |
28
|
|
|
*/ |
29
|
|
|
const RESPONSE_TYPE = 'JSON'; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* The plugin manager |
33
|
|
|
* |
34
|
|
|
* @var Manager |
35
|
|
|
*/ |
36
|
|
|
protected $xPluginManager; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Generated CSS code |
40
|
|
|
* |
41
|
|
|
* @var string|null |
42
|
|
|
*/ |
43
|
|
|
protected $sCssCode = null; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Generated Javascript code |
47
|
|
|
* |
48
|
|
|
* @var string|null |
49
|
|
|
*/ |
50
|
|
|
protected $sJsCode = null; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Generated Javascript ready script |
54
|
|
|
* |
55
|
|
|
* @var string|null |
56
|
|
|
*/ |
57
|
|
|
protected $sJsReady = null; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* The constructor |
61
|
|
|
* |
62
|
|
|
* @param Manager $xPluginManager |
63
|
|
|
*/ |
64
|
|
|
public function __construct(Manager $xPluginManager) |
65
|
|
|
{ |
66
|
|
|
$this->xPluginManager = $xPluginManager; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Get the base URI of the Jaxon library javascript files |
71
|
|
|
* |
72
|
|
|
* @return string |
73
|
|
|
*/ |
74
|
|
|
private function getJsLibUri() |
75
|
|
|
{ |
76
|
|
|
if(!$this->hasOption('js.lib.uri')) |
77
|
|
|
{ |
78
|
|
|
// return 'https://cdn.jsdelivr.net/jaxon/1.2.0/'; |
79
|
|
|
return 'https://cdn.jsdelivr.net/gh/jaxon-php/[email protected]/dist/'; |
80
|
|
|
} |
81
|
|
|
// Todo: check the validity of the URI |
82
|
|
|
return rtrim($this->getOption('js.lib.uri'), '/') . '/'; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Get the extension of the Jaxon library javascript files |
87
|
|
|
* |
88
|
|
|
* The returned string is '.min.js' if the files are minified. |
89
|
|
|
* |
90
|
|
|
* @return string |
91
|
|
|
*/ |
92
|
|
|
private function getJsLibExt() |
93
|
|
|
{ |
94
|
|
|
// $jsDelivrUri = 'https://cdn.jsdelivr.net'; |
95
|
|
|
// $nLen = strlen($jsDelivrUri); |
96
|
|
|
// The jsDelivr CDN only hosts minified files |
97
|
|
|
// if(($this->getOption('js.app.minify')) || substr($this->getJsLibUri(), 0, $nLen) == $jsDelivrUri) |
98
|
|
|
// Starting from version 2.0.0 of the js lib, the jsDelivr CDN also hosts non minified files. |
99
|
|
|
if(($this->getOption('js.app.minify'))) |
100
|
|
|
{ |
101
|
|
|
return '.min.js'; |
102
|
|
|
} |
103
|
|
|
return '.js'; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Check if the javascript code generated by Jaxon can be exported to an external file |
108
|
|
|
* |
109
|
|
|
* @return boolean |
110
|
|
|
*/ |
111
|
|
|
public function canExportJavascript() |
112
|
|
|
{ |
113
|
|
|
// Check config options |
114
|
|
|
// - The js.app.extern option must be set to true |
115
|
|
|
// - The js.app.uri and js.app.dir options must be set to non null values |
116
|
|
|
if(!$this->getOption('js.app.extern') || |
117
|
|
|
!$this->getOption('js.app.uri') || |
118
|
|
|
!$this->getOption('js.app.dir')) |
119
|
|
|
{ |
120
|
|
|
return false; |
121
|
|
|
} |
122
|
|
|
// Check dir access |
123
|
|
|
// - The js.app.dir must be writable |
124
|
|
|
$sJsAppDir = $this->getOption('js.app.dir'); |
125
|
|
|
if(!is_dir($sJsAppDir) || !is_writable($sJsAppDir)) |
126
|
|
|
{ |
127
|
|
|
return false; |
128
|
|
|
} |
129
|
|
|
return true; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Set the cache directory for the template engine |
134
|
|
|
* |
135
|
|
|
* @return void |
136
|
|
|
*/ |
137
|
|
|
private function setTemplateCacheDir() |
138
|
|
|
{ |
139
|
|
|
if($this->hasOption('core.template.cache_dir')) |
140
|
|
|
{ |
141
|
|
|
$this->setCacheDir($this->getOption('core.template.cache_dir')); |
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Generate a hash for all the javascript code generated by the library |
147
|
|
|
* |
148
|
|
|
* @return string |
149
|
|
|
*/ |
150
|
|
|
private function generateHash() |
151
|
|
|
{ |
152
|
|
|
$sHash = jaxon()->getVersion(); |
153
|
|
|
foreach($this->xPluginManager->getRequestPlugins() as $xPlugin) |
154
|
|
|
{ |
155
|
|
|
$sHash .= $xPlugin->generateHash(); |
156
|
|
|
} |
157
|
|
|
foreach($this->xPluginManager->getResponsePlugins() as $xPlugin) |
158
|
|
|
{ |
159
|
|
|
$sHash .= $xPlugin->generateHash(); |
160
|
|
|
} |
161
|
|
|
return md5($sHash); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Get the HTML tags to include Jaxon javascript files into the page |
166
|
|
|
* |
167
|
|
|
* @return string |
168
|
|
|
*/ |
169
|
|
|
private function makePluginsCode() |
170
|
|
|
{ |
171
|
|
|
if($this->sCssCode === null || $this->sJsCode === null || $this->sJsReady === null) |
172
|
|
|
{ |
173
|
|
|
$this->sCssCode = ''; |
174
|
|
|
$this->sJsCode = ''; |
175
|
|
|
$this->sJsReady = ''; |
176
|
|
|
foreach($this->xPluginManager->getResponsePlugins() as $xPlugin) |
177
|
|
|
{ |
178
|
|
|
if(($sCssCode = trim($xPlugin->getCss()))) |
179
|
|
|
{ |
180
|
|
|
$this->sCssCode .= rtrim($sCssCode, " \n") . "\n"; |
181
|
|
|
} |
182
|
|
|
if(($sJsCode = trim($xPlugin->getJs()))) |
183
|
|
|
{ |
184
|
|
|
$this->sJsCode .= rtrim($sJsCode, " \n") . "\n"; |
185
|
|
|
} |
186
|
|
View Code Duplication |
if(($sJsReady = trim($xPlugin->getScript()))) |
|
|
|
|
187
|
|
|
{ |
188
|
|
|
$this->sJsReady .= trim($sJsReady, " \n") . "\n"; |
189
|
|
|
} |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
$this->sJsReady = $this->render('jaxon::plugins/ready.js', ['sPluginScript' => $this->sJsReady]); |
193
|
|
|
foreach($this->xPluginManager->getRequestPlugins() as $xPlugin) |
194
|
|
|
{ |
195
|
|
View Code Duplication |
if(($sJsReady = trim($xPlugin->getScript()))) |
|
|
|
|
196
|
|
|
{ |
197
|
|
|
$this->sJsReady .= trim($sJsReady, " \n") . "\n"; |
198
|
|
|
} |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
foreach($this->xPluginManager->getPackages() as $sClass) |
202
|
|
|
{ |
203
|
|
|
$xPackage = jaxon()->di()->get($sClass); |
204
|
|
|
if(($sCssCode = trim($xPackage->css()))) |
205
|
|
|
{ |
206
|
|
|
$this->sCssCode .= rtrim($sCssCode, " \n") . "\n"; |
207
|
|
|
} |
208
|
|
|
if(($sJsCode = trim($xPackage->js()))) |
209
|
|
|
{ |
210
|
|
|
$this->sJsCode .= rtrim($sJsCode, " \n") . "\n"; |
211
|
|
|
} |
212
|
|
|
$xPackage = jaxon()->di()->get($sClass); |
213
|
|
|
if(($sJsReady = trim($xPackage->ready()))) |
214
|
|
|
{ |
215
|
|
|
$this->sJsReady .= trim($sJsReady, " \n") . "\n"; |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Get the HTML tags to include Jaxon javascript files into the page |
223
|
|
|
* |
224
|
|
|
* @return string |
225
|
|
|
*/ |
226
|
|
|
public function getJs() |
227
|
|
|
{ |
228
|
|
|
$sJsLibUri = $this->getJsLibUri(); |
229
|
|
|
$sJsLibExt = $this->getJsLibExt(); |
230
|
|
|
$sJsCoreUrl = $sJsLibUri . 'jaxon.core' . $sJsLibExt; |
231
|
|
|
$sJsDebugUrl = $sJsLibUri . 'jaxon.debug' . $sJsLibExt; |
232
|
|
|
// $sJsVerboseUrl = $sJsLibUri . 'jaxon.verbose' . $sJsLibExt; |
233
|
|
|
$sJsLanguageUrl = $sJsLibUri . 'lang/jaxon.' . $this->getOption('core.language') . $sJsLibExt; |
234
|
|
|
|
235
|
|
|
// Add component files to the javascript file array; |
236
|
|
|
$aJsFiles = array($sJsCoreUrl); |
237
|
|
|
if($this->getOption('core.debug.on')) |
238
|
|
|
{ |
239
|
|
|
$aJsFiles[] = $sJsDebugUrl; |
240
|
|
|
$aJsFiles[] = $sJsLanguageUrl; |
241
|
|
|
/*if($this->getOption('core.debug.verbose')) |
242
|
|
|
{ |
243
|
|
|
$aJsFiles[] = $sJsVerboseUrl; |
244
|
|
|
}*/ |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
// Set the template engine cache dir |
248
|
|
|
$this->setTemplateCacheDir(); |
249
|
|
|
$this->makePluginsCode(); |
250
|
|
|
|
251
|
|
|
return $this->render('jaxon::plugins/includes.js', [ |
252
|
|
|
'sJsOptions' => $this->getOption('js.app.options'), |
253
|
|
|
'aUrls' => $aJsFiles, |
254
|
|
|
]) . $this->sJsCode; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Get the HTML tags to include Jaxon CSS code and files into the page |
259
|
|
|
* |
260
|
|
|
* @return string |
261
|
|
|
*/ |
262
|
|
|
public function getCss() |
263
|
|
|
{ |
264
|
|
|
// Set the template engine cache dir |
265
|
|
|
$this->setTemplateCacheDir(); |
266
|
|
|
$this->makePluginsCode(); |
267
|
|
|
|
268
|
|
|
return $this->sCssCode; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Get the correspondances between previous and current config options |
273
|
|
|
* |
274
|
|
|
* They are used to keep the deprecated config options working. |
275
|
|
|
* They will be removed when the deprecated options will lot be supported anymore. |
276
|
|
|
* |
277
|
|
|
* @return array |
278
|
|
|
*/ |
279
|
|
|
private function getOptionVars() |
280
|
|
|
{ |
281
|
|
|
return [ |
282
|
|
|
'sResponseType' => self::RESPONSE_TYPE, |
283
|
|
|
'sVersion' => $this->getOption('core.version'), |
284
|
|
|
'sLanguage' => $this->getOption('core.language'), |
285
|
|
|
'bLanguage' => $this->hasOption('core.language') ? true : false, |
286
|
|
|
'sRequestURI' => $this->getOption('core.request.uri'), |
287
|
|
|
'sDefaultMode' => $this->getOption('core.request.mode'), |
288
|
|
|
'sDefaultMethod' => $this->getOption('core.request.method'), |
289
|
|
|
'sCsrfMetaName' => $this->getOption('core.request.csrf_meta'), |
290
|
|
|
'bDebug' => $this->getOption('core.debug.on'), |
291
|
|
|
'bVerboseDebug' => $this->getOption('core.debug.verbose'), |
292
|
|
|
'sDebugOutputID' => $this->getOption('core.debug.output_id'), |
293
|
|
|
'nResponseQueueSize' => $this->getOption('js.lib.queue_size'), |
294
|
|
|
'sStatusMessages' => $this->getOption('js.lib.show_status') ? 'true' : 'false', |
295
|
|
|
'sWaitCursor' => $this->getOption('js.lib.show_cursor') ? 'true' : 'false', |
296
|
|
|
'sDefer' => $this->getOption('js.app.options'), |
297
|
|
|
]; |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* Get the javascript code to be sent to the browser |
302
|
|
|
* |
303
|
|
|
* @return string |
304
|
|
|
*/ |
305
|
|
|
private function _getScript() |
306
|
|
|
{ |
307
|
|
|
$aVars = $this->getOptionVars(); |
308
|
|
|
$sYesScript = 'jaxon.ajax.response.process(command.response)'; |
309
|
|
|
$sNoScript = 'jaxon.confirm.skip(command);jaxon.ajax.response.process(command.response)'; |
310
|
|
|
$sConfirmScript = jaxon()->dialog()->confirm('msg', $sYesScript, $sNoScript); |
311
|
|
|
$aVars['sConfirmScript'] = $this->render('jaxon::plugins/confirm.js', ['sConfirmScript' => $sConfirmScript]); |
312
|
|
|
|
313
|
|
|
return $this->render('jaxon::plugins/config.js', $aVars) . "\n" . $this->sJsReady . "\n"; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* Get the javascript code to be sent to the browser |
318
|
|
|
* |
319
|
|
|
* Also call each of the request plugins giving them the opportunity |
320
|
|
|
* to output some javascript to the page being generated. |
321
|
|
|
* This is called only when the page is being loaded initially. |
322
|
|
|
* This is not called when processing a request. |
323
|
|
|
* |
324
|
|
|
* @return string |
325
|
|
|
*/ |
326
|
|
|
public function getScript() |
327
|
|
|
{ |
328
|
|
|
// Set the template engine cache dir |
329
|
|
|
$this->setTemplateCacheDir(); |
330
|
|
|
$this->makePluginsCode(); |
331
|
|
|
|
332
|
|
|
if($this->canExportJavascript()) |
333
|
|
|
{ |
334
|
|
|
$sJsAppURI = rtrim($this->getOption('js.app.uri'), '/') . '/'; |
335
|
|
|
$sJsAppDir = rtrim($this->getOption('js.app.dir'), '/') . '/'; |
336
|
|
|
|
337
|
|
|
// The plugins scripts are written into the javascript app dir |
338
|
|
|
$sHash = $this->generateHash(); |
339
|
|
|
$sOutFile = $sHash . '.js'; |
340
|
|
|
$sMinFile = $sHash . '.min.js'; |
341
|
|
|
if(!is_file($sJsAppDir . $sOutFile)) |
342
|
|
|
{ |
343
|
|
|
file_put_contents($sJsAppDir . $sOutFile, $this->_getScript()); |
344
|
|
|
} |
345
|
|
|
if(($this->getOption('js.app.minify')) && !is_file($sJsAppDir . $sMinFile)) |
346
|
|
|
{ |
347
|
|
|
if(($this->minify($sJsAppDir . $sOutFile, $sJsAppDir . $sMinFile))) |
348
|
|
|
{ |
349
|
|
|
$sOutFile = $sMinFile; |
350
|
|
|
} |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
// The returned code loads the generated javascript file |
354
|
|
|
$sScript = $this->render('jaxon::plugins/include.js', array( |
355
|
|
|
'sJsOptions' => $this->getOption('js.app.options'), |
356
|
|
|
'sUrl' => $sJsAppURI . $sOutFile, |
357
|
|
|
)); |
358
|
|
|
} |
359
|
|
|
else |
360
|
|
|
{ |
361
|
|
|
// The plugins scripts are wrapped with javascript tags |
362
|
|
|
$sScript = $this->render('jaxon::plugins/wrapper.js', array( |
363
|
|
|
'sJsOptions' => $this->getOption('js.app.options'), |
364
|
|
|
'sScript' => $this->_getScript(), |
365
|
|
|
)); |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
return $sScript; |
369
|
|
|
} |
370
|
|
|
} |
371
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.