Completed
Push — master ( 71f3ca...10d1fd )
by Thierry
01:44
created

Generator::canExportJavascript()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 3
nop 0
dl 0
loc 20
rs 8.9777
c 0
b 0
f 0
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\Code;
16
17
use Jaxon\Plugin\Manager;
18
19
class Generator
20
{
21
    use \Jaxon\Utils\Traits\Config;
22
    use \Jaxon\Utils\Traits\Cache;
23
    use \Jaxon\Utils\Traits\Minifier;
24
    use \Jaxon\Utils\Traits\Template;
25
26
    /**
27
     * The response type.
28
     *
29
     * @var string
30
     */
31
    const RESPONSE_TYPE = 'JSON';
32
33
    /**
34
     * The plugin manager
35
     *
36
     * @var Jaxon\Plugin\Manager
37
     */
38
    protected $xPluginManager;
39
40
    /**
41
     * Generated CSS code
42
     *
43
     * @var string|null
44
     */
45
    protected $sCssCode = null;
46
47
    /**
48
     * Generated Javascript code
49
     *
50
     * @var string|null
51
     */
52
    protected $sJsCode = null;
53
54
    /**
55
     * Generated Javascript ready script
56
     *
57
     * @var string|null
58
     */
59
    protected $sJsReady = null;
60
61
    /**
62
     * The constructor
63
     *
64
     * @param   Jaxon\Plugin\Manager    $xPluginManager
65
     */
66
    public function __construct(Manager $xPluginManager)
67
    {
68
        $this->xPluginManager = $xPluginManager;
0 ignored issues
show
Documentation Bug introduced by
It seems like $xPluginManager of type object<Jaxon\Plugin\Manager> is incompatible with the declared type object<Jaxon\Code\Jaxon\Plugin\Manager> of property $xPluginManager.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
69
    }
70
71
    /**
72
     * Get the base URI of the Jaxon library javascript files
73
     *
74
     * @return string
75
     */
76
    private function getJsLibUri()
77
    {
78
        if(!$this->hasOption('js.lib.uri'))
79
        {
80
            // return 'https://cdn.jsdelivr.net/jaxon/1.2.0/';
81
            return 'https://cdn.jsdelivr.net/gh/jaxon-php/[email protected]/dist/';
82
        }
83
        // Todo: check the validity of the URI
84
        return rtrim($this->getOption('js.lib.uri'), '/') . '/';
85
    }
86
87
    /**
88
     * Get the extension of the Jaxon library javascript files
89
     *
90
     * The returned string is '.min.js' if the files are minified.
91
     *
92
     * @return string
93
     */
94
    private function getJsLibExt()
95
    {
96
        // $jsDelivrUri = 'https://cdn.jsdelivr.net';
97
        // $nLen = strlen($jsDelivrUri);
98
        // The jsDelivr CDN only hosts minified files
99
        // if(($this->getOption('js.app.minify')) || substr($this->getJsLibUri(), 0, $nLen) == $jsDelivrUri)
100
        // Starting from version 2.0.0 of the js lib, the jsDelivr CDN also hosts non minified files.
101
        if(($this->getOption('js.app.minify')))
102
        {
103
            return '.min.js';
104
        }
105
        return '.js';
106
    }
107
108
    /**
109
     * Check if the javascript code generated by Jaxon can be exported to an external file
110
     *
111
     * @return boolean
112
     */
113
    public function canExportJavascript()
114
    {
115
        // Check config options
116
        // - The js.app.extern option must be set to true
117
        // - The js.app.uri and js.app.dir options must be set to non null values
118
        if(!$this->getOption('js.app.extern') ||
119
            !$this->getOption('js.app.uri') ||
120
            !$this->getOption('js.app.dir'))
121
        {
122
            return false;
123
        }
124
        // Check dir access
125
        // - The js.app.dir must be writable
126
        $sJsAppDir = $this->getOption('js.app.dir');
127
        if(!is_dir($sJsAppDir) || !is_writable($sJsAppDir))
128
        {
129
            return false;
130
        }
131
        return true;
132
    }
133
134
    /**
135
     * Set the cache directory for the template engine
136
     *
137
     * @return void
138
     */
139
    private function setTemplateCacheDir()
140
    {
141
        if($this->hasOption('core.template.cache_dir'))
142
        {
143
            $this->setCacheDir($this->getOption('core.template.cache_dir'));
144
        }
145
    }
146
147
    /**
148
     * Generate a hash for all the javascript code generated by the library
149
     *
150
     * @return string
151
     */
152
    private function generateHash()
153
    {
154
        $sHash = jaxon()->getVersion();
155
        foreach($this->xPluginManager->getRequestPlugins() as $xPlugin)
156
        {
157
            $sHash .= $xPlugin->generateHash();
158
        }
159
        foreach($this->xPluginManager->getResponsePlugins() as $xPlugin)
160
        {
161
            $sHash .= $xPlugin->generateHash();
162
        }
163
        return md5($sHash);
164
    }
165
166
    /**
167
     * Get the HTML tags to include Jaxon javascript files into the page
168
     *
169
     * @return string
170
     */
171
    private function makePluginsCode()
172
    {
173
        if($this->sCssCode === null || $this->sJsCode === null || $this->sJsReady === null)
174
        {
175
            $this->sCssCode = '';
176
            $this->sJsCode = '';
177
            $this->sJsReady = '';
178
            foreach($this->xPluginManager->getResponsePlugins() as $xPlugin)
179
            {
180
                if(($str = trim($xPlugin->getCss())))
181
                {
182
                    $this->sCssCode .= rtrim($str, " \n") . "\n";
183
                }
184
                if(($str = trim($xPlugin->getJs())))
185
                {
186
                    $this->sJsCode .= rtrim($str, " \n") . "\n";
187
                }
188 View Code Duplication
                if(($str = trim($xPlugin->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...
189
                {
190
                    $this->sJsReady .= "\n" . trim($str, " \n");
191
                }
192
            }
193
194
            $this->sJsReady = $this->render('jaxon::plugins/ready.js', ['sPluginScript' => $this->sJsReady]);
195
            foreach($this->xPluginManager->getRequestPlugins() as $xPlugin)
196
            {
197 View Code Duplication
                if(($str = trim($xPlugin->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...
198
                {
199
                    $this->sJsReady .= "\n" . trim($str, " \n");
200
                }
201
            }
202
203
            foreach($this->xPluginManager->getPackages() as $sClass)
204
            {
205
                $xPackage = jaxon()->di()->get($sClass);
206
                if(($str = trim($xPackage->css())))
207
                {
208
                    $this->sCssCode .= rtrim($str, " \n") . "\n";
209
                }
210
                if(($str = trim($xPackage->js())))
211
                {
212
                    $this->sJsCode .= rtrim($str, " \n") . "\n";
213
                }
214
                $xPackage = jaxon()->di()->get($sClass);
215
                if(($str = trim($xPackage->ready())))
216
                {
217
                    $this->sJsReady .= "\n" . trim($str, " \n");
218
                }
219
            }
220
        }
221
    }
222
223
    /**
224
     * Get the HTML tags to include Jaxon javascript files into the page
225
     *
226
     * @return string
227
     */
228
    public function getJs()
229
    {
230
        $sJsLibUri = $this->getJsLibUri();
231
        $sJsLibExt = $this->getJsLibExt();
232
        $sJsCoreUrl = $sJsLibUri . 'jaxon.core' . $sJsLibExt;
233
        $sJsDebugUrl = $sJsLibUri . 'jaxon.debug' . $sJsLibExt;
234
        // $sJsVerboseUrl = $sJsLibUri . 'jaxon.verbose' . $sJsLibExt;
235
        $sJsLanguageUrl = $sJsLibUri . 'lang/jaxon.' . $this->getOption('core.language') . $sJsLibExt;
236
237
        // Add component files to the javascript file array;
238
        $aJsFiles = array($sJsCoreUrl);
239
        if($this->getOption('core.debug.on'))
240
        {
241
            $aJsFiles[] = $sJsDebugUrl;
242
            $aJsFiles[] = $sJsLanguageUrl;
243
            /*if($this->getOption('core.debug.verbose'))
244
            {
245
                $aJsFiles[] = $sJsVerboseUrl;
246
            }*/
247
        }
248
249
        // Set the template engine cache dir
250
        $this->setTemplateCacheDir();
251
        $this->makePluginsCode();
252
253
        return $this->render('jaxon::plugins/includes.js', [
254
            'sJsOptions' => $this->getOption('js.app.options'),
255
            'aUrls' => $aJsFiles,
256
        ]) . $this->sJsCode;
257
    }
258
259
    /**
260
     * Get the HTML tags to include Jaxon CSS code and files into the page
261
     *
262
     * @return string
263
     */
264
    public function getCss()
265
    {
266
        // Set the template engine cache dir
267
        $this->setTemplateCacheDir();
268
        $this->makePluginsCode();
269
270
        return $this->sCssCode;
271
    }
272
273
    /**
274
     * Get the correspondances between previous and current config options
275
     *
276
     * They are used to keep the deprecated config options working.
277
     * They will be removed when the deprecated options will lot be supported anymore.
278
     *
279
     * @return array
280
     */
281
    private function getOptionVars()
282
    {
283
        return [
284
            'sResponseType'             => self::RESPONSE_TYPE,
285
            'sVersion'                  => $this->getOption('core.version'),
286
            'sLanguage'                 => $this->getOption('core.language'),
287
            'bLanguage'                 => $this->hasOption('core.language') ? true : false,
288
            'sRequestURI'               => $this->getOption('core.request.uri'),
289
            'sDefaultMode'              => $this->getOption('core.request.mode'),
290
            'sDefaultMethod'            => $this->getOption('core.request.method'),
291
            'sCsrfMetaName'             => $this->getOption('core.request.csrf_meta'),
292
            'bDebug'                    => $this->getOption('core.debug.on'),
293
            'bVerboseDebug'             => $this->getOption('core.debug.verbose'),
294
            'sDebugOutputID'            => $this->getOption('core.debug.output_id'),
295
            'nResponseQueueSize'        => $this->getOption('js.lib.queue_size'),
296
            'sStatusMessages'           => $this->getOption('js.lib.show_status') ? 'true' : 'false',
297
            'sWaitCursor'               => $this->getOption('js.lib.show_cursor') ? 'true' : 'false',
298
            'sDefer'                    => $this->getOption('js.app.options'),
299
        ];
300
    }
301
302
    /**
303
     * Get the javascript code to be sent to the browser
304
     *
305
     * @return string
306
     */
307
    private function _getScript()
308
    {
309
        $aVars = $this->getOptionVars();
310
        $sYesScript = 'jaxon.ajax.response.process(command.response)';
311
        $sNoScript = 'jaxon.confirm.skip(command);jaxon.ajax.response.process(command.response)';
312
        $sConfirmScript = jaxon()->dialog()->confirm('msg', $sYesScript, $sNoScript);
313
        $aVars['sConfirmScript'] = $this->render('jaxon::plugins/confirm.js', ['sConfirmScript' => $sConfirmScript]);
314
315
        return $this->render('jaxon::plugins/config.js', $aVars) . "\n" . $this->sJsReady . "\n";
316
    }
317
318
    /**
319
     * Get the javascript code to be sent to the browser
320
     *
321
     * Also call each of the request plugins giving them the opportunity
322
     * to output some javascript to the page being generated.
323
     * This is called only when the page is being loaded initially.
324
     * This is not called when processing a request.
325
     *
326
     * @return string
327
     */
328
    public function getScript()
329
    {
330
        // Set the template engine cache dir
331
        $this->setTemplateCacheDir();
332
        $this->makePluginsCode();
333
334
        if($this->canExportJavascript())
335
        {
336
            $sJsAppURI = rtrim($this->getOption('js.app.uri'), '/') . '/';
337
            $sJsAppDir = rtrim($this->getOption('js.app.dir'), '/') . '/';
338
339
            // The plugins scripts are written into the javascript app dir
340
            $sHash = $this->generateHash();
341
            $sOutFile = $sHash . '.js';
342
            $sMinFile = $sHash . '.min.js';
343
            if(!is_file($sJsAppDir . $sOutFile))
344
            {
345
                file_put_contents($sJsAppDir . $sOutFile, $this->_getScript());
346
            }
347
            if(($this->getOption('js.app.minify')) && !is_file($sJsAppDir . $sMinFile))
348
            {
349
                if(($this->minify($sJsAppDir . $sOutFile, $sJsAppDir . $sMinFile)))
350
                {
351
                    $sOutFile = $sMinFile;
352
                }
353
            }
354
355
            // The returned code loads the generated javascript file
356
            $sScript = $this->render('jaxon::plugins/include.js', array(
357
                'sJsOptions' => $this->getOption('js.app.options'),
358
                'sUrl' => $sJsAppURI . $sOutFile,
359
            ));
360
        }
361
        else
362
        {
363
            // The plugins scripts are wrapped with javascript tags
364
            $sScript = $this->render('jaxon::plugins/wrapper.js', array(
365
                'sJsOptions' => $this->getOption('js.app.options'),
366
                'sScript' => $this->_getScript(),
367
            ));
368
        }
369
370
        return $sScript;
371
    }
372
}
373