Completed
Push — master ( f42a52...0a9499 )
by Thierry
02:50 queued 01:16
created

CodeGenerator::getScript()   D

Complexity

Conditions 13
Paths 208

Size

Total Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
nc 208
nop 2
dl 0
loc 80
rs 4.983
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
use Jaxon\Utils\Template\Engine as TemplateEngine;
18
use Jaxon\Utils\Http\URI;
19
20
class CodeGenerator
21
{
22
    use \Jaxon\Features\Config;
23
    use \Jaxon\Features\Minifier;
24
25
    /**
26
     * The response type.
27
     *
28
     * @var string
29
     */
30
    const RESPONSE_TYPE = 'JSON';
31
32
    /**
33
     * The plugin manager
34
     *
35
     * @var Manager
36
     */
37
    protected $xPluginManager;
38
39
    /**
40
     * The Jaxon template engine
41
     *
42
     * @var TemplateEngine
43
     */
44
    protected $xTemplate;
45
46
    /**
47
     * Generated CSS code
48
     *
49
     * @var string|null
50
     */
51
    protected $sCssCode = null;
52
53
    /**
54
     * Generated Javascript code
55
     *
56
     * @var string|null
57
     */
58
    protected $sJsCode = null;
59
60
    /**
61
     * Generated Javascript ready script
62
     *
63
     * @var string|null
64
     */
65
    protected $sJsReady = null;
66
67
    /**
68
     * Default library URL
69
     *
70
     * @var string
71
     */
72
    protected $sJsLibraryUrl = 'https://cdn.jsdelivr.net/gh/jaxon-php/[email protected]/dist';
73
74
    /**
75
     * The constructor
76
     *
77
     * @param Manager    $xPluginManager
78
     */
79
    public function __construct(Manager $xPluginManager, TemplateEngine $xTemplate)
80
    {
81
        $this->xPluginManager = $xPluginManager;
82
        $this->xTemplate = $xTemplate;
83
    }
84
85
    /**
86
     * Get the base URI of the Jaxon library javascript files
87
     *
88
     * @return string
89
     */
90
    private function getJsLibUri()
91
    {
92
        return rtrim($this->getOption('js.lib.uri', $this->sJsLibraryUrl), '/') . '/';
93
    }
94
95
    /**
96
     * Get the extension of the Jaxon library javascript files
97
     *
98
     * The returned string is '.min.js' if the files are minified.
99
     *
100
     * @return string
101
     */
102
    private function getJsLibExt()
103
    {
104
        if(($this->getOption('js.app.minify')))
105
        {
106
            return '.min.js';
107
        }
108
        return '.js';
109
    }
110
111
    /**
112
     * Check if the javascript code generated by Jaxon can be exported to an external file
113
     *
114
     * @return boolean
115
     */
116
    public function canExportJavascript()
117
    {
118
        // Check config options
119
        // - The js.app.export option must be set to true
120
        // - The js.app.uri and js.app.dir options must be set to non null values
121
        if(!$this->getOption('js.app.export') ||
122
            !$this->getOption('js.app.uri') ||
123
            !$this->getOption('js.app.dir'))
124
        {
125
            return false;
126
        }
127
        // Check dir access
128
        // - The js.app.dir must be writable
129
        $sJsAppDir = $this->getOption('js.app.dir');
130
        if(!is_dir($sJsAppDir) || !is_writable($sJsAppDir))
131
        {
132
            return false;
133
        }
134
        return true;
135
    }
136
137
    /**
138
     * Generate a hash for all the javascript code generated by the library
139
     *
140
     * @return string
141
     */
142
    private function generateHash()
143
    {
144
        $sHash = jaxon()->getVersion();
145
        foreach($this->xPluginManager->getRequestPlugins() as $xPlugin)
146
        {
147
            $sHash .= $xPlugin->generateHash();
148
        }
149
        foreach($this->xPluginManager->getResponsePlugins() as $xPlugin)
150
        {
151
            $sHash .= $xPlugin->generateHash();
152
        }
153
        return md5($sHash);
154
    }
155
156
    /**
157
     * Get the HTML tags to include Jaxon javascript files into the page
158
     *
159
     * @return string
160
     */
161
    private function makePluginsCode()
162
    {
163
        if($this->sCssCode === null || $this->sJsCode === null || $this->sJsReady === null)
164
        {
165
            $this->sCssCode = '';
166
            $this->sJsCode = '';
167
            $this->sJsReady = '';
168
            foreach($this->xPluginManager->getResponsePlugins() as $xResponsePlugin)
169
            {
170
                if(($sCssCode = trim($xResponsePlugin->getCss())))
171
                {
172
                    $this->sCssCode .= rtrim($sCssCode, " \n") . "\n";
173
                }
174
                if(($sJsCode = trim($xResponsePlugin->getJs())))
175
                {
176
                    $this->sJsCode .= rtrim($sJsCode, " \n") . "\n";
177
                }
178 View Code Duplication
                if(($sJsReady = trim($xResponsePlugin->getScript())))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
179
                {
180
                    $this->sJsReady .= trim($sJsReady, " \n") . "\n";
181
                }
182
            }
183
184
            $this->sJsReady = $this->xTemplate->render('jaxon::plugins/ready.js', [
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->xTemplate->render...t' => $this->sJsReady)) can also be of type false. However, the property $sJsReady is declared as type string|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
185
                'sPluginScript' => $this->sJsReady,
186
            ]);
187
            foreach($this->xPluginManager->getRequestPlugins() as $xRequestPlugin)
188
            {
189 View Code Duplication
                if(($sJsReady = trim($xRequestPlugin->getScript())))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
190
                {
191
                    $this->sJsReady .= trim($sJsReady, " \n") . "\n";
192
                }
193
            }
194
195
            foreach($this->xPluginManager->getPackages() as $sPackageClass)
196
            {
197
                $xPackage = jaxon()->di()->get($sPackageClass);
198
                if(($sCssCode = trim($xPackage->css())))
199
                {
200
                    $this->sCssCode .= rtrim($sCssCode, " \n") . "\n";
201
                }
202
                if(($sJsCode = trim($xPackage->js())))
203
                {
204
                    $this->sJsCode .= rtrim($sJsCode, " \n") . "\n";
205
                }
206
                if(($sJsReady = trim($xPackage->ready())))
207
                {
208
                    $this->sJsReady .= trim($sJsReady, " \n") . "\n";
209
                }
210
            }
211
        }
212
    }
213
214
    /**
215
     * Get the HTML tags to include Jaxon javascript files into the page
216
     *
217
     * @return string
218
     */
219
    public function getJs()
220
    {
221
        $sJsLibUri = $this->getJsLibUri();
222
        $sJsLibExt = $this->getJsLibExt();
223
        $sJsCoreUrl = $sJsLibUri . 'jaxon.core' . $sJsLibExt;
224
        $sJsDebugUrl = $sJsLibUri . 'jaxon.debug' . $sJsLibExt;
225
        // $sJsVerboseUrl = $sJsLibUri . 'jaxon.verbose' . $sJsLibExt;
226
        $sJsLanguageUrl = $sJsLibUri . 'lang/jaxon.' . $this->getOption('core.language') . $sJsLibExt;
227
228
        // Add component files to the javascript file array;
229
        $aJsFiles = [$sJsCoreUrl];
230
        if($this->getOption('core.debug.on'))
231
        {
232
            $aJsFiles[] = $sJsDebugUrl;
233
            $aJsFiles[] = $sJsLanguageUrl;
234
            /*if($this->getOption('core.debug.verbose'))
235
            {
236
                $aJsFiles[] = $sJsVerboseUrl;
237
            }*/
238
        }
239
240
        // Set the template engine cache dir
241
        $this->makePluginsCode();
242
243
        return $this->xTemplate->render('jaxon::plugins/includes.js', [
244
            'sJsOptions' => $this->getOption('js.app.options', ''),
245
            'aUrls' => $aJsFiles,
246
        ]) . $this->sJsCode;
247
    }
248
249
    /**
250
     * Get the HTML tags to include Jaxon CSS code and files into the page
251
     *
252
     * @return string
253
     */
254
    public function getCss()
255
    {
256
        // Set the template engine cache dir
257
        $this->makePluginsCode();
258
259
        return $this->sCssCode;
260
    }
261
262
    /**
263
     * Get the correspondances between previous and current config options
264
     *
265
     * They are used to keep the deprecated config options working.
266
     * They will be removed when the deprecated options will lot be supported anymore.
267
     *
268
     * @return array
269
     */
270
    private function getOptionVars()
271
    {
272
        return [
273
            'sResponseType'             => self::RESPONSE_TYPE,
274
            'sVersion'                  => $this->getOption('core.version'),
275
            'sLanguage'                 => $this->getOption('core.language'),
276
            'bLanguage'                 => $this->hasOption('core.language') ? true : false,
277
            'sRequestURI'               => $this->getOption('core.request.uri'),
278
            'sDefaultMode'              => $this->getOption('core.request.mode'),
279
            'sDefaultMethod'            => $this->getOption('core.request.method'),
280
            'sCsrfMetaName'             => $this->getOption('core.request.csrf_meta'),
281
            'bDebug'                    => $this->getOption('core.debug.on'),
282
            'bVerboseDebug'             => $this->getOption('core.debug.verbose'),
283
            'sDebugOutputID'            => $this->getOption('core.debug.output_id'),
284
            'nResponseQueueSize'        => $this->getOption('js.lib.queue_size'),
285
            'sStatusMessages'           => $this->getOption('js.lib.show_status') ? 'true' : 'false',
286
            'sWaitCursor'               => $this->getOption('js.lib.show_cursor') ? 'true' : 'false',
287
            'sDefer'                    => $this->getOption('js.app.options', ''),
288
        ];
289
    }
290
291
    /**
292
     * Get the javascript code to be sent to the browser
293
     *
294
     * @return string
295
     */
296
    private function _getScript()
297
    {
298
        $aVars = $this->getOptionVars();
299
        $sYesScript = 'jaxon.ajax.response.process(command.response)';
300
        $sNoScript = 'jaxon.confirm.skip(command);jaxon.ajax.response.process(command.response)';
301
        $sConfirmScript = jaxon()->dialog()->confirm('msg', $sYesScript, $sNoScript);
302
        $aVars['sConfirmScript'] = $this->xTemplate->render('jaxon::plugins/confirm.js', [
303
            'sConfirmScript' => $sConfirmScript,
304
        ]);
305
306
        return $this->xTemplate->render('jaxon::plugins/config.js', $aVars) . "\n" . $this->sJsReady . "\n";
307
    }
308
309
    /**
310
     * Get the javascript code to be sent to the browser
311
     *
312
     * Also call each of the request plugins giving them the opportunity
313
     * to output some javascript to the page being generated.
314
     * This is called only when the page is being loaded initially.
315
     * This is not called when processing a request.
316
     *
317
     * @param boolean        $bIncludeJs            Also get the JS files
318
     * @param boolean        $bIncludeCss        Also get the CSS files
319
     *
320
     * @return string
321
     */
322
    public function getScript($bIncludeJs = false, $bIncludeCss = false)
323
    {
324
        if(!$this->getOption('core.request.uri'))
325
        {
326
            $this->setOption('core.request.uri', URI::detect());
327
        }
328
329
        // Set the template engine cache dir
330
        $this->makePluginsCode();
331
332
        $sScript = '';
333
        if(($bIncludeCss))
334
        {
335
            $sScript .= $this->getCss() . "\n";
336
        }
337
        if(($bIncludeJs))
338
        {
339
            $sScript .= $this->getJs() . "\n";
340
        }
341
342
        if($this->canExportJavascript())
343
        {
344
            $sJsAppURI = rtrim($this->getOption('js.app.uri'), '/') . '/';
345
            $sJsAppDir = rtrim($this->getOption('js.app.dir'), '/') . '/';
346
            $sFinalFile = $this->getOption('js.app.file');
347
            $sExtension = $this->getJsLibExt();
348
349
            // Check if the final file already exists
350
            if(($sFinalFile) && is_file($sJsAppDir . $sFinalFile . $sExtension))
351
            {
352
                $sOutFile = $sFinalFile . $sExtension;
353
            }
354
            else
355
            {
356
                // The plugins scripts are written into the javascript app dir
357
                $sHash = $this->generateHash();
358
                $sOutFile = $sHash . '.js';
359
                $sMinFile = $sHash . '.min.js';
360
                if(!is_file($sJsAppDir . $sOutFile))
361
                {
362
                    file_put_contents($sJsAppDir . $sOutFile, $this->_getScript());
363
                }
364
                if(($this->getOption('js.app.minify')))
365
                {
366
                    if(is_file($sJsAppDir . $sMinFile))
367
                    {
368
                        $sOutFile = $sMinFile; // The file was already minified
369
                    }
370
                    elseif(($this->minify($sJsAppDir . $sOutFile, $sJsAppDir . $sMinFile)))
371
                    {
372
                        $sOutFile = $sMinFile;
373
                    }
374
                }
375
                // Copy the file to its final location
376
                if(($sFinalFile))
377
                {
378
                    if(copy($sJsAppDir . $sOutFile, $sJsAppDir . $sFinalFile . $sExtension))
379
                    {
380
                        $sOutFile = $sFinalFile . $sExtension;
381
                    }
382
                }
383
            }
384
385
            // The returned code loads the generated javascript file
386
            $sScript .= $this->xTemplate->render('jaxon::plugins/include.js', [
387
                'sJsOptions' => $this->getOption('js.app.options', ''),
388
                'sUrl' => $sJsAppURI . $sOutFile,
389
            ]);
390
        }
391
        else
392
        {
393
            // The plugins scripts are wrapped with javascript tags
394
            $sScript .= $this->xTemplate->render('jaxon::plugins/wrapper.js', [
395
                'sJsOptions' => $this->getOption('js.app.options', ''),
396
                'sScript' => $this->_getScript(),
397
            ]);
398
        }
399
400
        return $sScript;
401
    }
402
}
403